From d3b31a791eb1568ab5b853b7c280cbbc92d31fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 31 Jul 2023 14:52:03 +0200 Subject: [PATCH] Expose executeShellCommand, RunCommandOptions, and fetchContents from the node package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/tough-lies-float.md | 6 ++ .../package.json | 1 - .../confluence/confluenceToMarkdown.ts | 7 +- .../package.json | 1 - .../src/actions/fetch/cookiecutter.ts | 4 +- .../package.json | 1 - .../src/actions/fetch/rails/index.ts | 6 +- .../src/actions/fetch/rails/railsNewRunner.ts | 2 +- plugins/scaffolder-backend/api-report.md | 30 +++----- plugins/scaffolder-backend/src/deprecated.ts | 28 +++++++ .../scaffolder/actions/builtin/fetch/index.ts | 1 - .../actions/builtin/fetch/plain.test.ts | 2 +- .../scaffolder/actions/builtin/fetch/plain.ts | 6 +- .../actions/builtin/fetch/plainFile.test.ts | 2 +- .../actions/builtin/fetch/plainFile.ts | 6 +- .../actions/builtin/fetch/template.test.ts | 2 +- .../actions/builtin/fetch/template.ts | 6 +- .../src/scaffolder/actions/builtin/helpers.ts | 52 ------------- .../src/scaffolder/actions/builtin/index.ts | 3 - plugins/scaffolder-node/api-report.md | 34 +++++++++ plugins/scaffolder-node/package.json | 7 +- .../src/actions/executeShellCommand.ts | 75 +++++++++++++++++++ .../src/actions/fetch.test.ts} | 4 +- .../src/actions/fetch.ts} | 2 + plugins/scaffolder-node/src/actions/index.ts | 5 ++ yarn.lock | 8 +- 26 files changed, 199 insertions(+), 102 deletions(-) create mode 100644 .changeset/tough-lies-float.md create mode 100644 plugins/scaffolder-node/src/actions/executeShellCommand.ts rename plugins/{scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts => scaffolder-node/src/actions/fetch.test.ts} (98%) rename plugins/{scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts => scaffolder-node/src/actions/fetch.ts} (99%) diff --git a/.changeset/tough-lies-float.md b/.changeset/tough-lies-float.md new file mode 100644 index 0000000000..2ad3b8623f --- /dev/null +++ b/.changeset/tough-lies-float.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +--- + +Deprecated `executeShellCommand`, `RunCommandOptions`, and `fetchContents` from `@backstage/plugin-scaffolder-backend`, since they are useful for Scaffolder modules (who should not be importing from the plugin package itself). You should now import these from `@backstage/plugin-scaffolder-backend-node` instead. `RunCommandOptions` was renamed in the Node package as `ExecuteShellCommandOptions`, for consistency. diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index b8ce9ea8d9..676eddfb12 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -34,7 +34,6 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", "fs-extra": "10.1.0", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts index 428f392555..71a3af11ae 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/confluenceToMarkdown.ts @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Config } from '@backstage/config'; import { UrlReader } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents } from '@backstage/plugin-scaffolder-backend'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; import { InputError, ConflictError } from '@backstage/errors'; import { NodeHtmlMarkdown } from 'node-html-markdown'; import fs from 'fs-extra'; diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index f95771822b..446d79e06c 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -33,7 +33,6 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", "command-exists": "^1.2.9", diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts index b63a044a20..8b084e6377 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts @@ -27,10 +27,10 @@ import fs from 'fs-extra'; import path, { resolve as resolvePath } from 'path'; import { Writable } from 'stream'; import { + createTemplateAction, fetchContents, executeShellCommand, -} from '@backstage/plugin-scaffolder-backend'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +} from '@backstage/plugin-scaffolder-node'; export class CookiecutterRunner { private readonly containerRunner?: ContainerRunner; diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 9bc7bbf06f..3697529c31 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -33,7 +33,6 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", "command-exists": "^1.2.9", diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts index 2a8a2554a2..2b34f978e3 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/index.ts @@ -19,8 +19,10 @@ import { JsonObject } from '@backstage/types'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import fs from 'fs-extra'; -import { fetchContents } from '@backstage/plugin-scaffolder-backend'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; import { resolve as resolvePath } from 'path'; import { RailsNewRunner } from './railsNewRunner'; diff --git a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts index 0ed02e293c..7e78c25d62 100644 --- a/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts +++ b/plugins/scaffolder-backend-module-rails/src/actions/fetch/rails/railsNewRunner.ts @@ -17,7 +17,7 @@ import { ContainerRunner } from '@backstage/backend-common'; import fs from 'fs-extra'; import path from 'path'; -import { executeShellCommand } from '@backstage/plugin-scaffolder-backend'; +import { executeShellCommand } from '@backstage/plugin-scaffolder-node'; import commandExists from 'command-exists'; import { railsArgumentResolver, diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 1957c3b3d2..4fe961056f 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -3,8 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - import { ActionContext as ActionContext_2 } from '@backstage/plugin-scaffolder-node'; import { CatalogApi } from '@backstage/catalog-client'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; @@ -13,7 +11,10 @@ import { Config } from '@backstage/config'; import { createPullRequest } from 'octokit-plugin-create-pull-request'; import { Duration } from 'luxon'; import { Entity } from '@backstage/catalog-model'; +import { executeShellCommand as executeShellCommand_2 } from '@backstage/plugin-scaffolder-node'; +import { ExecuteShellCommandOptions } from '@backstage/plugin-scaffolder-node'; import express from 'express'; +import { fetchContents as fetchContents_2 } from '@backstage/plugin-scaffolder-node'; import { GithubCredentialsProvider } from '@backstage/integration'; import { HumanDuration } from '@backstage/types'; import { IdentityApi } from '@backstage/plugin-auth-node'; @@ -34,7 +35,6 @@ import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE } from '@backstage/plugin-scaffolder- import { Schema } from 'jsonschema'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ScmIntegrations } from '@backstage/integration'; -import { SpawnOptionsWithoutStdio } from 'child_process'; import { TaskSecrets as TaskSecrets_2 } from '@backstage/plugin-scaffolder-node'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; @@ -43,7 +43,6 @@ import { TemplateActionOptions } from '@backstage/plugin-scaffolder-node'; import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; import { UrlReader } from '@backstage/backend-common'; -import { Writable } from 'stream'; import { ZodType } from 'zod'; import { ZodTypeDef } from 'zod'; @@ -816,17 +815,11 @@ export type DatabaseTaskStoreOptions = { database: PluginDatabaseManager | Knex; }; -// @public -export const executeShellCommand: (options: RunCommandOptions) => Promise; +// @public @deprecated +export const executeShellCommand: typeof executeShellCommand_2; -// @public -export function fetchContents(options: { - reader: UrlReader; - integrations: ScmIntegrations; - baseUrl?: string; - fetchUrl?: string; - outputPath: string; -}): Promise; +// @public @deprecated +export const fetchContents: typeof fetchContents_2; // @public (undocumented) export type OctokitWithPullRequestPluginClient = Octokit & { @@ -876,13 +869,8 @@ export interface RouterOptions { taskWorkers?: number; } -// @public (undocumented) -export type RunCommandOptions = { - command: string; - args: string[]; - options?: SpawnOptionsWithoutStdio; - logStream?: Writable; -}; +// @public @deprecated +export type RunCommandOptions = ExecuteShellCommandOptions; // @public (undocumented) export class ScaffolderEntitiesProcessor implements CatalogProcessor { diff --git a/plugins/scaffolder-backend/src/deprecated.ts b/plugins/scaffolder-backend/src/deprecated.ts index f8358b9071..d1a0f9ad5b 100644 --- a/plugins/scaffolder-backend/src/deprecated.ts +++ b/plugins/scaffolder-backend/src/deprecated.ts @@ -19,6 +19,9 @@ import { createTemplateAction as createTemplateActionNode, TaskSecrets as TaskSecretsNode, TemplateAction as TemplateActionNode, + executeShellCommand as executeShellCommandNode, + ExecuteShellCommandOptions as ExecuteShellCommandOptionsNode, + fetchContents as fetchContentsNode, } from '@backstage/plugin-scaffolder-node'; import { JsonObject } from '@backstage/types'; @@ -47,3 +50,28 @@ export type TaskSecrets = TaskSecretsNode; */ export type TemplateAction = TemplateActionNode; + +/** + * Options for {@link executeShellCommand}. + * + * @public + * @deprecated Use `ExecuteShellCommandOptions` from `@backstage/plugin-scaffolder-node` instead + */ +export type RunCommandOptions = ExecuteShellCommandOptionsNode; + +/** + * Run a command in a sub-process, normally a shell command. + * + * @public + * @deprecated Use `executeShellCommand` from `@backstage/plugin-scaffolder-node` instead + */ +export const executeShellCommand = executeShellCommandNode; + +/** + * A helper function that reads the contents of a directory from the given URL. + * Can be used in your own actions, and also used behind fetch:template and fetch:plain + * + * @public + * @deprecated Use `fetchContents` from `@backstage/plugin-scaffolder-node` instead + */ +export const fetchContents = fetchContentsNode; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts index 14a40f53c3..67f1bc360f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts @@ -17,4 +17,3 @@ export { createFetchPlainAction } from './plain'; export { createFetchPlainFileAction } from './plainFile'; export { createFetchTemplateAction } from './template'; -export { fetchContents } from './helpers'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts index ed37a39a98..3a67489eca 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.test.ts @@ -21,9 +21,9 @@ import { resolve as resolvePath } from 'path'; import { getVoidLogger, UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; +import { fetchContents } from '@backstage/plugin-scaffolder-node'; import { createFetchPlainAction } from './plain'; import { PassThrough } from 'stream'; -import { fetchContents } from './helpers'; describe('fetch:plain', () => { const integrations = ScmIntegrations.fromConfig( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts index c10f696bb9..e102c5bb74 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plain.ts @@ -16,8 +16,10 @@ import { UrlReader, resolveSafeChildPath } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents } from './helpers'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; /** * Downloads content and places it in the workspace, or optionally diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts index 5d9e99fc7d..e6ed858b09 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.test.ts @@ -21,9 +21,9 @@ import { resolve as resolvePath } from 'path'; import { getVoidLogger, UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; +import { fetchFile } from '@backstage/plugin-scaffolder-node'; import { createFetchPlainFileAction } from './plainFile'; import { PassThrough } from 'stream'; -import { fetchFile } from './helpers'; describe('fetch:plain:file', () => { const integrations = ScmIntegrations.fromConfig( diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts index 7b33ad93b4..96abe773fd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/plainFile.ts @@ -16,8 +16,10 @@ import { UrlReader, resolveSafeChildPath } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchFile } from './helpers'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchFile, +} from '@backstage/plugin-scaffolder-node'; /** * Downloads content and places it in the workspace, or optionally diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index 15548ffea7..aee1fdcb32 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -25,9 +25,9 @@ import { } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; import { PassThrough } from 'stream'; -import { fetchContents } from './helpers'; import { createFetchTemplateAction } from './template'; import { + fetchContents, ActionContext, TemplateAction, } from '@backstage/plugin-scaffolder-node'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts index be7b47521c..7f03cb2514 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -18,8 +18,10 @@ import { extname } from 'path'; import { resolveSafeChildPath, UrlReader } from '@backstage/backend-common'; import { InputError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents } from './helpers'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { + createTemplateAction, + fetchContents, +} from '@backstage/plugin-scaffolder-node'; import globby from 'globby'; import fs from 'fs-extra'; import { isBinaryFile } from 'isbinaryfile'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts index f51ab47f16..5ac92128ca 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/helpers.ts @@ -17,61 +17,9 @@ import { Git } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { assertError } from '@backstage/errors'; -import { spawn, SpawnOptionsWithoutStdio } from 'child_process'; import { Octokit } from 'octokit'; -import { PassThrough, Writable } from 'stream'; import { Logger } from 'winston'; -/** @public */ -export type RunCommandOptions = { - /** command to run */ - command: string; - /** arguments to pass the command */ - args: string[]; - /** options to pass to spawn */ - options?: SpawnOptionsWithoutStdio; - /** stream to capture stdout and stderr output */ - logStream?: Writable; -}; - -/** - * Run a command in a sub-process, normally a shell command. - * - * @public - */ -export const executeShellCommand = async (options: RunCommandOptions) => { - const { - command, - args, - options: spawnOptions, - logStream = new PassThrough(), - } = options; - await new Promise((resolve, reject) => { - const process = spawn(command, args, spawnOptions); - - process.stdout.on('data', stream => { - logStream.write(stream); - }); - - process.stderr.on('data', stream => { - logStream.write(stream); - }); - - process.on('error', error => { - return reject(error); - }); - - process.on('close', code => { - if (code !== 0) { - return reject( - new Error(`Command ${command} failed, exit code: ${code}`), - ); - } - return resolve(); - }); - }); -}; - export async function initRepoAndPush({ dir, remoteUrl, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts index 7846ff96fc..7ab98482ff 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/index.ts @@ -21,6 +21,3 @@ export * from './fetch'; export * from './filesystem'; export * from './publish'; export * from './github'; - -export { executeShellCommand } from './helpers'; -export type { RunCommandOptions } from './helpers'; diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index 46aba960d8..8f3a7af3e6 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -9,7 +9,10 @@ import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import { Logger } from 'winston'; import { Schema } from 'jsonschema'; +import { ScmIntegrations } from '@backstage/integration'; +import { SpawnOptionsWithoutStdio } from 'child_process'; import { TemplateInfo } from '@backstage/plugin-scaffolder-common'; +import { UrlReader } from '@backstage/backend-common'; import { UserEntity } from '@backstage/catalog-model'; import { Writable } from 'stream'; import { z } from 'zod'; @@ -67,6 +70,37 @@ export const createTemplateAction: < >, ) => TemplateAction; +// @public +export function executeShellCommand( + options: ExecuteShellCommandOptions, +): Promise; + +// @public +export type ExecuteShellCommandOptions = { + command: string; + args: string[]; + options?: SpawnOptionsWithoutStdio; + logStream?: Writable; +}; + +// @public +export function fetchContents(options: { + reader: UrlReader; + integrations: ScmIntegrations; + baseUrl?: string; + fetchUrl?: string; + outputPath: string; +}): Promise; + +// @public +export function fetchFile(options: { + reader: UrlReader; + integrations: ScmIntegrations; + baseUrl?: string; + fetchUrl?: string; + outputPath: string; +}): Promise; + // @alpha export interface ScaffolderActionsExtensionPoint { // (undocumented) diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 59e80f1c1c..053e9a1dec 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -30,17 +30,22 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/types": "workspace:^", + "fs-extra": "10.1.0", "jsonschema": "^1.2.6", "winston": "^3.2.1", "zod": "^3.21.4", "zod-to-json-schema": "^3.20.4" }, "devDependencies": { - "@backstage/cli": "workspace:^" + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^" }, "files": [ "alpha", diff --git a/plugins/scaffolder-node/src/actions/executeShellCommand.ts b/plugins/scaffolder-node/src/actions/executeShellCommand.ts new file mode 100644 index 0000000000..ce54989589 --- /dev/null +++ b/plugins/scaffolder-node/src/actions/executeShellCommand.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { spawn, SpawnOptionsWithoutStdio } from 'child_process'; +import { PassThrough, Writable } from 'stream'; + +/** + * Options for {@link executeShellCommand}. + * + * @public + */ +export type ExecuteShellCommandOptions = { + /** command to run */ + command: string; + /** arguments to pass the command */ + args: string[]; + /** options to pass to spawn */ + options?: SpawnOptionsWithoutStdio; + /** stream to capture stdout and stderr output */ + logStream?: Writable; +}; + +/** + * Run a command in a sub-process, normally a shell command. + * + * @public + */ +export async function executeShellCommand( + options: ExecuteShellCommandOptions, +): Promise { + const { + command, + args, + options: spawnOptions, + logStream = new PassThrough(), + } = options; + + await new Promise((resolve, reject) => { + const process = spawn(command, args, spawnOptions); + + process.stdout.on('data', stream => { + logStream.write(stream); + }); + + process.stderr.on('data', stream => { + logStream.write(stream); + }); + + process.on('error', error => { + return reject(error); + }); + + process.on('close', code => { + if (code !== 0) { + return reject( + new Error(`Command ${command} failed, exit code: ${code}`), + ); + } + return resolve(); + }); + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts b/plugins/scaffolder-node/src/actions/fetch.test.ts similarity index 98% rename from plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts rename to plugins/scaffolder-node/src/actions/fetch.test.ts index 3c4a677d4d..55b949fca7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.test.ts +++ b/plugins/scaffolder-node/src/actions/fetch.test.ts @@ -21,10 +21,10 @@ import { resolve as resolvePath } from 'path'; import { UrlReader } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; -import { fetchContents, fetchFile } from './helpers'; +import { fetchContents, fetchFile } from './fetch'; import os from 'os'; -describe('fetchContent helper', () => { +describe('fetchContents helper', () => { beforeEach(() => { jest.clearAllMocks(); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts b/plugins/scaffolder-node/src/actions/fetch.ts similarity index 99% rename from plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts rename to plugins/scaffolder-node/src/actions/fetch.ts index 301c9991c4..66bbc0a842 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/helpers.ts +++ b/plugins/scaffolder-node/src/actions/fetch.ts @@ -54,6 +54,8 @@ export async function fetchContents(options: { /** * A helper function that reads the content of a single file from the given URL. * Can be used in your own actions, and also used behind `fetch:plain:file` + * + * @public */ export async function fetchFile(options: { reader: UrlReader; diff --git a/plugins/scaffolder-node/src/actions/index.ts b/plugins/scaffolder-node/src/actions/index.ts index e4af098356..751173384b 100644 --- a/plugins/scaffolder-node/src/actions/index.ts +++ b/plugins/scaffolder-node/src/actions/index.ts @@ -18,4 +18,9 @@ export { createTemplateAction, type TemplateActionOptions, } from './createTemplateAction'; +export { + executeShellCommand, + type ExecuteShellCommandOptions, +} from './executeShellCommand'; +export { fetchContents, fetchFile } from './fetch'; export { type ActionContext, type TemplateAction } from './types'; diff --git a/yarn.lock b/yarn.lock index 5d4ddd09d3..45ca2930fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8173,7 +8173,6 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/types": "workspace:^" fs-extra: 10.1.0 @@ -8195,7 +8194,6 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/types": "workspace:^" "@types/command-exists": ^1.2.0 @@ -8235,7 +8233,6 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@backstage/types": "workspace:^" "@types/command-exists": ^1.2.0 @@ -8364,11 +8361,16 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-node@workspace:plugins/scaffolder-node" dependencies: + "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/types": "workspace:^" + fs-extra: 10.1.0 jsonschema: ^1.2.6 winston: ^3.2.1 zod: ^3.21.4