From 9d7121f98fb43f6b6f5ce4188953b1b9b28b0ce1 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Wed, 27 May 2026 11:13:29 +0100 Subject: [PATCH 1/4] Align repo orchestration with solana-program conventions This PR replaces system's bespoke `vars.env` + hand-rolled `Makefile` + in-repo composite setup action + per-repo publish workflows with the converged `solana-program/*` orchestration: a `Makefile` modelled on `program-metadata`'s, thin CI wrappers that delegate to reusable workflows in `solana-program/actions`, and `Cargo.toml`'s `[workspace.metadata]` blocks as the single source of truth for the nightly toolchain and Solana CLI version. The reusable workflows bring uniform format/lint/test/docs/powerset gates for free, NPM provenance and `cargo semver-checks` for publishing, and eliminate the need to maintain per-repo orchestration. The JS client's `test-js-%` target stays LiteSVM-flavoured (no validator dance) and `codama.mjs` reads the nightly from the Makefile so the toolchain pin lives in exactly one place. Repo admins will need to provision the `vars.APP_ID` + `secrets.PRIVATE_KEY` GitHub App and the `prod` environment with an NPM Trusted Publisher before the next publish under these workflows. --- .github/actions/setup/action.yml | 126 ---------------------- .github/workflows/main.yml | 117 ++++++-------------- .github/workflows/publish-js-client.yml | 99 ----------------- .github/workflows/publish-js.yml | 69 ++++++++++++ .github/workflows/publish-rust-client.yml | 124 --------------------- .github/workflows/publish-rust.yml | 79 ++++++++++++++ Cargo.toml | 6 ++ Makefile | 91 ++++++++++++---- README.md | 8 +- clients/js/README.md | 16 ++- clients/rust/README.md | 6 +- codama.json | 32 ------ codama.mjs | 36 +++++++ package.json | 3 +- pnpm-lock.yaml | 50 ++++----- scripts/cliff.toml | 58 ++++++++++ scripts/publish-js.sh | 38 ------- scripts/publish-rust.sh | 38 ------- vars.env | 2 - 19 files changed, 392 insertions(+), 606 deletions(-) delete mode 100644 .github/actions/setup/action.yml delete mode 100644 .github/workflows/publish-js-client.yml create mode 100644 .github/workflows/publish-js.yml delete mode 100644 .github/workflows/publish-rust-client.yml create mode 100644 .github/workflows/publish-rust.yml delete mode 100644 codama.json create mode 100644 codama.mjs create mode 100644 scripts/cliff.toml delete mode 100755 scripts/publish-js.sh delete mode 100755 scripts/publish-rust.sh delete mode 100644 vars.env diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml deleted file mode 100644 index 7587825e..00000000 --- a/.github/actions/setup/action.yml +++ /dev/null @@ -1,126 +0,0 @@ -name: Setup environment - -inputs: - cargo-cache-key: - description: The key to cache cargo dependencies. Skips cargo caching if not provided. - required: false - cargo-cache-fallback-key: - description: The fallback key to use when caching cargo dependencies. Default to not using a fallback key. - required: false - cargo-cache-local-key: - description: The key to cache local cargo dependencies. Skips local cargo caching if not provided. - required: false - pnpm: - description: Install pnpm if `true`. Defaults to `false`. - required: false - solana: - description: Install Solana if `true`. Defaults to `false`. - required: false - toolchain: - description: Rust toolchain to install. Comma-separated string of [`build`, `format`, `lint`, `test`]. - required: false - -runs: - using: 'composite' - steps: - - name: Set Environment Variables - shell: bash - run: | - source ./vars.env - echo "RUST_TOOLCHAIN_NIGHTLY=$RUST_TOOLCHAIN_NIGHTLY" >> "$GITHUB_ENV" - echo "SOLANA_CLI_VERSION=$SOLANA_CLI_VERSION" >> "$GITHUB_ENV" - - - name: Setup pnpm - if: ${{ inputs.pnpm == 'true' }} - uses: pnpm/action-setup@v3 - - - name: Setup Node.js - if: ${{ inputs.pnpm == 'true' }} - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: 'pnpm' - - - name: Install Dependencies - if: ${{ inputs.pnpm == 'true' }} - run: pnpm install --frozen-lockfile - shell: bash - - - name: Install Rust 'build' Toolchain - if: ${{ contains(inputs.toolchain, 'build') }} - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_TOOLCHAIN_NIGHTLY }} - - - name: Install Rust 'format' Toolchain - if: ${{ contains(inputs.toolchain, 'format') }} - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_TOOLCHAIN_NIGHTLY }} - components: rustfmt - - - name: Install Rust 'lint' Toolchain - if: ${{ contains(inputs.toolchain, 'lint') }} - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_TOOLCHAIN_NIGHTLY }} - components: clippy - - - name: Install Rust 'test' Toolchain - if: ${{ contains(inputs.toolchain, 'test') }} - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_TOOLCHAIN_NIGHTLY }} - - - name: Install Solana - if: ${{ inputs.solana == 'true' }} - uses: solana-program/actions/install-solana@v1 - with: - version: ${{ env.SOLANA_CLI_VERSION }} - cache: true - - - name: Install 'cargo-hack' - if: ${{ contains(inputs.toolchain, 'lint') }} - shell: bash - run: cargo install cargo-hack - - - name: Cache Cargo Dependencies - if: ${{ inputs.cargo-cache-key && !inputs.cargo-cache-fallback-key }} - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-${{ inputs.cargo-cache-key }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ inputs.cargo-cache-key }} - - - name: Cache Cargo Dependencies With Fallback - if: ${{ inputs.cargo-cache-key && inputs.cargo-cache-fallback-key }} - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-${{ inputs.cargo-cache-key }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-${{ inputs.cargo-cache-key }} - ${{ runner.os }}-${{ inputs.cargo-cache-fallback-key }}-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.os }}-${{ inputs.cargo-cache-fallback-key }} - - - name: Cache Local Cargo Dependencies - if: ${{ inputs.cargo-cache-local-key }} - uses: actions/cache@v4 - with: - path: | - .cargo/bin/ - .cargo/registry/index/ - .cargo/registry/cache/ - .cargo/git/db/ - key: ${{ runner.os }}-${{ inputs.cargo-cache-local-key }}-${{ hashFiles('**/Cargo.lock') }} - restore-keys: ${{ runner.os }}-${{ inputs.cargo-cache-local-key }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 90f6adc0..416e3269 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,98 +5,41 @@ on: branches: [main] pull_request: -jobs: - format_and_lint_client_js: - name: Format & Lint Client JS - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - pnpm: true - - - name: Format Client JS - run: make format-js - - - name: Lint Client JS - run: make lint-js - - format_and_lint_client_rust: - name: Format & Lint Client Rust - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - toolchain: format, lint - - - name: Format - run: make format-clients-rust - - - name: Lint / Clippy - run: make clippy-clients-rust - - - name: Lint / Docs - run: make lint-docs-clients-rust +env: + JS_PACKAGES: "['clients-js']" + RUST_PACKAGES: "['clients-rust']" - - name: Lint / Features - run: make lint-features-clients-rust - - generate_clients: - name: Check Client Generation +jobs: + set_env: + name: Set variables to be used in strategy definitions in reusable workflow runs-on: ubuntu-latest + outputs: + JS_PACKAGES: ${{ steps.compute.outputs.JS_PACKAGES }} + RUST_PACKAGES: ${{ steps.compute.outputs.RUST_PACKAGES }} + RUST_TOOLCHAIN_NIGHTLY: ${{ steps.compute.outputs.RUST_TOOLCHAIN_NIGHTLY }} + SOLANA_CLI_VERSION: ${{ steps.compute.outputs.SOLANA_CLI_VERSION }} steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Environment - uses: ./.github/actions/setup - with: - pnpm: true - toolchain: format - - - name: Generate Clients - run: make generate-clients + uses: solana-program/actions/setup-ubuntu@main - - name: Check Working Directory + - name: Compute variables + id: compute + shell: bash run: | - git status --porcelain - test -z "$(git status --porcelain)" - - test_client_js: - name: Test Client JS - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - pnpm: true - - - name: Test Client JS - run: make test-js - - test_client_rust: - name: Test Client 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-rust-client - toolchain: test - solana: true - - - name: Test Client Rust - run: make test-clients-rust + echo "JS_PACKAGES=${{ env.JS_PACKAGES }}" >> $GITHUB_OUTPUT + echo "RUST_PACKAGES=${{ env.RUST_PACKAGES }}" >> $GITHUB_OUTPUT + echo "RUST_TOOLCHAIN_NIGHTLY=$(make rust-toolchain-nightly)" >> "$GITHUB_OUTPUT" + echo "SOLANA_CLI_VERSION=$(make solana-cli-version)" >> "$GITHUB_OUTPUT" + + main: + needs: set_env + uses: solana-program/actions/.github/workflows/main.yml@main + with: + js-packages: ${{ needs.set_env.outputs.JS_PACKAGES }} + rust-packages: ${{ needs.set_env.outputs.RUST_PACKAGES }} + rustfmt-toolchain: ${{ needs.set_env.outputs.RUST_TOOLCHAIN_NIGHTLY }} + clippy-toolchain: ${{ needs.set_env.outputs.RUST_TOOLCHAIN_NIGHTLY }} + solana-cli-version: ${{ needs.set_env.outputs.SOLANA_CLI_VERSION }} diff --git a/.github/workflows/publish-js-client.yml b/.github/workflows/publish-js-client.yml deleted file mode 100644 index fa17f0c9..00000000 --- a/.github/workflows/publish-js-client.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Publish JS Client - -on: - workflow_dispatch: - inputs: - level: - description: Version level - required: true - default: patch - type: choice - options: - - patch - - minor - - major - - prerelease - - prepatch - - preminor - - premajor - tag: - description: NPM Tag (and preid for pre-releases) - required: true - type: string - default: latest - create_release: - description: Create a GitHub release - required: true - type: boolean - default: true - -jobs: - test_js: - name: Test JS client - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Setup Environment - uses: ./.github/actions/setup - with: - pnpm: true - - - name: Format - run: make format-js - - - name: Lint - run: make lint-js - - - name: Test - run: make test-js - - publish_js: - name: Publish JS client - runs-on: ubuntu-latest - needs: test_js - permissions: - contents: write - steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - token: ${{ secrets.ANZA_TEAM_PAT }} - - - name: Setup Environment - uses: ./.github/actions/setup - with: - pnpm: true - - - name: Ensure SOLANA_PROGRAM_NPM_TOKEN variable is set - env: - token: ${{ secrets.SOLANA_PROGRAM_NPM_TOKEN }} - if: ${{ env.token == '' }} - run: | - echo "The SOLANA_PROGRAM_NPM_TOKEN secret variable is not set" - echo "Go to \"Settings\" -> \"Secrets and variables\" -> \"Actions\" -> \"New repository secret\"." - exit 1 - - - name: NPM Authentication - run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" - env: - NODE_AUTH_TOKEN: ${{ secrets.SOLANA_PROGRAM_NPM_TOKEN }} - - - 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 JS Client - id: publish - run: ./scripts/publish-js.sh clients/js ${{ inputs.level }} ${{ inputs.tag }} - - - name: Push Commit and Tag - run: git push origin --follow-tags - - - name: Create GitHub release - if: github.event.inputs.create_release == 'true' - uses: ncipollo/release-action@v1 - with: - tag: js@v${{ steps.publish.outputs.new_version }} diff --git a/.github/workflows/publish-js.yml b/.github/workflows/publish-js.yml new file mode 100644 index 00000000..de3ac7b0 --- /dev/null +++ b/.github/workflows/publish-js.yml @@ -0,0 +1,69 @@ +name: Publish JS + +on: + workflow_dispatch: + inputs: + package-path: + description: Path to directory with package to release + required: true + default: 'clients/js' + type: choice + options: + - clients/js + level: + description: Version level + required: true + default: patch + type: choice + options: + - patch + - minor + - major + - prerelease + - prepatch + - preminor + - premajor + tag: + description: NPM Tag (and preid for pre-releases) + required: true + type: string + default: latest + create-release: + description: Create a GitHub release + required: true + type: boolean + default: true + +jobs: + set_env: + name: Set variables to be used in strategy definitions + runs-on: ubuntu-latest + outputs: + SOLANA_CLI_VERSION: ${{ steps.compute.outputs.SOLANA_CLI_VERSION }} + TARGET: ${{ steps.compute.outputs.TARGET }} + steps: + - name: Git Checkout + uses: actions/checkout@v6 + + - name: Summarise workflow inputs + uses: solana-program/actions/workflow-inputs@v1 + + - name: Compute variables + id: compute + shell: bash + run: | + echo "SOLANA_CLI_VERSION=$(make solana-cli-version)" >> "$GITHUB_OUTPUT" + TARGET=$(echo ${{ inputs.package-path }} | sed 's#/#-#') + echo "TARGET=$TARGET" >> "$GITHUB_OUTPUT" + + main: + needs: set_env + uses: solana-program/actions/.github/workflows/publish-js.yml@main + with: + solana-cli-version: ${{ needs.set_env.outputs.SOLANA_CLI_VERSION }} + target: ${{ needs.set_env.outputs.TARGET }} + package-path: ${{ inputs['package-path'] }} + level: ${{ inputs.level }} + tag: ${{ inputs.tag }} + create-release: ${{ inputs['create-release'] }} + secrets: inherit diff --git a/.github/workflows/publish-rust-client.yml b/.github/workflows/publish-rust-client.yml deleted file mode 100644 index cfa21981..00000000 --- a/.github/workflows/publish-rust-client.yml +++ /dev/null @@ -1,124 +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 - run: make format-clients-rust - - - name: Lint - run: make clippy-clients-rust - - - name: Test - run: make test-clients-rust - - publish_rust: - name: Publish Rust Client - runs-on: ubuntu-latest - needs: test_rust - permissions: - contents: write - steps: - - name: Git Checkout - uses: actions/checkout@v4 - with: - token: ${{ secrets.ANZA_TEAM_PAT }} - - - 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 - - ./scripts/publish-rust.sh clients/rust $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..305b11f2 --- /dev/null +++ b/.github/workflows/publish-rust.yml @@ -0,0 +1,79 @@ +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 + 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: + set_env: + name: Set variables to be used in strategy definitions + runs-on: ubuntu-latest + outputs: + RUST_TOOLCHAIN_NIGHTLY: ${{ steps.compute.outputs.RUST_TOOLCHAIN_NIGHTLY }} + SOLANA_CLI_VERSION: ${{ steps.compute.outputs.SOLANA_CLI_VERSION }} + TARGET: ${{ steps.compute.outputs.TARGET }} + steps: + - name: Git Checkout + uses: actions/checkout@v6 + + - name: Summarise workflow inputs + uses: solana-program/actions/workflow-inputs@v1 + + - name: Compute variables + id: compute + shell: bash + run: | + echo "RUST_TOOLCHAIN_NIGHTLY=$(make rust-toolchain-nightly)" >> "$GITHUB_OUTPUT" + echo "SOLANA_CLI_VERSION=$(make solana-cli-version)" >> "$GITHUB_OUTPUT" + TARGET=$(echo ${{ inputs['package-path'] }} | sed 's#/#-#') + echo "TARGET=$TARGET" >> "$GITHUB_OUTPUT" + + main: + needs: set_env + uses: solana-program/actions/.github/workflows/publish-rust.yml@main + with: + solana-cli-version: ${{ needs.set_env.outputs.SOLANA_CLI_VERSION }} + clippy-toolchain: ${{ needs.set_env.outputs.RUST_TOOLCHAIN_NIGHTLY }} + rustfmt-toolchain: ${{ needs.set_env.outputs.RUST_TOOLCHAIN_NIGHTLY }} + target: ${{ needs.set_env.outputs.TARGET }} + package-path: ${{ inputs['package-path'] }} + level: ${{ inputs.level }} + version: ${{ inputs.version }} + create-release: ${{ inputs['create-release'] }} + dry-run: ${{ inputs['dry-run'] }} + secrets: inherit diff --git a/Cargo.toml b/Cargo.toml index 35b78eb4..07e357f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,12 @@ homepage = "https://anza.xyz/" license = "Apache-2.0" edition = "2021" +[workspace.metadata.cli] +solana = "3.1.8" + +[workspace.metadata.toolchains] +nightly = "nightly-2026-01-22" + [workspace.dependencies] solana-account-info = "2.2.1" solana-cpi = "2.2.1" diff --git a/Makefile b/Makefile index fea5818c..cfb082d3 100644 --- a/Makefile +++ b/Makefile @@ -1,39 +1,86 @@ -include vars.env +RUST_TOOLCHAIN_NIGHTLY = $(shell toml get ./Cargo.toml workspace.metadata.toolchains.nightly) +SOLANA_CLI_VERSION = $(shell toml get ./Cargo.toml workspace.metadata.cli.solana) -nightly = +$(subst ",,${RUST_TOOLCHAIN_NIGHTLY}) +nightly = +${RUST_TOOLCHAIN_NIGHTLY} + +# This is a bit tricky -- findstring returns the found string, so we're looking +# for "directory-", returning that, and replacing "-" with "/" to change the +# first "-" to a "/". But if it isn't found, we replace "" with "", which works +# in the case where there is no subdirectory. +pattern-dir = $(firstword $(subst -, ,$1)) +find-pattern-dir = $(findstring $(call pattern-dir,$1)-,$1) +make-path = $(subst $(call find-pattern-dir,$1),$(subst -,/,$(call find-pattern-dir,$1)),$1) + +rust-toolchain-nightly: + @echo ${RUST_TOOLCHAIN_NIGHTLY} + +solana-cli-version: + @echo ${SOLANA_CLI_VERSION} + +cargo-nightly: + cargo $(nightly) $(ARGS) + +audit: + cargo audit \ + --ignore RUSTSEC-2022-0093 \ + --ignore RUSTSEC-2024-0344 \ + $(ARGS) clippy-%: - cargo $(nightly) clippy --manifest-path $(subst -,/,$*)/Cargo.toml + cargo $(nightly) clippy --manifest-path $(call make-path,$*)/Cargo.toml \ + --all-targets \ + --all-features \ + -- \ + --deny=warnings \ + --deny=clippy::arithmetic_side_effects $(ARGS) -format-%: - cargo $(nightly) fmt --check --manifest-path $(subst -,/,$*)/Cargo.toml +format-check-js-%: + cd $(call make-path,$*) && pnpm install && pnpm format $(ARGS) -format-%-fix: - cargo $(nightly) fmt --manifest-path $(subst -,/,$*)/Cargo.toml +format-check-%: + cargo $(nightly) fmt --check --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) -features-%: - cargo $(nightly) hack check --feature-powerset --all-targets --manifest-path $(subst -,/,$*)/Cargo.toml +powerset-%: + cargo $(nightly) hack check --feature-powerset --all-targets --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) -publish-%: - ./scripts/publish-rust.sh $(subst -,/,$*) +format-rust: + cargo $(nightly) fmt --all $(ARGS) -lint-docs-%: - RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo $(nightly) doc --all-features --no-deps --manifest-path $(subst -,/,$*)/Cargo.toml +build-doc-%: + RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo $(nightly) doc --all-features --no-deps --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) -lint-features-%: - cargo $(nightly) hack check --feature-powerset --all-targets --manifest-path $(subst -,/,$*)/Cargo.toml +# The JS client uses LiteSVM in-process, so we don't need to start a local +# validator. The System program is built into Solana and is loaded by LiteSVM +# automatically via the systemProgram() Kit plugin in test/_setup.ts. +test-js-%: + cd $(call make-path,$*) && pnpm install && pnpm build && pnpm test $(ARGS) test-%: - cargo $(nightly) test --manifest-path $(subst -,/,$*)/Cargo.toml + cargo $(nightly) test --manifest-path $(call make-path,$*)/Cargo.toml $(ARGS) + +lint-js-%: + cd $(call make-path,$*) && pnpm install && pnpm lint $(ARGS) generate-clients: pnpm codama run --all $(ARGS) -format-js: - cd ./clients/js && pnpm install && pnpm format +# Helpers for publishing +tag-name = $(lastword $(subst /, ,$(call make-path,$1))) +preid-arg = $(subst pre,--preid $2,$(findstring pre,$1)) +package-version = $(subst ",,$(shell jq -r '.version' $(call make-path,$1)/package.json)) +crate-version = $(subst ",,$(shell toml get $(call make-path,$1)/Cargo.toml package.version)) + +git-tag-js-%: + @echo "$(call tag-name,$*)@v$(call package-version,$*)" + +publish-js-%: + cd "$(call make-path,$*)" && pnpm install && pnpm version $(LEVEL) --no-git-tag-version $(call preid-arg,$(LEVEL),$(TAG)) && pnpm publish --no-git-checks --tag $(TAG) + +git-tag-rust-%: + @echo "$(call tag-name,$*)@v$(call crate-version,$*)" -lint-js: - cd ./clients/js && pnpm install && pnpm lint +publish-rust-dry-run-%: + cd "$(call make-path,$*)" && cargo release $(LEVEL) --tag-name "$(call tag-name,$*)@v{{version}}" -test-js: - cd ./clients/js && pnpm install && pnpm build && pnpm test +publish-rust-%: + cd "$(call make-path,$*)" && cargo release $(LEVEL) --tag-name "$(call tag-name,$*)@v{{version}}" --execute --no-confirm --dependent-version fix diff --git a/README.md b/README.md index fc1d1004..d987dfee 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,13 @@ The Solana System program and its generated clients. ## Project setup -The first thing you'll want to do is install NPM dependencies which will allow you to access all the scripts and tools provided by this template. +This repo uses a `Makefile` as its canonical command surface. Targets read the pinned nightly toolchain and Solana CLI version from `Cargo.toml`'s `[workspace.metadata]` blocks via [`toml-cli`](https://crates.io/crates/toml-cli), so you'll need it installed locally: + +```sh +cargo install toml-cli +``` + +Then install NPM dependencies to make the Codama renderers and per-client tooling available: ```sh pnpm install diff --git a/clients/js/README.md b/clients/js/README.md index 2c62454a..ada9cfec 100644 --- a/clients/js/README.md +++ b/clients/js/README.md @@ -4,23 +4,19 @@ A generated JavaScript library for the System program. ## Getting started -To build and test your JavaScript client from the root of the repository, you may use the following command. +The JS client tests use [LiteSVM](https://github.com/LiteSVM/litesvm) in-process, so no local validator is needed. From the root of the repository: ```sh -pnpm clients:js:test +make test-js-clients-js ``` -This will start a new local validator, if one is not already running, and run the tests for your JavaScript client. +This installs dependencies, builds the client, and runs the test suite. -## Available client scripts. +## Available client scripts -Alternatively, you can go into the client directory and run the tests directly. +Alternatively, you can go into the client directory and run the scripts directly. ```sh -# Start the validator. -pnpm validator:restart - -# Go into the client directory and run the tests. cd clients/js pnpm install pnpm build @@ -35,3 +31,5 @@ pnpm lint:fix pnpm format pnpm format:fix ``` + +Equivalent `make` targets from the repo root are `make lint-js-clients-js` and `make format-check-js-clients-js`. diff --git a/clients/rust/README.md b/clients/rust/README.md index c7b0d0bd..327cdd8b 100644 --- a/clients/rust/README.md +++ b/clients/rust/README.md @@ -4,10 +4,10 @@ A generated Rust library for the System program. ## Getting started -To build and test your Rust client from the root of the repository, you may use the following command. +To build and test the Rust client from the root of the repository: ```sh -pnpm clients:js:test +make test-clients-rust ``` -This will start a new local validator, if one is not already running, and run the tests for your Rust client. +Alternatively, you can `cd clients/rust` and use `cargo` directly. diff --git a/codama.json b/codama.json deleted file mode 100644 index 80c49d45..00000000 --- a/codama.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "idl": "program/idl.json", - "before": [], - "scripts": { - "js": { - "from": "@codama/renderers-js", - "args": [ - "clients/js", - { - "kitImportStrategy": "rootOnly", - "prettierOptions": { - "arrowParens": "avoid", - "printWidth": 120, - "singleQuote": true, - "tabWidth": 4 - } - } - ] - }, - "rust": { - "from": "@codama/renderers-rust", - "args": [ - "clients/rust", - { - "anchorTraits": false, - "formatCode": true, - "toolchain": "+nightly-2026-01-22" - } - ] - } - } -} diff --git a/codama.mjs b/codama.mjs new file mode 100644 index 00000000..3c49cf68 --- /dev/null +++ b/codama.mjs @@ -0,0 +1,36 @@ +import { execSync } from 'node:child_process'; + +const nightly = execSync('make --no-print-directory rust-toolchain-nightly').toString().trim(); + +export default { + idl: 'program/idl.json', + before: [], + scripts: { + js: { + from: '@codama/renderers-js', + args: [ + 'clients/js', + { + kitImportStrategy: 'rootOnly', + prettierOptions: { + arrowParens: 'avoid', + printWidth: 120, + singleQuote: true, + tabWidth: 4, + }, + }, + ], + }, + rust: { + from: '@codama/renderers-rust', + args: [ + 'clients/rust', + { + anchorTraits: false, + formatCode: true, + toolchain: `+${nightly}`, + }, + ], + }, + }, +}; diff --git a/package.json b/package.json index 1a080f39..c58c38b6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "devDependencies": { "@codama/renderers-js": "^2.2.0", "@codama/renderers-rust": "^3.0.0", - "codama": "^1.6.0" + "codama": "^1.6.0", + "typescript": "^5.9.3" }, "engines": { "node": ">=v20.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c4f214a..8db501c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,13 +10,16 @@ importers: devDependencies: '@codama/renderers-js': specifier: ^2.2.0 - version: 2.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + version: 2.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) '@codama/renderers-rust': specifier: ^3.0.0 - version: 3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + version: 3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) codama: specifier: ^1.6.0 version: 1.6.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 packages: @@ -249,8 +252,8 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true @@ -297,27 +300,27 @@ snapshots: '@codama/nodes': 1.5.0 '@codama/visitors-core': 1.5.0 - '@codama/renderers-js@2.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)': + '@codama/renderers-js@2.2.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: '@codama/errors': 1.5.0 '@codama/nodes': 1.5.0 '@codama/renderers-core': 1.3.5 '@codama/visitors-core': 1.5.0 - '@solana/codecs-strings': 6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@solana/codecs-strings': 6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) prettier: 3.8.1 semver: 7.7.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - '@codama/renderers-rust@3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)': + '@codama/renderers-rust@3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: '@codama/errors': 1.5.0 '@codama/nodes': 1.5.0 '@codama/renderers-core': 1.3.5 '@codama/visitors-core': 1.5.0 '@iarna/toml': 2.2.5 - '@solana/codecs-strings': 6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@solana/codecs-strings': 6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3) nunjucks: 3.2.4 semver: 7.7.3 transitivePeerDependencies: @@ -351,34 +354,34 @@ snapshots: '@iarna/toml@2.2.5': {} - '@solana/codecs-core@6.0.1(typescript@5.7.3)': + '@solana/codecs-core@6.0.1(typescript@5.9.3)': dependencies: - '@solana/errors': 6.0.1(typescript@5.7.3) + '@solana/errors': 6.0.1(typescript@5.9.3) optionalDependencies: - typescript: 5.7.3 + typescript: 5.9.3 - '@solana/codecs-numbers@6.0.1(typescript@5.7.3)': + '@solana/codecs-numbers@6.0.1(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.0.1(typescript@5.7.3) - '@solana/errors': 6.0.1(typescript@5.7.3) + '@solana/codecs-core': 6.0.1(typescript@5.9.3) + '@solana/errors': 6.0.1(typescript@5.9.3) optionalDependencies: - typescript: 5.7.3 + typescript: 5.9.3 - '@solana/codecs-strings@6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3)': + '@solana/codecs-strings@6.0.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.3)': dependencies: - '@solana/codecs-core': 6.0.1(typescript@5.7.3) - '@solana/codecs-numbers': 6.0.1(typescript@5.7.3) - '@solana/errors': 6.0.1(typescript@5.7.3) + '@solana/codecs-core': 6.0.1(typescript@5.9.3) + '@solana/codecs-numbers': 6.0.1(typescript@5.9.3) + '@solana/errors': 6.0.1(typescript@5.9.3) optionalDependencies: fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.7.3 + typescript: 5.9.3 - '@solana/errors@6.0.1(typescript@5.7.3)': + '@solana/errors@6.0.1(typescript@5.9.3)': dependencies: chalk: 5.6.2 commander: 14.0.3 optionalDependencies: - typescript: 5.7.3 + typescript: 5.9.3 a-sync-waterfall@1.0.1: {} @@ -516,5 +519,4 @@ snapshots: sisteransi@1.0.5: {} - typescript@5.7.3: - optional: true + typescript@5.9.3: {} diff --git a/scripts/cliff.toml b/scripts/cliff.toml new file mode 100644 index 00000000..62578ee4 --- /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/publish-js.sh b/scripts/publish-js.sh deleted file mode 100755 index 247a0ade..00000000 --- a/scripts/publish-js.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -library_path="${1:-}" -level="${2:-}" -tag="${3:-latest}" -dry_run="${DRY_RUN:-false}" - -if [[ -z "$library_path" || -z "$level" ]]; then - echo "Usage: $0 [tag]" - echo "Example: $0 clients/js patch beta" - exit 1 -fi - -cd "$library_path" -pnpm install - -# Build version args -version_args=(--no-git-tag-version) -if [[ "$level" == pre* ]]; then - version_args+=(--preid "$tag") -fi - -# Bump version and capture new version -new_version=$(pnpm version "$level" "${version_args[@]}" | tail -n1 | sed 's/^v//;s/\r$//') - -# CI output -if [[ -n "${CI:-}" ]]; then - echo "new_version=${new_version}" >> "$GITHUB_OUTPUT" -fi - -# Publish package -pnpm publish --no-git-checks --tag "$tag" - -# Git commit and tag -git commit -am "Publish JS client v${new_version}" -git tag -a "js@v${new_version}" -m "JS client v${new_version}" diff --git a/scripts/publish-rust.sh b/scripts/publish-rust.sh deleted file mode 100755 index ef778a64..00000000 --- a/scripts/publish-rust.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -library_path="${1:-}" -level="${2:-}" -dry_run="${DRY_RUN:-false}" - -if [[ -z "$library_path" || -z "$level" ]]; then - echo "Usage: $0 " - echo "Example: $0 clients/rust patch" - exit 1 -fi - -cd "$library_path" - -# Run cargo-release -if [[ "$dry_run" != "true" ]]; then - cargo release "$level" --no-push --no-tag --no-confirm --execute -else - cargo release "$level" - exit 0 -fi - -# Extract crate name and version using cargo metadata -metadata=$(cargo metadata --no-deps --format-version 1) -crate_name=$(echo "$metadata" | jq -r '.packages[0].name') -new_version=$(echo "$metadata" | jq -r '.packages[0].version') - -# CI output -if [[ -n "${CI:-}" ]]; then - echo "new_version=${new_version}" >> "$GITHUB_OUTPUT" -fi - -# Rebuild commit and tag -git reset --soft HEAD~1 -git commit -am "Publish ${crate_name} v${new_version}" -git tag -a "${crate_name}@v${new_version}" -m "${crate_name} v${new_version}" diff --git a/vars.env b/vars.env deleted file mode 100644 index 6b2b7c7e..00000000 --- a/vars.env +++ /dev/null @@ -1,2 +0,0 @@ -RUST_TOOLCHAIN_NIGHTLY="nightly-2026-01-22" -SOLANA_CLI_VERSION="3.1.8" From 6955867d861aa1f3d56357de79c27bfc04a637a7 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Wed, 27 May 2026 14:20:20 +0100 Subject: [PATCH 2/4] Fix CI by adding spellcheck plumbing and pinning audit ignores This commit fixes the two CI gates that were left unhandled by the orchestration alignment: spellcheck and audit. The reusable workflow at `solana-program/actions/.github/workflows/main.yml@main` runs `make spellcheck` and `make audit` unconditionally on every push, so both targets need to be present and pass. The spellcheck target is now wired up the same way as in `program-metadata` and `stake`, with a `scripts/spellcheck.toml` config and a `scripts/solana.dic` dictionary seeded from the union of both reference repos. The audit ignore list has been replaced with the set of RUSTSEC IDs that system's wider transitive dependency tree triggers (all in the `solana-client` RPC / TLS / pubsub stack), captured from a local run against an upgraded `cargo-audit` that understands CVSS 4.0. --- Makefile | 15 +++++++-- scripts/solana.dic | 72 +++++++++++++++++++++++++++++++++++++++++ scripts/spellcheck.toml | 6 ++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 scripts/solana.dic create mode 100644 scripts/spellcheck.toml diff --git a/Makefile b/Makefile index cfb082d3..790289cf 100644 --- a/Makefile +++ b/Makefile @@ -20,12 +20,23 @@ solana-cli-version: cargo-nightly: cargo $(nightly) $(ARGS) +# All ignored advisories are in transitive dependencies pulled in via +# `solana-client` and its RPC / TLS / pubsub stack. The generated +# `solana-system-client` surface itself does not depend on them directly. audit: cargo audit \ - --ignore RUSTSEC-2022-0093 \ - --ignore RUSTSEC-2024-0344 \ + --ignore RUSTSEC-2025-0141 \ + --ignore RUSTSEC-2026-0009 \ + --ignore RUSTSEC-2026-0037 \ + --ignore RUSTSEC-2026-0097 \ + --ignore RUSTSEC-2026-0098 \ + --ignore RUSTSEC-2026-0099 \ + --ignore RUSTSEC-2026-0104 \ $(ARGS) +spellcheck: + cargo spellcheck --code 1 $(ARGS) + clippy-%: cargo $(nightly) clippy --manifest-path $(call make-path,$*)/Cargo.toml \ --all-targets \ diff --git a/scripts/solana.dic b/scripts/solana.dic new file mode 100644 index 00000000..5288f8f2 --- /dev/null +++ b/scripts/solana.dic @@ -0,0 +1,72 @@ +1000 +activations +APY +arg/S +async +autogenerated +blockchain/S +blockhash/S +cli +codama +composability +config +cooldown +deallocated +decrypt/SGD +deserialization +deserialize/SRGD +entrypoint/S +enum/S +fn +IDL +infos +init +json +keypair/S +lamport/S +mergeable +metadata +metas +multisig/S +multisignature/S +namespace +noop/S +offchain +onchain +overallocate/SGD +param/S +pda/S +permissionless +pubkey/S +readme/S +realloc/S +redelegate +redelegated +redelegation +repo +representable +rpc +runtime +sdk +serde +sol/S +solana +spl +staker/S +struct/S +subcommand +svm +sysvar/S +timestamp/S +tuple/S +ui +undelegate +undelegated +unstake +unstaked +uri +validator/S +vec/S +warmup +withdrawer + diff --git a/scripts/spellcheck.toml b/scripts/spellcheck.toml new file mode 100644 index 00000000..67d80b07 --- /dev/null +++ b/scripts/spellcheck.toml @@ -0,0 +1,6 @@ +[Hunspell] +use_builtin = true +skip_os_lookups = false +search_dirs = ["."] +extra_dictionaries = ["solana.dic"] + From 201b1c53b6b9f8cbd5336959ceb3b43b74fcba2b Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Wed, 27 May 2026 15:02:47 +0100 Subject: [PATCH 3/4] Make cargo-spellcheck actually load the custom dictionary --- Cargo.toml | 3 +++ scripts/solana.dic | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 07e357f8..a46b9dba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ edition = "2021" [workspace.metadata.cli] solana = "3.1.8" +[workspace.metadata.spellcheck] +config = "scripts/spellcheck.toml" + [workspace.metadata.toolchains] nightly = "nightly-2026-01-22" diff --git a/scripts/solana.dic b/scripts/solana.dic index 5288f8f2..ac5fb013 100644 --- a/scripts/solana.dic +++ b/scripts/solana.dic @@ -40,6 +40,8 @@ permissionless pubkey/S readme/S realloc/S +recent_blockhashes +RecentBlockhashes redelegate redelegated redelegation From 9d2054b06ac6ec15e61e5ba2311d4cc36fad5f54 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Thu, 28 May 2026 08:27:00 +0100 Subject: [PATCH 4/4] Remove unnecessary cargo audit ignores --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index 790289cf..e697717d 100644 --- a/Makefile +++ b/Makefile @@ -25,10 +25,8 @@ cargo-nightly: # `solana-system-client` surface itself does not depend on them directly. audit: cargo audit \ - --ignore RUSTSEC-2025-0141 \ --ignore RUSTSEC-2026-0009 \ --ignore RUSTSEC-2026-0037 \ - --ignore RUSTSEC-2026-0097 \ --ignore RUSTSEC-2026-0098 \ --ignore RUSTSEC-2026-0099 \ --ignore RUSTSEC-2026-0104 \