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..a46b9dba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,15 @@ homepage = "https://anza.xyz/" license = "Apache-2.0" edition = "2021" +[workspace.metadata.cli] +solana = "3.1.8" + +[workspace.metadata.spellcheck] +config = "scripts/spellcheck.toml" + +[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..e697717d 100644 --- a/Makefile +++ b/Makefile @@ -1,39 +1,95 @@ -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) + +# 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-2026-0009 \ + --ignore RUSTSEC-2026-0037 \ + --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 $(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/scripts/solana.dic b/scripts/solana.dic new file mode 100644 index 00000000..ac5fb013 --- /dev/null +++ b/scripts/solana.dic @@ -0,0 +1,74 @@ +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 +recent_blockhashes +RecentBlockhashes +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"] + 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"