diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c3674af..f45b307 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,15 +43,31 @@ jobs: echo "Commits since last release:" echo "$commits" + # Release ONLY when a commit subject since the last tag carries a release-worthy + # conventional prefix. docs/chore/ci/refactor/test/style/build/perf land WITHOUT + # cutting a release. A mixed push (docs + a real fix) still releases: the scan + # matches any release-worthy commit in the range regardless of the others. + # [skip ci] (guarded on the job) remains an optional belt-and-suspenders override. bump="patch" - if echo "$commits" | grep -qiE "^(feat|feature)(\(.+\))?!:|BREAKING CHANGE"; then - bump="major" + release="false" + if [ -z "$last_tag" ]; then + # Initial release: no prior tag -> cut the first release at the current VERSION. + release="true" + elif echo "$commits" | grep -qiE "^(feat|feature|fix)(\(.+\))?!:|BREAKING[ -]CHANGE"; then + bump="major"; release="true" elif echo "$commits" | grep -qiE "^(feat|feature)(\(.+\))?:"; then - bump="minor" + bump="minor"; release="true" + elif echo "$commits" | grep -qiE "^fix(\(.+\))?:"; then + bump="patch"; release="true" fi echo "bump=$bump" >> "$GITHUB_OUTPUT" - echo "Bump type: $bump" + echo "release=$release" >> "$GITHUB_OUTPUT" + if [ "$release" = "true" ]; then + echo "Release decision: RELEASE (bump=$bump)" + else + echo "Release decision: SKIP - no feat:/fix:/feat!: commit since ${last_tag:-}; docs/chore/ci do not cut a release" + fi - name: Compute new version id: new @@ -92,14 +108,14 @@ jobs: fi - name: Update VERSION file - if: steps.check.outputs.skip == 'false' + if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' env: NEW_VERSION: ${{ steps.new.outputs.version }} run: | echo "$NEW_VERSION" > VERSION - name: Sync release docs - if: steps.check.outputs.skip == 'false' + if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' uses: TMHSDigital/Developer-Tools-Directory/.github/actions/release-doc-sync@v1 with: plugin-version: ${{ steps.new.outputs.version }} @@ -107,7 +123,7 @@ jobs: meta-repo-ref: v1.15.1 - name: Commit version bump - if: steps.check.outputs.skip == 'false' + if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" @@ -120,7 +136,7 @@ jobs: fi - name: Create and push tag - if: steps.check.outputs.skip == 'false' + if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' run: | new_version="${{ steps.new.outputs.version }}" IFS='.' read -r major minor _patch <<< "$new_version" @@ -134,7 +150,7 @@ jobs: git push origin "v$major.$minor" --force - name: Create GitHub Release - if: steps.check.outputs.skip == 'false' + if: steps.check.outputs.skip == 'false' && steps.bump.outputs.release == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/AGENTS.md b/AGENTS.md index 190dd63..b620074 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,12 +46,16 @@ Blender-Developer-Tools/ ## Branching and commit model - Single `main` branch. No develop or release branches. -- Conventional commits drive the auto-release workflow: +- Conventional commits drive the auto-release workflow. It scans the commit subjects since + the last tag and releases only when at least one is release-worthy: - `feat:` triggers a minor bump - `fix:` triggers a patch bump - - `feat!:` or `BREAKING CHANGE` triggers a major bump - - Other types (`chore:`, `docs:`, etc.) skip the release path entirely - when only those non-content paths change. + - `feat!:` / `fix!:` / `BREAKING CHANGE` triggers a major bump + - Other types (`chore:`, `docs:`, `ci:`, `refactor:`, etc.) do **not** cut a release: the + workflow runs, decides there is nothing to release, and exits without a tag or version + bump. A mixed push still releases if any commit in range is a `feat:`/`fix:`. + - `[skip ci]` in the head commit still bypasses the workflow entirely. With the commit-type + gate above it is now an optional override, not a requirement for non-release commits. - Commit messages should describe the why, not the what. ## Blender version targeting diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d798007..049be71 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,7 +116,7 @@ The drift-check workflow enforces these on every push and PR. 1. **Update docs** if you change skill or rule lists, content counts, or versioning (`README.md`, `CLAUDE.md`, `ROADMAP.md` as appropriate). The release workflow rewrites `CHANGELOG.md`, `CLAUDE.md` `**Version:**` line, and `ROADMAP.md` `**Current:**` line automatically when a `feat:` or `fix:` commit lands on `main`, so only edit those files for content beyond the version markers. 2. **Open a PR** against `main` with a clear title and summary of changes. -3. **Use Conventional Commits** for the PR title (and ideally the merge commit). Prefixes: `feat:` (minor bump), `fix:` (patch bump), `feat!:` or `BREAKING CHANGE` (major bump), `chore:` / `docs:` / `refactor:` (no release). +3. **Use Conventional Commits** for the PR title (and the squash-merge subject, which is what the release workflow scans). Prefixes: `feat:` (minor bump), `fix:` (patch bump), `feat!:` / `fix!:` / `BREAKING CHANGE` (major bump), `chore:` / `docs:` / `ci:` / `refactor:` (no release — the workflow runs and exits without tagging). A mixed range still releases if any commit since the last tag is a `feat:`/`fix:`. `[skip ci]` remains an optional override and is no longer required to avoid a release for non-release commits. 4. **Respond to review** feedback; CI must pass before merge. ## Developer Certificate of Origin and Inbound License Grant