-
Notifications
You must be signed in to change notification settings - Fork 6.5k
fix: Split release process to sync pyproject.toml version with git tags #1732
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b1423aa
fix: split release process to sync pyproject.toml version with git taβ¦
mnriem f16853d
Update .github/workflows/RELEASE-PROCESS.md
mnriem e435ab8
Update .github/workflows/scripts/simulate-release.sh
mnriem d2aac8d
Update .github/workflows/release.yml
mnriem cc2754c
Update .github/workflows/release-trigger.yml
mnriem 88e50a1
fix: harden release-trigger against shell injection and fix stale docs
mnriem File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| # Release Process | ||
|
|
||
| This document describes the automated release process for Spec Kit. | ||
|
|
||
| ## Overview | ||
|
|
||
| The release process is split into two workflows to ensure version consistency: | ||
|
|
||
| 1. **Release Trigger Workflow** (`release-trigger.yml`) - Manages versioning and triggers release | ||
| 2. **Release Workflow** (`release.yml`) - Builds and publishes artifacts | ||
|
|
||
| This separation ensures that git tags always point to commits with the correct version in `pyproject.toml`. | ||
|
|
||
| ## Before Creating a Release | ||
|
|
||
| **Important**: Write clear, descriptive commit messages! | ||
|
|
||
| ### How CHANGELOG.md Works | ||
|
|
||
| The CHANGELOG is **automatically generated** from your git commit messages: | ||
|
|
||
| 1. **During Development**: Write clear, descriptive commit messages: | ||
| ```bash | ||
| git commit -m "feat: Add new authentication feature" | ||
| git commit -m "fix: Resolve timeout issue in API client (#123)" | ||
| git commit -m "docs: Update installation instructions" | ||
| ``` | ||
|
|
||
| 2. **When Releasing**: The release trigger workflow automatically: | ||
| - Finds all commits since the last release tag | ||
| - Formats them as changelog entries | ||
| - Inserts them into CHANGELOG.md | ||
| - Commits the updated changelog before creating the new tag | ||
|
|
||
| ### Commit Message Best Practices | ||
|
|
||
| Good commit messages make good changelogs: | ||
| - **Be descriptive**: "Add user authentication" not "Update files" | ||
| - **Reference issues/PRs**: Include `(#123)` for automated linking | ||
| - **Use conventional commits** (optional): `feat:`, `fix:`, `docs:`, `chore:` | ||
| - **Keep it concise**: One line is ideal, details go in commit body | ||
|
|
||
| **Example commits that become good changelog entries:** | ||
| ``` | ||
| fix: prepend YAML frontmatter to Cursor .mdc files (#1699) | ||
| feat: add generic agent support with customizable command directories (#1639) | ||
| docs: document dual-catalog system for extensions (#1689) | ||
| ``` | ||
|
|
||
| ## Creating a Release | ||
|
|
||
| ### Option 1: Auto-Increment (Recommended for patches) | ||
|
|
||
| 1. Go to **Actions** β **Release Trigger** | ||
| 2. Click **Run workflow** | ||
| 3. Leave the version field **empty** | ||
| 4. Click **Run workflow** | ||
|
|
||
| The workflow will: | ||
| - Auto-increment the patch version (e.g., `0.1.10` β `0.1.11`) | ||
| - Update `pyproject.toml` | ||
| - Update `CHANGELOG.md` by adding a new section for the release based on commits since the last tag | ||
| - Commit changes | ||
| - Create and push git tag | ||
| - Trigger the release workflow automatically | ||
|
|
||
| ### Option 2: Manual Version (For major/minor bumps) | ||
|
|
||
| 1. Go to **Actions** β **Release Trigger** | ||
| 2. Click **Run workflow** | ||
| 3. Enter the desired version (e.g., `0.2.0` or `v0.2.0`) | ||
| 4. Click **Run workflow** | ||
|
|
||
| The workflow will: | ||
| - Use your specified version | ||
| - Update `pyproject.toml` | ||
| - Update `CHANGELOG.md` by adding a new section for the release based on commits since the last tag | ||
| - Commit changes | ||
| - Create and push git tag | ||
| - Trigger the release workflow automatically | ||
|
|
||
| ## What Happens Next | ||
|
|
||
| Once the release trigger workflow completes: | ||
|
|
||
| 1. The git tag is pushed to GitHub | ||
| 2. The **Release Workflow** is automatically triggered | ||
| 3. Release artifacts are built for all supported agents | ||
| 4. A GitHub Release is created with all assets | ||
| 5. Release notes are generated from PR titles | ||
|
|
||
| ## Workflow Details | ||
|
|
||
| ### Release Trigger Workflow | ||
|
|
||
| **File**: `.github/workflows/release-trigger.yml` | ||
|
|
||
| **Trigger**: Manual (`workflow_dispatch`) | ||
|
|
||
| **Permissions Required**: `contents: write` | ||
|
|
||
| **Steps**: | ||
| 1. Checkout repository | ||
| 2. Determine version (manual or auto-increment) | ||
| 3. Check if tag already exists (prevents duplicates) | ||
| 4. Update `pyproject.toml` | ||
| 5. Update `CHANGELOG.md` | ||
| 6. Commit changes | ||
| 7. Create and push tag | ||
|
|
||
| ### Release Workflow | ||
|
|
||
| **File**: `.github/workflows/release.yml` | ||
|
|
||
| **Trigger**: Tag push (`v*`) | ||
|
|
||
| **Permissions Required**: `contents: write` | ||
|
|
||
| **Steps**: | ||
| 1. Checkout repository at tag | ||
| 2. Extract version from tag name | ||
| 3. Check if release already exists | ||
| 4. Build release package variants (all agents Γ shell/powershell) | ||
| 5. Generate release notes from commits | ||
| 6. Create GitHub Release with all assets | ||
|
|
||
| ## Version Constraints | ||
|
|
||
| - Tags must follow format: `v{MAJOR}.{MINOR}.{PATCH}` | ||
| - Example valid versions: `v0.1.11`, `v0.2.0`, `v1.0.0` | ||
| - Auto-increment only bumps patch version | ||
| - Cannot create duplicate tags (workflow will fail) | ||
|
|
||
| ## Benefits of This Approach | ||
|
|
||
| β **Version Consistency**: Git tags point to commits with matching `pyproject.toml` version | ||
|
|
||
| β **Single Source of Truth**: Version set once, used everywhere | ||
|
|
||
| β **Prevents Drift**: No more manual version synchronization needed | ||
|
|
||
| β **Clean Separation**: Versioning logic separate from artifact building | ||
|
|
||
| β **Flexibility**: Supports both auto-increment and manual versioning | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### No Commits Since Last Release | ||
|
|
||
| If you run the release trigger workflow when there are no new commits since the last tag: | ||
| - The workflow will still succeed | ||
| - The CHANGELOG will show "- Initial release" if it's the first release | ||
| - Or it will be empty if there are no commits | ||
| - Consider adding meaningful commits before releasing | ||
|
|
||
| **Best Practice**: Use descriptive commit messages - they become your changelog! | ||
|
|
||
| ### Tag Already Exists | ||
|
|
||
| If you see "Error: Tag vX.Y.Z already exists!", you need to: | ||
| - Choose a different version number, or | ||
| - Delete the existing tag if it was created in error | ||
|
|
||
| ### Release Workflow Didn't Trigger | ||
|
|
||
| Check that: | ||
| - The release trigger workflow completed successfully | ||
| - The tag was pushed (check repository tags) | ||
| - The release workflow is enabled in Actions settings | ||
|
|
||
| ### Version Mismatch | ||
|
|
||
| If `pyproject.toml` doesn't match the latest tag: | ||
| - Run the release trigger workflow to sync versions | ||
| - Or manually update `pyproject.toml` and push changes before running the release trigger | ||
|
|
||
| ## Legacy Behavior (Pre-v0.1.10) | ||
|
|
||
| Before this change, the release workflow: | ||
| - Created tags automatically on main branch pushes | ||
| - Updated `pyproject.toml` AFTER creating the tag | ||
| - Resulted in tags pointing to commits with outdated versions | ||
|
|
||
| This has been fixed in v0.1.10+. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| name: Release Trigger | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| version: | ||
| description: 'Version to release (e.g., 0.1.11). Leave empty to auto-increment patch version.' | ||
| required: false | ||
| type: string | ||
|
|
||
| jobs: | ||
| bump-version: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Configure Git | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
|
|
||
| - name: Determine version | ||
| id: version | ||
| env: | ||
| INPUT_VERSION: ${{ github.event.inputs.version }} | ||
| run: | | ||
| if [[ -n "$INPUT_VERSION" ]]; then | ||
| # Manual version specified - strip optional v prefix | ||
| VERSION="${INPUT_VERSION#v}" | ||
| # Validate strict semver format to prevent injection | ||
| if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | ||
| echo "Error: Invalid version format '$VERSION'. Must be X.Y.Z (e.g. 1.2.3 or v1.2.3)" | ||
| exit 1 | ||
| fi | ||
| echo "version=$VERSION" >> $GITHUB_OUTPUT | ||
| echo "tag=v$VERSION" >> $GITHUB_OUTPUT | ||
| echo "Using manual version: $VERSION" | ||
| else | ||
| # Auto-increment patch version | ||
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") | ||
| echo "Latest tag: $LATEST_TAG" | ||
|
|
||
| # Extract version number and increment | ||
| VERSION=$(echo $LATEST_TAG | sed 's/v//') | ||
| IFS='.' read -ra VERSION_PARTS <<< "$VERSION" | ||
| MAJOR=${VERSION_PARTS[0]:-0} | ||
| MINOR=${VERSION_PARTS[1]:-0} | ||
| PATCH=${VERSION_PARTS[2]:-0} | ||
|
|
||
| # Increment patch version | ||
| PATCH=$((PATCH + 1)) | ||
| NEW_VERSION="$MAJOR.$MINOR.$PATCH" | ||
|
|
||
| echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT | ||
| echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT | ||
| echo "Auto-incremented version: $NEW_VERSION" | ||
| fi | ||
|
|
||
| - name: Check if tag already exists | ||
| run: | | ||
| if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then | ||
| echo "Error: Tag ${{ steps.version.outputs.tag }} already exists!" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Update pyproject.toml | ||
| run: | | ||
| sed -i "s/version = \".*\"/version = \"${{ steps.version.outputs.version }}\"/" pyproject.toml | ||
| echo "Updated pyproject.toml to version ${{ steps.version.outputs.version }}" | ||
|
|
||
| - name: Update CHANGELOG.md | ||
| run: | | ||
| if [ -f "CHANGELOG.md" ]; then | ||
| DATE=$(date +%Y-%m-%d) | ||
|
|
||
| # Get the previous tag to compare commits | ||
| PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | ||
|
|
||
| echo "Generating changelog from commits..." | ||
| if [[ -n "$PREVIOUS_TAG" ]]; then | ||
| echo "Changes since $PREVIOUS_TAG" | ||
|
|
||
| # Get commits since last tag, format as bullet points | ||
| # Extract PR numbers and format nicely | ||
| COMMITS=$(git log --oneline "$PREVIOUS_TAG"..HEAD --no-merges --pretty=format:"- %s" 2>/dev/null || echo "- Initial release") | ||
| else | ||
| echo "No previous tag found - this is the first release" | ||
| COMMITS="- Initial release" | ||
| fi | ||
|
|
||
| # Create new changelog entry | ||
| { | ||
| head -n 8 CHANGELOG.md | ||
| echo "" | ||
| echo "## [${{ steps.version.outputs.version }}] - $DATE" | ||
| echo "" | ||
| echo "### Changed" | ||
| echo "" | ||
| echo "$COMMITS" | ||
| echo "" | ||
| tail -n +9 CHANGELOG.md | ||
| } > CHANGELOG.md.tmp | ||
| mv CHANGELOG.md.tmp CHANGELOG.md | ||
mnriem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| echo "β Updated CHANGELOG.md with commits since $PREVIOUS_TAG" | ||
| else | ||
| echo "No CHANGELOG.md found" | ||
| fi | ||
|
|
||
| - name: Commit version bump | ||
| run: | | ||
| if [ -f "CHANGELOG.md" ]; then | ||
| git add pyproject.toml CHANGELOG.md | ||
| else | ||
| git add pyproject.toml | ||
| fi | ||
|
|
||
| if git diff --cached --quiet; then | ||
| echo "No changes to commit" | ||
| else | ||
| git commit -m "chore: bump version to ${{ steps.version.outputs.version }}" | ||
| echo "Changes committed" | ||
| fi | ||
| - name: Create and push tag | ||
| run: | | ||
| git tag -a "${{ steps.version.outputs.tag }}" -m "Release ${{ steps.version.outputs.tag }}" | ||
| git push origin main | ||
| git push origin "${{ steps.version.outputs.tag }}" | ||
mnriem marked this conversation as resolved.
Show resolved
Hide resolved
mnriem marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| echo "Tag ${{ steps.version.outputs.tag }} created and pushed" | ||
|
|
||
| - name: Summary | ||
| run: | | ||
| echo "β Version bumped to ${{ steps.version.outputs.version }}" | ||
| echo "β Tag ${{ steps.version.outputs.tag }} created and pushed" | ||
| echo "π Release workflow will now build artifacts automatically" | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.