From 3a664fc565516edfddf4f530c4a96f21c51ca1e3 Mon Sep 17 00:00:00 2001 From: Ian Duffy Date: Thu, 1 Oct 2026 22:49:02 +0100 Subject: [PATCH 1/4] feat(ENG-14534): open a cloudsmith-cli-action bump PR on each release Add a publish-action release job. It clones cloudsmith-cli-action as cloudsmith-bot and runs the action's scripts/bump-cli-version.sh. Then it makes a signed commit and opens a pull request, as publish-homebrew does for the tap. Without the ACTION_PUSH_TOKEN secret, the job skips. Move the Cloudsmith Bot commit-signing setup into the setup-cloudsmith-bot-git composite action, which both jobs use. Co-Authored-By: Claude Opus 5.5 --- .../setup-cloudsmith-bot-git/action.yml | 27 +++++++ .github/workflows/release.yml | 73 ++++++++++++++++--- 2 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 .github/actions/setup-cloudsmith-bot-git/action.yml diff --git a/.github/actions/setup-cloudsmith-bot-git/action.yml b/.github/actions/setup-cloudsmith-bot-git/action.yml new file mode 100644 index 00000000..4ee926b0 --- /dev/null +++ b/.github/actions/setup-cloudsmith-bot-git/action.yml @@ -0,0 +1,27 @@ +name: Set up Cloudsmith Bot git signing +description: >- + Configure git to commit as Cloudsmith Bot and to sign each commit with the + SSH key of the bot. + +inputs: + ssh-signing-key: + description: Private SSH key that signs the commits of Cloudsmith Bot. + required: true + +runs: + using: composite + steps: + - shell: bash + env: + CLOUDSMITH_BOT_SSH_KEY: ${{ inputs.ssh-signing-key }} + run: | + set -euo pipefail + SIGNING_KEY="${RUNNER_TEMP}/cloudsmith-bot-signing-key" + printf '%s\n' "${CLOUDSMITH_BOT_SSH_KEY}" > "${SIGNING_KEY}" + chmod 600 "${SIGNING_KEY}" + ssh-keygen -y -f "${SIGNING_KEY}" > "${SIGNING_KEY}.pub" + git config --global user.name "Cloudsmith Bot" + git config --global user.email "vftbot@cloudsmith.io" + git config --global gpg.format ssh + git config --global user.signingkey "${SIGNING_KEY}.pub" + git config --global commit.gpgsign true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e1d459e..181d822f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -619,11 +619,16 @@ jobs: echo "verified ${URL}" done + - name: Set up commit signing as Cloudsmith Bot + if: env.HAS_TAP_TOKEN == 'true' + uses: ./.github/actions/setup-cloudsmith-bot-git + with: + ssh-signing-key: ${{ secrets.CLOUDSMITH_BOT_SSH_KEY }} + - name: Open a bump pull request against the tap repository if: env.HAS_TAP_TOKEN == 'true' env: GH_TOKEN: ${{ secrets.TAP_PUSH_TOKEN }} - CLOUDSMITH_BOT_SSH_KEY: ${{ secrets.CLOUDSMITH_BOT_SSH_KEY }} TAP_REPO: ${{ github.repository_owner }}/homebrew-cloudsmith-cli run: | set -euo pipefail @@ -637,15 +642,6 @@ jobs: echo "tap already up to date; skipping" exit 0 fi - SIGNING_KEY="${RUNNER_TEMP}/cloudsmith-bot-signing-key" - printf '%s\n' "${CLOUDSMITH_BOT_SSH_KEY}" > "${SIGNING_KEY}" - chmod 600 "${SIGNING_KEY}" - ssh-keygen -y -f "${SIGNING_KEY}" > "${SIGNING_KEY}.pub" - git -C tap config user.name "Cloudsmith Bot" - git -C tap config user.email "vftbot@cloudsmith.io" - git -C tap config gpg.format ssh - git -C tap config user.signingkey "${SIGNING_KEY}.pub" - git -C tap config commit.gpgsign true git -C tap checkout -b "${BRANCH}" git -C tap add Formula/cloudsmith-cli.rb Aliases/cloudsmith git -C tap commit -m "Bump cloudsmith-cli to v${VERSION}" @@ -663,6 +659,63 @@ jobs: if: env.HAS_TAP_TOKEN != 'true' run: echo "TAP_PUSH_TOKEN not configured; formula staged as a workflow artifact only." + publish-action: + # Opens a pull request against /cloudsmith-cli-action that pins the + # default cli-version of the action to this release. The bump script in + # the action repository also adds a release to its CHANGELOG.md. + # The action repository requires reviewed pull requests. When the pull + # request merges, the action repository tests, tags and releases it. + needs: [validate, publish-github] + runs-on: ubuntu-24.04 + timeout-minutes: 10 + permissions: + contents: read + env: + HAS_ACTION_TOKEN: ${{ secrets.ACTION_PUSH_TOKEN != '' }} + VERSION: ${{ needs.validate.outputs.version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up commit signing as Cloudsmith Bot + if: env.HAS_ACTION_TOKEN == 'true' + uses: ./.github/actions/setup-cloudsmith-bot-git + with: + ssh-signing-key: ${{ secrets.CLOUDSMITH_BOT_SSH_KEY }} + + - name: Open a CLI bump pull request against the action repository + if: env.HAS_ACTION_TOKEN == 'true' + env: + GH_TOKEN: ${{ secrets.ACTION_PUSH_TOKEN }} + ACTION_REPO: ${{ github.repository_owner }}/cloudsmith-cli-action + run: | + set -euo pipefail + BRANCH="release/cloudsmith-cli-v${VERSION}" + gh auth setup-git + git clone --depth 1 "https://github.com/${ACTION_REPO}.git" action + ACTION_VERSION="$(cd action && bash scripts/bump-cli-version.sh "${VERSION}")" + if [ -z "${ACTION_VERSION}" ]; then + echo "action already pins cloudsmith-cli v${VERSION}; skipping" + exit 0 + fi + git -C action checkout -b "${BRANCH}" + git -C action add action.yml CHANGELOG.md + git -C action commit -m "Bump cloudsmith-cli to v${VERSION}" + git -C action push --force origin "${BRANCH}" + if [ -z "$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" ]; then + gh pr create --repo "${ACTION_REPO}" \ + --head "${BRANCH}" --base master \ + --title "Bump cloudsmith-cli to v${VERSION}" \ + --body "Automated update that pins the default cli-version to cloudsmith-cli v${VERSION}. When this pull request merges and the tests pass, the action releases v${ACTION_VERSION} and moves its major tag. Merge with a squash merge." + else + echo "pull request for ${BRANCH} already open" + fi + + - name: Report a skipped action update + if: env.HAS_ACTION_TOKEN != 'true' + run: echo "ACTION_PUSH_TOKEN not configured; action update skipped." + publish-github: needs: - validate From 199c7db2013d165cd44c221904779a3df532e972 Mon Sep 17 00:00:00 2001 From: Ian Duffy Date: Thu, 1 Oct 2026 22:55:52 +0100 Subject: [PATCH 2/4] fix(ENG-14534): replace an unmerged action bump with the newest release Rebuild one release/cloudsmith-cli branch from the action master on each CLI release, and update the open pull request. Two CLI releases before a merge then cannot produce two pull requests with the same action version. Run the job in one concurrency group. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 181d822f..250a4191 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -665,9 +665,15 @@ jobs: # the action repository also adds a release to its CHANGELOG.md. # The action repository requires reviewed pull requests. When the pull # request merges, the action repository tests, tags and releases it. + # Each run rebuilds one branch from master, so a newer CLI release + # replaces a bump that is not merged yet. The bump never repeats an + # action version and never pins an older CLI. needs: [validate, publish-github] runs-on: ubuntu-24.04 timeout-minutes: 10 + concurrency: + group: publish-action + cancel-in-progress: false permissions: contents: read env: @@ -691,7 +697,8 @@ jobs: ACTION_REPO: ${{ github.repository_owner }}/cloudsmith-cli-action run: | set -euo pipefail - BRANCH="release/cloudsmith-cli-v${VERSION}" + BRANCH="release/cloudsmith-cli" + TITLE="Bump cloudsmith-cli to v${VERSION}" gh auth setup-git git clone --depth 1 "https://github.com/${ACTION_REPO}.git" action ACTION_VERSION="$(cd action && bash scripts/bump-cli-version.sh "${VERSION}")" @@ -701,15 +708,17 @@ jobs: fi git -C action checkout -b "${BRANCH}" git -C action add action.yml CHANGELOG.md - git -C action commit -m "Bump cloudsmith-cli to v${VERSION}" + git -C action commit -m "${TITLE}" git -C action push --force origin "${BRANCH}" - if [ -z "$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" ]; then + BODY="Automated update that pins the default cli-version to cloudsmith-cli v${VERSION}. When this pull request merges and the tests pass, the action releases v${ACTION_VERSION} and moves its major tag. Merge with a squash merge." + PR_NUMBER="$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" + if [ -z "${PR_NUMBER}" ]; then gh pr create --repo "${ACTION_REPO}" \ --head "${BRANCH}" --base master \ - --title "Bump cloudsmith-cli to v${VERSION}" \ - --body "Automated update that pins the default cli-version to cloudsmith-cli v${VERSION}. When this pull request merges and the tests pass, the action releases v${ACTION_VERSION} and moves its major tag. Merge with a squash merge." + --title "${TITLE}" --body "${BODY}" else - echo "pull request for ${BRANCH} already open" + gh pr edit "${PR_NUMBER}" --repo "${ACTION_REPO}" \ + --title "${TITLE}" --body "${BODY}" fi - name: Report a skipped action update From 0c2f80da78051748f70dadbf423244e224dc3739 Mon Sep 17 00:00:00 2001 From: Ian Duffy Date: Thu, 1 Oct 2026 23:23:54 +0100 Subject: [PATCH 3/4] fix(ENG-14534): keep a newer pending action bump on an older re-run Read the pin on the open release/cloudsmith-cli pull request before the bump. When that pin is newer than this release, skip the job, so that a re-run of an older CLI release cannot replace a newer pending bump. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 250a4191..0011b03b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -667,7 +667,8 @@ jobs: # request merges, the action repository tests, tags and releases it. # Each run rebuilds one branch from master, so a newer CLI release # replaces a bump that is not merged yet. The bump never repeats an - # action version and never pins an older CLI. + # action version. It never pins a CLI older than master or the open pull + # request already pins. needs: [validate, publish-github] runs-on: ubuntu-24.04 timeout-minutes: 10 @@ -701,6 +702,16 @@ jobs: TITLE="Bump cloudsmith-cli to v${VERSION}" gh auth setup-git git clone --depth 1 "https://github.com/${ACTION_REPO}.git" action + PR_NUMBER="$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" + if [ -n "${PR_NUMBER}" ]; then + git -C action fetch --quiet --depth 1 origin "${BRANCH}" + PENDING_VERSION="$(git -C action show FETCH_HEAD:action.yml | yq '.inputs["cli-version"].default')" + NEWEST_VERSION="$(printf '%s\n' "${PENDING_VERSION}" "${VERSION}" | sort -V | tail -n 1)" + if [ "${PENDING_VERSION}" != "${VERSION}" ] && [ "${NEWEST_VERSION}" = "${PENDING_VERSION}" ]; then + echo "pull request #${PR_NUMBER} already pins newer cloudsmith-cli v${PENDING_VERSION}; skipping" + exit 0 + fi + fi ACTION_VERSION="$(cd action && bash scripts/bump-cli-version.sh "${VERSION}")" if [ -z "${ACTION_VERSION}" ]; then echo "action already pins cloudsmith-cli v${VERSION}; skipping" @@ -711,7 +722,6 @@ jobs: git -C action commit -m "${TITLE}" git -C action push --force origin "${BRANCH}" BODY="Automated update that pins the default cli-version to cloudsmith-cli v${VERSION}. When this pull request merges and the tests pass, the action releases v${ACTION_VERSION} and moves its major tag. Merge with a squash merge." - PR_NUMBER="$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" if [ -z "${PR_NUMBER}" ]; then gh pr create --repo "${ACTION_REPO}" \ --head "${BRANCH}" --base master \ From aaf51cc7feb4094955b69fac37193c4afffc5c44 Mon Sep 17 00:00:00 2001 From: Ian Duffy Date: Mon, 5 Oct 2026 13:09:58 +0100 Subject: [PATCH 4/4] fix(ENG-14534): queue every action bump and retry when the PR closes Keep all pending publish-action jobs in the concurrency queue. A re-run of an older tag no longer cancels a newer pending release. Check the bump pull request after the push. When it closed during the update, start the update again from the latest master. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/release.yml | 67 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0011b03b..c4158527 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -668,13 +668,15 @@ jobs: # Each run rebuilds one branch from master, so a newer CLI release # replaces a bump that is not merged yet. The bump never repeats an # action version. It never pins a CLI older than master or the open pull - # request already pins. + # request already pins. When the pull request closes during the update, + # the update starts again from master. needs: [validate, publish-github] runs-on: ubuntu-24.04 timeout-minutes: 10 concurrency: group: publish-action cancel-in-progress: false + queue: max permissions: contents: read env: @@ -701,35 +703,44 @@ jobs: BRANCH="release/cloudsmith-cli" TITLE="Bump cloudsmith-cli to v${VERSION}" gh auth setup-git - git clone --depth 1 "https://github.com/${ACTION_REPO}.git" action - PR_NUMBER="$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" - if [ -n "${PR_NUMBER}" ]; then - git -C action fetch --quiet --depth 1 origin "${BRANCH}" - PENDING_VERSION="$(git -C action show FETCH_HEAD:action.yml | yq '.inputs["cli-version"].default')" - NEWEST_VERSION="$(printf '%s\n' "${PENDING_VERSION}" "${VERSION}" | sort -V | tail -n 1)" - if [ "${PENDING_VERSION}" != "${VERSION}" ] && [ "${NEWEST_VERSION}" = "${PENDING_VERSION}" ]; then - echo "pull request #${PR_NUMBER} already pins newer cloudsmith-cli v${PENDING_VERSION}; skipping" + for attempt in 1 2 3; do + rm -rf action + git clone --depth 1 "https://github.com/${ACTION_REPO}.git" action + PR_NUMBER="$(gh pr list --repo "${ACTION_REPO}" --head "${BRANCH}" --state open --json number --jq '.[].number')" + if [ -n "${PR_NUMBER}" ]; then + git -C action fetch --quiet --depth 1 origin "${BRANCH}" + PENDING_VERSION="$(git -C action show FETCH_HEAD:action.yml | yq '.inputs["cli-version"].default')" + NEWEST_VERSION="$(printf '%s\n' "${PENDING_VERSION}" "${VERSION}" | sort -V | tail -n 1)" + if [ "${PENDING_VERSION}" != "${VERSION}" ] && [ "${NEWEST_VERSION}" = "${PENDING_VERSION}" ]; then + echo "pull request #${PR_NUMBER} already pins newer cloudsmith-cli v${PENDING_VERSION}; skipping" + exit 0 + fi + fi + ACTION_VERSION="$(cd action && bash scripts/bump-cli-version.sh "${VERSION}")" + if [ -z "${ACTION_VERSION}" ]; then + echo "action already pins cloudsmith-cli v${VERSION}; skipping" exit 0 fi - fi - ACTION_VERSION="$(cd action && bash scripts/bump-cli-version.sh "${VERSION}")" - if [ -z "${ACTION_VERSION}" ]; then - echo "action already pins cloudsmith-cli v${VERSION}; skipping" - exit 0 - fi - git -C action checkout -b "${BRANCH}" - git -C action add action.yml CHANGELOG.md - git -C action commit -m "${TITLE}" - git -C action push --force origin "${BRANCH}" - BODY="Automated update that pins the default cli-version to cloudsmith-cli v${VERSION}. When this pull request merges and the tests pass, the action releases v${ACTION_VERSION} and moves its major tag. Merge with a squash merge." - if [ -z "${PR_NUMBER}" ]; then - gh pr create --repo "${ACTION_REPO}" \ - --head "${BRANCH}" --base master \ - --title "${TITLE}" --body "${BODY}" - else - gh pr edit "${PR_NUMBER}" --repo "${ACTION_REPO}" \ - --title "${TITLE}" --body "${BODY}" - fi + git -C action checkout -b "${BRANCH}" + git -C action add action.yml CHANGELOG.md + git -C action commit -m "${TITLE}" + git -C action push --force origin "${BRANCH}" + BODY="Automated update that pins the default cli-version to cloudsmith-cli v${VERSION}. When this pull request merges and the tests pass, the action releases v${ACTION_VERSION} and moves its major tag. Merge with a squash merge." + if [ -z "${PR_NUMBER}" ]; then + gh pr create --repo "${ACTION_REPO}" \ + --head "${BRANCH}" --base master \ + --title "${TITLE}" --body "${BODY}" + exit 0 + fi + if [ "$(gh pr view "${PR_NUMBER}" --repo "${ACTION_REPO}" --json state --jq .state)" = OPEN ]; then + gh pr edit "${PR_NUMBER}" --repo "${ACTION_REPO}" \ + --title "${TITLE}" --body "${BODY}" + exit 0 + fi + echo "pull request #${PR_NUMBER} closed during attempt ${attempt}; starting again from master" + done + echo "pull request for ${BRANCH} kept closing during the update" >&2 + exit 1 - name: Report a skipped action update if: env.HAS_ACTION_TOKEN != 'true'