From d6b8a45e78f263fd7d30e167dd3ef05002aec7e8 Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Thu, 16 Jul 2026 12:18:23 +0200 Subject: [PATCH 1/2] chore: start 0.1.2-SNAPSHOT development --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4aba3ab..f7dc0ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ # MAJOR — breaking change to the handwritten public API or generated API surface # MINOR — new endpoint/model coverage from a spec version bump # PATCH — bug fixes, dependency updates, preprocessing fixes -version=0.1.0-SNAPSHOT +version=0.1.2-SNAPSHOT From 6c8d655aae3fbbadc6ad9da9805b95acf4c5a640 Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Thu, 16 Jul 2026 12:21:25 +0200 Subject: [PATCH 2/2] fix: open release version-bump pull requests --- .github/workflows/release.yml | 97 ++++++++++++++++------------------- AGENTS.md | 25 +++++---- CONTRIBUTING.md | 61 ++++++++++++++++++++++ 3 files changed, 116 insertions(+), 67 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ac3d8d0..0bfa355 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,6 +50,7 @@ jobs: shell: bash env: RELEASE_TAG_INPUT: ${{ inputs.tag }} + GITHUB_TOKEN: ${{ github.token }} run: | set -euo pipefail @@ -61,8 +62,13 @@ jobs: exit 1 fi - git fetch --force --tags origin \ + git_auth_header="$( + printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n' + )" + git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic $git_auth_header" \ + fetch --force --tags origin \ refs/heads/main:refs/remotes/origin/main + unset git_auth_header tag_ref="refs/tags/$release_tag" if ! git show-ref --verify --quiet "$tag_ref"; then @@ -299,10 +305,16 @@ jobs: env: RELEASE_TAG: ${{ needs.central_publish.outputs.tag }} EXPECTED_COMMIT: ${{ needs.central_publish.outputs.commit }} + GITHUB_TOKEN: ${{ github.token }} run: | set -euo pipefail tag_ref="refs/tags/$RELEASE_TAG" - git fetch --force --tags origin + git_auth_header="$( + printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n' + )" + git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic $git_auth_header" \ + fetch --force --tags origin + unset git_auth_header if ! actual_commit="$(git rev-parse --verify "${tag_ref}^{commit}")"; then echo "::error::Verified release tag '$RELEASE_TAG' is no longer available" exit 1 @@ -365,16 +377,14 @@ jobs: --title "$RELEASE_TAG" bump_main: - name: Start next patch development + name: Open next patch development pull request needs: - central_publish - github_release runs-on: ubuntu-latest permissions: contents: write - outputs: - snapshot_commit: ${{ steps.bump.outputs.commit || steps.recovered-bump.outputs.commit }} - snapshot_required: ${{ steps.bump.outputs.snapshot_required || steps.recovered-bump.outputs.snapshot_required }} + pull-requests: write steps: - name: Checkout repository @@ -426,17 +436,32 @@ jobs: ;; esac - - name: Commit and push next development version - id: bump + - name: Create next development version pull request if: steps.next-version.outputs.should_push == 'true' shell: bash env: NEXT_VERSION: ${{ steps.next-version.outputs.version }} + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail + branch="release/start-$NEXT_VERSION" + existing_pr="$( + gh pr list \ + --base main \ + --head "$branch" \ + --state open \ + --json url \ + --jq '.[0].url' + )" + if [[ -n "$existing_pr" ]]; then + echo "::notice::Version-bump pull request already exists: $existing_pr" + exit 0 + fi + git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git switch -c "$branch" git add -- gradle.properties mapfile -t staged_files < <(git diff --cached --name-only) @@ -446,49 +471,13 @@ jobs: fi git commit -m "chore: start $NEXT_VERSION development" - bump_commit="$(git rev-parse HEAD)" - if ! git push origin HEAD:main; then - if ! remote_ref="$(git ls-remote --exit-code origin refs/heads/main)"; then - echo "::error::Version-bump push failed and remote main could not be resolved" - exit 1 - fi - remote_main_sha="$(awk '$2 == "refs/heads/main" { print $1; exit }' <<< "$remote_ref")" - if [[ "$remote_main_sha" != "$bump_commit" ]]; then - echo "::error::Unable to push the version bump to main. Main may have advanced, or branch protection may reject github-actions[bot]." - exit 1 - fi - echo "::notice::Push reported failure, but remote main contains the exact version-bump commit; continuing recovery" - fi - echo "commit=$bump_commit" >> "$GITHUB_OUTPUT" - echo "snapshot_required=true" >> "$GITHUB_OUTPUT" - - - name: Recover an already-completed version bump - id: recovered-bump - if: steps.next-version.outputs.status == 'equal' - shell: bash - run: | - set -euo pipefail - bump_commit="$(git rev-parse HEAD)" - if ! remote_ref="$(git ls-remote --exit-code origin refs/heads/main)"; then - echo "::error::Unable to resolve remote main while recovering the version bump" - exit 1 - fi - remote_main_sha="$(awk '$2 == "refs/heads/main" { print $1; exit }' <<< "$remote_ref")" - if [[ "$remote_main_sha" != "$bump_commit" ]]; then - echo "::error::Checked-out main changed while recovering the version bump" - exit 1 - fi - echo "commit=$bump_commit" >> "$GITHUB_OUTPUT" - echo "snapshot_required=true" >> "$GITHUB_OUTPUT" - - publish_next_snapshot: - name: Publish next development snapshot - needs: bump_main - if: needs.bump_main.outputs.snapshot_required == 'true' - uses: ./.github/workflows/publish-snapshot.yml - with: - commit: ${{ needs.bump_main.outputs.snapshot_commit }} - fail_if_stale: true - secrets: - MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} - MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + git push origin "HEAD:refs/heads/$branch" + + pr_url="$( + gh pr create \ + --base main \ + --head "$branch" \ + --title "chore: start $NEXT_VERSION development" \ + --body "Automated post-release version bump. This PR changes only \`gradle.properties\`." + )" + echo "::notice::Opened version-bump pull request: $pr_url" diff --git a/AGENTS.md b/AGENTS.md index c1e7b77..31e9f7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -313,8 +313,7 @@ rechecks remote `main`, and publishes using the same files: It reads `MAVEN_CENTRAL_USERNAME` and `MAVEN_CENTRAL_PASSWORD` from repository or organization secrets. Pull requests, pushes to other branches, failed builds, and stale `main` pushes do not -publish. The Release workflow also calls this reusable workflow with the exact version-bump commit, -because pushes made with `GITHUB_TOKEN` do not start another Build workflow. +publish. ### Release spec inputs @@ -355,22 +354,21 @@ protected `maven-central` environment, the workflow runs in this order: 7. Create the tag's GitHub Release with generated notes. If a published release exists, leave it unchanged; if a draft exists, publish that draft without replacing its title, notes, or assets. 8. Read the latest `main`, calculate the next patch version (for example, - `1.2.3` → `1.2.4-SNAPSHOT`), and commit only `gradle.properties` directly to `main`. -9. Pass the exact version-bump commit to the reusable Publish Snapshot workflow, which builds and - publishes the new development snapshot from frozen OpenAPI inputs. + `1.2.3` → `1.2.4-SNAPSHOT`), then create a pull request containing only + `gradle.properties`. +9. A maintainer merges that pull request through normal branch protection. The Build workflow runs + for the resulting `main` push and publishes the new development snapshot. -If a version-bump push reports an ambiguous failure, the workflow checks whether remote `main` -contains the exact local commit before failing. A rerun that finds the expected next version already -on `main` recovers that commit and requests snapshot publication again. +The version-bump branch name is derived from the target snapshot version. A rerun reuses an +existing open pull request instead of creating a duplicate. Do not queue multiple release dispatches. With `cancel-in-progress: false`, GitHub retains at most one running and one pending run in the release concurrency group; a newer dispatch can replace an older pending run. -The workflow defaults to `contents: read`; only the GitHub Release and version-bump jobs receive -`contents: write`. Branch protection or repository rulesets must allow `github-actions[bot]` to -push the direct version-bump commit to `main`; otherwise Central and the GitHub Release can succeed -while the final bump fails. +The workflow defaults to `contents: read`; the GitHub Release job receives `contents: write`, and +the version-bump job receives `contents: write` plus `pull-requests: write` to create its +non-protected branch and pull request. Branch protection remains enforced for the merge to `main`. ### Recovery and partial failures @@ -389,7 +387,8 @@ ambiguous NMCP failure: - If only the GitHub Release or version-bump job failed after Central publication succeeded, use GitHub Actions' **Re-run failed jobs**. This does not rerun the successful Central publication job. GitHub Release creation and the version bump are designed to tolerate an already-completed - result; an existing draft release is published in place. + result; an existing draft release is published in place, and an existing open version-bump pull + request is reused. Never enable recovery merely because a run failed. First verify that the exact release is public on Maven Central; recovery refuses missing, unexpected, or mismatched POMs. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 10044c0..d7520eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,6 +24,67 @@ being closed for missing information.) This repo uses merge commits, so there is no need to squash commits before opening a PR. +## Maintainer releases + +Stable releases are performed through GitHub. Create the tag and GitHub Release in the GitHub UI, +then use the GitHub Actions **Release** workflow to publish the artifacts to Maven Central. + +Before starting, ensure the intended release commit and the current Release workflow are on +`main`. You must also be allowed to create release tags by the repository ruleset. If GitHub +rejects tag creation with `GH013` and “creations being restricted,” ask a repository administrator +to allow release-tag creation or add you to the ruleset bypass list. + +### 1. Create the tag and GitHub Release + +In GitHub, open **Releases → Draft a new release**: + +1. In **Choose a tag**, enter a stable version in the exact form `vMAJOR.MINOR.PATCH`, such as + `v1.2.3`, then select **Create new tag**. +2. Set the target branch to `main`. +3. Enter the release title and notes. +4. Select **Publish release**. + +Publishing the GitHub Release creates the remote tag. The tag must point to a commit reachable +from `main`. + +### 2. Run the GitHub Release workflow + +In GitHub, open **Actions → Release → Run workflow**: + +1. Select the `main` branch. +2. Set `tag` to the tag created with the GitHub Release, such as `v1.2.3`. +3. Leave `recover_existing_release` disabled for a normal release. +4. Run the workflow. + +### 3. Approve Maven Central publication + +Approve the protected `maven-central` environment deployment when GitHub requests approval. + +The workflow validates the tag, builds and signs the artifacts, publishes to Maven Central, +leaves the already-published GitHub Release unchanged, and opens a pull request for the next +patch `-SNAPSHOT` version. + +### 4. Merge the next-development-version pull request + +After the release workflow opens its version-bump pull request, approve and merge it through the +normal `main` branch-protection process. The Build workflow then runs for the resulting `main` +commit and publishes the next development snapshot. + +### 5. Verify the release + +Confirm that the GitHub Actions workflow completed successfully and that the release POM is +available from Maven Central: + +```text +https://repo.maven.apache.org/maven2/markets/alpaca/alpaca-java/1.2.3/alpaca-java-1.2.3.pom +``` + +### Recovery after an ambiguous Maven Central result + +Enable `recover_existing_release` only after confirming the exact release POM is publicly +available from Maven Central following an ambiguous publication failure. Never use recovery for a +new release or merely because a workflow run failed. + ## Coding Guidelines ### Generated vs handwritten code