From 9d30f860694bc257a2a8097d5d5fc28c7b095b93 Mon Sep 17 00:00:00 2001 From: bri becker <39387238+bbckr@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:51:40 +0200 Subject: [PATCH] feat(scaffolder): add new plugin for extending scaffolder actions (#67) --- README.md | 2 + .../.eslintrc.js | 5 + .../scaffolder-backend-module-hcl/.gitignore | 1 + .../scaffolder-backend-module-hcl/README.md | 30 +++ .../package.json | 48 ++++ .../src/actions/hcl/hcl.test.ts | 119 ++++++++++ .../src/actions/hcl/hcl.ts | 221 ++++++++++++++++++ .../src/actions/hcl/index.ts | 10 + .../src/actions/index.ts | 5 + .../src/index.ts | 5 + .../src/module.ts | 29 +++ yarn.lock | 212 ++++++++++++++++- 12 files changed, 679 insertions(+), 8 deletions(-) create mode 100644 plugins/scaffolder-backend-module-hcl/.eslintrc.js create mode 100644 plugins/scaffolder-backend-module-hcl/.gitignore create mode 100644 plugins/scaffolder-backend-module-hcl/README.md create mode 100644 plugins/scaffolder-backend-module-hcl/package.json create mode 100644 plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.test.ts create mode 100644 plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.ts create mode 100644 plugins/scaffolder-backend-module-hcl/src/actions/hcl/index.ts create mode 100644 plugins/scaffolder-backend-module-hcl/src/actions/index.ts create mode 100644 plugins/scaffolder-backend-module-hcl/src/index.ts create mode 100644 plugins/scaffolder-backend-module-hcl/src/module.ts diff --git a/README.md b/README.md index 4929e19..502629a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ This SeatGeek Backstage Plugins Collection offers the following plugins: - [plugins/aws-catalog-backend](plugins/aws-catalog-backend) - **Gitlab Catalog**: the Gitlab Catalog Plugin offers catalog integrations with the Gitlab API. - [plugins/gitlab-catalog-backend](plugins/gitlab-catalog-backend/) +- **HCL Scaffolder Actions**: the HCL Scaffolder Actions plugin includes custom actions for working with HCL in your Backstage Software Templates. + - [plugins/scaffolder-backend-module-hcl](plugins/scaffolder-backend-module-hcl/) Each of the plugins contain instructions for installation and development within their respective locations. diff --git a/plugins/scaffolder-backend-module-hcl/.eslintrc.js b/plugins/scaffolder-backend-module-hcl/.eslintrc.js new file mode 100644 index 0000000..37b7680 --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/.eslintrc.js @@ -0,0 +1,5 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/scaffolder-backend-module-hcl/.gitignore b/plugins/scaffolder-backend-module-hcl/.gitignore new file mode 100644 index 0000000..f937fcb --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/.gitignore @@ -0,0 +1 @@ +testdir/ diff --git a/plugins/scaffolder-backend-module-hcl/README.md b/plugins/scaffolder-backend-module-hcl/README.md new file mode 100644 index 0000000..1c22692 --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/README.md @@ -0,0 +1,30 @@ +# scaffolder-backend-module-hcl + +This contains a collection of actions to use in scaffolder templates for working with HCL (Hashicorp Configuration Language). See https://github.com/hashicorp/hcl to learn more about HCL. + +## Getting started + +### From your Backstage root directory + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @seatgeek/backstage-plugin-scaffolder-backend-module-hcl +``` + +Then ensure that both the scaffolder and this module are added to your backend: + +```typescript +// In packages/backend/src/index.ts +const backend = createBackend(); +// ... +backend.add(import('@seatgeek/backstage-plugin-scaffolder-backend-module-hcl')); +``` + +After that you can use the actions in your template. + +## Actions + +- `hcl:merge` Merge HCL strings +- `hcl:merge:write` Merge HCL strings and write to a file +- `hcl:merge:files` Merge HCL files +- `hcl:merge:files:write` Merge HCL files and write to a file diff --git a/plugins/scaffolder-backend-module-hcl/package.json b/plugins/scaffolder-backend-module-hcl/package.json new file mode 100644 index 0000000..c79027b --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/package.json @@ -0,0 +1,48 @@ +{ + "name": "@seatgeek/backstage-plugin-scaffolder-backend-module-hcl", + "version": "0.0.0-semantically-released", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module", + "pluginId": "scaffolder", + "pluginPackages": [ + "@seatgeek/backstage-plugin-scaffolder-backend-module-hcl" + ], + "pluginPackage": "@backstage/plugin-scaffolder-backend" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test --no-cache", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "^0.24.1", + "@backstage/backend-plugin-api": "^0.8.1", + "@backstage/config": "^1.2.0", + "@backstage/plugin-scaffolder-node": "^0.4.10", + "@seatgeek/node-hcl": "^1.0.0", + "fs-extra": "^11.2.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@backstage/cli": "^0.27.0", + "@testing-library/jest-dom": "^5.10.1", + "@types/fs-extra": "^11.0.4", + "msw": "^1.0.0", + "stream": "^0.0.3" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.test.ts b/plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.test.ts new file mode 100644 index 0000000..355ab94 --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.test.ts @@ -0,0 +1,119 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +import { getVoidLogger } from '@backstage/backend-common'; +import { randomBytes } from 'crypto'; +import { writeFileSync } from 'fs-extra'; +import { tmpdir } from 'os'; +import { PassThrough } from 'stream'; +import { createHclMergeAction, createHclMergeFilesAction } from './hcl'; + +// Since we have to +const TMP_DIR = tmpdir(); + +describe('createHclMergeAction', () => { + const mockContext = { + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + checkpoint: jest.fn(), + getInitiatorCredentials: jest.fn(), + workspacePath: '.', + }; + + it('should merge HCL files', async () => { + const a = ` + variable "name" { + description = "Name to be used on all the resources as identifier" + type = string + default = "" + }`; + + const b = ` + variable "name" { + type = string + default = "my-name" + }`; + + const expected = `variable "name" { + default = "my-name" + description = "Name to be used on all the resources as identifier" + type = string +} + +`; + + const mockCtx = { + ...mockContext, + input: { + aSourceContent: a, + bSourceContent: b, + }, + }; + + await createHclMergeAction().handler(mockCtx); + + expect(mockCtx.output.mock.calls[0][0]).toEqual('hcl'); + expect(mockCtx.output.mock.calls[0][1]).toEqual(expected); + }); +}); + +describe('createHclMergeFilesAction', () => { + const mockContext = { + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + checkpoint: jest.fn(), + getInitiatorCredentials: jest.fn(), + workspacePath: TMP_DIR, + }; + + it('should merge HCL files', async () => { + const a = ` + variable "name" { + description = "Name to be used on all the resources as identifier" + type = string + default = "" + }`; + + const b = ` + variable "name" { + type = string + default = "my-name" + }`; + + const expected = `variable "name" { + default = "my-name" + description = "Name to be used on all the resources as identifier" + type = string +} + +`; + + const aPath = `${mockContext.workspacePath}/${randomBytes(12).toString( + 'hex', + )}.hcl`; + await writeFileSync(aPath, a, 'utf8'); + + const bPath = `${mockContext.workspacePath}/${randomBytes(12).toString( + 'hex', + )}.hcl`; + await writeFileSync(bPath, b, 'utf8'); + + const mockCtx = { + ...mockContext, + input: { + aSourcePath: aPath, + bSourcePath: bPath, + }, + }; + + await createHclMergeFilesAction().handler(mockCtx); + + expect(mockCtx.output.mock.calls[0][0]).toEqual('hcl'); + expect(mockCtx.output.mock.calls[0][1]).toEqual(expected); + }); +}); diff --git a/plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.ts b/plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.ts new file mode 100644 index 0000000..8c65d26 --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/src/actions/hcl/hcl.ts @@ -0,0 +1,221 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; +import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; +import { merge } from '@seatgeek/node-hcl'; + +import { ensureDirSync, readFileSync, writeFileSync } from 'fs-extra'; +import { dirname } from 'path'; +import { z } from 'zod'; + +async function readFileSafe(path: string): Promise { + try { + return readFileSync(path, 'utf8'); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + console.warn( + `file not found at path ${path}, defaulting to empty string`, + ); + return ''; + } + + console.error(`error reading hcl file: ${(error as Error).message}`); + throw error; + } +} + +async function mergeWrite( + a: string, + b: string, + outPath: string, +): Promise { + const out = await merge(a, b); + + try { + await writeFileSync(outPath, out, 'utf8'); + } catch (error) { + console.error(`error writing hcl file: ${(error as Error).message}`); + } +} + +async function mergeFiles(aPath: string, bPath: string): Promise { + const a = await readFileSafe(aPath); + const b = await readFileSafe(bPath); + + return await merge(a, b); +} + +async function mergeFilesWrite( + aPath: string, + bPath: string, + outPath: string, +): Promise { + const out = await mergeFiles(aPath, bPath); + + try { + await writeFileSync(outPath, out, 'utf8'); + } catch (error) { + console.error(`error writing hcl file: ${(error as Error).message}`); + } +} + +export const createHclMergeAction = () => { + const inputSchema = z.object({ + aSourceContent: z.string().describe('The HCL content to be merged'), + bSourceContent: z.string().describe('The HCL content to be merged'), + }); + + return createTemplateAction<{ + aSourceContent: string; + bSourceContent: string; + }>({ + id: 'hcl:merge', + schema: { + input: inputSchema, + output: z.object({ + hcl: z.string(), + }), + }, + async handler(ctx) { + const input = inputSchema.safeParse(ctx.input); + if (!input.success) { + throw new Error( + `Invalid input: ${Object.keys(input.error.flatten().fieldErrors)}`, + ); + } + + const out = await merge( + input.data.aSourceContent, + input.data.bSourceContent, + ); + ctx.output('hcl', out); + }, + }); +}; + +export const createHclMergeWriteAction = () => { + const inputSchema = z.object({ + aSourceContent: z.string().describe('The HCL content to be merged'), + bSourceContent: z.string().describe('The HCL content to be merged'), + outputPath: z + .string() + .describe('The path to write the merged HCL content to'), + }); + + return createTemplateAction<{ + aSourceContent: string; + bSourceContent: string; + outputPath: string; + }>({ + id: 'hcl:merge:write', + schema: { + input: inputSchema, + }, + async handler(ctx) { + const input = inputSchema.safeParse(ctx.input); + if (!input.success) { + throw new Error( + `Invalid input: ${Object.keys(input.error.flatten().fieldErrors)}`, + ); + } + + const outPath = resolveSafeChildPath( + ctx.workspacePath, + input.data.outputPath, + ); + + ensureDirSync(dirname(outPath)); + + await mergeWrite( + input.data.aSourceContent, + input.data.bSourceContent, + outPath, + ); + }, + }); +}; + +export const createHclMergeFilesAction = () => { + const inputSchema = z.object({ + aSourcePath: z.string().describe('The path to the HCL file to be merged'), + bSourcePath: z.string().describe('The path to the HCL file to be merged'), + }); + + return createTemplateAction<{ aSourcePath: string; bSourcePath: string }>({ + id: 'hcl:merge:files', + schema: { + input: inputSchema, + output: z.object({ + hcl: z.string(), + }), + }, + async handler(ctx) { + const input = inputSchema.safeParse(ctx.input); + if (!input.success) { + throw new Error( + `Invalid input: ${Object.keys(input.error.flatten().fieldErrors)}`, + ); + } + + const aPath = resolveSafeChildPath( + ctx.workspacePath, + input.data.aSourcePath, + ); + const bPath = resolveSafeChildPath( + ctx.workspacePath, + input.data.bSourcePath, + ); + + const out = await mergeFiles(aPath, bPath); + ctx.output('hcl', out); + }, + }); +}; + +export const createHclMergeFilesWriteAction = () => { + const inputSchema = z.object({ + aSourcePath: z.string().describe('The path to the HCL file to be merged'), + bSourcePath: z.string().describe('The path to the HCL file to be merged'), + outputPath: z + .string() + .describe('The path to write the merged HCL content to'), + }); + + return createTemplateAction<{ + aSourcePath: string; + bSourcePath: string; + outputPath: string; + }>({ + id: 'hcl:merge:files:write', + schema: { + input: inputSchema, + }, + async handler(ctx) { + const input = inputSchema.safeParse(ctx.input); + if (!input.success) { + throw new Error( + `Invalid input: ${Object.keys(input.error.flatten().fieldErrors)}`, + ); + } + + const aPath = resolveSafeChildPath( + ctx.workspacePath, + input.data.aSourcePath, + ); + const bPath = resolveSafeChildPath( + ctx.workspacePath, + input.data.bSourcePath, + ); + const outPath = resolveSafeChildPath( + ctx.workspacePath, + input.data.outputPath, + ); + + ensureDirSync(dirname(outPath)); + + await mergeFilesWrite(aPath, bPath, outPath); + }, + }); +}; diff --git a/plugins/scaffolder-backend-module-hcl/src/actions/hcl/index.ts b/plugins/scaffolder-backend-module-hcl/src/actions/hcl/index.ts new file mode 100644 index 0000000..9368e0b --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/src/actions/hcl/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +export { + createHclMergeAction, + createHclMergeFilesAction, + createHclMergeFilesWriteAction, + createHclMergeWriteAction, +} from './hcl'; diff --git a/plugins/scaffolder-backend-module-hcl/src/actions/index.ts b/plugins/scaffolder-backend-module-hcl/src/actions/index.ts new file mode 100644 index 0000000..836ece6 --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/src/actions/index.ts @@ -0,0 +1,5 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +export * from './hcl'; diff --git a/plugins/scaffolder-backend-module-hcl/src/index.ts b/plugins/scaffolder-backend-module-hcl/src/index.ts new file mode 100644 index 0000000..192dd12 --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/src/index.ts @@ -0,0 +1,5 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +export { scaffolderBackendModuleActions as default } from './module'; diff --git a/plugins/scaffolder-backend-module-hcl/src/module.ts b/plugins/scaffolder-backend-module-hcl/src/module.ts new file mode 100644 index 0000000..bc951fe --- /dev/null +++ b/plugins/scaffolder-backend-module-hcl/src/module.ts @@ -0,0 +1,29 @@ +/* + * Copyright SeatGeek + * Licensed under the terms of the Apache-2.0 license. See LICENSE file in project root for terms. + */ +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import * as actions from './actions'; + +export const scaffolderBackendModuleActions = createBackendModule({ + pluginId: 'scaffolder', + moduleId: 'scaffolder-backend-module-hcl', + register({ registerInit }) { + registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + config: coreServices.rootConfig, + }, + async init({ scaffolder }) { + scaffolder.addActions(actions.createHclMergeAction()); + scaffolder.addActions(actions.createHclMergeWriteAction()); + scaffolder.addActions(actions.createHclMergeFilesAction()); + scaffolder.addActions(actions.createHclMergeFilesWriteAction()); + }, + }); + }, +}); diff --git a/yarn.lock b/yarn.lock index 41b3bad..b9111fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3565,6 +3565,76 @@ yauzl "^3.0.0" yn "^4.0.0" +"@backstage/backend-common@^0.24.1": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@backstage/backend-common/-/backend-common-0.24.1.tgz#62253f854c840b3564a21ab945658fbfd49e05a6" + integrity sha512-U4CHgO1Ob1v4StgMolNpVRGg1c3LqhUY2L5ztjdKu3yuwgQcSTWi/sQTtua4OTWTupmhkyYGfroAoeE1QFqUCA== + dependencies: + "@aws-sdk/abort-controller" "^3.347.0" + "@aws-sdk/client-codecommit" "^3.350.0" + "@aws-sdk/client-s3" "^3.350.0" + "@aws-sdk/credential-providers" "^3.350.0" + "@aws-sdk/types" "^3.347.0" + "@backstage/backend-dev-utils" "^0.1.5" + "@backstage/backend-plugin-api" "^0.8.1" + "@backstage/cli-common" "^0.1.14" + "@backstage/config" "^1.2.0" + "@backstage/config-loader" "^1.9.0" + "@backstage/errors" "^1.2.4" + "@backstage/integration" "^1.14.0" + "@backstage/integration-aws-node" "^0.1.12" + "@backstage/plugin-auth-node" "^0.5.1" + "@backstage/types" "^1.1.1" + "@google-cloud/storage" "^7.0.0" + "@keyv/memcache" "^1.3.5" + "@keyv/redis" "^2.5.3" + "@kubernetes/client-node" "0.20.0" + "@manypkg/get-packages" "^1.1.3" + "@octokit/rest" "^19.0.3" + "@types/cors" "^2.8.6" + "@types/dockerode" "^3.3.0" + "@types/express" "^4.17.6" + "@types/luxon" "^3.0.0" + "@types/webpack-env" "^1.15.2" + archiver "^6.0.0" + base64-stream "^1.0.0" + compression "^1.7.4" + concat-stream "^2.0.0" + cors "^2.8.5" + dockerode "^4.0.0" + express "^4.17.1" + express-promise-router "^4.1.0" + fs-extra "^11.2.0" + git-url-parse "^14.0.0" + helmet "^6.0.0" + isomorphic-git "^1.23.0" + jose "^5.0.0" + keyv "^4.5.2" + knex "^3.0.0" + lodash "^4.17.21" + logform "^2.3.2" + luxon "^3.0.0" + minimatch "^9.0.0" + minimist "^1.2.5" + morgan "^1.10.0" + mysql2 "^3.0.0" + node-fetch "^2.7.0" + node-forge "^1.3.1" + p-limit "^3.1.0" + path-to-regexp "^6.2.1" + pg "^8.11.3" + pg-format "^1.0.4" + raw-body "^2.4.1" + selfsigned "^2.0.0" + stoppable "^1.1.0" + tar "^6.1.12" + triple-beam "^1.4.1" + uuid "^9.0.0" + winston "^3.2.1" + winston-transport "^4.5.0" + yauzl "^3.0.0" + yn "^4.0.0" + "@backstage/backend-defaults@0.4.3", "@backstage/backend-defaults@^0.4.2", "@backstage/backend-defaults@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@backstage/backend-defaults/-/backend-defaults-0.4.3.tgz#9078d176ae0fd1df77d2073286ea943c0fbfbee7" @@ -3679,6 +3749,23 @@ knex "^3.0.0" luxon "^3.0.0" +"@backstage/backend-plugin-api@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@backstage/backend-plugin-api/-/backend-plugin-api-0.8.1.tgz#da1a2baea63098ae0c7da88ecad58e4b96ee90ac" + integrity sha512-Ckr/aE+jSZzwooH6nRCRWhtJFhm4P1JTyukH8gygP0wIkQGdoC7n3Xt7cheGP2fMV//9p5NZ+sfNZTr8LpO8hg== + dependencies: + "@backstage/cli-common" "^0.1.14" + "@backstage/config" "^1.2.0" + "@backstage/errors" "^1.2.4" + "@backstage/plugin-auth-node" "^0.5.1" + "@backstage/plugin-permission-common" "^0.8.1" + "@backstage/types" "^1.1.1" + "@types/express" "^4.17.6" + "@types/luxon" "^3.0.0" + express "^4.17.1" + knex "^3.0.0" + luxon "^3.0.0" + "@backstage/backend-tasks@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@backstage/backend-tasks/-/backend-tasks-0.6.0.tgz#088b8f4aac183998e26d4dc0f1791be077026f01" @@ -4473,6 +4560,29 @@ zod "^3.22.4" zod-to-json-schema "^3.21.4" +"@backstage/plugin-auth-node@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@backstage/plugin-auth-node/-/plugin-auth-node-0.5.1.tgz#1637cda18bd98cabdb1d57cb8f678cd1b05f6b7b" + integrity sha512-GDudLG6nJrHAX9ot41wvgCnDJlN+nrOtxY2JwEJ1txX1GZD1a67oDI7vpnZ8Bdb/kXZMBVRstCR4XT47CdJFkg== + dependencies: + "@backstage/backend-common" "^0.24.1" + "@backstage/backend-plugin-api" "^0.8.1" + "@backstage/catalog-client" "^1.6.6" + "@backstage/catalog-model" "^1.6.0" + "@backstage/config" "^1.2.0" + "@backstage/errors" "^1.2.4" + "@backstage/types" "^1.1.1" + "@types/express" "*" + "@types/passport" "^1.0.3" + express "^4.17.1" + jose "^5.0.0" + lodash "^4.17.21" + node-fetch "^2.7.0" + passport "^0.7.0" + winston "^3.2.1" + zod "^3.22.4" + zod-to-json-schema "^3.21.4" + "@backstage/plugin-auth-react@^0.1.5": version "0.1.5" resolved "https://registry.yarnpkg.com/@backstage/plugin-auth-react/-/plugin-auth-react-0.1.5.tgz#3b2e1bdbf203f98eea49dc3e6cf59ac3b520f04b" @@ -5020,6 +5130,29 @@ "@backstage/plugin-permission-common" "^0.8.1" "@backstage/types" "^1.1.1" +"@backstage/plugin-scaffolder-node@^0.4.10": + version "0.4.10" + resolved "https://registry.yarnpkg.com/@backstage/plugin-scaffolder-node/-/plugin-scaffolder-node-0.4.10.tgz#30f6bcce20ed15b074a604342296e6f8590eacaf" + integrity sha512-CHXvC27L4kxUM9rqL96aeCCiGTNKACKcZArJNGZST9EXMIjtjxHVVkdU2/D882qAspHxX2fUp78mu7Sam82fTA== + dependencies: + "@backstage/backend-common" "^0.24.1" + "@backstage/backend-plugin-api" "^0.8.1" + "@backstage/catalog-model" "^1.6.0" + "@backstage/errors" "^1.2.4" + "@backstage/integration" "^1.14.0" + "@backstage/plugin-scaffolder-common" "^1.5.5" + "@backstage/types" "^1.1.1" + concat-stream "^2.0.0" + fs-extra "^11.2.0" + globby "^11.0.0" + isomorphic-git "^1.23.0" + jsonschema "^1.2.6" + p-limit "^3.1.0" + tar "^6.1.12" + winston "^3.2.1" + zod "^3.22.4" + zod-to-json-schema "^3.20.4" + "@backstage/plugin-scaffolder-node@^0.4.9": version "0.4.9" resolved "https://registry.yarnpkg.com/@backstage/plugin-scaffolder-node/-/plugin-scaffolder-node-0.4.9.tgz#ca2e90e372044127a8b7d65770b2c45bdb181d4b" @@ -9053,6 +9186,13 @@ version "0.0.0" uid "" +"@seatgeek/node-hcl@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@seatgeek/node-hcl/-/node-hcl-1.0.0.tgz#817a4c2cdfa9b9cd90d2a1fa9484b32bf3412373" + integrity sha512-B1wmUCXcTElhD1g/IX0jYfEtOqK7vrSZ8/ZjLCWXfjxejUYRTtfxh+Ojqzz/L2KuIk0tRP6wmvbtnOIwtllviw== + dependencies: + fs-extra "^11.2.0" + "@sec-ant/readable-stream@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz#60de891bb126abfdc5410fdc6166aca065f10a0c" @@ -11783,6 +11923,14 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" @@ -11896,6 +12044,13 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== + dependencies: + "@types/node" "*" + "@types/jsonwebtoken@^9.0.0": version "9.0.5" resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588" @@ -12112,7 +12267,7 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@*", "@types/react-dom@<18.0.0", "@types/react-dom@^17": +"@types/react-dom@*", "@types/react-dom@<18.0.0": version "17.0.25" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.25.tgz#e0e5b3571e1069625b3a3da2b279379aa33a0cb5" integrity sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA== @@ -14233,9 +14388,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001580: - version "1.0.30001581" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz#0dfd4db9e94edbdca67d57348ebc070dece279f4" - integrity sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ== + version "1.0.30001660" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz" + integrity sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg== cardinal@^2.1.1: version "2.1.1" @@ -14754,6 +14909,11 @@ component-emitter@^1.3.0: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== +component-emitter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-2.0.0.tgz#3a137dfe66fcf2efe3eab7cb7d5f51741b3620c6" + integrity sha512-4m5s3Me2xxlVKG9PkZpQqHQR7bgpnN7joDMJ4yvVkVXngjoITG76IaZmzmywSeRTeTpc6N6r3H3+KyUurV8OYw== + compress-commons@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-5.0.1.tgz#e46723ebbab41b50309b27a0e0f6f3baed2d6590" @@ -27569,6 +27729,13 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== +stream@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/stream/-/stream-0.0.3.tgz#3f3934a900a561ce3e2b9ffbd2819cead32699d9" + integrity sha512-aMsbn7VKrl4A2T7QAQQbzgN7NVc70vgF5INQrBXqn4dCXN1zy3L9HGgLO5s7PExmdrzTJ8uR/27aviW8or8/+A== + dependencies: + component-emitter "^2.0.0" + streamroller@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" @@ -27637,7 +27804,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -27711,7 +27887,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -27725,6 +27901,13 @@ strip-ansi@5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -29747,8 +29930,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - name wrap-ansi-cjs +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -29766,6 +29948,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -30088,6 +30279,11 @@ zod@^3.22.4: resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== +zod@^3.23.8: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== + zwitch@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"