From f2dbe804d4b22e22a4a3569c046fc2e1a164de50 Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:07:48 +0000 Subject: [PATCH] Add a GitHub Actions workflow for publishing a release (#691) In #689, a GitHub Actions workflow was added for the first stage of the release process - preparing the release PR. Now, another workflow has been added for the second stage of the release process - publishing to https://crates.io and creating the GitHub Release. This workflow makes use of the `cargo-release` tool, which calculates the crate dependency graph for us, so we don't need to hardcode a crate publish ordering: https://github.com/crate-ci/cargo-release/blob/master/docs/reference.md This tool also skips any already-published crates, so if say a release failed halfway through publishing to crates.io, the job could be retriggered and would successfully resume publishing. The new workflow does however perform a single check to see if the GitHub Release already exists, to prevent accidentally re-running an already completed successfully release (which might move the git tag): https://cli.github.com/manual/gh_release_view Credentials are configured via the `CARGO_REGISTRY_TOKEN` env var, which has been set to the scoped crates.io API token of a service account. That service account has already been added to the `libcnb*` and `libherokbuildpack` crates: https://doc.rust-lang.org/cargo/reference/config.html#credentials https://crates.io/users/heroku-languages-release-bot https://github.com/heroku/languages-team/pull/146 The awk-based changelog extraction technique was inspired by: https://stackoverflow.com/questions/38972736/how-to-print-lines-between-two-patterns-inclusive-or-exclusive-in-sed-awk-or Closes #595. GUS-W-14177169. --- .github/workflows/release.yml | 66 +++++++++++++++++++++++++++++++++++ RELEASING.md | 22 ++---------- 2 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..5e1ff879 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,66 @@ +name: Release + +on: workflow_dispatch + +env: + CARGO_TERM_COLOR: always + +jobs: + release: + name: Release + # Prevent accidentally performing a release from a branch other than `main`. + if: github.ref == 'refs/heads/main' + runs-on: pub-hk-ubuntu-22.04-small + steps: + - name: Get token for GH application (Linguist) + uses: heroku/use-app-token-action@main + id: generate-token + with: + app_id: ${{ vars.LINGUIST_GH_APP_ID }} + private_key: ${{ secrets.LINGUIST_GH_PRIVATE_KEY }} + + - name: Checkout + uses: actions/checkout@v4 + with: + # Using the GH application token here will configure the local git config for this repo with credentials + # that can be used to make signed commits that are attributed to the GH application user + token: ${{ steps.generate-token.outputs.app_token }} + + - name: Update Rust toolchain + run: rustup update + + - name: Rust Cache + uses: Swatinem/rust-cache@v2.7.0 + + - name: Install cargo-release + run: cargo install cargo-release + + - name: Record new crate version + id: new-version + run: echo "version=$(yq '.workspace.package.version' Cargo.toml)" >> "${GITHUB_OUTPUT}" + + - name: Check GitHub release does not already exist + run: | + if gh release view 'v${{ steps.new-version.outputs.version }}' --json url --jq '.url'; then + echo "Aborting since a GitHub release already exists for v${{ steps.new-version.outputs.version }}!" >&2 + echo "If you are sure you want to recreate the release, delete the existing one first." >&2 + exit 1 + fi + + - name: Extract changelog entry + id: changelog-entry + run: echo "content=$(awk '/^## \[${{ steps.new-version.outputs.version }}\]/{flag=1; next} /^## /{flag=0} flag' CHANGELOG.md)" >> "${GITHUB_OUTPUT}" + + - name: Publish to crates.io + # cargo-release calculates the dependency graph for us, and also skips any already + # published packages - preventing overwrites and allowing for retrying the job. + run: cargo release publish --execute --no-confirm + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + - name: Create GitHub Release + uses: softprops/action-gh-release@v0.1.15 + with: + token: ${{ steps.generate-token.outputs.app_token }} + tag_name: v${{ steps.new-version.outputs.version }} + body: ${{ steps.changelog-entry.outputs.content }} diff --git a/RELEASING.md b/RELEASING.md index 64d0ad57..01225565 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -3,22 +3,6 @@ All crates are released at the same time and with the same version, even if there are no changes to a crate. This makes it easier to gauge cross-crate compatibility. -## Prepare Release - -1. Trigger the [Prepare release](https://github.com/heroku/libcnb.rs/actions/workflows/prepare-release.yml) GitHub Actions workflow with a suitable `{patch,minor,major}` version bump. - -## Release - -1. Once the release preparation PR has been opened, review it (including ensuring the changelog is accurate) and then merge. -2. On your local machine, run `git switch main && git pull` to ensure you're on the `main` branch with the latest changes -3. Create a (lightweight) Git tag for the release and push it: (i.e. for version `1.1.38`: `git tag v1.1.38 && git push origin v1.1.38`) -4. Use `cargo` to release all crates, making sure to release dependencies of other crates first: - 1. `cargo publish -p libcnb-common` - 2. `cargo publish -p libcnb-proc-macros` - 3. `cargo publish -p libcnb-data` - 4. `cargo publish -p libcnb-package` - 5. `cargo publish -p libcnb-cargo` - 6. `cargo publish -p libcnb-test` - 7. `cargo publish -p libcnb` - 8. `cargo publish -p libherokubuildpack` -5. Create a GitHub release from the tag created earlier. Use the markdown for the release from [CHANGELOG.md](./CHANGELOG.md) as the release description. +1. Trigger the [Prepare release](https://github.com/heroku/libcnb.rs/actions/workflows/prepare-release.yml) GitHub Actions workflow on `main`, with a suitable `{patch,minor,major}` version bump. +2. Once the release preparation PR has been opened, review it (including ensuring the changelog is accurate) and then merge. +3. Trigger the [Release](https://github.com/heroku/libcnb.rs/actions/workflows/release.yml) GitHub Actions workflow on `main`, which will publish the crates to and create a GitHub Release.