From 4adf4662648f7a8c765980ab974c30ec4e493a21 Mon Sep 17 00:00:00 2001 From: Loris Leiva Date: Thu, 25 Jul 2024 14:59:25 +0100 Subject: [PATCH] Add CLI versions to workspace Cargo.toml and Solana check scripts (#75) --- .changeset/cuddly-cheetahs-kick.md | 5 ++ .../base/.github/actions/setup/action.yml.njk | 23 ++++---- template/base/.github/workflows/main.yml.njk | 33 +++-------- template/base/Cargo.toml.njk | 6 ++ template/base/package.json | 4 +- .../base/scripts/check-solana-version.mjs | 18 ++++++ template/base/scripts/ci/set-env.mjs | 5 -- template/base/scripts/ci/set-env.mjs.njk | 13 +++++ template/base/scripts/link-solana-version.mjs | 56 +++++++++++++++++++ template/base/scripts/utils.mjs | 17 ++++++ .../clients/base/scripts/start-validator.mjs | 3 + .../clients/js/clients/js/package.json.njk | 4 +- 12 files changed, 142 insertions(+), 45 deletions(-) create mode 100644 .changeset/cuddly-cheetahs-kick.md create mode 100644 template/base/scripts/check-solana-version.mjs delete mode 100644 template/base/scripts/ci/set-env.mjs create mode 100644 template/base/scripts/ci/set-env.mjs.njk create mode 100644 template/base/scripts/link-solana-version.mjs diff --git a/.changeset/cuddly-cheetahs-kick.md b/.changeset/cuddly-cheetahs-kick.md new file mode 100644 index 0000000..70cdb96 --- /dev/null +++ b/.changeset/cuddly-cheetahs-kick.md @@ -0,0 +1,5 @@ +--- +"create-solana-program": patch +--- + +Add CLI versions to workspace Cargo.toml and Solana check scripts diff --git a/template/base/.github/actions/setup/action.yml.njk b/template/base/.github/actions/setup/action.yml.njk index 4a3bca7..0c17c8b 100644 --- a/template/base/.github/actions/setup/action.yml.njk +++ b/template/base/.github/actions/setup/action.yml.njk @@ -3,7 +3,7 @@ name: Setup environment inputs: {% if programFramework === 'anchor' %} anchor: - description: The Anchor version to install. Skips if not provided. + description: Install Anchor if `true`. Defaults to `false`. required: false {% endif %} cargo-cache-key: @@ -18,14 +18,11 @@ inputs: clippy: description: Install Clippy if `true`. Defaults to `false`. required: false - node: - description: The Node.js version to install. Required. - required: true rustfmt: description: Install Rustfmt if `true`. Defaults to `false`. required: false solana: - description: The Solana version to install. Skips if not provided. + description: Install Solana if `true`. Defaults to `false`. required: false runs: @@ -37,7 +34,7 @@ runs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: {% raw %}${{ inputs.node }}{% endraw %} + node-version: 18 cache: 'pnpm' - name: Install Dependencies @@ -50,13 +47,13 @@ runs: {% if solanaVersion.withoutPatch === '2.0' %} - name: Install Protobuf Compiler (Temporary Workaround for Solana 2.0) - if: {% raw %}${{ inputs.solana || inputs.rustfmt == 'true' || inputs.clippy == 'true' }}{% endraw %} + if: {% raw %}${{ inputs.solana == 'true' || inputs.rustfmt == 'true' || inputs.clippy == 'true' }}{% endraw %} shell: bash run: | sudo apt-get update sudo apt-get install -y protobuf-compiler -{% endif %} +{% endif %} - name: Install Rustfmt if: {% raw %}${{ inputs.rustfmt == 'true' }}{% endraw %} uses: dtolnay/rust-toolchain@master @@ -72,21 +69,21 @@ runs: components: clippy - name: Install Solana - if: {% raw %}${{ inputs.solana }}{% endraw %} + if: {% raw %}${{ inputs.solana == 'true' }}{% endraw %} uses: metaplex-foundation/actions/install-solana@v1 with: - version: {% raw %}${{ inputs.solana }}{% endraw %} + version: {% raw %}${{ env.SOLANA_VERSION }}{% endraw %} cache: true {% if programFramework === 'anchor' %} - name: Install Anchor - if: {% raw %}${{ inputs.anchor != '' }}{% endraw %} + if: {% raw %}${{ inputs.anchor == 'true' }}{% endraw %} uses: metaplex-foundation/actions/install-anchor-cli@v1 with: - version: {% raw %}${{ inputs.anchor }}{% endraw %} + version: {% raw %}${{ env.ANCHOR_VERSION }}{% endraw %} cache: true -{% endif %} +{% endif %} - name: Cache Cargo Dependencies if: {% raw %}${{ inputs.cargo-cache-key && !inputs.cargo-cache-fallback-key }}{% endraw %} uses: actions/cache@v4 diff --git a/template/base/.github/workflows/main.yml.njk b/template/base/.github/workflows/main.yml.njk index d8a600a..874c63e 100644 --- a/template/base/.github/workflows/main.yml.njk +++ b/template/base/.github/workflows/main.yml.njk @@ -6,13 +6,6 @@ on: pull_request: branches: [main] -env: - NODE_VERSION: 18 - SOLANA_VERSION: {{ solanaVersion.full }} -{% if programFramework === 'anchor' %} - ANCHOR_VERSION: {{ anchorVersion.full }} -{% endif %} - jobs: format_and_lint_programs: name: Format & Lint Programs @@ -25,7 +18,6 @@ jobs: uses: ./.github/actions/setup with: clippy: true - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} rustfmt: true - name: Format Programs @@ -44,8 +36,6 @@ jobs: - name: Setup Environment uses: ./.github/actions/setup - with: - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} - name: Format Client JS run: pnpm clients:js:format @@ -66,7 +56,6 @@ jobs: uses: ./.github/actions/setup with: clippy: true - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} rustfmt: true - name: Format Client Rust @@ -88,10 +77,9 @@ jobs: uses: ./.github/actions/setup with: cargo-cache-key: cargo-programs - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} - solana: {% raw %}${{ env.SOLANA_VERSION }}{% endraw %} + solana: true {% if programFramework === 'anchor' %} - anchor: {% raw %}${{ env.ANCHOR_VERSION }}{% endraw %} + anchor: true {% endif %} - name: Build Programs @@ -123,10 +111,9 @@ jobs: with: cargo-cache-key: cargo-program-tests cargo-cache-fallback-key: cargo-programs - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} - solana: {% raw %}${{ env.SOLANA_VERSION }}{% endraw %} + solana: true {% if programFramework === 'anchor' %} - anchor: {% raw %}${{ env.ANCHOR_VERSION }}{% endraw %} + anchor: true {% endif %} - name: Test Programs @@ -145,10 +132,9 @@ jobs: with: cargo-cache-key: cargo-programs cargo-cache-local-key: cargo-local - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} {% if programFramework === 'anchor' %} - solana: {% raw %}${{ env.SOLANA_VERSION }}{% endraw %} - anchor: {% raw %}${{ env.ANCHOR_VERSION }}{% endraw %} + solana: true + anchor: true {% endif %} - name: Generate IDLs @@ -171,7 +157,6 @@ jobs: - name: Setup Environment uses: ./.github/actions/setup with: - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} rustfmt: true - name: Generate Clients @@ -195,8 +180,7 @@ jobs: - name: Setup Environment uses: ./.github/actions/setup with: - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} - solana: {% raw %}${{ env.SOLANA_VERSION }}{% endraw %} + solana: true - name: Restore Program Builds uses: actions/cache/restore@v4 @@ -221,8 +205,7 @@ jobs: uses: ./.github/actions/setup with: cargo-cache-key: cargo-rust-client - node: {% raw %}${{ env.NODE_VERSION }}{% endraw %} - solana: {% raw %}${{ env.SOLANA_VERSION }}{% endraw %} + solana: true - name: Restore Program Builds uses: actions/cache/restore@v4 diff --git a/template/base/Cargo.toml.njk b/template/base/Cargo.toml.njk index f97be20..5bc3e48 100644 --- a/template/base/Cargo.toml.njk +++ b/template/base/Cargo.toml.njk @@ -11,6 +11,12 @@ members = ["program"] overflow-checks = true {% endif %} +[workspace.metadata.cli] +{% if programFramework === 'anchor' %} +anchor = "{{ anchorVersion.full }}" +{% endif %} +solana = "{{ solanaVersion.full }}" + # Specify Rust toolchains for rustfmt, clippy, and build. # Any unprovided toolchains default to stable. [workspace.metadata.toolchains] diff --git a/template/base/package.json b/template/base/package.json index ad7d959..882841a 100644 --- a/template/base/package.json +++ b/template/base/package.json @@ -6,7 +6,9 @@ "programs:clean": "zx ./scripts/program/clean.mjs", "programs:format": "zx ./scripts/program/format.mjs", "programs:lint": "zx ./scripts/program/lint.mjs", - "generate:idls": "zx ./scripts/generate-idls.mjs" + "generate:idls": "zx ./scripts/generate-idls.mjs", + "solana:check": "zx ./scripts/check-solana-version.mjs", + "solana:link": "zx ./scripts/link-solana-version.mjs" }, "devDependencies": { "@iarna/toml": "^2.2.5", diff --git a/template/base/scripts/check-solana-version.mjs b/template/base/scripts/check-solana-version.mjs new file mode 100644 index 0000000..f0844be --- /dev/null +++ b/template/base/scripts/check-solana-version.mjs @@ -0,0 +1,18 @@ +#!/usr/bin/env zx +import 'zx/globals'; +import { getInstalledSolanaVersion, getSolanaVersion } from './utils.mjs'; + +const expectedVersion = getSolanaVersion(); +const installedVersion = await getInstalledSolanaVersion(); + +if (installedVersion !== expectedVersion) { + echo( + chalk.yellow('[ WARNING ]'), + `The installed Solana version ${installedVersion} does not match the expected version ${expectedVersion}.` + ); +} else { + echo( + chalk.green('[ SUCCESS ]'), + `The expected Solana version ${expectedVersion} is installed.` + ); +} diff --git a/template/base/scripts/ci/set-env.mjs b/template/base/scripts/ci/set-env.mjs deleted file mode 100644 index fce8848..0000000 --- a/template/base/scripts/ci/set-env.mjs +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env zx -import { getToolchain } from '../utils.mjs'; - -await $`echo "TOOLCHAIN_FORMAT=${getToolchain('format')}" >> $GITHUB_ENV`; -await $`echo "TOOLCHAIN_LINT=${getToolchain('lint')}" >> $GITHUB_ENV`; diff --git a/template/base/scripts/ci/set-env.mjs.njk b/template/base/scripts/ci/set-env.mjs.njk new file mode 100644 index 0000000..1a80f6b --- /dev/null +++ b/template/base/scripts/ci/set-env.mjs.njk @@ -0,0 +1,13 @@ +#!/usr/bin/env zx +{% if programFramework === 'anchor' %} +import { getCargoMetadata, getSolanaVersion, getToolchain } from '../utils.mjs'; +{% else %} +import { getSolanaVersion, getToolchain } from '../utils.mjs'; +{% endif %} + +{% if programFramework === 'anchor' %} +await $`echo "ANCHOR_VERSION=${getCargoMetadata()?.cli?.anchor}" >> $GITHUB_ENV`; +{% endif %} +await $`echo "SOLANA_VERSION=${getSolanaVersion()}" >> $GITHUB_ENV`; +await $`echo "TOOLCHAIN_FORMAT=${getToolchain('format')}" >> $GITHUB_ENV`; +await $`echo "TOOLCHAIN_LINT=${getToolchain('lint')}" >> $GITHUB_ENV`; diff --git a/template/base/scripts/link-solana-version.mjs b/template/base/scripts/link-solana-version.mjs new file mode 100644 index 0000000..acc0a04 --- /dev/null +++ b/template/base/scripts/link-solana-version.mjs @@ -0,0 +1,56 @@ +#!/usr/bin/env zx +import 'zx/globals'; +import { getInstalledSolanaVersion, getSolanaVersion } from './utils.mjs'; + +const installedVersion = await getInstalledSolanaVersion(); +const expectedVersion = getSolanaVersion(); + +if (installedVersion === expectedVersion) { + echo( + chalk.green('[ SUCCESS ]'), + `The expected Solana version ${expectedVersion} is installed.` + ); + process.exit(0); +} + +const installPath = path.join( + os.homedir(), + '.local', + 'share', + 'solana', + 'install' +); +const releasePath = path.join( + installPath, + 'releases', + expectedVersion, + 'solana-release' +); +const activeReleasePath = path.join(installPath, 'active_release'); +const hasRelease = await fs.exists(releasePath); + +if (hasRelease) { + await $`rm -f "${activeReleasePath}"`; + await $`ln -s "${releasePath}" "${activeReleasePath}"`; + echo( + chalk.green('[ SUCCESS ]'), + `Successfully switched from Solana version ${installedVersion} to ${expectedVersion} to match the project's requirements.` + ); + process.exit(0); +} + +echo( + chalk.yellow('[ WARNING ]'), + `Cannot switch from Solana version ${installedVersion} to ${expectedVersion} because it is not installed.` +); + +const installRelease = await question('Should we install it now? [y/N] '); +if (installRelease === 'y') { + echo(`Installing Solana ${expectedVersion}...`); + await $`sh -c "$(curl -sSfL https://release.solana.com/v${expectedVersion}/install)"`; +} + +echo( + chalk.green('[ SUCCESS ]'), + `Successfully switched from Solana version ${installedVersion} to ${expectedVersion} to match the project's requirements.` +); diff --git a/template/base/scripts/utils.mjs b/template/base/scripts/utils.mjs index d72935c..274f217 100644 --- a/template/base/scripts/utils.mjs +++ b/template/base/scripts/utils.mjs @@ -84,6 +84,10 @@ export function getCargoMetadata(folder) { return folder ? cargo?.package?.metadata : cargo?.workspace?.metadata; } +export function getSolanaVersion() { + return getCargoMetadata()?.cli?.solana; +} + export function getToolchain(operation) { return getCargoMetadata()?.toolchains?.[operation]; } @@ -111,3 +115,16 @@ export function partitionArguments(args, delimiter) { ? [args.slice(0, index), args.slice(index + 1)] : [args, []]; } + +export async function getInstalledSolanaVersion() { + try { + const { stdout } = await $`solana --version`.quiet(); + return stdout.match(/(\d+\.\d+\.\d+)/)?.[1]; + } catch (error) { + echo( + chalk.red('[ ERROR ]'), + `No Solana installation found. Please install Solana ${getSolanaVersion()} before proceeding.` + ); + process.exit(1); + } +} diff --git a/template/clients/base/scripts/start-validator.mjs b/template/clients/base/scripts/start-validator.mjs index 278379f..1adbcab 100755 --- a/template/clients/base/scripts/start-validator.mjs +++ b/template/clients/base/scripts/start-validator.mjs @@ -10,6 +10,9 @@ import { getProgramFolders, } from './utils.mjs'; +// Check Solana version. +await $`pnpm solana:check`; + // Options and arguments. const restart = argv['restart']; diff --git a/template/clients/js/clients/js/package.json.njk b/template/clients/js/clients/js/package.json.njk index 18b54dd..721f00b 100644 --- a/template/clients/js/clients/js/package.json.njk +++ b/template/clients/js/clients/js/package.json.njk @@ -54,7 +54,9 @@ "typescript": "^5.5.3" }, "ava": { - "nodeArguments": ["--no-warnings"], + "nodeArguments": [ + "--no-warnings" + ], "require": [ "@solana/webcrypto-ed25519-polyfill" ],