From a44f243288e1cc0e7bf22802f01f08b812c059e4 Mon Sep 17 00:00:00 2001 From: Jon C Date: Thu, 31 Jul 2025 16:06:04 +0200 Subject: [PATCH 1/3] CI: Add publish workflow #### Problem The repo has no automated publishing workflow. #### Summary of changes Following how it's done at token-2022, add the workflow! This means adding a script, package.json entry, git-cliff configuration, and GH actions workflow file. Note that this requires #67 to land first. --- .github/workflows/publish-rust.yml | 179 +++++++++++++++++++++++++++++ package.json | 1 + scripts/cliff.toml | 58 ++++++++++ scripts/rust/publish.mjs | 41 +++++++ 4 files changed, 279 insertions(+) create mode 100644 .github/workflows/publish-rust.yml create mode 100644 scripts/cliff.toml create mode 100644 scripts/rust/publish.mjs diff --git a/.github/workflows/publish-rust.yml b/.github/workflows/publish-rust.yml new file mode 100644 index 0000000..39d72c4 --- /dev/null +++ b/.github/workflows/publish-rust.yml @@ -0,0 +1,179 @@ +name: Publish Rust Crate + +on: + workflow_dispatch: + inputs: + package_path: + description: Path to directory with package to release + required: true + default: 'clients/cli' + type: choice + options: + - interface + - program + level: + description: Level + required: true + default: patch + type: choice + options: + - patch + - minor + - major + - rc + - beta + - alpha + - release + - version + version: + description: Version (used with level "version") + required: false + type: string + dry_run: + description: Dry run + required: true + default: true + type: boolean + create_release: + description: Create a GitHub release + required: true + type: boolean + default: true + +jobs: + test: + name: Test Rust Crate + runs-on: ubuntu-latest + steps: + - name: Git Checkout + uses: actions/checkout@v4 + + - name: Setup Environment + uses: ./.github/actions/setup + with: + clippy: true + rustfmt: true + solana: true + cargo-cache-key: cargo-test-publish-${{ inputs.package_path }} + cargo-cache-fallback-key: cargo-test-publish + + - name: Format + run: pnpm zx ./scripts/rust/format.mjs "${{ inputs.package_path }}" + + - name: Lint + run: pnpm zx ./scripts/rust/lint.mjs "${{ inputs.package_path }}" + + - name: Build program + run: pnpm programs:build + + - name: Test + run: pnpm zx ./scripts/rust/test.mjs "${{ inputs.package_path }}" + + semver: + name: Check Semver + runs-on: ubuntu-latest + steps: + - name: Git checkout + uses: actions/checkout@v4 + + - name: Setup Environment + uses: ./.github/actions/setup + with: + cargo-cache-key: cargo-publish-semver-${{ inputs.package_path }} + cargo-cache-fallback-key: cargo-publish-semver + + - name: Install cargo-semver-checks + uses: taiki-e/install-action@v2 + with: + tool: cargo-semver-checks,cargo-release + + - name: Set Git Author (required for cargo-release) + run: | + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + + - name: Set Version + run: | + if [ "${{ inputs.level }}" == "version" ]; then + LEVEL=${{ inputs.version }} + else + LEVEL=${{ inputs.level }} + fi + cargo release $LEVEL --manifest-path "${{ inputs.package_path }}/Cargo.toml" --no-tag --no-publish --no-push --no-confirm --execute + + - name: Check semver + run: pnpm rust:semver --manifest-path "${{ inputs.package_path }}/Cargo.toml" + + publish: + name: Publish Rust Crate + runs-on: ubuntu-latest + needs: [test, semver] + permissions: + contents: write + steps: + - name: Git Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.ANZA_TEAM_PAT }} + fetch-depth: 0 # get the whole history for git-cliff + + - name: Setup Environment + uses: ./.github/actions/setup + with: + cargo-cache-key: cargo-publish-${{ inputs.package_path }} + cargo-cache-fallback-key: cargo-publish + + - name: Install cargo-release + uses: taiki-e/install-action@v2 + with: + tool: cargo-release + + - name: Ensure CARGO_REGISTRY_TOKEN variable is set + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + if: ${{ env.CARGO_REGISTRY_TOKEN == '' }} + run: | + echo "The CARGO_REGISTRY_TOKEN secret variable is not set" + echo "Go to \"Settings\" -> \"Secrets and variables\" -> \"Actions\" -> \"New repository secret\"." + exit 1 + + - name: Set Git Author + run: | + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + + - name: Publish Crate + id: publish + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: | + if [ "${{ inputs.level }}" == "version" ]; then + LEVEL=${{ inputs.version }} + else + LEVEL=${{ inputs.level }} + fi + + if [ "${{ inputs.dry_run }}" == "true" ]; then + OPTIONS="--dry-run" + else + OPTIONS="" + fi + + pnpm rust:publish "${{ inputs.package_path }}" $LEVEL $OPTIONS + + - name: Generate a changelog + if: github.event.inputs.create_release == 'true' + uses: orhun/git-cliff-action@v4 + with: + config: "scripts/cliff.toml" + args: "${{ steps.publish.outputs.old_git_tag }}"..main --include-path "${{ inputs.package_path }}/**" --github-repo "${{ github.repository }}" + env: + OUTPUT: TEMP_CHANGELOG.md + GITHUB_REPO: ${{ github.repository }} + + - name: Create GitHub release + if: github.event.inputs.create_release == 'true' && github.event.inputs.dry_run != 'true' + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.publish.outputs.new_git_tag }} + bodyFile: TEMP_CHANGELOG.md diff --git a/package.json b/package.json index cdcdb75..f488fe5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "template:upgrade": "zx ./scripts/upgrade-template.mjs", "rust:spellcheck": "cargo spellcheck --code 1", "rust:audit": "zx ./scripts/rust/audit.mjs", + "rust:publish": "zx ./scripts/rust/publish.mjs", "rust:semver": "cargo semver-checks" }, "devDependencies": { diff --git a/scripts/cliff.toml b/scripts/cliff.toml new file mode 100644 index 0000000..62578ee --- /dev/null +++ b/scripts/cliff.toml @@ -0,0 +1,58 @@ +# git-cliff configuration file +# https://git-cliff.org/docs/configuration +# +# Lines starting with "#" are comments. +# Configuration options are organized into tables and keys. +# See documentation for more information on available options. + +[changelog] +header = """ +## What's new +""" +# template for the changelog body +# https://tera.netlify.app/docs +body = """ +{% for group, commits in commits | group_by(attribute="group") %}\ + {% for commit in commits %} + - {{ commit.message | upper_first | split(pat="\n") | first | trim }}\ + {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\ + {% endfor %}\ +{% endfor %} +""" +# remove the leading and trailing whitespace from the template +trim = true +footer = """ +""" +postprocessors = [ ] +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = false +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^build\\(deps\\)", skip = true }, + { message = "^build\\(deps-dev\\)", skip = true }, + { message = "^ci", skip = true }, + { body = ".*", group = "Changes" }, +] +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false +# filter out the commits that are not matched by commit parsers +filter_commits = false +# glob pattern for matching git tags +tag_pattern = "v[0-9]*" +# regex for skipping tags +skip_tags = "" +# regex for ignoring tags +ignore_tags = "" +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "newest" +# limit the number of commits included in the changelog. +# limit_commits = 42 diff --git a/scripts/rust/publish.mjs b/scripts/rust/publish.mjs new file mode 100644 index 0000000..28bd91b --- /dev/null +++ b/scripts/rust/publish.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env zx +import 'zx/globals'; +import { cliArguments, getCargo, workingDirectory } from '../utils.mjs'; + +const dryRun = argv['dry-run'] ?? false; +const [folder, level] = cliArguments(); +if (!folder) { + throw new Error('A path to a directory with a Rust package — e.g. "clients/cli" — must be provided.'); +} +if (!level) { + throw new Error('A version level — e.g. "patch" — must be provided.'); +} + +cd(path.join(workingDirectory, folder)); + +const packageToml = getCargo(folder).package; +const oldVersion = packageToml.version; +const packageName = packageToml.name; +const tagName = path.basename(folder); + +// Publish the new version, commit the repo change, tag it, and push it all. +const releaseArgs = dryRun + ? [] + : ['--tag-name', `${tagName}@v{{version}}`, '--no-confirm', '--execute']; +await $`cargo release ${level} ${releaseArgs}`; + +// Stop here if this is a dry run. +if (dryRun) { + process.exit(0); +} + +// Get the new version. +const newVersion = getCargo(folder).package.version; +const newGitTag = `${tagName}@v${newVersion}`; +const oldGitTag = `${tagName}@v${oldVersion}`; + +// Expose the new version to CI if needed. +if (process.env.CI) { + await $`echo "new_git_tag=${newGitTag}" >> $GITHUB_OUTPUT`; + await $`echo "old_git_tag=${oldGitTag}" >> $GITHUB_OUTPUT`; +} From 94a5949d9615ffe91d6ad7985e72deadf36fcbde Mon Sep 17 00:00:00 2001 From: Jon C Date: Thu, 31 Jul 2025 16:12:29 +0200 Subject: [PATCH 2/3] Fix yaml --- .github/workflows/publish-rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-rust.yml b/.github/workflows/publish-rust.yml index 39d72c4..a93a4c9 100644 --- a/.github/workflows/publish-rust.yml +++ b/.github/workflows/publish-rust.yml @@ -166,7 +166,7 @@ jobs: uses: orhun/git-cliff-action@v4 with: config: "scripts/cliff.toml" - args: "${{ steps.publish.outputs.old_git_tag }}"..main --include-path "${{ inputs.package_path }}/**" --github-repo "${{ github.repository }}" + args: ${{ steps.publish.outputs.old_git_tag }}..HEAD --include-path "${{ inputs.package_path }}/**" --github-repo ${{ github.repository }} env: OUTPUT: TEMP_CHANGELOG.md GITHUB_REPO: ${{ github.repository }} From cede50452d8cc0e0a3d331a4d991a4a16b726dea Mon Sep 17 00:00:00 2001 From: Jon C Date: Fri, 1 Aug 2025 12:21:33 +0200 Subject: [PATCH 3/3] Proper default --- .github/workflows/publish-rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-rust.yml b/.github/workflows/publish-rust.yml index a93a4c9..8ffd449 100644 --- a/.github/workflows/publish-rust.yml +++ b/.github/workflows/publish-rust.yml @@ -6,7 +6,7 @@ on: package_path: description: Path to directory with package to release required: true - default: 'clients/cli' + default: 'interface' type: choice options: - interface