diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d572e14c..0dbdfb13 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -57,10 +57,8 @@ jobs: check_name: "Test Results ${{ matrix.python-version }}" github_retries: 10 secondary_rate_limit_wait_seconds: 60.0 - # Version source of truth: pyproject.toml [project] version. - # - Patch: CI bumps patch on every push to main (and commits it). - # - Minor/major: Update version in pyproject.toml (e.g. uv version 3.22.0 or uv version --bump minor), - # merge with message "Release 3.22.0"; CI will use that version as-is and tag it. + # Version source of truth: pyproject.toml [project] version. User must update it before merging to main. + # PRs are checked so that version differs from main. On push to main, CI tags and publishes that version. get-version: if: github.repository == 'adobe/buildrunner' runs-on: ubuntu-latest @@ -69,7 +67,6 @@ jobs: contents: write outputs: current-version: ${{ steps.version-number.outputs.CURRENT_VERSION }} - should-tag: ${{ steps.tag-decision.outputs.should_tag }} steps: - uses: actions/checkout@v2 with: @@ -80,31 +77,74 @@ jobs: python-version: 3.9 - name: Install dependencies run: uv sync --locked - - name: Bump version (patch only when not the bot’s bump commit) - id: bump - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'Bump version to') - run: uv version --bump patch - name: Get current version id: version-number run: echo "CURRENT_VERSION=$(uv version --short)" >> $GITHUB_OUTPUT - name: Print current version run: echo CURRENT_VERSION ${{ steps.version-number.outputs.CURRENT_VERSION }} - - name: Set tag/publish decision - id: tag-decision - run: | - if [ "${{ steps.bump.outcome }}" = "success" ]; then echo "should_tag=true" >> $GITHUB_OUTPUT; exit 0; fi - echo "should_tag=false" >> $GITHUB_OUTPUT - - name: Print tag decision - run: echo "should_tag=${{ steps.tag-decision.outputs.should_tag }}" - - name: Commit and push version bump - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, 'Bump version to') + # PR check: version must differ from main and be strictly greater (semver: major.minor.patch). + # MAJOR may not decrease; MINOR may not decrease unless MAJOR increased; PATCH may not decrease unless MAJOR or MINOR changed. + version-changed: + if: github.repository == 'adobe/buildrunner' && github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + python-version: 3.9 + - name: Check version increased from main run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add pyproject.toml uv.lock - git diff --staged --quiet || (git commit -m "Bump version to ${{ steps.version-number.outputs.CURRENT_VERSION }}" && git push) + PR_VERSION=$(uv version --short) + git fetch origin main + MAIN_VERSION=$(git show origin/main:pyproject.toml | sed -n 's/^version *= *"\(.*\)".*/\1/p' | tr -d ' ') + if [ -z "$MAIN_VERSION" ]; then + echo "Could not read version from main's pyproject.toml" + exit 1 + fi + # Parse major.minor.patch (default missing to 0; e.g. "3" -> 3.0.0, "3.22" -> 3.22.0) + main_major=$(echo "$MAIN_VERSION" | cut -d. -f1) + main_minor=$(echo "$MAIN_VERSION" | cut -d. -f2) + main_patch=$(echo "$MAIN_VERSION" | cut -d. -f3) + [ -z "$main_minor" ] && main_minor=0 + [ -z "$main_patch" ] && main_patch=0 + pr_major=$(echo "$PR_VERSION" | cut -d. -f1) + pr_minor=$(echo "$PR_VERSION" | cut -d. -f2) + pr_patch=$(echo "$PR_VERSION" | cut -d. -f3) + [ -z "$pr_minor" ] && pr_minor=0 + [ -z "$pr_patch" ] && pr_patch=0 + # Require PR version strictly greater than main (at least one component increased, none decreased per semver rules) + fail_msg="Version must be strictly greater than main ($MAIN_VERSION). Rules: MAJOR may not decrease; MINOR may not decrease unless MAJOR increased; PATCH may not decrease unless MAJOR or MINOR changed. Bump with e.g. \`uv version --bump patch\`." + if [ "$pr_major" -lt "$main_major" ]; then + echo "::error::MAJOR decreased ($main_major → $pr_major). $fail_msg" + exit 1 + fi + if [ "$pr_major" -gt "$main_major" ]; then + echo "Version OK: $MAIN_VERSION → $PR_VERSION" + exit 0 + fi + if [ "$pr_minor" -lt "$main_minor" ]; then + echo "::error::MINOR decreased ($main_minor → $pr_minor) without MAJOR increase. $fail_msg" + exit 1 + fi + if [ "$pr_minor" -gt "$main_minor" ]; then + echo "Version OK: $MAIN_VERSION → $PR_VERSION" + exit 0 + fi + if [ "$pr_patch" -lt "$main_patch" ]; then + echo "::error::PATCH decreased ($main_patch → $pr_patch) without MAJOR or MINOR change. $fail_msg" + exit 1 + fi + if [ "$pr_patch" -gt "$main_patch" ]; then + echo "Version OK: $MAIN_VERSION → $PR_VERSION" + exit 0 + fi + echo "::error::Version unchanged ($MAIN_VERSION). $fail_msg" + exit 1 tag-commit: - if: github.repository == 'adobe/buildrunner' && github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.get-version.outputs.should-tag == 'true' + if: github.repository == 'adobe/buildrunner' && github.event_name == 'push' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest needs: [test, get-version] steps: @@ -115,7 +155,7 @@ jobs: - name: Tag commit run: git tag ${{ needs.get-version.outputs.current-version }} && git push --tags publish-pypi: - if: github.repository == 'adobe/buildrunner' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.get-version.outputs.should-tag == 'true')) + if: github.repository == 'adobe/buildrunner' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main')) runs-on: ubuntu-latest needs: [test, get-version] steps: @@ -142,7 +182,7 @@ jobs: user: __token__ password: ${{ secrets.ADOBE_BOT_PYPI_TOKEN }} publish-docker: - if: github.repository == 'adobe/buildrunner' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.get-version.outputs.should-tag == 'true')) + if: github.repository == 'adobe/buildrunner' && (github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main')) runs-on: ubuntu-latest needs: [test, get-version] steps: diff --git a/VERSIONING.md b/VERSIONING.md index a09396f8..3ffebef0 100644 --- a/VERSIONING.md +++ b/VERSIONING.md @@ -1,22 +1,26 @@ # Versioning The **source of truth** for the version is `pyproject.toml` → `[project]` → `version`. -CI uses that value and the resulting git tag as the release version. +You are responsible for updating it for every release. CI tags and publishes whatever version is on `main` when you push. ## Where to update version -- **Single place:** `pyproject.toml` — set `version = "X.Y.Z"` (e.g. `"3.21"` or `"3.21.0"`). +- **Single place:** `pyproject.toml` — set `version = "X.Y.Z"` (e.g. `"3.22.0"`). You can use `uv version 3.22.0` or `uv version --bump patch` (etc.) and commit the change. -## How versions are produced +## Releasing (patch, minor, or major) | You want | What to do | |----------|------------| -| **Patch** (e.g. 3.21.0 → 3.21.1) | Nothing. Push to `main`; CI bumps patch, commits it, tags that version, and publishes. | -| **Minor** (e.g. 3.21 → 3.22.0) | Update version in `pyproject.toml` (e.g. `uv version 3.22.0` or `uv version --bump minor`). Commit with message **`Release 3.22.0`** and merge to `main`. CI will use that version as-is (no bump), tag it, and publish. | -| **Major** (e.g. 3.21 → 4.0.0) | Same as minor: set `version = "4.0.0"` in `pyproject.toml`, commit **`Release 4.0.0`**, merge to `main`. | +| **Patch** (e.g. 3.22.0 → 3.22.1) | Run `uv version --bump patch` (or set `version = "3.22.1"` in `pyproject.toml`). Commit, open a PR to `main`, merge. CI will tag and publish. | +| **Minor** (e.g. 3.22 → 3.23.0) | Run `uv version 3.23.0` or `uv version --bump minor`. Commit, open a PR to `main`, merge. CI will tag and publish. | +| **Major** (e.g. 3.x → 4.0.0) | Set `version = "4.0.0"` in `pyproject.toml` (or `uv version 4.0.0`). Commit, open a PR to `main`, merge. CI will tag and publish. | + +## PR check: version must increase + +For every **pull request targeting `main`**, CI runs a check that the version in `pyproject.toml` is **strictly greater** than the version on `main`: at least one of major.minor.patch must increase, and none may decrease (except MINOR/PATCH may reset when MAJOR increases, or PATCH when MINOR increases). MAJOR may never decrease; MINOR may not decrease unless MAJOR increased; PATCH may not decrease unless MAJOR or MINOR changed. If the version is unchanged or violates these rules, the check fails. ## Summary -- **Track/update** major.minor.patch in **`pyproject.toml`**. -- **Patch releases:** automatic on every push to `main` (CI bumps and commits). -- **Minor/major releases:** set the version in `pyproject.toml` and use a commit message starting with **`Release X.Y.Z`** so CI does not bump again and tags that version. +- **You** update the version in **`pyproject.toml`** for every release (patch, minor, or major). +- Open a PR to `main`; CI will fail the “version changed” check until the version is updated. +- After you merge to `main`, CI tags that version and publishes to PyPI and the container registry. diff --git a/pyproject.toml b/pyproject.toml index e13e6188..0049bb8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "buildrunner" -version = "3.22" +version = "3.22.1" description = "Docker-based build tool" readme = "README.rst" requires-python = ">=3.9" diff --git a/uv.lock b/uv.lock index fc7fb508..56a3df2a 100644 --- a/uv.lock +++ b/uv.lock @@ -105,7 +105,7 @@ wheels = [ [[package]] name = "buildrunner" -version = "3.22" +version = "3.22.1" source = { editable = "." } dependencies = [ { name = "bcrypt" },