From 9742434f155aa0fe3cee68c07382380954e15cfa Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 16:15:18 -0700 Subject: [PATCH 1/5] ci: test release candidate in pull request when available --- .circleci/config.yml | 55 +++++--------- .github/workflows/release.yml | 135 ++++++++++++++++++++++++++++++++++ Makefile | 14 ++-- 3 files changed, 161 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 39e6540b..086a057e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -272,49 +272,34 @@ jobs: done fi - run: - name: "Update release description and upload assets to GitHub" + name: "Upload assets to GitHub release" command: | - # Replace double quotes with single quotes RELEASE_ID=$(curl -H "Accept: application/vnd.github+json" -H "Authorization: token $GITHUB_RELEASE_TOKEN" "https://api.github.com/repos/slackapi/slack-cli/releases/tags/$RELEASE_REF" | jq -r .id); - PRERELEASE="true"; - TARGET="${CIRCLE_SHA1}"; - if [ "<< parameters.production >>" != "false" ]; - then - PRERELEASE="false"; - TARGET="main"; - fi - BODY="{\"tag_name\":\"${RELEASE_REF}\",\"name\":\"${RELEASE_REF}\",\"target_commitish\":\"${TARGET}\",\"draft\":false,\"prerelease\":${PRERELEASE}," if [ "$RELEASE_ID" = "null" ]; then - BODY+="\"generate_release_notes\":true}" - echo "Creating a new GitHub release with: ${BODY}" - RELEASE_ID=$(curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token $GITHUB_RELEASE_TOKEN" https://api.github.com/repos/slackapi/slack-cli/releases -d "${BODY}" | jq -r .id) - echo "... complete. RELEASE_ID=${RELEASE_ID}" - else - echo "Overwriting existing GitHub Release data; generating pre-release notes ..." + PRERELEASE="true" + TARGET="${CIRCLE_SHA1}" + if [ "<< parameters.production >>" != "false" ]; then + PRERELEASE="false" + TARGET="main" + fi + echo "Creating GitHub release for $RELEASE_REF..." + RELEASE_ID=$(curl -X POST -H "Accept: application/vnd.github+json" -H "Authorization: token $GITHUB_RELEASE_TOKEN" https://api.github.com/repos/slackapi/slack-cli/releases -d "{\"tag_name\":\"${RELEASE_REF}\",\"name\":\"${RELEASE_REF}\",\"target_commitish\":\"${TARGET}\",\"draft\":false,\"prerelease\":${PRERELEASE},\"generate_release_notes\":true}" | jq -r .id) + elif [ "<< parameters.production >>" = "false" ]; then LAST_SEMVER_TAG=$(git describe --tags --match 'v*.*.*' --abbrev=0 | cut -d"-" -f1) - # When generating release notes, setting the target tag_name property to an existing tag has GitHub ignore the target_commitish property - # So, set the tag name to the target commit to generate release notes from the last semver tag to the target commit - # See https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#generate-release-notes-content-for-a-release--parameters - # Note the following _does not change releases or tags_ - it only creates release notes, just like clicking "Generate Release Notes" on the GitHub Releases page. - CHANGELOG=$(curl -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_RELEASE_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/slackapi/slack-cli/releases/generate-notes -d "{\"tag_name\":\"${TARGET}\",\"target_commitish\":\"${TARGET}\",\"previous_tag_name\":\"${LAST_SEMVER_TAG}\"}" | jq .body) - echo "Will use release notes: ${CHANGELOG}" - BODY+="\"body\":$CHANGELOG}" - echo "Updating existing GitHub pre-release ${RELEASE_ID} with ${BODY}" - curl -X PATCH -H "Accept: application/vnd.github+json" -H "Authorization: token $GITHUB_RELEASE_TOKEN" https://api.github.com/repos/slackapi/slack-cli/releases/$RELEASE_ID -d "${BODY}" - echo "... complete." + CHANGELOG=$(curl -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_RELEASE_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/slackapi/slack-cli/releases/generate-notes -d "{\"tag_name\":\"${CIRCLE_SHA1}\",\"target_commitish\":\"${CIRCLE_SHA1}\",\"previous_tag_name\":\"${LAST_SEMVER_TAG}\"}" | jq .body) + curl -X PATCH -H "Accept: application/vnd.github+json" -H "Authorization: token $GITHUB_RELEASE_TOKEN" https://api.github.com/repos/slackapi/slack-cli/releases/$RELEASE_ID -d "{\"tag_name\":\"${RELEASE_REF}\",\"name\":\"${RELEASE_REF}\",\"target_commitish\":\"${CIRCLE_SHA1}\",\"draft\":false,\"prerelease\":true,\"body\":$CHANGELOG}" fi - binaries=`find << parameters.artifact_dir >>/slack_* -maxdepth 1 -not -type d` - for binary in $binaries - do + binaries=$(find << parameters.artifact_dir >>/slack_* -maxdepth 1 -not -type d) + for binary in $binaries; do curl -X POST \ - -H "Authorization: token $GITHUB_RELEASE_TOKEN" \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Content-Type: $(file -b --mime-type ${binary})" \ - -H "Content-Length: $(wc -c <${binary} | xargs)" \ - -T "${binary}" \ - "https://uploads.github.com/repos/slackapi/slack-cli/releases/$RELEASE_ID/assets?name=$(basename ${binary})" | cat + -H "Authorization: token $GITHUB_RELEASE_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + -H "Content-Type: $(file -b --mime-type ${binary})" \ + -H "Content-Length: $(wc -c <${binary} | xargs)" \ + -T "${binary}" \ + "https://uploads.github.com/repos/slackapi/slack-cli/releases/$RELEASE_ID/assets?name=$(basename ${binary})" | cat done - store_artifacts: path: << parameters.artifact_dir >> diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..5724d4ee --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,135 @@ +name: Release + +on: + push: + branches: + - main + workflow_dispatch: + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + rc: + name: RC + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Gather credentials + id: credentials + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.GH_APP_ID_RELEASER }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY_RELEASER }} + + - name: Checkout repo + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + persist-credentials: true + token: ${{ steps.credentials.outputs.token }} + + - name: Set up Go + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: go.mod + + - name: Decide next version + id: version + env: + GH_TOKEN: ${{ steps.credentials.outputs.token }} + run: | + # Get the latest release tag + LATEST_TAG=$(git describe --tags --match 'v*.*.*' --abbrev=0) + + # Get PR numbers since latest tag + PR_NUMBERS=$(git log "${LATEST_TAG}..HEAD" --oneline | grep -oP '#\K[0-9]+' || true) + + if [ -z "$PR_NUMBERS" ]; then + echo "No new PRs since $LATEST_TAG" + echo "has_changes=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Search for highest semver label + SEMVER="patch" + for PR in $PR_NUMBERS; do + LABELS=$(gh pr view "$PR" --json labels --jq '.labels[].name' 2>/dev/null || true) + if echo "$LABELS" | grep -q "semver:major"; then + SEMVER="major" + break + elif echo "$LABELS" | grep -q "semver:minor"; then + SEMVER="minor" + fi + done + + # Parse current version + VERSION="${LATEST_TAG#v}" + MAJOR=$(echo "$VERSION" | cut -d. -f1) + MINOR=$(echo "$VERSION" | cut -d. -f2) + PATCH=$(echo "$VERSION" | cut -d. -f3) + + # Compute next version + case "$SEMVER" in + major) + NEXT_VERSION="$((MAJOR + 1)).0.0" + ;; + minor) + NEXT_VERSION="${MAJOR}.$((MINOR + 1)).0" + ;; + patch) + NEXT_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" + ;; + esac + + echo "has_changes=true" >> "$GITHUB_OUTPUT" + echo "next=$NEXT_VERSION" >> "$GITHUB_OUTPUT" + echo "semver=$SEMVER" >> "$GITHUB_OUTPUT" + echo "Bumping $LATEST_TAG to v$NEXT_VERSION" + + - name: Generate release commit + if: steps.version.outputs.has_changes == 'true' + env: + RELEASE_VERSION: ${{ steps.version.outputs.next }} + run: | + git config user.name "slack-cli-releaser[bot]" + git config user.email "122933866+slack-cli-releaser[bot]@users.noreply.github.com" + make rc RELEASE_VERSION="$RELEASE_VERSION" + git push origin "HEAD:rc" --force + + - name: Create or update release PR + if: steps.version.outputs.has_changes == 'true' + env: + GH_TOKEN: ${{ steps.credentials.outputs.token }} + RELEASE_VERSION: ${{ steps.version.outputs.next }} + SEMVER: ${{ steps.version.outputs.semver }} + run: | + PR_TITLE="chore: release slack-cli v${RELEASE_VERSION}" + PR_BODY=$(cat </dev/null || true) + + if [ -n "$EXISTING_PR" ]; then + gh pr edit "$EXISTING_PR" --remove-label "semver:patch" --remove-label "semver:minor" --remove-label "semver:major" 2>/dev/null || true + gh pr edit "$EXISTING_PR" --title "$PR_TITLE" --body "$PR_BODY" --milestone "Next Release" --label "release" --label "semver:${SEMVER}" + echo "Updated PR #$EXISTING_PR" + else + gh pr create --head rc --base main --title "$PR_TITLE" --body "$PR_BODY" --milestone "Next Release" --label "release" --label "semver:${SEMVER}" + echo "Created new release PR" + fi diff --git a/Makefile b/Makefile index 6fe26858..ff903c33 100644 --- a/Makefile +++ b/Makefile @@ -73,18 +73,18 @@ build-ci: clean build-snapshot: clean BUILD_VERSION="$(BUILD_VERSION)" LDFLAGS="$(LDFLAGS)" go tool goreleaser --snapshot --clean --skip=publish --config .goreleaser.yml -# Update documentation in a commit tagged as the release -# Usage: `make tag RELEASE_VERSION=3.7.0-example` -.PHONY: tag -tag: +# Create a release candidate commit with updated docs +# Usage: `make rc RELEASE_VERSION=3.7.0-example` +.PHONY: rc +rc: git diff --quiet --cached git diff --quiet @if echo "$(RELEASE_VERSION)" | grep -q '^v'; then \ echo "Error: Release version should not begin with a version prefix."; \ exit 1; \ fi - @printf "$(FONT_BOLD)Creating Branch$(FONT_RESET)\n" - git checkout -b "chore-release-$(RELEASE_VERSION)" + @printf "$(FONT_BOLD)Building CLI$(FONT_RESET)\n" + $(MAKE) build-ci LDFLAGS="-X 'github.com/slackapi/slack-cli/internal/version.Version=v$(RELEASE_VERSION)'" @printf "$(FONT_BOLD)Updating Docs$(FONT_RESET)\n" rm -rf ./docs/reference/commands ./docs/reference/errors.md ./bin/slack docgen ./docs/reference --skip-update @@ -103,5 +103,3 @@ tag: git add docs/guides/installing-the-slack-cli-for-windows.md @printf "$(FONT_BOLD)Git Commit$(FONT_RESET)\n" git commit -m "chore: release slack-cli v$(RELEASE_VERSION)" -# @printf "$(FONT_BOLD)Git Tag$(FONT_RESET)\n" -# git tag v$(RELEASE_VERSION) From dce228cfcdf968a8d86a659bce4bb25a9fcd5845 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 16:18:52 -0700 Subject: [PATCH 2/5] test: attempt to release candidate from pull request --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5724d4ee..03110aeb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,7 @@ name: Release on: + pull_request: push: branches: - main From af2d652565473af7b41ab3bff52d0e94ac5f13ba Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 16:27:36 -0700 Subject: [PATCH 3/5] fix: use full refspec for pushing to rc branch Co-Authored-By: Claude --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 03110aeb..23443e37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -97,7 +97,7 @@ jobs: git config user.name "slack-cli-releaser[bot]" git config user.email "122933866+slack-cli-releaser[bot]@users.noreply.github.com" make rc RELEASE_VERSION="$RELEASE_VERSION" - git push origin "HEAD:rc" --force + git push origin "HEAD:refs/heads/rc" --force - name: Create or update release PR if: steps.version.outputs.has_changes == 'true' From 4ba87b193ca23f3751db40b28df873157c4c0829 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 16:31:14 -0700 Subject: [PATCH 4/5] fix: checkout main branch for rc generation Co-Authored-By: Claude --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 23443e37..83ec3556 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,6 +29,7 @@ jobs: with: fetch-depth: 0 persist-credentials: true + ref: main token: ${{ steps.credentials.outputs.token }} - name: Set up Go From 009d97ea662e84735b8fec25dd3b9ddc1aedf900 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 16:33:52 -0700 Subject: [PATCH 5/5] fix: call make tag until rc lands on main Co-Authored-By: Claude --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83ec3556..938998ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -97,7 +97,7 @@ jobs: run: | git config user.name "slack-cli-releaser[bot]" git config user.email "122933866+slack-cli-releaser[bot]@users.noreply.github.com" - make rc RELEASE_VERSION="$RELEASE_VERSION" + make tag RELEASE_VERSION="$RELEASE_VERSION" git push origin "HEAD:refs/heads/rc" --force - name: Create or update release PR