Expose executeShellCommand, RunCommandOptions, and fetchContents from the node package

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2023-07-31 14:52:03 +02:00
parent 28e34123f1
commit d3b31a791e
26 changed files with 199 additions and 102 deletions
+6
View File
@@ -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.
@@ -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",
@@ -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';
@@ -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",
@@ -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;
@@ -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",
@@ -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';
@@ -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,
+9 -21
View File
@@ -3,8 +3,6 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="node" />
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<void>;
// @public @deprecated
export const executeShellCommand: typeof executeShellCommand_2;
// @public
export function fetchContents(options: {
reader: UrlReader;
integrations: ScmIntegrations;
baseUrl?: string;
fetchUrl?: string;
outputPath: string;
}): Promise<void>;
// @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 {
@@ -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<TInput extends JsonObject> =
TemplateActionNode<TInput>;
/**
* 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;
@@ -17,4 +17,3 @@
export { createFetchPlainAction } from './plain';
export { createFetchPlainFileAction } from './plainFile';
export { createFetchTemplateAction } from './template';
export { fetchContents } from './helpers';
@@ -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(
@@ -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
@@ -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(
@@ -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
@@ -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';
@@ -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';
@@ -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<void>((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,
@@ -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';
+34
View File
@@ -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<TActionInput, TActionOutput>;
// @public
export function executeShellCommand(
options: ExecuteShellCommandOptions,
): Promise<void>;
// @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<void>;
// @public
export function fetchFile(options: {
reader: UrlReader;
integrations: ScmIntegrations;
baseUrl?: string;
fetchUrl?: string;
outputPath: string;
}): Promise<void>;
// @alpha
export interface ScaffolderActionsExtensionPoint {
// (undocumented)
+6 -1
View File
@@ -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",
@@ -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<void> {
const {
command,
args,
options: spawnOptions,
logStream = new PassThrough(),
} = options;
await new Promise<void>((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();
});
});
}
@@ -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();
});
@@ -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;
@@ -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';
+5 -3
View File
@@ -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