458 lines
14 KiB
Markdown
458 lines
14 KiB
Markdown
# ADR-008: Versioning Strategy
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
StarPunk is an IndieWeb CMS currently in active development, working toward its first production release. We need a comprehensive versioning strategy that:
|
|
|
|
1. **Communicates clearly** what type of changes each release contains
|
|
2. **Works with Python ecosystem** tools (pip, uv, PyPI compatibility)
|
|
3. **Aligns with IndieWeb values** of simplicity and sustainability
|
|
4. **Supports the project lifecycle** from development through maintenance
|
|
5. **Enables dependency management** for users who may build on StarPunk
|
|
6. **Provides predictability** for users about what to expect in updates
|
|
|
|
### Current State
|
|
|
|
The project currently uses informal version terminology:
|
|
- "V1" for the overall first release goal
|
|
- "Phase 1.1", "Phase 1.2" for development milestones
|
|
- "v1.1", "v2.0" for future iteration references
|
|
|
|
This works for internal planning but lacks the precision needed for:
|
|
- Public releases
|
|
- Dependency management
|
|
- Communicating breaking changes
|
|
- Git tagging and release management
|
|
|
|
### Requirements
|
|
|
|
1. **Version number format** that indicates change severity
|
|
2. **Python ecosystem compliance** (PEP 440)
|
|
3. **Git workflow integration** (tagging, branching)
|
|
4. **Changelog format** for human-readable history
|
|
5. **Pre-release versioning** for alphas/betas if needed
|
|
6. **Upgrade communication** strategy
|
|
7. **Simplicity** appropriate for an indie project
|
|
|
|
## Decision
|
|
|
|
We will adopt **Semantic Versioning 2.0.0 (SemVer)** with **Python PEP 440 compliance** for all StarPunk releases.
|
|
|
|
### Version Number Format
|
|
|
|
**Structure**: `MAJOR.MINOR.PATCH[-PRERELEASE]`
|
|
|
|
**Examples**:
|
|
- `0.1.0` - Development version (Phase 1.1 complete)
|
|
- `0.2.0` - Development version (Phase 1.2 complete)
|
|
- `1.0.0` - First stable release (all V1 features complete)
|
|
- `1.0.1` - Bug fix release
|
|
- `1.1.0` - Feature release (backward compatible)
|
|
- `2.0.0` - Major release (breaking changes)
|
|
- `1.0.0a1` - Alpha pre-release (PEP 440 format)
|
|
- `1.0.0b2` - Beta pre-release (PEP 440 format)
|
|
- `1.0.0rc1` - Release candidate (PEP 440 format)
|
|
|
|
### Version Component Rules
|
|
|
|
**MAJOR version** - Increment when making incompatible changes:
|
|
- Breaking API changes
|
|
- Database schema changes requiring migration
|
|
- Configuration file format changes requiring user intervention
|
|
- Removal of deprecated features
|
|
- Major architectural changes
|
|
|
|
**MINOR version** - Increment when adding functionality in backward-compatible manner:
|
|
- New features
|
|
- New API endpoints
|
|
- Non-breaking enhancements
|
|
- Optional new configuration parameters
|
|
- Significant performance improvements
|
|
|
|
**PATCH version** - Increment for backward-compatible bug fixes:
|
|
- Bug fixes
|
|
- Security patches
|
|
- Documentation corrections
|
|
- Minor performance improvements
|
|
- Dependency updates (without feature changes)
|
|
|
|
### Development Phase (0.x.y)
|
|
|
|
During development (pre-1.0), we use `0.MINOR.PATCH`:
|
|
- MINOR increments for phase completions (Phase 1.1 → 0.1.0, Phase 1.2 → 0.2.0)
|
|
- PATCH increments for bug fixes during development
|
|
- Breaking changes are allowed without major version increment
|
|
- Public API is not considered stable
|
|
|
|
### Git Tagging Convention
|
|
|
|
**Format**: `vMAJOR.MINOR.PATCH[-PRERELEASE]`
|
|
|
|
**Examples**:
|
|
- `v0.1.0` - Development version tag
|
|
- `v1.0.0` - Stable release tag
|
|
- `v1.0.1` - Bug fix release tag
|
|
- `v1.0.0-alpha.1` - Alpha pre-release tag (Git format)
|
|
|
|
**Tag type**: Annotated tags (not lightweight)
|
|
- Contains tagger, date, message
|
|
- Can include release notes
|
|
- Can be GPG signed
|
|
|
|
### Version Storage
|
|
|
|
**Primary source of truth**: `starpunk/__init__.py`
|
|
```python
|
|
__version__ = "1.0.0"
|
|
__version_info__ = (1, 0, 0)
|
|
```
|
|
|
|
**Secondary locations**:
|
|
- `pyproject.toml` - Package metadata (if used)
|
|
- Git tags - Release markers
|
|
- `CHANGELOG.md` - Human-readable history
|
|
|
|
**Synchronization**: Manual for V1 (simple, no automation dependencies)
|
|
|
|
### Changelog Format
|
|
|
|
**File**: `CHANGELOG.md`
|
|
|
|
**Format**: Based on [Keep a Changelog](https://keepachangelog.com/)
|
|
|
|
**Categories**:
|
|
- Added - New features
|
|
- Changed - Changes to existing functionality
|
|
- Deprecated - Features that will be removed
|
|
- Removed - Features that have been removed
|
|
- Fixed - Bug fixes
|
|
- Security - Security vulnerability fixes
|
|
|
|
**Example**:
|
|
```markdown
|
|
## [1.0.0] - 2024-11-18
|
|
|
|
### Added
|
|
- IndieAuth authentication via IndieLogin
|
|
- Micropub endpoint for publishing
|
|
- RSS feed generation
|
|
- File-based note storage
|
|
|
|
### Security
|
|
- Implemented path traversal protection
|
|
- Added CSRF protection for authentication flows
|
|
```
|
|
|
|
### Pre-Release Versioning
|
|
|
|
**Format**: PEP 440 compliant
|
|
- Alpha: `1.0.0a1`, `1.0.0a2`
|
|
- Beta: `1.0.0b1`, `1.0.0b2`
|
|
- Release Candidate: `1.0.0rc1`, `1.0.0rc2`
|
|
|
|
**Git tags use hyphen**: `v1.0.0-alpha.1` (for readability)
|
|
**Python `__version__` uses PEP 440**: `1.0.0a1` (for pip compatibility)
|
|
|
|
### Phase-to-Version Mapping
|
|
|
|
**Implementation phases** (internal planning) map to **version numbers** (public releases):
|
|
|
|
```
|
|
Phase 1.1 complete → Version 0.1.0
|
|
Phase 1.2 complete → Version 0.2.0
|
|
Phase 1.3 complete → Version 0.3.0
|
|
Phase 2.1 complete → Version 0.4.0
|
|
All V1 complete → Version 1.0.0
|
|
V1.1 features → Version 1.1.0
|
|
V2 features → Version 2.0.0
|
|
```
|
|
|
|
**Clarification**:
|
|
- "V1" refers to feature scope, not version number
|
|
- Version 1.0.0 implements the "V1 feature set"
|
|
- Phases are development milestones, versions are public releases
|
|
|
|
## Rationale
|
|
|
|
### Why Semantic Versioning?
|
|
|
|
1. **Industry Standard**: Used by Flask, Django, Requests, and most Python packages
|
|
2. **Clear Communication**: Version number immediately conveys impact of changes
|
|
3. **Predictable**: Users know what to expect from each version increment
|
|
4. **Dependency Management**: Works seamlessly with pip version specifiers
|
|
5. **Simple**: Easy to understand and apply without complex rules
|
|
|
|
### Why Not Calendar Versioning (CalVer)?
|
|
|
|
CalVer (e.g., `2024.11.18`) was considered but rejected:
|
|
|
|
**Pros**:
|
|
- Shows when software was released
|
|
- No ambiguity about version order
|
|
|
|
**Cons**:
|
|
- Doesn't communicate impact of changes (patch vs breaking change)
|
|
- Less common in Python ecosystem
|
|
- Doesn't help users assess upgrade risk
|
|
- Overkill for indie project release cadence
|
|
|
|
**Conclusion**: SemVer's semantic meaning is more valuable than date information
|
|
|
|
### Why Not ZeroVer (0.x forever)?
|
|
|
|
Some projects stay at 0.x.y indefinitely to signal "still evolving". Rejected because:
|
|
|
|
- Creates uncertainty about production readiness
|
|
- Version 1.0.0 signals "ready for production use"
|
|
- We have a clear 1.0 feature scope (V1)
|
|
- Users deserve clarity about stability
|
|
|
|
### Why PEP 440 Compliance?
|
|
|
|
**PEP 440** is Python's version identification standard:
|
|
|
|
- Required for PyPI publication (if we ever publish)
|
|
- Compatible with pip, uv, and all Python package managers
|
|
- Slightly different pre-release format than SemVer (e.g., `1.0.0a1` vs `1.0.0-alpha.1`)
|
|
- Used by all major Python frameworks
|
|
|
|
**Decision**: Use PEP 440 format in Python code, SemVer-style in Git tags (Git tags are more flexible)
|
|
|
|
### Why Manual Version Management (V1)?
|
|
|
|
Considered automation tools:
|
|
- `bump2version` - Automates version bumping
|
|
- `python-semantic-release` - Determines version from commit messages
|
|
- `setuptools_scm` - Derives version from Git tags
|
|
|
|
**Decision**: Manual for V1 because:
|
|
1. Simple - no extra dependencies
|
|
2. Full control - explicit about versions
|
|
3. Aligns with indie philosophy - minimal tooling
|
|
4. Can add automation later if needed
|
|
|
|
### Why Annotated Tags?
|
|
|
|
**Annotated tags** vs lightweight tags:
|
|
|
|
Annotated tags:
|
|
- Contain metadata (tagger, date, message)
|
|
- Can include release notes
|
|
- Can be GPG signed
|
|
- Treated as full objects in Git
|
|
|
|
**Decision**: Always use annotated tags for releases
|
|
|
|
### Why CHANGELOG.md?
|
|
|
|
**Changelog** provides human-readable release history:
|
|
- Users can quickly see what changed
|
|
- Easier to read than Git commits
|
|
- Standard location (`CHANGELOG.md`)
|
|
- Standard format (Keep a Changelog)
|
|
- Can be generated from commits or written manually
|
|
|
|
**Decision**: Maintain manually for V1 (precise control over messaging)
|
|
|
|
## Consequences
|
|
|
|
### Positive
|
|
|
|
1. **Clear Communication**: Users know exactly what each version means
|
|
2. **Ecosystem Compatibility**: Works with all Python tools
|
|
3. **Predictable Upgrades**: Users can assess risk before upgrading
|
|
4. **Professional Image**: Signals mature, well-maintained software
|
|
5. **Dependency Management**: Other projects can depend on StarPunk versions
|
|
6. **Git Integration**: Clean tagging and release workflow
|
|
7. **Flexible**: Can add automation later without changing format
|
|
8. **Standards-Based**: Uses established, documented standards
|
|
|
|
### Negative
|
|
|
|
1. **Manual Effort**: Requires discipline to update version in multiple places
|
|
2. **Coordination**: Must remember to update version, changelog, and tag
|
|
3. **Breaking Change Discipline**: Must carefully evaluate what constitutes breaking change
|
|
4. **Learning Curve**: Contributors need to understand SemVer rules
|
|
|
|
### Neutral
|
|
|
|
1. **0.x Signals Development**: May discourage early adopters who want "1.0" stability
|
|
2. **Commitment to Backward Compatibility**: Once at 1.0, breaking changes require major version
|
|
3. **Changelog Maintenance**: Ongoing effort to document changes
|
|
|
|
### Mitigations
|
|
|
|
**For manual effort**:
|
|
- Document clear release process in `versioning-strategy.md`
|
|
- Create release checklist
|
|
- Consider automation in V2+ if needed
|
|
|
|
**For breaking change discipline**:
|
|
- Deprecate features one version before removal when possible
|
|
- Document breaking changes prominently
|
|
- Provide upgrade guides for major versions
|
|
|
|
**For 0.x concerns**:
|
|
- Clearly communicate that 0.x is pre-production
|
|
- Move to 1.0.0 once V1 features are complete and tested
|
|
- Don't stay at 0.x longer than necessary
|
|
|
|
## Implementation
|
|
|
|
### Immediate Actions
|
|
|
|
1. **Set current version**: `0.1.0` (Phase 1.1 development)
|
|
2. **Create `starpunk/__init__.py`**:
|
|
```python
|
|
__version__ = "0.1.0"
|
|
__version_info__ = (0, 1, 0)
|
|
```
|
|
3. **Create `CHANGELOG.md`** with `[Unreleased]` section
|
|
4. **Update README.md** with version information
|
|
5. **Tag current state**: `git tag -a v0.1.0 -m "Development version 0.1.0"`
|
|
|
|
### Release Workflow
|
|
|
|
When ready to release:
|
|
|
|
1. Update `starpunk/__init__.py` with new version
|
|
2. Update `CHANGELOG.md` with release date
|
|
3. Commit: `git commit -m "Bump version to X.Y.Z"`
|
|
4. Tag: `git tag -a vX.Y.Z -m "Release X.Y.Z: [description]"`
|
|
5. Push: `git push origin main vX.Y.Z`
|
|
|
|
### Documentation
|
|
|
|
- Create comprehensive `docs/standards/versioning-strategy.md`
|
|
- Document examples, decision tree, and FAQ
|
|
- Include upgrade guide template
|
|
- Reference in README.md
|
|
|
|
### Future Enhancements
|
|
|
|
**V2+ Considerations**:
|
|
- Add `bump2version` for automation if manual process becomes tedious
|
|
- Consider API versioning if need to support multiple incompatible API versions
|
|
- Add database schema versioning if migrations become complex
|
|
- Automated changelog generation from commit messages
|
|
|
|
## Alternatives Considered
|
|
|
|
### Alternative 1: Calendar Versioning (CalVer)
|
|
|
|
**Format**: `YYYY.MM.PATCH` (e.g., `2024.11.0`)
|
|
|
|
**Pros**:
|
|
- Shows release date
|
|
- Clear chronological order
|
|
- Used by Ubuntu, PyCharm
|
|
|
|
**Cons**:
|
|
- Doesn't communicate change impact
|
|
- Less common in Python web frameworks
|
|
- Doesn't indicate breaking changes
|
|
- Overkill for indie project cadence
|
|
|
|
**Rejected**: Semantic meaning more important than date
|
|
|
|
### Alternative 2: Simple Integer Versioning
|
|
|
|
**Format**: `1`, `2`, `3` (e.g., like TeX: 3.14159...)
|
|
|
|
**Pros**:
|
|
- Extremely simple
|
|
- No ambiguity
|
|
|
|
**Cons**:
|
|
- No information about change severity
|
|
- Doesn't work with Python dependency tools
|
|
- Not standard in Python ecosystem
|
|
- Too minimalist for practical use
|
|
|
|
**Rejected**: Too simplistic, poor ecosystem fit
|
|
|
|
### Alternative 3: Modified SemVer (Django-style)
|
|
|
|
**Format**: `MAJOR.FEATURE.PATCH` (e.g., Django's `4.2.7`)
|
|
|
|
**Pros**:
|
|
- Used by Django
|
|
- Separates features from bug fixes
|
|
|
|
**Cons**:
|
|
- Non-standard SemVer interpretation
|
|
- Confusing: when to increment feature vs major?
|
|
- Doesn't clearly indicate breaking changes
|
|
- Less predictable
|
|
|
|
**Rejected**: Standard SemVer is clearer
|
|
|
|
### Alternative 4: ZeroVer (0.x forever)
|
|
|
|
**Format**: Stay at `0.x.y` indefinitely
|
|
|
|
**Pros**:
|
|
- Signals "always evolving"
|
|
- No commitment to stability
|
|
|
|
**Cons**:
|
|
- Users can't tell when production-ready
|
|
- Doesn't signal stability improvements
|
|
- Version 1.0.0 has meaning: "ready to use"
|
|
|
|
**Rejected**: We have clear 1.0 goals, should signal when achieved
|
|
|
|
### Alternative 5: Hybrid SemVer + CalVer
|
|
|
|
**Format**: `YYYY.MINOR.PATCH` (e.g., `2024.1.0`)
|
|
|
|
**Pros**:
|
|
- Combines date and semantic information
|
|
|
|
**Cons**:
|
|
- Unusual, confusing
|
|
- Not standard
|
|
- Year increments don't mean anything semantically
|
|
|
|
**Rejected**: Combines worst of both approaches
|
|
|
|
## References
|
|
|
|
### Standards
|
|
|
|
- [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) - Official SemVer specification
|
|
- [PEP 440 - Version Identification](https://peps.python.org/pep-0440/) - Python version numbering standard
|
|
- [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Changelog format standard
|
|
- [Calendar Versioning](https://calver.org/) - CalVer specification (considered but not adopted)
|
|
|
|
### Examples from Python Ecosystem
|
|
|
|
- [Flask Versioning](https://github.com/pallets/flask/releases) - Uses SemVer
|
|
- [Django Release Process](https://docs.djangoproject.com/en/stable/internals/release-process/) - Modified SemVer
|
|
- [Requests Versioning](https://github.com/psf/requests/releases) - Uses SemVer
|
|
- [FastAPI Versioning](https://github.com/tiangolo/fastapi/releases) - Uses SemVer
|
|
|
|
### Tools
|
|
|
|
- [bump2version](https://github.com/c4urself/bump2version) - Version bump automation
|
|
- [python-semantic-release](https://python-semantic-release.readthedocs.io/) - Automated semantic releases
|
|
- [setuptools_scm](https://github.com/pypa/setuptools_scm) - SCM-based versioning
|
|
|
|
### Internal Documentation
|
|
|
|
- [Versioning Strategy](/home/phil/Projects/starpunk/docs/standards/versioning-strategy.md) - Complete versioning specification
|
|
- [Architecture Overview](/home/phil/Projects/starpunk/docs/architecture/overview.md) - System architecture
|
|
- [Development Setup](/home/phil/Projects/starpunk/docs/standards/development-setup.md) - Development workflow
|
|
|
|
---
|
|
|
|
**ADR**: 008
|
|
**Date**: 2024-11-18
|
|
**Status**: Accepted
|
|
**Decision**: Adopt Semantic Versioning 2.0.0 with PEP 440 compliance
|
|
**Supersedes**: None
|