diff --git a/package.json b/package.json index aa36e66..c740f2d 100644 --- a/package.json +++ b/package.json @@ -8,25 +8,25 @@ "validator:start": "tsx ./scripts/helpers/start-validator.mts", "validator:restart": "pnpm validator:start --restart", "validator:stop": "tsx ./scripts/helpers/stop-validator.mts", - "js:format": "tsx ./scripts/js/format.mts", - "js:lint": "tsx ./scripts/js/lint.mts", - "js:publish": "tsx ./scripts/js/publish.mts", - "js:test": "tsx ./scripts/js/test.mts", - "rust:format": "tsx ./scripts/rust/format.mts clients/rust", - "rust:lint": "tsx ./scripts/rust/lint.mts clients/rust", - "rust:lint:clippy": "tsx ./scripts/rust/lint-clippy.mts clients/rust", - "rust:lint:docs": "tsx ./scripts/rust/lint-docs.mts clients/rust", - "rust:lint:features": "tsx ./scripts/rust/lint-features.mts clients/rust", - "rust:publish": "tsx ./scripts/rust/publish.mts clients/rust", - "rust:test": "tsx ./scripts/rust/test.mts clients/rust", - "interface:format": "tsx ./scripts/rust/format.mts interface", - "interface:lint": "tsx ./scripts/rust/lint.mts interface", - "interface:lint:clippy": "tsx ./scripts/rust/lint-clippy.mts interface", - "interface:lint:docs": "tsx ./scripts/rust/lint-docs.mts interface", - "interface:lint:features": "tsx ./scripts/rust/lint-features.mts interface", - "interface:publish": "tsx ./scripts/rust/publish.mts interface", - "interface:test": "tsx ./scripts/rust/test.mts interface", - "interface:wasm": "tsx ./scripts/rust/wasm.mts interface", + "js:format": "tsx ./scripts/js.mts format clients/js", + "js:lint": "tsx ./scripts/js.mts lint clients/js", + "js:publish": "tsx ./scripts/js.mts publish clients/js", + "js:test": "tsx ./scripts/js.mts test clients/js", + "rust:format": "tsx ./scripts/rust.mts format clients/rust", + "rust:lint": "tsx ./scripts/rust.mts lint clients/rust", + "rust:lint:clippy": "tsx ./scripts/rust.mts lint-clippy clients/rust", + "rust:lint:docs": "tsx ./scripts/rust.mts lint-docs clients/rust", + "rust:lint:features": "tsx ./scripts/rust.mts lint-features clients/rust", + "rust:publish": "tsx ./scripts/rust.mts publish clients/rust", + "rust:test": "tsx ./scripts/rust.mts test clients/rust", + "interface:format": "tsx ./scripts/rust.mts format interface", + "interface:lint": "tsx ./scripts/rust.mts lint interface", + "interface:lint:clippy": "tsx ./scripts/rust.mts lint-clippy interface", + "interface:lint:docs": "tsx ./scripts/rust.mts lint-docs interface", + "interface:lint:features": "tsx ./scripts/rust.mts lint-features interface", + "interface:publish": "tsx ./scripts/rust.mts publish interface", + "interface:test": "tsx ./scripts/rust.mts test interface", + "interface:wasm": "tsx ./scripts/rust.mts wasm interface", "template:upgrade": "tsx ./scripts/helpers/upgrade-template.ts" }, "devDependencies": { diff --git a/scripts/helpers/utils.mts b/scripts/helpers/utils.mts index 5a9c132..d75fad7 100644 --- a/scripts/helpers/utils.mts +++ b/scripts/helpers/utils.mts @@ -119,6 +119,22 @@ export function partitionArguments( : [args, []]; } +export function partitionArgumentsWithDefaultArgs( + args: string[], + delimiter: string, + defaultArgs?: string[], +): [string[], string[]] { + const [providedCargoArgs, providedCommandArgs] = partitionArguments(args, delimiter); + if (defaultArgs) { + const [defaultCargoArgs, defaultCommandArgs] = partitionArguments(defaultArgs, delimiter); + return [ + [...defaultCargoArgs, ...providedCargoArgs], + [...defaultCommandArgs, ...providedCommandArgs], + ]; + } + return [providedCargoArgs, providedCommandArgs]; +} + export async function getInstalledSolanaVersion(): Promise { try { const { stdout } = await $`solana --version`.quiet(); @@ -128,9 +144,10 @@ export async function getInstalledSolanaVersion(): Promise { } } -export function parseCliArguments(): { manifestPath: string; args: string[] } { - // Command-line arguments. - const args = cliArguments(); +export function parseCliArguments(): { command: string, libraryPath: string; args: string[] } { + const command = process.argv[2]; + const args = process.argv.slice(3); + // Extract the relative crate directory from the command-line arguments. This // is the only required argument. const relativePath = args.shift(); @@ -140,7 +157,8 @@ export function parseCliArguments(): { manifestPath: string; args: string[] } { } return { - manifestPath: path.join(workingDirectory, relativePath, 'Cargo.toml'), + command, + libraryPath: path.join(workingDirectory, relativePath), args, }; } diff --git a/scripts/js.mts b/scripts/js.mts new file mode 100644 index 0000000..2b3a742 --- /dev/null +++ b/scripts/js.mts @@ -0,0 +1,99 @@ +#!/usr/bin/env zx + +// Script for working with JavaScript projects. + +import 'zx/globals'; +import { + parseCliArguments, + partitionArgumentsWithDefaultArgs, +} from './helpers/utils.mts'; + +enum Command { + Format = 'format', + Lint = 'lint', + Test = 'test', + Publish = 'publish', +} + +const { command, libraryPath, args } = parseCliArguments(); + +async function pnpm( + command: string, + build = false, +) { + const [pnpmArgs, commandArgs] = partitionArgumentsWithDefaultArgs(args, '--'); + cd(libraryPath); + await $`pnpm install`; + if (build) { + await $`pnpm build`; + } + await $`pnpm ${command} ${pnpmArgs} -- ${commandArgs}`; +} + +async function format() { + return pnpm('format'); +} + +async function lint() { + return pnpm('lint'); +} + +async function test() { + // Start the local validator, or restart it if it is already running. + await $`pnpm validator:restart`; + + // Build the client and run the tests. + return pnpm('test', true); +} + +async function publish() { + const [level, tag = 'latest'] = args; + if (!level) { + throw new Error('A version level — e.g. "path" — must be provided.'); + } + + // Go to the directory and install the dependencies. + cd(libraryPath); + await $`pnpm install`; + + // Update the version. + const versionArgs = [ + '--no-git-tag-version', + ...(level.startsWith('pre') ? [`--preid ${tag}`] : []), + ]; + let { stdout } = await $`pnpm version ${level} ${versionArgs}`; + const newVersion = stdout.slice(1).trim(); + + // Expose the new version to CI if needed. + if (process.env.CI) { + await $`echo "new_version=${newVersion}" >> $GITHUB_OUTPUT`; + } + + // Publish the package. + // This will also build the package before publishing (see prepublishOnly script). + await $`pnpm publish --no-git-checks --tag ${tag}`; + + // Commit the new version. + await $`git commit -am "Publish JS client v${newVersion}"`; + + // Tag the new version. + await $`git tag -a js@v${newVersion} -m "JS client v${newVersion}"`; +} + + +switch (command) { + case Command.Format: + await format(); + break; + case Command.Lint: + await lint(); + break; + case Command.Test: + await test(); + break; + case Command.Publish: + await publish(); + break; + default: + throw new Error(`Unknown command: ${command}`); +} diff --git a/scripts/js/format.mts b/scripts/js/format.mts deleted file mode 100644 index 5557740..0000000 --- a/scripts/js/format.mts +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { cliArguments, workingDirectory } from '../helpers/utils.mts'; - -// Format the client using Prettier. -cd(path.join(workingDirectory, 'clients', 'js')); -await $`pnpm install`; -await $`pnpm format ${cliArguments()}`; diff --git a/scripts/js/lint.mts b/scripts/js/lint.mts deleted file mode 100644 index 9c2c09c..0000000 --- a/scripts/js/lint.mts +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { cliArguments, workingDirectory } from '../helpers/utils.mts'; - -// Check the client using ESLint. -cd(path.join(workingDirectory, 'clients', 'js')); -await $`pnpm install`; -await $`pnpm lint ${cliArguments()}`; diff --git a/scripts/js/publish.mts b/scripts/js/publish.mts deleted file mode 100644 index 97bfecc..0000000 --- a/scripts/js/publish.mts +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { cliArguments, workingDirectory } from '../helpers/utils.mts'; - -const [level, tag = 'latest'] = cliArguments(); -if (!level) { - throw new Error('A version level — e.g. "path" — must be provided.'); -} - -// Go to the client directory and install the dependencies. -cd(path.join(workingDirectory, 'clients', 'js')); -await $`pnpm install`; - -// Update the version. -const versionArgs = [ - '--no-git-tag-version', - ...(level.startsWith('pre') ? [`--preid ${tag}`] : []), -]; -let { stdout } = await $`pnpm version ${level} ${versionArgs}`; -const newVersion = stdout.slice(1).trim(); - -// Expose the new version to CI if needed. -if (process.env.CI) { - await $`echo "new_version=${newVersion}" >> $GITHUB_OUTPUT`; -} - -// Publish the package. -// This will also build the package before publishing (see prepublishOnly script). -await $`pnpm publish --no-git-checks --tag ${tag}`; - -// Commit the new version. -await $`git commit -am "Publish JS client v${newVersion}"`; - -// Tag the new version. -await $`git tag -a js@v${newVersion} -m "JS client v${newVersion}"`; diff --git a/scripts/js/test.mts b/scripts/js/test.mts deleted file mode 100644 index c0ba4a5..0000000 --- a/scripts/js/test.mts +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { cliArguments, workingDirectory } from '../helpers/utils.mts'; - -// Start the local validator, or restart it if it is already running. -await $`pnpm validator:restart`; - -// Build the client and run the tests. -cd(path.join(workingDirectory, 'clients', 'js')); -await $`pnpm install`; -await $`pnpm build`; -await $`pnpm test ${cliArguments()}`; diff --git a/scripts/rust.mts b/scripts/rust.mts new file mode 100644 index 0000000..9269f8b --- /dev/null +++ b/scripts/rust.mts @@ -0,0 +1,155 @@ +#!/usr/bin/env zx + +// Script for working with Rust projects. + +import 'zx/globals'; +import { + getCargo, + getToolchainArgument, + parseCliArguments, + partitionArgumentsWithDefaultArgs, + popArgument, + workingDirectory, +} from './helpers/utils.mts'; + +enum Command { + Format = 'format', + LintClippy = 'lint-clippy', + LintDocs = 'lint-docs', + LintFeatures = 'lint-features', + Lint = 'lint', + Test = 'test', + Wasm = 'wasm', + Publish = 'publish', +} + +const { command, libraryPath, args } = parseCliArguments(); +const manifestPath = path.join(libraryPath, 'Cargo.toml'); + +async function cargo( + toolchain: string, + command: string | string[], + defaultArgs?: string[], + variables?: [string, string][], +) { + const [cargoArgs, commandArgs] = partitionArgumentsWithDefaultArgs(args, '--', defaultArgs); + variables?.forEach(([k, v]) => $.env[k] = v); + await $`cargo ${toolchain} ${command} --manifest-path ${manifestPath} ${cargoArgs} -- ${commandArgs}`; +} + +async function format() { + return cargo( + getToolchainArgument('format'), + 'fmt', + popArgument(args, '--fix') ? [] : ['--', '--check'], + ); +} + +async function lintClippy() { + return cargo( + getToolchainArgument('lint'), + 'clippy', + popArgument(args, '--fix') ? ['--fix'] : [], + ); +} + +async function lintDocs() { + return cargo( + getToolchainArgument('lint'), + 'doc', + ['--all-features', '--no-deps'], + [['RUSTDOCFLAGS', '--cfg docsrs -D warnings']], + ); +} + +async function lintFeatures() { + return cargo( + getToolchainArgument('lint'), + ['hack', 'check'], + ['--feature-powerset', '--all-targets'], + ); +} + +async function test() { + return cargo( + getToolchainArgument('test'), + 'test', + ['--all-features'], + [['SBF_OUT_DIR', path.join(workingDirectory, 'target', 'deploy')]] + ); +} + +async function wasm() { + await $`wasm-pack build --target nodejs --dev ${path.dirname(manifestPath)} --features bincode ${args}`; +} + +async function publish() { + const dryRun = argv['dry-run'] ?? false; + const [level] = args; + if (!level) { + throw new Error('A version level — e.g. "path" — must be provided.'); + } + + // Go to the client directory and install the dependencies. + cd(path.dirname(manifestPath)); + + // Publish the new version. + const releaseArgs = dryRun + ? [] + : ['--no-push', '--no-tag', '--no-confirm', '--execute']; + await $`cargo release ${level} ${releaseArgs}`; + + // Stop here if this is a dry run. + if (dryRun) { + process.exit(0); + } + + // Get the crate information. + const toml = getCargo(path.dirname(manifestPath)); + const crateName = toml.package['name']; + const newVersion = toml.package['version']; + + // Expose the new version to CI if needed. + if (process.env.CI) { + await $`echo "new_version=${newVersion}" >> $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 ${crateName} v${newVersion}"`; + + // Tag the new version. + await $`git tag -a ${crateName}@v${newVersion} -m "${crateName} v${newVersion}"`; +} + + +switch (command) { + case Command.Format: + await format(); + break; + case Command.LintClippy: + await lintClippy(); + break; + case Command.LintDocs: + await lintDocs(); + break; + case Command.LintFeatures: + await lintFeatures(); + break; + case Command.Lint: + await Promise.all([lintClippy(), lintDocs(), lintFeatures()]); + break; + case Command.Test: + await test(); + break; + case Command.Wasm: + await wasm(); + break; + case Command.Publish: + await publish(); + break; + default: + throw new Error(`Unknown command: ${command}`); +} diff --git a/scripts/rust/format.mts b/scripts/rust/format.mts deleted file mode 100644 index 4863d42..0000000 --- a/scripts/rust/format.mts +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { - getToolchainArgument, - parseCliArguments, - partitionArguments, - popArgument, -} from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); -// Configure additional arguments here, e.g.: -// ['--arg1', '--arg2', ...args] -const formatArgs = args; - -const fix = popArgument(args, '--fix'); -const [cargoArgs, fmtArgs] = partitionArguments(formatArgs, '--'); -const toolchain = getToolchainArgument('format'); - -await $`cargo ${toolchain} fmt --manifest-path ${manifestPath} ${cargoArgs} -- ${fix ? '' : '--check'} ${fmtArgs}`; diff --git a/scripts/rust/lint-clippy.mts b/scripts/rust/lint-clippy.mts deleted file mode 100644 index 377cb79..0000000 --- a/scripts/rust/lint-clippy.mts +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { - getToolchainArgument, - parseCliArguments, - popArgument, -} from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); -// Configure additional arguments here, e.g.: -// ['--arg1', '--arg2', ...args] -const clippyArgs = args; -// Note: need to use nightly clippy as frozen-abi proc-macro generates -// a lot of code (frozen-abi is enabled only under nightly due to the -// use of unstable rust feature). Likewise, frozen-abi(-macro) crates' -// unit tests are only compiled under nightly. -const toolchain = getToolchainArgument('lint'); -// Check if the `--fix` argument is present. -const fix = popArgument(clippyArgs, '--fix'); - -await $`cargo ${toolchain} clippy --manifest-path ${manifestPath} ${fix ? '--fix' : ''} ${clippyArgs}`; diff --git a/scripts/rust/lint-docs.mts b/scripts/rust/lint-docs.mts deleted file mode 100644 index 867e751..0000000 --- a/scripts/rust/lint-docs.mts +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { getToolchainArgument, parseCliArguments } from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); -// Configure additional arguments here, e.g.: -// ['--arg1', '--arg2', ...args] -const docArgs = args; -const toolchain = getToolchainArgument('lint'); - -await $`RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo ${toolchain} doc --manifest-path ${manifestPath} --all-features --no-deps ${docArgs}`; diff --git a/scripts/rust/lint-features.mts b/scripts/rust/lint-features.mts deleted file mode 100644 index 9ebbe4d..0000000 --- a/scripts/rust/lint-features.mts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { getToolchainArgument, parseCliArguments } from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); -// Configure additional arguments here, e.g.: -// ['--arg1', '--arg2', ...args] -const featuresArgs = ['--exclude-features', 'frozen-abi', ...args]; -const toolchain = getToolchainArgument('lint'); - -// Check feature powerset. -await $`cargo ${toolchain} hack check --manifest-path ${manifestPath} --feature-powerset --all-targets ${featuresArgs}`; diff --git a/scripts/rust/lint.mts b/scripts/rust/lint.mts deleted file mode 100644 index 8bfe272..0000000 --- a/scripts/rust/lint.mts +++ /dev/null @@ -1,17 +0,0 @@ -// Script to lint a crate. -// -// This script runs the following sub-scripts: -// - lint-clippy.mjs -// - lint-docs.mjs -// - lint-features.mjs - -import { cliArguments, workingDirectory } from '../helpers/utils.mts'; - -const scripts = path.join(workingDirectory, 'scripts', 'rust'); - -// clippy -await $`tsx ${path.join(scripts, 'lint-clippy.mjs')} ${cliArguments()}`; -// rustdoc -await $`tsx ${path.join(scripts, 'lint-docs.mjs')} ${cliArguments()}`; -// features -await $`tsx ${path.join(scripts, 'lint-features.mjs')} ${cliArguments()}`; diff --git a/scripts/rust/publish.mts b/scripts/rust/publish.mts deleted file mode 100644 index 3f669bf..0000000 --- a/scripts/rust/publish.mts +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { getCargo, parseCliArguments } from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); - -const dryRun = argv['dry-run'] ?? false; -const [level] = args; -if (!level) { - throw new Error('A version level — e.g. "path" — must be provided.'); -} - -// Go to the client directory and install the dependencies. -cd(path.dirname(manifestPath)); - -// Publish the new version. -const releaseArgs = dryRun - ? [] - : ['--no-push', '--no-tag', '--no-confirm', '--execute']; -await $`cargo release ${level} ${releaseArgs}`; - -// Stop here if this is a dry run. -if (dryRun) { - process.exit(0); -} - -// Get the crate information. -const toml = getCargo(path.dirname(manifestPath)); -const crateName = toml.package['name']; -const newVersion = toml.package['version']; - -// Expose the new version to CI if needed. -if (process.env.CI) { - await $`echo "new_version=${newVersion}" >> $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 ${crateName} v${newVersion}"`; - -// Tag the new version. -await $`git tag -a ${crateName}@v${newVersion} -m "${crateName} v${newVersion}"`; diff --git a/scripts/rust/test.mts b/scripts/rust/test.mts deleted file mode 100644 index d3ad191..0000000 --- a/scripts/rust/test.mts +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { - getToolchainArgument, - parseCliArguments, - workingDirectory, -} from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); -// Configure additional arguments here, e.g.: -// ['--arg1', '--arg2', ...args] -const testArgs = args; - -const toolchain = getToolchainArgument('test'); - -const hasSolfmt = await which('solfmt', { nothrow: true }); -const sbfOutDir = path.join(workingDirectory, 'target', 'deploy'); - -// Run the tests. -if (hasSolfmt) { - await $`SBF_OUT_DIR=${sbfOutDir} cargo ${toolchain} test --all-features --manifest-path ${manifestPath} ${testArgs} >&1 | solfmt`; -} else { - await $`SBF_OUT_DIR=${sbfOutDir} cargo ${toolchain} test --all-features --manifest-path ${manifestPath} ${testArgs}`; -} diff --git a/scripts/rust/wasm.mts b/scripts/rust/wasm.mts deleted file mode 100644 index 6d48b9b..0000000 --- a/scripts/rust/wasm.mts +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env zx -import 'zx/globals'; -import { parseCliArguments } from '../helpers/utils.mts'; - -// Extract the crate directory from the command-line arguments. -const { manifestPath, args } = parseCliArguments(); -// Configure additional arguments here, e.g.: -// ['--arg1', '--arg2', ...args] -const wasmArgs = args; - -await $`wasm-pack build --target nodejs --dev ${path.dirname(manifestPath)} --features bincode ${wasmArgs}`;