From 02155207379d429f395c1d5d6b0860eb93bfbe49 Mon Sep 17 00:00:00 2001 From: Jon C Date: Thu, 6 Mar 2025 17:34:14 +0100 Subject: [PATCH] CI: Add automated publish job #### Problem It's time to publish spl-token that uses all of the component crates to make downstream users happy, but there's no publish job on the repo. #### Summary of changes This PR is as bit of a grab-bag of changes, but they're all to make the automated publish job work. The changes contained are: * move semver check job to only run during publish * update publish script to be generic, copying from the libraries repo * update testing script to be generic for rust packages by not running `cargo test-sbf` and instead doing it by hand, like in token-2022 * rename testing script to `test.mjs` * remove `test-sbf` feature from spl-token tests * update solana tools / toolchain versions --- .github/workflows/main.yml | 29 ++--- .github/workflows/publish-rust-client.yml | 122 ------------------- .github/workflows/publish-rust.yml | 140 ++++++++++++++++++++++ Cargo.toml | 11 +- package.json | 6 +- program/tests/assert_instruction_count.rs | 2 - program/tests/close_account.rs | 2 - program/tests/processor.rs | 2 - program/tests/setup.rs | 2 - rust-toolchain.toml | 2 +- scripts/rust/publish.mjs | 35 +++--- scripts/rust/{test-sbf.mjs => test.mjs} | 4 +- 12 files changed, 179 insertions(+), 178 deletions(-) delete mode 100644 .github/workflows/publish-rust-client.yml create mode 100644 .github/workflows/publish-rust.yml rename scripts/rust/{test-sbf.mjs => test.mjs} (56%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1a90eed6..52cfd078 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -81,26 +81,6 @@ jobs: - name: Run cargo-audit run: pnpm rust:audit - semver_rust: - name: Check semver Rust - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - cargo-cache-key: cargo-semver - - - name: Install cargo-audit - uses: taiki-e/install-action@v2 - with: - tool: cargo-semver-checks - - - name: Run semver checks - run: pnpm rust:semver - spellcheck_rust: name: Spellcheck Rust runs-on: ubuntu-latest @@ -214,7 +194,7 @@ jobs: test_program: name: Test Program runs-on: ubuntu-latest - needs: format_and_lint_program + needs: build_program steps: - name: Git Checkout uses: actions/checkout@v4 @@ -223,7 +203,12 @@ jobs: uses: ./.github/actions/setup with: cargo-cache-key: cargo-test-program - solana: true + + - name: Restore Program Builds + uses: actions/cache/restore@v4 + with: + path: ./**/*.so + key: ${{ runner.os }}-builds-${{ github.sha }} - name: Test run: pnpm programs:test diff --git a/.github/workflows/publish-rust-client.yml b/.github/workflows/publish-rust-client.yml deleted file mode 100644 index ee3f09d1..00000000 --- a/.github/workflows/publish-rust-client.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Publish Rust Client - -on: - workflow_dispatch: - inputs: - level: - description: Level - required: true - default: patch - type: choice - options: - - patch - - minor - - major - - rc - - beta - - alpha - - release - - version - version: - description: 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_rust: - name: Test Rust client - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - cargo-cache-key: cargo-rust-client - clippy: true - rustfmt: true - solana: true - - - name: Format Rust Client - run: pnpm clients:rust:format - - - name: Lint Rust Client - run: pnpm clients:rust:lint - - - name: Test Rust Client - run: pnpm clients:rust:test - - publish_rust: - name: Publish Rust Client - runs-on: ubuntu-latest - needs: test_rust - permissions: - contents: write - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - cargo-cache-key: cargo-publish-rust-client - cargo-cache-fallback-key: cargo-rust-client - clippy: true - rustfmt: true - - - name: Install Cargo Release - run: which cargo-release || cargo install cargo-release - - - name: Ensure CARGO_REGISTRY_TOKEN variable is set - env: - token: ${{ secrets.CARGO_REGISTRY_TOKEN }} - if: ${{ env.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 Rust Client - 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 clients:rust:publish $LEVEL $OPTIONS - - - name: Push Commit and Tag - if: github.event.inputs.dry_run != 'true' - run: git push origin --follow-tags - - - name: Create GitHub release - if: github.event.inputs.create_release == 'true' && github.event.inputs.dry_run != 'true' - uses: ncipollo/release-action@v1 - with: - tag: rust@v${{ steps.publish.outputs.new_version }} diff --git a/.github/workflows/publish-rust.yml b/.github/workflows/publish-rust.yml new file mode 100644 index 00000000..ecbf9865 --- /dev/null +++ b/.github/workflows/publish-rust.yml @@ -0,0 +1,140 @@ +name: Publish Rust Crate + +on: + workflow_dispatch: + inputs: + package_path: + description: Path to directory with package to release + required: true + default: 'clients/rust' + type: choice + options: + - clients/rust + - interface + - program + - p-token + level: + description: Level + required: true + default: patch + type: choice + options: + - patch + - minor + - major + 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: Install cargo-audit + uses: taiki-e/install-action@v2 + with: + tool: cargo-semver-checks + + - 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 programs + run: pnpm programs:build + + - name: Test + run: pnpm zx ./scripts/rust/test.mjs "${{ inputs.package_path }}" + + - name: Check semver + run: pnpm rust:semver ${{ inputs.package_path }} --release-type ${{ inputs.level }} + + publish: + name: Publish Rust Crate + runs-on: ubuntu-latest + needs: test + 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 + run: which cargo-release || cargo install 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.dry_run }}" == "true" ]; then + OPTIONS="--dry-run" + else + OPTIONS="" + fi + + pnpm rust:publish "${{ inputs.package_path }}" "${{ inputs.level }}" $OPTIONS + + - name: Generate a changelog + if: github.event.inputs.create_release == 'true' + uses: orhun/git-cliff-action@v3 + 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/Cargo.toml b/Cargo.toml index ec0aeabf..e18e2991 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,13 +10,18 @@ check-cfg = [ ] [workspace.metadata.cli] -solana = "2.1.0" +solana = "2.2.0" # Specify Rust toolchains for rustfmt, clippy, and build. # Any unprovided toolchains default to stable. [workspace.metadata.toolchains] -format = "nightly-2024-08-08" -lint = "nightly-2024-08-08" +format = "nightly-2024-11-22" +lint = "nightly-2024-11-22" [workspace.metadata.spellcheck] config = "scripts/spellcheck.toml" + +[workspace.metadata.release] +pre-release-commit-message = "Publish {{crate_name}} v{{version}}" +tag-message = "Publish {{crate_name}} v{{version}}" +consolidate-commits = false diff --git a/package.json b/package.json index 412f6a80..07c04bfd 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "programs:build": "zx ./scripts/rust/build-sbf.mjs program", "programs:format": "zx ./scripts/rust/format.mjs program", "programs:lint": "zx ./scripts/rust/lint.mjs program", - "programs:test": "zx ./scripts/rust/test-sbf.mjs program", + "programs:test": "zx ./scripts/rust/test.mjs program", "solana:check": "zx ./scripts/check-solana-version.mjs", "solana:link": "zx ./scripts/link-solana-version.mjs", "generate": "pnpm generate:clients", @@ -18,11 +18,11 @@ "clients:js:test": "zx ./scripts/js/test.mjs", "clients:rust:format": "zx ./scripts/rust/format.mjs clients/rust", "clients:rust:lint": "zx ./scripts/rust/lint.mjs clients/rust", - "clients:rust:publish": "zx ./scripts/rust/publish.mjs clients/rust", - "clients:rust:test": "zx ./scripts/rust/test-sbf.mjs clients/rust", + "clients:rust:test": "zx ./scripts/rust/test.mjs clients/rust", "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/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs index 0143a111..0fda1229 100644 --- a/program/tests/assert_instruction_count.rs +++ b/program/tests/assert_instruction_count.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "test-sbf")] - mod setup; use { diff --git a/program/tests/close_account.rs b/program/tests/close_account.rs index cb1ac551..17f03548 100644 --- a/program/tests/close_account.rs +++ b/program/tests/close_account.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "test-sbf")] - mod setup; use { diff --git a/program/tests/processor.rs b/program/tests/processor.rs index 3e8ad71b..025577a1 100644 --- a/program/tests/processor.rs +++ b/program/tests/processor.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "test-sbf")] - //! Program state processor tests use { diff --git a/program/tests/setup.rs b/program/tests/setup.rs index 4365dead..00677240 100644 --- a/program/tests/setup.rs +++ b/program/tests/setup.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "test-sbf")] - use { solana_sdk::{ account::Account as SolanaAccount, program_pack::Pack, pubkey::Pubkey, rent::Rent, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1de01fa4..fcb78ec5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.81.0" +channel = "1.84.1" diff --git a/scripts/rust/publish.mjs b/scripts/rust/publish.mjs index b7a9fa61..28bd91b8 100644 --- a/scripts/rust/publish.mjs +++ b/scripts/rust/publish.mjs @@ -3,18 +3,25 @@ import 'zx/globals'; import { cliArguments, getCargo, workingDirectory } from '../utils.mjs'; const dryRun = argv['dry-run'] ?? false; -const [level] = cliArguments(); +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. "path" — must be provided.'); + throw new Error('A version level — e.g. "patch" — must be provided.'); } -// Go to the client directory and install the dependencies. -cd(path.join(workingDirectory, 'clients', 'rust')); +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. +// Publish the new version, commit the repo change, tag it, and push it all. const releaseArgs = dryRun ? [] - : ['--no-push', '--no-tag', '--no-confirm', '--execute']; + : ['--tag-name', `${tagName}@v{{version}}`, '--no-confirm', '--execute']; await $`cargo release ${level} ${releaseArgs}`; // Stop here if this is a dry run. @@ -23,18 +30,12 @@ if (dryRun) { } // Get the new version. -const newVersion = getCargo(path.join('clients', 'rust')).package.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_version=${newVersion}" >> $GITHUB_OUTPUT`; + await $`echo "new_git_tag=${newGitTag}" >> $GITHUB_OUTPUT`; + await $`echo "old_git_tag=${oldGitTag}" >> $GITHUB_OUTPUT`; } - -// Soft reset the last commit so we can create our own commit and tag. -await $`git reset --soft HEAD~1`; - -// Commit the new version. -await $`git commit -am "Publish Rust client v${newVersion}"`; - -// Tag the new version. -await $`git tag -a rust@v${newVersion} -m "Rust client v${newVersion}"`; diff --git a/scripts/rust/test-sbf.mjs b/scripts/rust/test.mjs similarity index 56% rename from scripts/rust/test-sbf.mjs rename to scripts/rust/test.mjs index 1c118354..6142f0bb 100644 --- a/scripts/rust/test-sbf.mjs +++ b/scripts/rust/test.mjs @@ -3,6 +3,6 @@ import 'zx/globals'; import { cliArguments, workingDirectory } from '../utils.mjs'; const [folder, ...args] = cliArguments(); +const sbfOutDir = path.join(workingDirectory, 'target', 'deploy'); const manifestPath = path.join(workingDirectory, folder, 'Cargo.toml'); - -await $`RUST_LOG=error cargo test-sbf --manifest-path ${manifestPath} ${args}`; +await $`RUST_LOG=error SBF_OUT_DIR=${sbfOutDir} cargo test --manifest-path ${manifestPath} ${args}`;