From e374a090f144d4b305683ec6a295a0cea0e21653 Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Tue, 23 Dec 2025 09:57:52 +0100 Subject: [PATCH 1/2] feat: update release flow --- .github/workflows/bump-action.yml | 31 ----------- .github/workflows/create-draft-release.yml | 39 ++++++++++++++ .github/workflows/post-announce.yml | 60 ++++++++++++++++++++++ .github/workflows/release.yml | 29 +++++++---- CONTRIBUTING.md | 15 +++++- dist-workspace.toml | 6 ++- 6 files changed, 137 insertions(+), 43 deletions(-) delete mode 100644 .github/workflows/bump-action.yml create mode 100644 .github/workflows/create-draft-release.yml create mode 100644 .github/workflows/post-announce.yml diff --git a/.github/workflows/bump-action.yml b/.github/workflows/bump-action.yml deleted file mode 100644 index b6f9395b..00000000 --- a/.github/workflows/bump-action.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Bump action runner version - -on: - workflow_call: - inputs: - plan: - required: true - type: string - -jobs: - main: - runs-on: ubuntu-latest - env: - PLAN: ${{ inputs.plan }} - steps: - - name: Plan details - run: | - echo "Plan details: $PLAN" - - - name: Trigger action runner version bump workflow - env: - GH_TOKEN: ${{ secrets.PAT_CODSPEED_ACTION }} - run: | - IS_PRE_RELEASE=$(echo ${PLAN} | jq '.announcement_is_prerelease') - if [ "${IS_PRE_RELEASE}" == "true" ]; then - echo "Skipping action runner version bump for pre-releases" - exit 0 - fi - NEW_VERSION=$(echo ${PLAN} | jq '.releases[] | select(.app_name == "codspeed-runner") | .app_version') - # Trigger the bump-runner-version workflow in the CodSpeedHQ/actions repository - gh workflow run bump-runner-version.yml -R CodSpeedHQ/action -f version=${NEW_VERSION} diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml new file mode 100644 index 00000000..eb854c9b --- /dev/null +++ b/.github/workflows/create-draft-release.yml @@ -0,0 +1,39 @@ +name: Create draft release + +on: + workflow_call: + inputs: + plan: + required: true + type: string + +jobs: + main: + runs-on: ubuntu-latest + env: + PLAN: ${{ inputs.plan }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Plan details + run: | + echo "Plan details: $PLAN" + + - uses: actions/checkout@v4 + + - name: Create draft release + run: | + RELEASE_TAG=$(echo ${PLAN} | jq -r '.announcement_tag') + ANNOUNCEMENT_TITLE=$(echo ${PLAN} | jq -r '.announcement_title') + ANNOUNCEMENT_BODY=$(echo ${PLAN} | jq -r '.announcement_github_body') + + # Write body to file to avoid quoting issues + echo "$ANNOUNCEMENT_BODY" > /tmp/release-notes.txt + + echo "Creating draft release ${RELEASE_TAG}" + # The release will be undrafted by cargo-dist after it has uploaded the artifacts + gh release create "${RELEASE_TAG}" \ + --draft \ + --title "${ANNOUNCEMENT_TITLE}" \ + --notes-file /tmp/release-notes.txt + + echo "Release created successfully" diff --git a/.github/workflows/post-announce.yml b/.github/workflows/post-announce.yml new file mode 100644 index 00000000..5482b9d2 --- /dev/null +++ b/.github/workflows/post-announce.yml @@ -0,0 +1,60 @@ +name: Bump action runner version + +on: + workflow_call: + inputs: + plan: + required: true + type: string + +jobs: + main: + runs-on: ubuntu-latest + env: + PLAN: ${{ inputs.plan }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Plan details + run: | + echo "Plan details: $PLAN" + + - name: Check if runner was released + id: check_runner + run: | + IS_PRE_RELEASE=$(echo ${PLAN} | jq '.announcement_is_prerelease') + NEW_VERSION=$(echo ${PLAN} | jq -r '.releases[] | select(.app_name == "codspeed-runner") | .app_version') + RELEASE_TAG=$(echo ${PLAN} | jq -r '.announcement_tag') + + echo "is_pre_release=${IS_PRE_RELEASE}" >> "$GITHUB_OUTPUT" + echo "new_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" + echo "release_tag=${RELEASE_TAG}" >> "$GITHUB_OUTPUT" + + if [ "${IS_PRE_RELEASE}" == "true" ]; then + echo "runner_released=false" >> "$GITHUB_OUTPUT" + echo "Pre-release detected, cargo-dist has already undrafted this release, nothing more to do" + elif [ -z "${NEW_VERSION}" ] || [ "${NEW_VERSION}" == "null" ]; then + echo "runner_released=false" >> "$GITHUB_OUTPUT" + echo "A package other than the runner has been released, cargo-dist has already undrafted this release, nothing more to do" + else + echo "runner_released=true" >> "$GITHUB_OUTPUT" + echo "Runner version ${NEW_VERSION} was released" + fi + + - name: Mark release as latest + if: steps.check_runner.outputs.runner_released == 'true' + run: | + RELEASE_TAG="${{ steps.check_runner.outputs.release_tag }}" + REPO_OWNER=$(echo ${PLAN} | jq -r '.releases[0].hosting.github.owner') + REPO_NAME=$(echo ${PLAN} | jq -r '.releases[0].hosting.github.repo') + echo "Marking release ${RELEASE_TAG} as latest" + gh release edit "${RELEASE_TAG}" -R "${REPO_OWNER}/${REPO_NAME}" --latest + + - name: Trigger action runner version bump workflow + if: steps.check_runner.outputs.runner_released == 'true' + env: + GH_TOKEN: ${{ secrets.PAT_CODSPEED_ACTION }} + run: | + NEW_VERSION="${{ steps.check_runner.outputs.new_version }}" + echo "Triggering action runner version bump for version ${NEW_VERSION}" + # Trigger the bump-runner-version workflow in the CodSpeedHQ/action repository + gh workflow run bump-runner-version.yml -R CodSpeedHQ/action -f version=${NEW_VERSION} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0bf01019..62540dca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,8 +10,8 @@ # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # -# Note that the GitHub Release will be created with a generated -# title/body based on your changelogs. +# Note that a GitHub Release with this tag is assumed to exist as a draft +# with the appropriate title/body, and will be undrafted for you. name: Release permissions: @@ -165,11 +165,21 @@ jobs: ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} + custom-create-draft-release: + needs: + - plan + if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' }} + uses: ./.github/workflows/create-draft-release.yml + with: + plan: ${{ needs.plan.outputs.val }} + secrets: inherit + # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts + - custom-create-draft-release runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -216,9 +226,10 @@ jobs: needs: - plan - build-local-artifacts + - custom-create-draft-release - build-global-artifacts # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) - if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} + if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') && (needs.custom-create-draft-release.result == 'skipped' || needs.custom-create-draft-release.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-22.04" @@ -269,14 +280,12 @@ jobs: - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" - ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" - ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | - # Write and read notes from a file to avoid quoting breaking things - echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt + # If we're editing a release in place, we need to upload things ahead of time + gh release upload "${{ needs.plan.outputs.tag }}" artifacts/* - gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* + gh release edit "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --draft=false announce: needs: @@ -295,11 +304,11 @@ jobs: persist-credentials: false submodules: recursive - custom-bump-action: + custom-post-announce: needs: - plan - announce - uses: ./.github/workflows/bump-action.yml + uses: ./.github/workflows/post-announce.yml with: plan: ${{ needs.plan.outputs.val }} secrets: inherit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a9f3de4f..564126f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,6 +80,19 @@ cargo release --execute patch cargo release --execute beta ``` +### Release Flow Details + +When you run `cargo release --execute `, the following happens: + +1. **cargo-release** bumps the version, creates a commit and a git tag, then pushes them to GitHub +2. **GitHub Actions release workflow** triggers on the tag: + - Custom `cargo-dist` job creates a draft GitHub release + - `cargo-dist` builds artifacts for all platforms, uploads them to the draft release, and then publishes it +3. Only if it is a runner release: + - Custom post announce job marks it as "latest" and triggers action repo workflow + +This ensures only stable runner releases are marked as "latest" in GitHub. + ## Known issue -If one of the crates is currenlty in beta version, for example the runner is in beta version 4.4.2-beta.1, any alpha release will fail for the any crate, saying that only minor, major or patch releases is supported. +- If one of the crates is currenlty in beta version, for example the runner is in beta version 4.4.2-beta.1, any alpha release will fail for the any crate, saying that only minor, major or patch releases is supported. diff --git a/dist-workspace.toml b/dist-workspace.toml index 6c8ffe6c..f7f30358 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -16,13 +16,17 @@ unix-archive = ".tar.gz" # Which actions to run on pull requests pr-run-mode = "plan" # Post-announce jobs to run in CI -post-announce-jobs = ["./bump-action"] +post-announce-jobs = ["./post-announce"] # Path that installers should place binaries in install-path = "CARGO_HOME" # Whether to install an updater program install-updater = false # Build only the required packages, and individually precise-builds = true +# Don't create GitHub releases, we handle that manually to manually control which release is marked as latest +create-release = false +# Use the stage just after plan because we need its output to create the draft release +local-artifacts-jobs = ["./create-draft-release"] [dist.github-custom-runners] aarch64-unknown-linux-musl = "buildjet-2vcpu-ubuntu-2204-arm" From 7a1b311626fc77cfe7d9de8cb0d94d56fbadec3d Mon Sep 17 00:00:00 2001 From: Guillaume Lagrange Date: Tue, 23 Dec 2025 11:05:02 +0100 Subject: [PATCH 2/2] feat: add script to clean up alpha releases --- scripts/cleanup_alpha.sh | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 scripts/cleanup_alpha.sh diff --git a/scripts/cleanup_alpha.sh b/scripts/cleanup_alpha.sh new file mode 100755 index 00000000..34c406c7 --- /dev/null +++ b/scripts/cleanup_alpha.sh @@ -0,0 +1,106 @@ +#!/bin/bash +set -e + +echo "=== Alpha Release Cleanup Script ===" +echo "" + +# Fetch all alpha releases +echo "Fetching alpha releases..." +RELEASES=$(gh release list --limit 1000 | grep "alpha" | awk -F'\t' '{print $3}' || true) + +# Fetch all alpha tags +echo "Fetching alpha tags..." +git fetch --tags 2>/dev/null || true +TAGS=$(git tag -l "*alpha*" || true) + +# Combine and get unique names +ALL_ITEMS=$(echo -e "$RELEASES\n$TAGS" | sort -u | grep -v '^$' || true) + +echo "" +echo "=========================================" +echo "FOUND ITEMS:" +echo "=========================================" +echo "" + +if [ -z "$ALL_ITEMS" ]; then + echo "No alpha releases or tags found." + exit 0 +fi + +# Display each item with its status +for ITEM in $ALL_ITEMS; do + HAS_RELEASE="" + HAS_TAG="" + + if echo "$RELEASES" | grep -q "^${ITEM}$"; then + HAS_RELEASE="✓" + else + HAS_RELEASE="✗" + fi + + if echo "$TAGS" | grep -q "^${ITEM}$"; then + HAS_TAG="✓" + else + HAS_TAG="✗" + fi + + echo " - $ITEM [Release: $HAS_RELEASE | Tag: $HAS_TAG]" +done + +echo "" +echo "=========================================" +echo "" + +# Process each item +for ITEM in $ALL_ITEMS; do + HAS_RELEASE=false + HAS_TAG=false + + if echo "$RELEASES" | grep -q "^${ITEM}$"; then + HAS_RELEASE=true + fi + + if echo "$TAGS" | grep -q "^${ITEM}$"; then + HAS_TAG=true + fi + + read -p "Delete '$ITEM'? (y/N) " -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Delete release if it exists + if [ "$HAS_RELEASE" = true ]; then + echo -n " Deleting release... " + if gh release delete "$ITEM" --yes 2>/dev/null; then + echo "✓ deleted" + else + echo "✗ failed" + fi + fi + + # Delete remote tag if it exists + if [ "$HAS_TAG" = true ]; then + echo -n " Deleting remote tag... " + if git push origin ":refs/tags/$ITEM" 2>/dev/null; then + echo "✓ deleted" + else + echo "✗ failed (may not exist on remote)" + fi + + # Delete local tag + echo -n " Deleting local tag... " + if git tag -d "$ITEM" 2>/dev/null; then + echo "✓ deleted" + else + echo "✗ failed" + fi + fi + else + echo " Skipped." + fi + echo "" +done + +echo "=========================================" +echo "✓ Cleanup complete!" +echo "========================================="