From 2fbf4ef3a9e73b4c6fd1bb253ae0a6cfd59c9794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CJamesHenry=E2=80=9D?= Date: Wed, 21 Feb 2024 21:39:39 +0400 Subject: [PATCH] feat: add executor --- packages/rust/executors.json | 6 ++ .../src/executors/release-publish/executor.ts | 87 +++++++++++++++++++ .../src/executors/release-publish/schema.d.ts | 4 + .../src/executors/release-publish/schema.json | 18 ++++ packages/rust/src/graph.ts | 17 +++- packages/rust/src/models/cargo-metadata.ts | 11 ++- 6 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 packages/rust/src/executors/release-publish/executor.ts create mode 100644 packages/rust/src/executors/release-publish/schema.d.ts create mode 100644 packages/rust/src/executors/release-publish/schema.json diff --git a/packages/rust/executors.json b/packages/rust/executors.json index c52617d..146787d 100644 --- a/packages/rust/executors.json +++ b/packages/rust/executors.json @@ -30,6 +30,12 @@ "implementation": "./src/executors/napi/executor", "schema": "./src/executors/napi/schema.json", "description": "Wrapper to run the napi-cli" + }, + "release-publish": { + "implementation": "./src/executors/release-publish/executor", + "schema": "./src/executors/release-publish/schema.json", + "description": "DO NOT INVOKE DIRECTLY WITH `nx run`. Use `nx release publish` instead.", + "hidden": true } } } diff --git a/packages/rust/src/executors/release-publish/executor.ts b/packages/rust/src/executors/release-publish/executor.ts new file mode 100644 index 0000000..8893a6c --- /dev/null +++ b/packages/rust/src/executors/release-publish/executor.ts @@ -0,0 +1,87 @@ +import { ExecutorContext, joinPathFragments, output } from '@nx/devkit'; +import { execSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { relative } from 'node:path'; +import { env as appendLocalEnv } from 'npm-run-path'; +import { parseCargoToml } from '../../utils/toml'; +import { PublishExecutorSchema } from './schema'; +import chalk = require('chalk'); + +const LARGE_BUFFER = 1024 * 1000000; + +function processEnv(color: boolean) { + const env = { + ...process.env, + ...appendLocalEnv(), + }; + + if (color) { + env.FORCE_COLOR = `${color}`; + } + return env; +} + +export default async function runExecutor( + options: PublishExecutorSchema, + context: ExecutorContext +) { + /** + * We need to check both the env var and the option because the executor may have been triggered + * indirectly via dependsOn, in which case the env var will be set, but the option will not. + */ + const isDryRun = process.env.NX_DRY_RUN === 'true' || options.dryRun || false; + + const projectConfig = + context.projectsConfigurations!.projects[context.projectName!]!; + + const packageRoot = joinPathFragments( + context.root, + options.packageRoot ?? projectConfig.root + ); + const workspaceRelativePackageRoot = relative(context.root, packageRoot); + + const cargoTomlPath = joinPathFragments(packageRoot, 'Cargo.toml'); + const cargoTomlContents = readFileSync(cargoTomlPath, 'utf-8'); + const cargoToml = parseCargoToml(cargoTomlContents); + const crateName = cargoToml.package.name; + + const cargoPublishCommandSegments = [ + `cargo publish --allow-dirty -p ${crateName} --target-dir ${workspaceRelativePackageRoot}/target`, + ]; + + if (isDryRun) { + cargoPublishCommandSegments.push(`--dry-run`); + } + + try { + const command = cargoPublishCommandSegments.join(' '); + output.logSingleLine(`Running "${command}"...`); + + execSync(command, { + maxBuffer: LARGE_BUFFER, + env: processEnv(true), + cwd: packageRoot, + stdio: 'inherit', + }); + + console.log(''); + + if (isDryRun) { + console.log( + `Would publish to https://crates.io, but ${chalk.keyword('orange')( + '[dry-run]' + )} was set` + ); + } else { + console.log(`Published to https://crates.io`); + } + + return { + success: true, + }; + } catch (err: any) { + return { + success: false, + }; + } +} diff --git a/packages/rust/src/executors/release-publish/schema.d.ts b/packages/rust/src/executors/release-publish/schema.d.ts new file mode 100644 index 0000000..90fb809 --- /dev/null +++ b/packages/rust/src/executors/release-publish/schema.d.ts @@ -0,0 +1,4 @@ +export interface PublishExecutorSchema { + packageRoot?: string; + dryRun?: boolean; +} diff --git a/packages/rust/src/executors/release-publish/schema.json b/packages/rust/src/executors/release-publish/schema.json new file mode 100644 index 0000000..7ff8eb8 --- /dev/null +++ b/packages/rust/src/executors/release-publish/schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://json-schema.org/schema", + "version": 2, + "title": "Implementation details of `nx release publish`", + "description": "DO NOT INVOKE DIRECTLY WITH `nx run`. Use `nx release publish` instead.", + "type": "object", + "properties": { + "packageRoot": { + "type": "string", + "description": "The root directory of the directory (containing a manifest file at its root) to publish. Defaults to the project root." + }, + "dryRun": { + "type": "boolean", + "description": "Whether to run the command without actually publishing the package to the registry." + } + }, + "required": [] +} diff --git a/packages/rust/src/graph.ts b/packages/rust/src/graph.ts index 6f0c5fd..85605da 100644 --- a/packages/rust/src/graph.ts +++ b/packages/rust/src/graph.ts @@ -39,11 +39,24 @@ export const createNodes: CreateNodes = [ const root = normalizePath( dirname(relative(ctx.workspaceRoot, pkg.manifest_path)) ); + + // TODO(cammisuli): provide defaults for non-project.json workspaces + const targets: ProjectConfiguration['targets'] = {}; + + // Apply nx-release-publish target for non-private projects + const isPrivate = pkg.publish?.length === 0; + if (!isPrivate) { + targets['nx-release-publish'] = { + dependsOn: ['^nx-release-publish'], + executor: '@monodon/rust:release-publish', + options: {}, + }; + } + projects[root] = { root, name: pkg.name, - // TODO(cammisuli): provide defaults for non-project.json workspaces - targets: {}, + targets, release: { version: { generator: '@monodon/rust:release-version', diff --git a/packages/rust/src/models/cargo-metadata.ts b/packages/rust/src/models/cargo-metadata.ts index 53fd7ae..bf23f82 100644 --- a/packages/rust/src/models/cargo-metadata.ts +++ b/packages/rust/src/models/cargo-metadata.ts @@ -26,7 +26,16 @@ export interface Package { features: Features; manifest_path: string; metadata: Metadata; - publish: string[]; + /** + * From the docs: + * "List of registries to which this package may be published. + * Publishing is unrestricted if null, and forbidden if an empty array." + * + * Additional observation: + * false can be used by the end user but it will be converted to an empty + * array in the cargo metadata output. + */ + publish: string[] | null; authors: string[]; categories: string[]; default_run: any;