@backstage/plugin-scaffolder-backend 0.15.2 → 0.15.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/scaffolder/actions/createTemplateAction.ts","../src/scaffolder/actions/builtin/catalog/register.ts","../src/scaffolder/actions/builtin/catalog/write.ts","../src/scaffolder/actions/builtin/debug/log.ts","../src/scaffolder/actions/builtin/fetch/helpers.ts","../src/scaffolder/actions/builtin/fetch/plain.ts","../src/scaffolder/actions/builtin/fetch/template.ts","../src/scaffolder/actions/builtin/filesystem/delete.ts","../src/scaffolder/actions/builtin/filesystem/rename.ts","../src/scaffolder/actions/builtin/helpers.ts","../src/scaffolder/actions/builtin/publish/util.ts","../src/scaffolder/actions/builtin/publish/azure.ts","../src/scaffolder/actions/builtin/publish/bitbucket.ts","../src/scaffolder/actions/builtin/publish/file.ts","../src/scaffolder/actions/builtin/publish/github.ts","../src/scaffolder/actions/builtin/publish/githubPullRequest.ts","../src/scaffolder/actions/builtin/publish/gitlab.ts","../src/scaffolder/actions/builtin/github/githubActionsDispatch.ts","../src/scaffolder/actions/builtin/createBuiltinActions.ts","../src/scaffolder/actions/TemplateActionRegistry.ts","../src/lib/catalog/CatalogEntityClient.ts","../src/scaffolder/tasks/DatabaseTaskStore.ts","../src/scaffolder/tasks/StorageTaskBroker.ts","../src/scaffolder/tasks/helper.ts","../src/scaffolder/tasks/TaskWorker.ts","../src/service/helpers.ts","../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputBase, TemplateAction } from './types';\n\nexport const createTemplateAction = <Input extends InputBase>(\n templateAction: TemplateAction<Input>,\n): TemplateAction<any> => {\n // TODO(blam): Can add some more validation here to validate the action later on\n return templateAction;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { getEntityName } from '@backstage/catalog-model';\nimport { createTemplateAction } from '../../createTemplateAction';\n\nexport function createCatalogRegisterAction(options: {\n catalogClient: CatalogApi;\n integrations: ScmIntegrations;\n}) {\n const { catalogClient, integrations } = options;\n\n return createTemplateAction<\n | { catalogInfoUrl: string }\n | { repoContentsUrl: string; catalogInfoPath?: string }\n >({\n id: 'catalog:register',\n description:\n 'Registers entities from a catalog descriptor file in the workspace into the software catalog.',\n schema: {\n input: {\n oneOf: [\n {\n type: 'object',\n required: ['catalogInfoUrl'],\n properties: {\n catalogInfoUrl: {\n title: 'Catalog Info URL',\n description:\n 'An absolute URL pointing to the catalog info file location',\n type: 'string',\n },\n },\n },\n {\n type: 'object',\n required: ['repoContentsUrl'],\n properties: {\n repoContentsUrl: {\n title: 'Repository Contents URL',\n description:\n 'An absolute URL pointing to the root of a repository directory tree',\n type: 'string',\n },\n catalogInfoPath: {\n title: 'Fetch URL',\n description:\n 'A relative path from the repo root pointing to the catalog info file, defaults to /catalog-info.yaml',\n type: 'string',\n },\n },\n },\n ],\n },\n },\n async handler(ctx) {\n const { input } = ctx;\n\n let catalogInfoUrl;\n if ('catalogInfoUrl' in input) {\n catalogInfoUrl = input.catalogInfoUrl;\n } else {\n const { repoContentsUrl, catalogInfoPath = '/catalog-info.yaml' } =\n input;\n const integration = integrations.byUrl(repoContentsUrl);\n if (!integration) {\n throw new InputError(\n `No integration found for host ${repoContentsUrl}`,\n );\n }\n\n catalogInfoUrl = integration.resolveUrl({\n base: repoContentsUrl,\n url: catalogInfoPath,\n });\n }\n\n ctx.logger.info(`Registering ${catalogInfoUrl} in the catalog`);\n\n const result = await catalogClient.addLocation(\n {\n type: 'url',\n target: catalogInfoUrl,\n },\n ctx.token ? { token: ctx.token } : {},\n );\n if (result.entities.length >= 1) {\n const { kind, name, namespace } = getEntityName(result.entities[0]);\n ctx.output('entityRef', `${kind}:${namespace}/${name}`);\n ctx.output('catalogInfoUrl', catalogInfoUrl);\n }\n },\n });\n}\n","/*\n * Copyright 2021 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { resolve as resolvePath } from 'path';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport * as yaml from 'yaml';\nimport { Entity } from '@backstage/catalog-model';\n\nexport function createCatalogWriteAction() {\n return createTemplateAction<{ name?: string; entity: Entity }>({\n id: 'catalog:write',\n description: 'Writes the catalog-info.yaml for your template',\n schema: {\n input: {\n type: 'object',\n properties: {\n entity: {\n title: 'Entity info to write catalog-info.yaml',\n description:\n 'You can provide the same values used in the Entity schema.',\n type: 'object',\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logStream.write(`Writing catalog-info.yaml`);\n const { entity } = ctx.input;\n\n await fs.writeFile(\n resolvePath(ctx.workspacePath, 'catalog-info.yaml'),\n yaml.stringify(entity),\n );\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { readdir, stat } from 'fs-extra';\nimport { relative, resolve } from 'path';\nimport { createTemplateAction } from '../../createTemplateAction';\n\n/**\n * This task is useful for local development and testing of both the scaffolder\n * and scaffolder templates.\n */\nexport function createDebugLogAction() {\n return createTemplateAction<{ message?: string; listWorkspace?: boolean }>({\n id: 'debug:log',\n description:\n 'Writes a message into the log or lists all files in the workspace.',\n schema: {\n input: {\n type: 'object',\n properties: {\n message: {\n title: 'Message to output.',\n type: 'string',\n },\n listWorkspace: {\n title: 'List all files in the workspace, if true.',\n type: 'boolean',\n },\n },\n },\n },\n async handler(ctx) {\n if (ctx.input?.message) {\n ctx.logStream.write(ctx.input.message);\n }\n\n if (ctx.input?.listWorkspace) {\n const files = await recursiveReadDir(ctx.workspacePath);\n ctx.logStream.write(\n `Workspace:\\n${files\n .map(f => ` - ${relative(ctx.workspacePath, f)}`)\n .join('\\n')}`,\n );\n }\n },\n });\n}\n\nexport async function recursiveReadDir(dir: string): Promise<string[]> {\n const subdirs = await readdir(dir);\n const files = await Promise.all(\n subdirs.map(async subdir => {\n const res = resolve(dir, subdir);\n return (await stat(res)).isDirectory() ? recursiveReadDir(res) : [res];\n }),\n );\n return files.reduce((a, f) => a.concat(f), []);\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { resolve as resolvePath, isAbsolute } from 'path';\nimport { UrlReader } from '@backstage/backend-common';\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { JsonValue } from '@backstage/config';\n\nexport async function fetchContents({\n reader,\n integrations,\n baseUrl,\n fetchUrl = '.',\n outputPath,\n}: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n baseUrl?: string;\n fetchUrl?: JsonValue;\n outputPath: string;\n}) {\n if (typeof fetchUrl !== 'string') {\n throw new InputError(\n `Invalid url parameter, expected string, got ${typeof fetchUrl}`,\n );\n }\n\n let fetchUrlIsAbsolute = false;\n try {\n // eslint-disable-next-line no-new\n new URL(fetchUrl);\n fetchUrlIsAbsolute = true;\n } catch {\n /* ignored */\n }\n\n // We handle both file locations and url ones\n if (!fetchUrlIsAbsolute && baseUrl?.startsWith('file://')) {\n const basePath = baseUrl.slice('file://'.length);\n if (isAbsolute(fetchUrl)) {\n throw new InputError(\n `Fetch URL may not be absolute for file locations, ${fetchUrl}`,\n );\n }\n const srcDir = resolvePath(basePath, '..', fetchUrl);\n await fs.copy(srcDir, outputPath);\n } else {\n let readUrl;\n\n if (fetchUrlIsAbsolute) {\n readUrl = fetchUrl;\n } else if (baseUrl) {\n const integration = integrations.byUrl(baseUrl);\n if (!integration) {\n throw new InputError(`No integration found for location ${baseUrl}`);\n }\n\n readUrl = integration.resolveUrl({\n url: fetchUrl,\n base: baseUrl,\n });\n } else {\n throw new InputError(\n `Failed to fetch, template location could not be determined and the fetch URL is relative, ${fetchUrl}`,\n );\n }\n\n const res = await reader.readTree(readUrl);\n await fs.ensureDir(outputPath);\n await res.dir({ targetDir: outputPath });\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UrlReader, resolveSafeChildPath } from '@backstage/backend-common';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { fetchContents } from './helpers';\nimport { createTemplateAction } from '../../createTemplateAction';\n\nexport function createFetchPlainAction(options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n}) {\n const { reader, integrations } = options;\n\n return createTemplateAction<{ url: string; targetPath?: string }>({\n id: 'fetch:plain',\n description:\n \"Downloads content and places it in the workspace, or optionally in a subdirectory specified by the 'targetPath' input option.\",\n schema: {\n input: {\n type: 'object',\n required: ['url'],\n properties: {\n url: {\n title: 'Fetch URL',\n description:\n 'Relative path or absolute URL pointing to the directory tree to fetch',\n type: 'string',\n },\n targetPath: {\n title: 'Target Path',\n description:\n 'Target path within the working directory to download the contents to.',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logger.info('Fetching plain content from remote URL');\n\n // Finally move the template result into the task workspace\n const targetPath = ctx.input.targetPath ?? './';\n const outputPath = resolveSafeChildPath(ctx.workspacePath, targetPath);\n\n await fetchContents({\n reader,\n integrations,\n baseUrl: ctx.baseUrl,\n fetchUrl: ctx.input.url,\n outputPath,\n });\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { resolve as resolvePath, extname } from 'path';\nimport { resolveSafeChildPath, UrlReader } from '@backstage/backend-common';\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { fetchContents } from './helpers';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport globby from 'globby';\nimport nunjucks from 'nunjucks';\nimport fs from 'fs-extra';\nimport { isBinaryFile } from 'isbinaryfile';\n\n/*\n * Maximise compatibility with Jinja (and therefore cookiecutter)\n * using nunjucks jinja compat mode. Since this method mutates\n * the global nunjucks instance, we can't enable this per-template,\n * or only for templates with cookiecutter compat enabled, so the\n * next best option is to explicitly enable it globally and allow\n * folks to rely on jinja compatibility behaviour in fetch:template\n * templates if they wish.\n *\n * cf. https://mozilla.github.io/nunjucks/api.html#installjinjacompat\n */\nnunjucks.installJinjaCompat();\n\ntype CookieCompatInput = {\n copyWithoutRender?: string[];\n cookiecutterCompat?: boolean;\n};\n\ntype ExtensionInput = {\n templateFileExtension?: string | boolean;\n};\n\nexport type FetchTemplateInput = {\n url: string;\n targetPath?: string;\n values: any;\n} & CookieCompatInput &\n ExtensionInput;\n\nexport function createFetchTemplateAction(options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n}) {\n const { reader, integrations } = options;\n\n return createTemplateAction<FetchTemplateInput>({\n id: 'fetch:template',\n description:\n \"Downloads a skeleton, templates variables into file and directory names and content, and places the result in the workspace, or optionally in a subdirectory specified by the 'targetPath' input option.\",\n schema: {\n input: {\n type: 'object',\n required: ['url'],\n properties: {\n url: {\n title: 'Fetch URL',\n description:\n 'Relative path or absolute URL pointing to the directory tree to fetch',\n type: 'string',\n },\n targetPath: {\n title: 'Target Path',\n description:\n 'Target path within the working directory to download the contents to. Defaults to the working directory root.',\n type: 'string',\n },\n values: {\n title: 'Template Values',\n description: 'Values to pass on to the templating engine',\n type: 'object',\n },\n copyWithoutRender: {\n title: 'Copy Without Render',\n description:\n 'An array of glob patterns. Any files or directories which match are copied without being processed as templates.',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n cookiecutterCompat: {\n title: 'Cookiecutter compatibility mode',\n description:\n 'Enable features to maximise compatibility with templates built for fetch:cookiecutter',\n type: 'boolean',\n },\n templateFileExtension: {\n title: 'Template File Extension',\n description:\n 'If set, only files with the given extension will be templated. If set to `true`, the default extension `.njk` is used.',\n type: ['string', 'boolean'],\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logger.info('Fetching template content from remote URL');\n\n const workDir = await ctx.createTemporaryDirectory();\n const templateDir = resolvePath(workDir, 'template');\n\n const targetPath = ctx.input.targetPath ?? './';\n const outputDir = resolveSafeChildPath(ctx.workspacePath, targetPath);\n\n if (\n ctx.input.copyWithoutRender &&\n !Array.isArray(ctx.input.copyWithoutRender)\n ) {\n throw new InputError(\n 'Fetch action input copyWithoutRender must be an Array',\n );\n }\n\n if (\n ctx.input.templateFileExtension &&\n (ctx.input.copyWithoutRender || ctx.input.cookiecutterCompat)\n ) {\n throw new InputError(\n 'Fetch action input extension incompatible with copyWithoutRender and cookiecutterCompat',\n );\n }\n\n let extension: string | false = false;\n if (ctx.input.templateFileExtension) {\n extension =\n ctx.input.templateFileExtension === true\n ? '.njk'\n : ctx.input.templateFileExtension;\n if (!extension.startsWith('.')) {\n extension = `.${extension}`;\n }\n }\n\n await fetchContents({\n reader,\n integrations,\n baseUrl: ctx.baseUrl,\n fetchUrl: ctx.input.url,\n outputPath: templateDir,\n });\n\n ctx.logger.info('Listing files and directories in template');\n const allEntriesInTemplate = await globby(`**/*`, {\n cwd: templateDir,\n dot: true,\n onlyFiles: false,\n markDirectories: true,\n });\n\n const nonTemplatedEntries = new Set(\n (\n await Promise.all(\n (ctx.input.copyWithoutRender || []).map(pattern =>\n globby(pattern, {\n cwd: templateDir,\n dot: true,\n onlyFiles: false,\n markDirectories: true,\n }),\n ),\n )\n ).flat(),\n );\n\n // Create a templater\n const templater = nunjucks.configure({\n ...(ctx.input.cookiecutterCompat\n ? {}\n : {\n tags: {\n // TODO(mtlewis/orkohunter): Document Why we are changing the literals? Not here, but on scaffolder docs. ADR?\n variableStart: '${{',\n variableEnd: '}}',\n },\n }),\n // We don't want this builtin auto-escaping, since uses HTML escape sequences\n // like `&quot;` - the correct way to escape strings in our case depends on\n // the file type.\n autoescape: false,\n });\n\n if (ctx.input.cookiecutterCompat) {\n // The \"jsonify\" filter built into cookiecutter is common\n // in fetch:cookiecutter templates, so when compat mode\n // is enabled we alias the \"dump\" filter from nunjucks as\n // jsonify. Dump accepts an optional `spaces` parameter\n // which enables indented output, but when this parameter\n // is not supplied it works identically to jsonify.\n //\n // cf. https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html?highlight=jsonify#jsonify-extension\n // cf. https://mozilla.github.io/nunjucks/templating.html#dump\n templater.addFilter('jsonify', templater.getFilter('dump'));\n }\n\n // Cookiecutter prefixes all parameters in templates with\n // `cookiecutter.`. To replicate this, we wrap our parameters\n // in an object with a `cookiecutter` property when compat\n // mode is enabled.\n const { cookiecutterCompat, values } = ctx.input;\n const context = {\n [cookiecutterCompat ? 'cookiecutter' : 'values']: values,\n };\n\n ctx.logger.info(\n `Processing ${allEntriesInTemplate.length} template files/directories with input values`,\n ctx.input.values,\n );\n\n for (const location of allEntriesInTemplate) {\n let renderFilename: boolean;\n let renderContents: boolean;\n\n let localOutputPath = location;\n if (extension) {\n renderFilename = true;\n renderContents = extname(localOutputPath) === extension;\n if (renderContents) {\n localOutputPath = localOutputPath.slice(0, -extension.length);\n }\n } else {\n renderFilename = renderContents = !nonTemplatedEntries.has(location);\n }\n if (renderFilename) {\n localOutputPath = templater.renderString(localOutputPath, context);\n }\n const outputPath = resolvePath(outputDir, localOutputPath);\n\n if (!renderContents && !extension) {\n ctx.logger.info(\n `Copying file/directory ${location} without processing.`,\n );\n }\n\n if (location.endsWith('/')) {\n ctx.logger.info(\n `Writing directory ${location} to template output path.`,\n );\n await fs.ensureDir(outputPath);\n } else {\n const inputFilePath = resolvePath(templateDir, location);\n\n if (await isBinaryFile(inputFilePath)) {\n ctx.logger.info(\n `Copying binary file ${location} to template output path.`,\n );\n await fs.copy(inputFilePath, outputPath);\n } else {\n ctx.logger.info(\n `Writing file ${location} to template output path.`,\n );\n const inputFileContents = await fs.readFile(inputFilePath, 'utf-8');\n await fs.outputFile(\n outputPath,\n renderContents\n ? templater.renderString(inputFileContents, context)\n : inputFileContents,\n );\n }\n }\n }\n\n ctx.logger.info(`Template result written to ${outputDir}`);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { InputError } from '@backstage/errors';\nimport { resolveSafeChildPath } from '@backstage/backend-common';\nimport fs from 'fs-extra';\n\nexport const createFilesystemDeleteAction = () => {\n return createTemplateAction<{ files: string[] }>({\n id: 'fs:delete',\n description: 'Deletes files and directories from the workspace',\n schema: {\n input: {\n required: ['files'],\n type: 'object',\n properties: {\n files: {\n title: 'Files',\n description: 'A list of files and directories that will be deleted',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n },\n },\n },\n async handler(ctx) {\n if (!Array.isArray(ctx.input?.files)) {\n throw new InputError('files must be an Array');\n }\n\n for (const file of ctx.input.files) {\n const filepath = resolveSafeChildPath(ctx.workspacePath, file);\n\n try {\n await fs.remove(filepath);\n ctx.logger.info(`File ${filepath} deleted successfully`);\n } catch (err) {\n ctx.logger.error(`Failed to delete file ${filepath}:`, err);\n throw err;\n }\n }\n },\n });\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { resolveSafeChildPath } from '@backstage/backend-common';\n\nimport { InputError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/config';\nimport fs from 'fs-extra';\n\ninterface FilesToRename extends JsonObject {\n from: string;\n to: string;\n}\n\nexport const createFilesystemRenameAction = () => {\n return createTemplateAction<{ files: FilesToRename }>({\n id: 'fs:rename',\n description: 'Renames files and directories within the workspace',\n schema: {\n input: {\n required: ['files'],\n type: 'object',\n properties: {\n files: {\n title: 'Files',\n description:\n 'A list of file and directory names that will be renamed',\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to'],\n properties: {\n from: {\n type: 'string',\n title: 'The source location of the file to be renamed',\n },\n to: {\n type: 'string',\n title: 'The destination of the new file',\n },\n overwrite: {\n type: 'boolean',\n title:\n 'Overwrite existing file or directory, default is false',\n },\n },\n },\n },\n },\n },\n },\n async handler(ctx) {\n if (!Array.isArray(ctx.input?.files)) {\n throw new InputError('files must be an Array');\n }\n\n for (const file of ctx.input.files) {\n if (!file.from || !file.to) {\n throw new InputError('each file must have a from and to property');\n }\n\n const sourceFilepath = resolveSafeChildPath(\n ctx.workspacePath,\n file.from,\n );\n const destFilepath = resolveSafeChildPath(ctx.workspacePath, file.to);\n\n try {\n await fs.move(sourceFilepath, destFilepath, {\n overwrite: file.overwrite ?? false,\n });\n ctx.logger.info(\n `File ${sourceFilepath} renamed to ${destFilepath} successfully`,\n );\n } catch (err) {\n ctx.logger.error(\n `Failed to rename file ${sourceFilepath} to ${destFilepath}:`,\n err,\n );\n throw err;\n }\n }\n },\n });\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { spawn } from 'child_process';\nimport { PassThrough, Writable } from 'stream';\nimport { Logger } from 'winston';\nimport { Git } from '@backstage/backend-common';\nimport { Octokit } from '@octokit/rest';\n\nexport type RunCommandOptions = {\n command: string;\n args: string[];\n logStream?: Writable;\n};\n\nexport const runCommand = async ({\n command,\n args,\n logStream = new PassThrough(),\n}: RunCommandOptions) => {\n await new Promise<void>((resolve, reject) => {\n const process = spawn(command, args);\n\n process.stdout.on('data', stream => {\n logStream.write(stream);\n });\n\n process.stderr.on('data', stream => {\n logStream.write(stream);\n });\n\n process.on('error', error => {\n return reject(error);\n });\n\n process.on('close', code => {\n if (code !== 0) {\n return reject(`Command ${command} failed, exit code: ${code}`);\n }\n return resolve();\n });\n });\n};\n\nexport async function initRepoAndPush({\n dir,\n remoteUrl,\n auth,\n logger,\n defaultBranch = 'master',\n commitMessage = 'Initial commit',\n gitAuthorInfo,\n}: {\n dir: string;\n remoteUrl: string;\n auth: { username: string; password: string };\n logger: Logger;\n defaultBranch?: string;\n commitMessage?: string;\n gitAuthorInfo?: { name?: string; email?: string };\n}): Promise<void> {\n const git = Git.fromAuth({\n username: auth.username,\n password: auth.password,\n logger,\n });\n\n await git.init({\n dir,\n defaultBranch,\n });\n\n await git.add({ dir, filepath: '.' });\n\n // use provided info if possible, otherwise use fallbacks\n const authorInfo = {\n name: gitAuthorInfo?.name ?? 'Scaffolder',\n email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io',\n };\n\n await git.commit({\n dir,\n message: commitMessage,\n author: authorInfo,\n committer: authorInfo,\n });\n\n await git.addRemote({\n dir,\n url: remoteUrl,\n remote: 'origin',\n });\n\n await git.push({\n dir,\n remote: 'origin',\n });\n}\n\ntype BranchProtectionOptions = {\n client: Octokit;\n owner: string;\n repoName: string;\n logger: Logger;\n requireCodeOwnerReviews: boolean;\n defaultBranch?: string;\n};\n\nexport const enableBranchProtectionOnDefaultRepoBranch = async ({\n repoName,\n client,\n owner,\n logger,\n requireCodeOwnerReviews,\n defaultBranch = 'master',\n}: BranchProtectionOptions): Promise<void> => {\n const tryOnce = async () => {\n try {\n await client.repos.updateBranchProtection({\n mediaType: {\n /**\n * 👇 we need this preview because allowing a custom\n * reviewer count on branch protection is a preview\n * feature\n *\n * More here: https://docs.github.com/en/rest/overview/api-previews#require-multiple-approving-reviews\n */\n previews: ['luke-cage-preview'],\n },\n owner,\n repo: repoName,\n branch: defaultBranch,\n required_status_checks: { strict: true, contexts: [] },\n restrictions: null,\n enforce_admins: true,\n required_pull_request_reviews: {\n required_approving_review_count: 1,\n require_code_owner_reviews: requireCodeOwnerReviews,\n },\n });\n } catch (e) {\n if (\n e.message.includes(\n 'Upgrade to GitHub Pro or make this repository public to enable this feature',\n )\n ) {\n logger.warn(\n 'Branch protection was not enabled as it requires GitHub Pro for private repositories',\n );\n } else {\n throw e;\n }\n }\n };\n\n try {\n await tryOnce();\n } catch (e) {\n if (!e.message.includes('Branch not found')) {\n throw e;\n }\n\n // GitHub has eventual consistency. Fail silently, wait, and try again.\n await new Promise(resolve => setTimeout(resolve, 600));\n await tryOnce();\n }\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { join as joinPath, normalize as normalizePath } from 'path';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\n\nexport const getRepoSourceDirectory = (\n workspacePath: string,\n sourcePath: string | undefined,\n) => {\n if (sourcePath) {\n const safeSuffix = normalizePath(sourcePath).replace(\n /^(\\.\\.(\\/|\\\\|$))+/,\n '',\n );\n return joinPath(workspacePath, safeSuffix);\n }\n return workspacePath;\n};\nexport type RepoSpec = {\n repo: string;\n host: string;\n owner?: string;\n organization?: string;\n workspace?: string;\n project?: string;\n};\n\nexport const parseRepoUrl = (\n repoUrl: string,\n integrations: ScmIntegrationRegistry,\n): RepoSpec => {\n let parsed;\n try {\n parsed = new URL(`https://${repoUrl}`);\n } catch (error) {\n throw new InputError(\n `Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`,\n );\n }\n const host = parsed.host;\n const owner = parsed.searchParams.get('owner') ?? undefined;\n const organization = parsed.searchParams.get('organization') ?? undefined;\n const workspace = parsed.searchParams.get('workspace') ?? undefined;\n const project = parsed.searchParams.get('project') ?? undefined;\n\n const type = integrations.byHost(host)?.type;\n\n if (!type) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n if (type === 'bitbucket') {\n if (host === 'bitbucket.org') {\n if (!workspace) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing workspace`,\n );\n }\n }\n if (!project) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing project`,\n );\n }\n } else {\n if (!owner) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing owner`,\n );\n }\n }\n\n const repo = parsed.searchParams.get('repo');\n if (!repo) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing repo`,\n );\n }\n\n return { host, owner, repo, organization, workspace, project };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { initRepoAndPush } from '../helpers';\nimport { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces';\nimport { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Config } from '@backstage/config';\n\nexport function createPublishAzureAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n return createTemplateAction<{\n repoUrl: string;\n description?: string;\n defaultBranch?: string;\n sourcePath?: string;\n }>({\n id: 'publish:azure',\n description:\n 'Initializes a git repository of the content in the workspace, and publishes it to Azure.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n type: 'string',\n },\n description: {\n title: 'Repository Description',\n type: 'string',\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const { repoUrl, defaultBranch = 'master' } = ctx.input;\n\n const { owner, repo, host, organization } = parseRepoUrl(\n repoUrl,\n integrations,\n );\n\n if (!organization) {\n throw new InputError(\n `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing organization`,\n );\n }\n\n const integrationConfig = integrations.azure.byHost(host);\n\n if (!integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n if (!integrationConfig.config.token) {\n throw new InputError(`No token provided for Azure Integration ${host}`);\n }\n const authHandler = getPersonalAccessTokenHandler(\n integrationConfig.config.token,\n );\n\n const webApi = new WebApi(`https://${host}/${organization}`, authHandler);\n const client = await webApi.getGitApi();\n const createOptions: GitRepositoryCreateOptions = { name: repo };\n const returnedRepo = await client.createRepository(createOptions, owner);\n\n if (!returnedRepo) {\n throw new InputError(\n `Unable to create the repository with Organization ${organization}, Project ${owner} and Repo ${repo}.\n Please make sure that both the Org and Project are typed corrected and exist.`,\n );\n }\n const remoteUrl = returnedRepo.remoteUrl;\n\n if (!remoteUrl) {\n throw new InputError(\n 'No remote URL returned from create repository for Azure',\n );\n }\n\n // blam: Repo contents is serialized into the path,\n // so it's just the base path I think\n const repoContentsUrl = remoteUrl;\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl,\n defaultBranch,\n auth: {\n username: 'notempty',\n password: integrationConfig.config.token,\n },\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport {\n BitbucketIntegrationConfig,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport fetch from 'cross-fetch';\nimport { initRepoAndPush } from '../helpers';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { Config } from '@backstage/config';\n\nconst createBitbucketCloudRepository = async (opts: {\n workspace: string;\n project: string;\n repo: string;\n description: string;\n repoVisibility: 'private' | 'public';\n authorization: string;\n}) => {\n const {\n workspace,\n project,\n repo,\n description,\n repoVisibility,\n authorization,\n } = opts;\n\n const options: RequestInit = {\n method: 'POST',\n body: JSON.stringify({\n scm: 'git',\n description: description,\n is_private: repoVisibility === 'private',\n project: { key: project },\n }),\n headers: {\n Authorization: authorization,\n 'Content-Type': 'application/json',\n },\n };\n\n let response: Response;\n try {\n response = await fetch(\n `https://api.bitbucket.org/2.0/repositories/${workspace}/${repo}`,\n options,\n );\n } catch (e) {\n throw new Error(`Unable to create repository, ${e}`);\n }\n\n if (response.status !== 200) {\n throw new Error(\n `Unable to create repository, ${response.status} ${\n response.statusText\n }, ${await response.text()}`,\n );\n }\n\n const r = await response.json();\n let remoteUrl = '';\n for (const link of r.links.clone) {\n if (link.name === 'https') {\n remoteUrl = link.href;\n }\n }\n\n // TODO use the urlReader to get the default branch\n const repoContentsUrl = `${r.links.html.href}/src/master`;\n return { remoteUrl, repoContentsUrl };\n};\n\nconst createBitbucketServerRepository = async (opts: {\n host: string;\n project: string;\n repo: string;\n description: string;\n repoVisibility: 'private' | 'public';\n authorization: string;\n apiBaseUrl?: string;\n}) => {\n const {\n host,\n project,\n repo,\n description,\n authorization,\n repoVisibility,\n apiBaseUrl,\n } = opts;\n\n let response: Response;\n const options: RequestInit = {\n method: 'POST',\n body: JSON.stringify({\n name: repo,\n description: description,\n public: repoVisibility === 'public',\n }),\n headers: {\n Authorization: authorization,\n 'Content-Type': 'application/json',\n },\n };\n\n try {\n const baseUrl = apiBaseUrl ? apiBaseUrl : `https://${host}/rest/api/1.0`;\n response = await fetch(`${baseUrl}/projects/${project}/repos`, options);\n } catch (e) {\n throw new Error(`Unable to create repository, ${e}`);\n }\n\n if (response.status !== 201) {\n throw new Error(\n `Unable to create repository, ${response.status} ${\n response.statusText\n }, ${await response.text()}`,\n );\n }\n\n const r = await response.json();\n let remoteUrl = '';\n for (const link of r.links.clone) {\n if (link.name === 'http') {\n remoteUrl = link.href;\n }\n }\n\n const repoContentsUrl = `${r.links.self[0].href}`;\n return { remoteUrl, repoContentsUrl };\n};\n\nconst getAuthorizationHeader = (config: BitbucketIntegrationConfig) => {\n if (config.username && config.appPassword) {\n const buffer = Buffer.from(\n `${config.username}:${config.appPassword}`,\n 'utf8',\n );\n\n return `Basic ${buffer.toString('base64')}`;\n }\n\n if (config.token) {\n return `Bearer ${config.token}`;\n }\n\n throw new Error(\n `Authorization has not been provided for Bitbucket. Please add either username + appPassword or token to the Integrations config`,\n );\n};\n\nconst performEnableLFS = async (opts: {\n authorization: string;\n host: string;\n project: string;\n repo: string;\n}) => {\n const { authorization, host, project, repo } = opts;\n\n const options: RequestInit = {\n method: 'PUT',\n headers: {\n Authorization: authorization,\n },\n };\n\n const { ok, status, statusText } = await fetch(\n `https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`,\n options,\n );\n\n if (!ok)\n throw new Error(\n `Failed to enable LFS in the repository, ${status}: ${statusText}`,\n );\n};\n\nexport function createPublishBitbucketAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n return createTemplateAction<{\n repoUrl: string;\n description: string;\n defaultBranch?: string;\n repoVisibility: 'private' | 'public';\n sourcePath?: string;\n enableLFS: boolean;\n }>({\n id: 'publish:bitbucket',\n description:\n 'Initializes a git repository of the content in the workspace, and publishes it to Bitbucket.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n type: 'string',\n },\n description: {\n title: 'Repository Description',\n type: 'string',\n },\n repoVisibility: {\n title: 'Repository Visibility',\n type: 'string',\n enum: ['private', 'public'],\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n enableLFS: {\n title:\n 'Enable LFS for the repository. Only available for hosted Bitbucket.',\n type: 'boolean',\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n description,\n defaultBranch = 'master',\n repoVisibility = 'private',\n enableLFS = false,\n } = ctx.input;\n\n const { workspace, project, repo, host } = parseRepoUrl(\n repoUrl,\n integrations,\n );\n\n // Workspace is only required for bitbucket cloud\n if (host === 'bitbucket.org') {\n if (!workspace) {\n throw new InputError(\n `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`,\n );\n }\n }\n\n // Project is required for both bitbucket cloud and bitbucket server\n if (!project) {\n throw new InputError(\n `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`,\n );\n }\n\n const integrationConfig = integrations.bitbucket.byHost(host);\n\n if (!integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n const authorization = getAuthorizationHeader(integrationConfig.config);\n const apiBaseUrl = integrationConfig.config.apiBaseUrl;\n\n const createMethod =\n host === 'bitbucket.org'\n ? createBitbucketCloudRepository\n : createBitbucketServerRepository;\n\n const { remoteUrl, repoContentsUrl } = await createMethod({\n authorization,\n host,\n workspace: workspace || '',\n project,\n repo,\n repoVisibility,\n description,\n apiBaseUrl,\n });\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl,\n auth: {\n username: integrationConfig.config.username\n ? integrationConfig.config.username\n : 'x-token-auth',\n password: integrationConfig.config.appPassword\n ? integrationConfig.config.appPassword\n : integrationConfig.config.token ?? '',\n },\n defaultBranch,\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n if (enableLFS && host !== 'bitbucket.org') {\n await performEnableLFS({ authorization, host, project, repo });\n }\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { dirname } from 'path';\nimport { InputError } from '@backstage/errors';\nimport { createTemplateAction } from '../../createTemplateAction';\n\n/**\n * This task is useful for local development and testing of both the scaffolder\n * and scaffolder templates.\n *\n * This action is not installed by default and should not be installed in\n * production, as it writes the files to the local filesystem of the scaffolder.\n */\nexport function createPublishFileAction() {\n return createTemplateAction<{ path: string }>({\n id: 'publish:file',\n description: 'Writes contents of the workspace to a local directory',\n schema: {\n input: {\n type: 'object',\n required: ['path'],\n properties: {\n path: {\n title: 'Path to a directory where the output will be written',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const { path } = ctx.input;\n\n const exists = await fs.pathExists(path);\n if (exists) {\n throw new InputError('Output path already exists');\n }\n await fs.ensureDir(dirname(path));\n await fs.copy(ctx.workspacePath, path);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\nimport {\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { Octokit } from '@octokit/rest';\nimport {\n enableBranchProtectionOnDefaultRepoBranch,\n initRepoAndPush,\n} from '../helpers';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Config } from '@backstage/config';\n\ntype Permission = 'pull' | 'push' | 'admin' | 'maintain' | 'triage';\ntype Collaborator = { access: Permission; username: string };\n\nexport function createPublishGithubAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n const credentialsProviders = new Map(\n integrations.github.list().map(integration => {\n const provider = GithubCredentialsProvider.create(integration.config);\n return [integration.config.host, provider];\n }),\n );\n\n return createTemplateAction<{\n repoUrl: string;\n description?: string;\n access?: string;\n defaultBranch?: string;\n sourcePath?: string;\n requireCodeOwnerReviews?: boolean;\n repoVisibility: 'private' | 'internal' | 'public';\n collaborators: Collaborator[];\n topics?: string[];\n }>({\n id: 'publish:github',\n description:\n 'Initializes a git repository of contents in workspace and publishes it to GitHub.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n description: {\n title: 'Repository Description',\n type: 'string',\n },\n access: {\n title: 'Repository Access',\n description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`,\n type: 'string',\n },\n requireCodeOwnerReviews: {\n title:\n 'Require an approved review in PR including files with a designated Code Owner',\n type: 'boolean',\n },\n repoVisibility: {\n title: 'Repository Visibility',\n type: 'string',\n enum: ['private', 'public', 'internal'],\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n collaborators: {\n title: 'Collaborators',\n description: 'Provide additional users with permissions',\n type: 'array',\n items: {\n type: 'object',\n required: ['username', 'access'],\n properties: {\n access: {\n type: 'string',\n description: 'The type of access for the user',\n enum: ['push', 'pull', 'admin', 'maintain', 'triage'],\n },\n username: {\n type: 'string',\n description: 'The username or group',\n },\n },\n },\n },\n topics: {\n title: 'Topics',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n description,\n access,\n requireCodeOwnerReviews = false,\n repoVisibility = 'private',\n defaultBranch = 'master',\n collaborators,\n topics,\n } = ctx.input;\n\n const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);\n\n if (!owner) {\n throw new InputError(\n `No owner provided for host: ${host}, and repo ${repo}`,\n );\n }\n\n const credentialsProvider = credentialsProviders.get(host);\n const integrationConfig = integrations.github.byHost(host);\n\n if (!credentialsProvider || !integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n // TODO(blam): Consider changing this API to have owner, repo interface instead of URL as the it's\n // needless to create URL and then parse again the other side.\n const { token } = await credentialsProvider.getCredentials({\n url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(\n repo,\n )}`,\n });\n\n if (!token) {\n throw new InputError(\n `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,\n );\n }\n\n const client = new Octokit({\n auth: token,\n baseUrl: integrationConfig.config.apiBaseUrl,\n previews: ['nebula-preview'],\n });\n\n const user = await client.users.getByUsername({\n username: owner,\n });\n\n const repoCreationPromise =\n user.data.type === 'Organization'\n ? client.repos.createInOrg({\n name: repo,\n org: owner,\n private: repoVisibility === 'private',\n visibility: repoVisibility,\n description: description,\n })\n : client.repos.createForAuthenticatedUser({\n name: repo,\n private: repoVisibility === 'private',\n description: description,\n });\n\n const { data: newRepo } = await repoCreationPromise;\n if (access?.startsWith(`${owner}/`)) {\n const [, team] = access.split('/');\n await client.teams.addOrUpdateRepoPermissionsInOrg({\n org: owner,\n team_slug: team,\n owner,\n repo,\n permission: 'admin',\n });\n // No need to add access if it's the person who owns the personal account\n } else if (access && access !== owner) {\n await client.repos.addCollaborator({\n owner,\n repo,\n username: access,\n permission: 'admin',\n });\n }\n\n if (collaborators) {\n for (const {\n access: permission,\n username: team_slug,\n } of collaborators) {\n try {\n await client.teams.addOrUpdateRepoPermissionsInOrg({\n org: owner,\n team_slug,\n owner,\n repo,\n permission,\n });\n } catch (e) {\n ctx.logger.warn(\n `Skipping ${permission} access for ${team_slug}, ${e.message}`,\n );\n }\n }\n }\n\n if (topics) {\n try {\n await client.repos.replaceAllTopics({\n owner,\n repo,\n names: topics.map(t => t.toLowerCase()),\n });\n } catch (e) {\n ctx.logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`);\n }\n }\n\n const remoteUrl = newRepo.clone_url;\n const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl,\n defaultBranch,\n auth: {\n username: 'x-access-token',\n password: token,\n },\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n try {\n await enableBranchProtectionOnDefaultRepoBranch({\n owner,\n client,\n repoName: newRepo.name,\n logger: ctx.logger,\n defaultBranch,\n requireCodeOwnerReviews,\n });\n } catch (e) {\n ctx.logger.warn(\n `Skipping: default branch protection on '${newRepo.name}', ${e.message}`,\n );\n }\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { readFile } from 'fs-extra';\nimport path from 'path';\nimport { parseRepoUrl } from './util';\n\nimport {\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { zipObject } from 'lodash';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Octokit } from '@octokit/rest';\nimport { InputError, CustomErrorBase } from '@backstage/errors';\nimport { createPullRequest } from 'octokit-plugin-create-pull-request';\nimport globby from 'globby';\n\nclass GithubResponseError extends CustomErrorBase {}\n\ntype CreatePullRequestResponse = {\n data: { html_url: string };\n};\n\nexport interface PullRequestCreator {\n createPullRequest(\n options: createPullRequest.Options,\n ): Promise<CreatePullRequestResponse | null>;\n}\n\nexport type PullRequestCreatorConstructor = (\n octokit: Octokit,\n) => PullRequestCreator;\n\nexport type GithubPullRequestActionInput = {\n title: string;\n branchName: string;\n description: string;\n repoUrl: string;\n targetPath?: string;\n sourcePath?: string;\n};\n\nexport type ClientFactoryInput = {\n integrations: ScmIntegrationRegistry;\n host: string;\n owner: string;\n repo: string;\n};\n\nexport const defaultClientFactory = async ({\n integrations,\n owner,\n repo,\n host = 'github.com',\n}: ClientFactoryInput): Promise<PullRequestCreator> => {\n const integrationConfig = integrations.github.byHost(host)?.config;\n\n if (!integrationConfig) {\n throw new InputError(`No integration for host ${host}`);\n }\n\n const credentialsProvider =\n GithubCredentialsProvider.create(integrationConfig);\n\n if (!credentialsProvider) {\n throw new InputError(\n `No matching credentials for host ${host}, please check your integrations config`,\n );\n }\n\n const { token } = await credentialsProvider.getCredentials({\n url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(\n repo,\n )}`,\n });\n\n if (!token) {\n throw new InputError(\n `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,\n );\n }\n\n const OctokitPR = Octokit.plugin(createPullRequest);\n\n return new OctokitPR({\n auth: token,\n baseUrl: integrationConfig.apiBaseUrl,\n });\n};\n\ninterface CreateGithubPullRequestActionOptions {\n integrations: ScmIntegrationRegistry;\n clientFactory?: (input: ClientFactoryInput) => Promise<PullRequestCreator>;\n}\n\nexport const createPublishGithubPullRequestAction = ({\n integrations,\n clientFactory = defaultClientFactory,\n}: CreateGithubPullRequestActionOptions) => {\n return createTemplateAction<GithubPullRequestActionInput>({\n id: 'publish:github:pull-request',\n schema: {\n input: {\n required: ['repoUrl', 'title', 'description', 'branchName'],\n type: 'object',\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n branchName: {\n type: 'string',\n title: 'Branch Name',\n description: 'The name for the branch',\n },\n title: {\n type: 'string',\n title: 'Pull Request Name',\n description: 'The name for the pull request',\n },\n description: {\n type: 'string',\n title: 'Pull Request Description',\n description: 'The description of the pull request',\n },\n sourcePath: {\n type: 'string',\n title: 'Working Subdirectory',\n description:\n 'Subdirectory of working directory to copy changes from',\n },\n targetPath: {\n type: 'string',\n title: 'Repository Subdirectory',\n description: 'Subdirectory of repository to apply changes to',\n },\n },\n },\n output: {\n required: ['remoteUrl'],\n type: 'object',\n properties: {\n remoteUrl: {\n type: 'string',\n title: 'Pull Request URL',\n description: 'Link to the pull request in Github',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n branchName,\n title,\n description,\n targetPath,\n sourcePath,\n } = ctx.input;\n\n const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);\n\n if (!owner) {\n throw new InputError(\n `No owner provided for host: ${host}, and repo ${repo}`,\n );\n }\n\n const client = await clientFactory({ integrations, host, owner, repo });\n const fileRoot = sourcePath\n ? path.resolve(ctx.workspacePath, sourcePath)\n : ctx.workspacePath;\n\n const localFilePaths = await globby(['./**', './**/.*', '!.git'], {\n cwd: fileRoot,\n gitignore: true,\n dot: true,\n });\n\n const fileContents = await Promise.all(\n localFilePaths.map(p => readFile(path.resolve(fileRoot, p))),\n );\n\n const repoFilePaths = localFilePaths.map(repoFilePath => {\n return targetPath ? `${targetPath}/${repoFilePath}` : repoFilePath;\n });\n\n const changes = [\n {\n files: zipObject(\n repoFilePaths,\n fileContents.map(buf => buf.toString()),\n ),\n commit: title,\n },\n ];\n\n try {\n const response = await client.createPullRequest({\n owner,\n repo,\n title,\n changes,\n body: description,\n head: branchName,\n });\n\n if (!response) {\n throw new GithubResponseError('null response from Github');\n }\n\n ctx.output('remoteUrl', response.data.html_url);\n } catch (e) {\n throw new GithubResponseError('Pull request creation failed', e);\n }\n },\n });\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { Gitlab } from '@gitbeaker/node';\nimport { initRepoAndPush } from '../helpers';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Config } from '@backstage/config';\n\nexport function createPublishGitlabAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n return createTemplateAction<{\n repoUrl: string;\n defaultBranch?: string;\n repoVisibility: 'private' | 'internal' | 'public';\n sourcePath?: string;\n }>({\n id: 'publish:gitlab',\n description:\n 'Initializes a git repository of the content in the workspace, and publishes it to GitLab.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n type: 'string',\n },\n repoVisibility: {\n title: 'Repository Visibility',\n type: 'string',\n enum: ['private', 'public', 'internal'],\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n repoVisibility = 'private',\n defaultBranch = 'master',\n } = ctx.input;\n\n const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);\n\n if (!owner) {\n throw new InputError(\n `No owner provided for host: ${host}, and repo ${repo}`,\n );\n }\n\n const integrationConfig = integrations.gitlab.byHost(host);\n\n if (!integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n if (!integrationConfig.config.token) {\n throw new InputError(`No token available for host ${host}`);\n }\n\n const client = new Gitlab({\n host: integrationConfig.config.baseUrl,\n token: integrationConfig.config.token,\n });\n\n let { id: targetNamespace } = (await client.Namespaces.show(owner)) as {\n id: number;\n };\n\n if (!targetNamespace) {\n const { id } = (await client.Users.current()) as {\n id: number;\n };\n targetNamespace = id;\n }\n\n const { http_url_to_repo } = await client.Projects.create({\n namespace_id: targetNamespace,\n name: repo,\n visibility: repoVisibility,\n });\n\n const remoteUrl = (http_url_to_repo as string).replace(/\\.git$/, '');\n const repoContentsUrl = `${remoteUrl}/-/blob/master`;\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl: http_url_to_repo as string,\n defaultBranch,\n auth: {\n username: 'oauth2',\n password: integrationConfig.config.token,\n },\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { InputError } from '@backstage/errors';\nimport {\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { Octokit } from '@octokit/rest';\nimport { parseRepoUrl } from '../publish/util';\nimport { createTemplateAction } from '../../createTemplateAction';\n\nexport function createGithubActionsDispatchAction(options: {\n integrations: ScmIntegrationRegistry;\n}) {\n const { integrations } = options;\n\n const credentialsProviders = new Map(\n integrations.github.list().map(integration => {\n const provider = GithubCredentialsProvider.create(integration.config);\n return [integration.config.host, provider];\n }),\n );\n\n return createTemplateAction<{\n repoUrl: string;\n workflowId: string;\n branchOrTagName: string;\n }>({\n id: 'github:actions:dispatch',\n description:\n 'Dispatches a GitHub Action workflow for a given branch or tag',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl', 'workflowId', 'branchOrTagName'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n workflowId: {\n title: 'Workflow ID',\n description: 'The GitHub Action Workflow filename',\n type: 'string',\n },\n branchOrTagName: {\n title: 'Branch or Tag name',\n description:\n 'The git branch or tag name used to dispatch the workflow',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const { repoUrl, workflowId, branchOrTagName } = ctx.input;\n\n const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);\n\n if (!owner) {\n throw new InputError(\n `No owner provided for host: ${host}, and repo ${repo}`,\n );\n }\n\n ctx.logger.info(\n `Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`,\n );\n\n const credentialsProvider = credentialsProviders.get(host);\n const integrationConfig = integrations.github.byHost(host);\n\n if (!credentialsProvider || !integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n const { token } = await credentialsProvider.getCredentials({\n url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(\n repo,\n )}`,\n });\n\n if (!token) {\n throw new InputError(\n `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,\n );\n }\n\n const client = new Octokit({\n auth: token,\n baseUrl: integrationConfig.config.apiBaseUrl,\n previews: ['nebula-preview'],\n });\n\n await client.rest.actions.createWorkflowDispatch({\n owner,\n repo,\n workflow_id: workflowId,\n ref: branchOrTagName,\n });\n\n ctx.logger.info(`Workflow ${workflowId} dispatched successfully`);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ContainerRunner, UrlReader } from '@backstage/backend-common';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { Config } from '@backstage/config';\nimport {\n createCatalogWriteAction,\n createCatalogRegisterAction,\n} from './catalog';\n\nimport { createDebugLogAction } from './debug';\nimport { createFetchPlainAction, createFetchTemplateAction } from './fetch';\nimport { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter';\nimport {\n createFilesystemDeleteAction,\n createFilesystemRenameAction,\n} from './filesystem';\nimport {\n createPublishAzureAction,\n createPublishBitbucketAction,\n createPublishGithubAction,\n createPublishGithubPullRequestAction,\n createPublishGitlabAction,\n} from './publish';\nimport { createGithubActionsDispatchAction } from './github';\n\nexport const createBuiltinActions = (options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n catalogClient: CatalogApi;\n containerRunner: ContainerRunner;\n config: Config;\n}) => {\n const { reader, integrations, containerRunner, catalogClient, config } =\n options;\n\n return [\n createFetchPlainAction({\n reader,\n integrations,\n }),\n createFetchCookiecutterAction({\n reader,\n integrations,\n containerRunner,\n }),\n createFetchTemplateAction({\n integrations,\n reader,\n }),\n createPublishGithubAction({\n integrations,\n config,\n }),\n createPublishGithubPullRequestAction({\n integrations,\n }),\n createPublishGitlabAction({\n integrations,\n config,\n }),\n createPublishBitbucketAction({\n integrations,\n config,\n }),\n createPublishAzureAction({\n integrations,\n config,\n }),\n createDebugLogAction(),\n createCatalogRegisterAction({ catalogClient, integrations }),\n createCatalogWriteAction(),\n createFilesystemDeleteAction(),\n createFilesystemRenameAction(),\n createGithubActionsDispatchAction({\n integrations,\n }),\n ];\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputBase, TemplateAction } from './types';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\n\nexport class TemplateActionRegistry {\n private readonly actions = new Map<string, TemplateAction<any>>();\n\n register<Parameters extends InputBase>(action: TemplateAction<Parameters>) {\n if (this.actions.has(action.id)) {\n throw new ConflictError(\n `Template action with ID '${action.id}' has already been registered`,\n );\n }\n this.actions.set(action.id, action);\n }\n\n get(actionId: string): TemplateAction<any> {\n const action = this.actions.get(actionId);\n if (!action) {\n throw new NotFoundError(\n `Template action with ID '${actionId}' is not registered.`,\n );\n }\n return action;\n }\n\n list(): TemplateAction<any>[] {\n return [...this.actions.values()];\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TemplateEntityV1beta2 } from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\n\n/**\n * A catalog client tailored for reading out entity data from the catalog.\n */\nexport class CatalogEntityClient {\n constructor(private readonly catalogClient: CatalogApi) {}\n\n /**\n * Looks up a single template using a template name.\n *\n * Throws a NotFoundError or ConflictError if 0 or multiple templates are found.\n */\n async findTemplate(\n templateName: string,\n options?: { token?: string },\n ): Promise<TemplateEntityV1beta2> {\n const { items: templates } = (await this.catalogClient.getEntities(\n {\n filter: {\n kind: 'template',\n 'metadata.name': templateName,\n },\n },\n options,\n )) as { items: TemplateEntityV1beta2[] };\n\n if (templates.length !== 1) {\n if (templates.length > 1) {\n throw new ConflictError(\n 'Templates lookup resulted in multiple matches',\n );\n } else {\n throw new NotFoundError('Template not found');\n }\n }\n\n return templates[0];\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/config';\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport {\n DbTaskEventRow,\n DbTaskRow,\n Status,\n TaskEventType,\n TaskSecrets,\n TaskSpec,\n TaskStore,\n TaskStoreEmitOptions,\n TaskStoreGetEventsOptions,\n} from './types';\nimport { DateTime } from 'luxon';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-scaffolder-backend',\n 'migrations',\n);\n\nexport type RawDbTaskRow = {\n id: string;\n spec: string;\n status: Status;\n last_heartbeat_at?: string;\n created_at: string;\n secrets?: string;\n};\n\nexport type RawDbTaskEventRow = {\n id: number;\n task_id: string;\n body: string;\n event_type: TaskEventType;\n created_at: string;\n};\n\nexport class DatabaseTaskStore implements TaskStore {\n static async create(knex: Knex): Promise<DatabaseTaskStore> {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n return new DatabaseTaskStore(knex);\n }\n\n constructor(private readonly db: Knex) {}\n\n async getTask(taskId: string): Promise<DbTaskRow> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select();\n if (!result) {\n throw new NotFoundError(`No task with id '${taskId}' found`);\n }\n try {\n const spec = JSON.parse(result.spec);\n const secrets = result.secrets ? JSON.parse(result.secrets) : undefined;\n return {\n id: result.id,\n spec,\n status: result.status,\n lastHeartbeatAt: result.last_heartbeat_at,\n createdAt: result.created_at,\n secrets,\n };\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);\n }\n }\n\n async createTask(\n spec: TaskSpec,\n secrets?: TaskSecrets,\n ): Promise<{ taskId: string }> {\n const taskId = uuid();\n await this.db<RawDbTaskRow>('tasks').insert({\n id: taskId,\n spec: JSON.stringify(spec),\n secrets: secrets ? JSON.stringify(secrets) : undefined,\n status: 'open',\n });\n return { taskId };\n }\n\n async claimTask(): Promise<DbTaskRow | undefined> {\n return this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n status: 'open',\n })\n .limit(1)\n .select();\n\n if (!task) {\n return undefined;\n }\n\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({ id: task.id, status: 'open' })\n .update({\n status: 'processing',\n last_heartbeat_at: this.db.fn.now(),\n });\n\n if (updateCount < 1) {\n return undefined;\n }\n\n try {\n const spec = JSON.parse(task.spec);\n const secrets = task.secrets ? JSON.parse(task.secrets) : undefined;\n return {\n id: task.id,\n spec,\n status: 'processing',\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n secrets,\n };\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${task.id}', ${error}`);\n }\n });\n }\n\n async heartbeatTask(taskId: string): Promise<void> {\n const updateCount = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId, status: 'processing' })\n .update({\n last_heartbeat_at: this.db.fn.now(),\n });\n if (updateCount === 0) {\n throw new ConflictError(`No running task with taskId ${taskId} found`);\n }\n }\n\n async listStaleTasks({ timeoutS }: { timeoutS: number }): Promise<{\n tasks: { taskId: string }[];\n }> {\n const rawRows = await this.db<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere(\n 'last_heartbeat_at',\n '<=',\n this.db.client.config.client === 'sqlite3'\n ? this.db.raw(`datetime('now', ?)`, [`-${timeoutS} seconds`])\n : this.db.raw(`dateadd('second', ?, ?)`, [\n `-${timeoutS}`,\n this.db.fn.now(),\n ]),\n );\n const tasks = rawRows.map(row => ({\n taskId: row.id,\n }));\n return { tasks };\n }\n\n async completeTask({\n taskId,\n status,\n eventBody,\n }: {\n taskId: string;\n status: Status;\n eventBody: JsonObject;\n }): Promise<void> {\n let oldStatus: string;\n if (status === 'failed' || status === 'completed') {\n oldStatus = 'processing';\n } else {\n throw new Error(\n `Invalid status update of run '${taskId}' to status '${status}'`,\n );\n }\n await this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n })\n .limit(1)\n .select();\n\n if (!task) {\n throw new Error(`No task with taskId ${taskId} found`);\n }\n if (task.status !== oldStatus) {\n throw new ConflictError(\n `Refusing to update status of run '${taskId}' to status '${status}' ` +\n `as it is currently '${task.status}', expected '${oldStatus}'`,\n );\n }\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n status: oldStatus,\n })\n .update({\n status,\n secrets: null as any,\n });\n if (updateCount !== 1) {\n throw new ConflictError(\n `Failed to update status to '${status}' for taskId ${taskId}`,\n );\n }\n\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'completion',\n body: JSON.stringify(eventBody),\n });\n });\n }\n\n async emitLogEvent({ taskId, body }: TaskStoreEmitOptions): Promise<void> {\n const serliazedBody = JSON.stringify(body);\n await this.db<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'log',\n body: serliazedBody,\n });\n }\n\n async listEvents({\n taskId,\n after,\n }: TaskStoreGetEventsOptions): Promise<{ events: DbTaskEventRow[] }> {\n const rawEvents = await this.db<RawDbTaskEventRow>('task_events')\n .where({\n task_id: taskId,\n })\n .andWhere(builder => {\n if (typeof after === 'number') {\n builder.where('id', '>', after).orWhere('event_type', 'completion');\n }\n })\n .orderBy('id')\n .select();\n\n const events = rawEvents.map(event => {\n try {\n const body = JSON.parse(event.body) as JsonObject;\n return {\n id: Number(event.id),\n taskId,\n body,\n type: event.event_type,\n createdAt:\n typeof event.created_at === 'string'\n ? DateTime.fromSQL(event.created_at, { zone: 'UTC' }).toISO()\n : event.created_at,\n };\n } catch (error) {\n throw new Error(\n `Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`,\n );\n }\n });\n return { events };\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject } from '@backstage/config';\nimport { Logger } from 'winston';\nimport {\n CompletedTaskState,\n Task,\n TaskSecrets,\n TaskSpec,\n TaskStore,\n TaskBroker,\n DispatchResult,\n DbTaskEventRow,\n DbTaskRow,\n} from './types';\n\nexport class TaskAgent implements Task {\n private isDone = false;\n\n private heartbeatTimeoutId?: ReturnType<typeof setInterval>;\n\n static create(state: TaskState, storage: TaskStore, logger: Logger) {\n const agent = new TaskAgent(state, storage, logger);\n agent.startTimeout();\n return agent;\n }\n\n // Runs heartbeat internally\n private constructor(\n private readonly state: TaskState,\n private readonly storage: TaskStore,\n private readonly logger: Logger,\n ) {}\n\n get spec() {\n return this.state.spec;\n }\n\n get secrets() {\n return this.state.secrets;\n }\n\n async getWorkspaceName() {\n return this.state.taskId;\n }\n\n get done() {\n return this.isDone;\n }\n\n async emitLog(message: string, metadata?: JsonObject): Promise<void> {\n await this.storage.emitLogEvent({\n taskId: this.state.taskId,\n body: { message, ...metadata },\n });\n }\n\n async complete(\n result: CompletedTaskState,\n metadata?: JsonObject,\n ): Promise<void> {\n await this.storage.completeTask({\n taskId: this.state.taskId,\n status: result === 'failed' ? 'failed' : 'completed',\n eventBody: {\n message: `Run completed with status: ${result}`,\n ...metadata,\n },\n });\n this.isDone = true;\n if (this.heartbeatTimeoutId) {\n clearTimeout(this.heartbeatTimeoutId);\n }\n }\n\n private startTimeout() {\n this.heartbeatTimeoutId = setTimeout(async () => {\n try {\n await this.storage.heartbeatTask(this.state.taskId);\n this.startTimeout();\n } catch (error) {\n this.isDone = true;\n\n this.logger.error(\n `Heartbeat for task ${this.state.taskId} failed`,\n error,\n );\n }\n }, 1000);\n }\n}\n\ninterface TaskState {\n spec: TaskSpec;\n taskId: string;\n secrets?: TaskSecrets;\n}\n\nfunction defer() {\n let resolve = () => {};\n const promise = new Promise<void>(_resolve => {\n resolve = _resolve;\n });\n return { promise, resolve };\n}\n\nexport class StorageTaskBroker implements TaskBroker {\n constructor(\n private readonly storage: TaskStore,\n private readonly logger: Logger,\n ) {}\n private deferredDispatch = defer();\n\n async claim(): Promise<Task> {\n for (;;) {\n const pendingTask = await this.storage.claimTask();\n if (pendingTask) {\n return TaskAgent.create(\n {\n taskId: pendingTask.id,\n spec: pendingTask.spec,\n secrets: pendingTask.secrets,\n },\n this.storage,\n this.logger,\n );\n }\n\n await this.waitForDispatch();\n }\n }\n\n async dispatch(\n spec: TaskSpec,\n secrets?: TaskSecrets,\n ): Promise<DispatchResult> {\n const taskRow = await this.storage.createTask(spec, secrets);\n this.signalDispatch();\n return {\n taskId: taskRow.taskId,\n };\n }\n\n async get(taskId: string): Promise<DbTaskRow> {\n return this.storage.getTask(taskId);\n }\n\n observe(\n options: {\n taskId: string;\n after: number | undefined;\n },\n callback: (\n error: Error | undefined,\n result: { events: DbTaskEventRow[] },\n ) => void,\n ): () => void {\n const { taskId } = options;\n\n let cancelled = false;\n const unsubscribe = () => {\n cancelled = true;\n };\n\n (async () => {\n let after = options.after;\n while (!cancelled) {\n const result = await this.storage.listEvents({ taskId, after: after });\n const { events } = result;\n if (events.length) {\n after = events[events.length - 1].id;\n try {\n callback(undefined, result);\n } catch (error) {\n callback(error, { events: [] });\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n })();\n\n return unsubscribe;\n }\n\n async vacuumTasks(timeoutS: { timeoutS: number }): Promise<void> {\n const { tasks } = await this.storage.listStaleTasks(timeoutS);\n await Promise.all(\n tasks.map(async task => {\n try {\n await this.storage.completeTask({\n taskId: task.taskId,\n status: 'failed',\n eventBody: {\n message:\n 'The task was cancelled because the task worker lost connection to the task broker',\n },\n });\n } catch (error) {\n this.logger.warn(`Failed to cancel task '${task.taskId}', ${error}`);\n }\n }),\n );\n }\n\n private waitForDispatch() {\n return this.deferredDispatch.promise;\n }\n\n private signalDispatch() {\n this.deferredDispatch.resolve();\n this.deferredDispatch = defer();\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isArray } from 'lodash';\n\n/**\n * Returns true if the input is not `false`, `undefined`, `null`, `\"\"`, `0`, or\n * `[]`. This behavior is based on the behavior of handlebars, see\n * https://handlebarsjs.com/guide/builtin-helpers.html#if\n */\nexport function isTruthy(value: any): boolean {\n return isArray(value) ? value.length > 0 : !!value;\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject, JsonValue } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport fs from 'fs-extra';\nimport * as Handlebars from 'handlebars';\nimport { validate as validateJsonSchema } from 'jsonschema';\nimport path from 'path';\nimport { PassThrough } from 'stream';\nimport * as winston from 'winston';\nimport { Logger } from 'winston';\nimport { parseRepoUrl } from '../actions/builtin/publish/util';\nimport { TemplateActionRegistry } from '../actions/TemplateActionRegistry';\nimport { isTruthy } from './helper';\nimport { Task, TaskBroker } from './types';\nimport { ScmIntegrations } from '@backstage/integration';\n\ntype Options = {\n logger: Logger;\n taskBroker: TaskBroker;\n workingDirectory: string;\n actionRegistry: TemplateActionRegistry;\n integrations: ScmIntegrations;\n};\n\nexport class TaskWorker {\n private readonly handlebars: typeof Handlebars;\n\n constructor(private readonly options: Options) {\n this.handlebars = Handlebars.create();\n\n // TODO(blam): this should be a public facing API but it's a little\n // scary right now, so we're going to lock it off like the component API is\n // in the frontend until we can work out a nice way to do it.\n this.handlebars.registerHelper('parseRepoUrl', repoUrl => {\n return JSON.stringify(parseRepoUrl(repoUrl, options.integrations));\n });\n\n this.handlebars.registerHelper('projectSlug', repoUrl => {\n const { owner, repo } = parseRepoUrl(repoUrl, options.integrations);\n return `${owner}/${repo}`;\n });\n\n this.handlebars.registerHelper('json', obj => JSON.stringify(obj));\n\n this.handlebars.registerHelper('not', value => !isTruthy(value));\n\n this.handlebars.registerHelper('eq', (a, b) => a === b);\n }\n\n start() {\n (async () => {\n for (;;) {\n const task = await this.options.taskBroker.claim();\n await this.runOneTask(task);\n }\n })();\n }\n\n async runOneTask(task: Task) {\n let workspacePath: string | undefined = undefined;\n try {\n const { actionRegistry } = this.options;\n\n workspacePath = path.join(\n this.options.workingDirectory,\n await task.getWorkspaceName(),\n );\n await fs.ensureDir(workspacePath);\n await task.emitLog(\n `Starting up task with ${task.spec.steps.length} steps`,\n );\n\n const templateCtx: {\n parameters: JsonObject;\n steps: {\n [stepName: string]: { output: { [outputName: string]: JsonValue } };\n };\n } = { parameters: task.spec.values, steps: {} };\n\n for (const step of task.spec.steps) {\n const metadata = { stepId: step.id };\n try {\n const taskLogger = winston.createLogger({\n level: process.env.LOG_LEVEL || 'info',\n format: winston.format.combine(\n winston.format.colorize(),\n winston.format.timestamp(),\n winston.format.simple(),\n ),\n defaultMeta: {},\n });\n\n const stream = new PassThrough();\n stream.on('data', async data => {\n const message = data.toString().trim();\n if (message?.length > 1) {\n await task.emitLog(message, metadata);\n }\n });\n\n taskLogger.add(new winston.transports.Stream({ stream }));\n\n if (step.if !== undefined) {\n // Support passing values like false to disable steps\n let skip = !step.if;\n\n // Evaluate strings as handlebar templates\n if (typeof step.if === 'string') {\n const condition = JSON.parse(\n JSON.stringify(step.if),\n (_key, value) => {\n if (typeof value === 'string') {\n const templated = this.handlebars.compile(value, {\n noEscape: true,\n data: false,\n preventIndent: true,\n })(templateCtx);\n\n // If it's just an empty string, treat it as undefined\n if (templated === '') {\n return undefined;\n }\n\n try {\n return JSON.parse(templated);\n } catch {\n return templated;\n }\n }\n\n return value;\n },\n );\n\n skip = !isTruthy(condition);\n }\n\n if (skip) {\n await task.emitLog(`Skipped step ${step.name}`, {\n ...metadata,\n status: 'skipped',\n });\n continue;\n }\n }\n\n await task.emitLog(`Beginning step ${step.name}`, {\n ...metadata,\n status: 'processing',\n });\n\n const action = actionRegistry.get(step.action);\n if (!action) {\n throw new Error(`Action '${step.action}' does not exist`);\n }\n\n const input =\n step.input &&\n JSON.parse(JSON.stringify(step.input), (_key, value) => {\n if (typeof value === 'string') {\n const templated = this.handlebars.compile(value, {\n noEscape: true,\n data: false,\n preventIndent: true,\n })(templateCtx);\n\n // If it smells like a JSON object then give it a parse as an object and if it fails return the string\n if (\n (templated.startsWith('\"') && templated.endsWith('\"')) ||\n (templated.startsWith('{') && templated.endsWith('}')) ||\n (templated.startsWith('[') && templated.endsWith(']'))\n ) {\n try {\n // Don't recursively JSON parse the values of this string.\n // Shouldn't need to, don't want to encourage the use of returning handlebars from somewhere else\n return JSON.parse(templated);\n } catch {\n return templated;\n }\n }\n return templated;\n }\n\n return value;\n });\n\n if (action.schema?.input) {\n const validateResult = validateJsonSchema(\n input,\n action.schema.input,\n );\n if (!validateResult.valid) {\n const errors = validateResult.errors.join(', ');\n throw new InputError(\n `Invalid input passed to action ${action.id}, ${errors}`,\n );\n }\n }\n\n const stepOutputs: { [name: string]: JsonValue } = {};\n\n // Keep track of all tmp dirs that are created by the action so we can remove them after\n const tmpDirs = new Array<string>();\n\n this.options.logger.debug(`Running ${action.id} with input`, {\n input: JSON.stringify(input, null, 2),\n });\n\n await action.handler({\n baseUrl: task.spec.baseUrl,\n logger: taskLogger,\n logStream: stream,\n input,\n token: task.secrets?.token,\n workspacePath,\n async createTemporaryDirectory() {\n const tmpDir = await fs.mkdtemp(\n `${workspacePath}_step-${step.id}-`,\n );\n tmpDirs.push(tmpDir);\n return tmpDir;\n },\n output(name: string, value: JsonValue) {\n stepOutputs[name] = value;\n },\n });\n\n // Remove all temporary directories that were created when executing the action\n for (const tmpDir of tmpDirs) {\n await fs.remove(tmpDir);\n }\n\n templateCtx.steps[step.id] = { output: stepOutputs };\n\n await task.emitLog(`Finished step ${step.name}`, {\n ...metadata,\n status: 'completed',\n });\n } catch (error) {\n await task.emitLog(String(error.stack), {\n ...metadata,\n status: 'failed',\n });\n throw error;\n }\n }\n\n const output = JSON.parse(\n JSON.stringify(task.spec.output),\n (_key, value) => {\n if (typeof value === 'string') {\n const templated = this.handlebars.compile(value, {\n noEscape: true,\n data: false,\n preventIndent: true,\n })(templateCtx);\n\n // If it's just an empty string, treat it as undefined\n if (templated === '') {\n return undefined;\n }\n\n // If it smells like a JSON object then give it a parse as an object and if it fails return the string\n if (\n (templated.startsWith('\"') && templated.endsWith('\"')) ||\n (templated.startsWith('{') && templated.endsWith('}')) ||\n (templated.startsWith('[') && templated.endsWith(']'))\n ) {\n try {\n // Don't recursively JSON parse the values of this string.\n // Shouldn't need to, don't want to encourage the use of returning handlebars from somewhere else\n return JSON.parse(templated);\n } catch {\n return templated;\n }\n }\n return templated;\n }\n return value;\n },\n );\n\n await task.complete('completed', { output });\n } catch (error) {\n await task.complete('failed', {\n error: { name: error.name, message: error.message },\n });\n } finally {\n if (workspacePath) {\n await fs.remove(workspacePath);\n }\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n LOCATION_ANNOTATION,\n parseLocationReference,\n SOURCE_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport { Logger } from 'winston';\n\nexport async function getWorkingDirectory(\n config: Config,\n logger: Logger,\n): Promise<string> {\n if (!config.has('backend.workingDirectory')) {\n return os.tmpdir();\n }\n\n const workingDirectory = config.getString('backend.workingDirectory');\n try {\n // Check if working directory exists and is writable\n await fs.access(workingDirectory, fs.constants.F_OK | fs.constants.W_OK);\n logger.info(`using working directory: ${workingDirectory}`);\n } catch (err) {\n logger.error(\n `working directory ${workingDirectory} ${\n err.code === 'ENOENT' ? 'does not exist' : 'is not writable'\n }`,\n );\n throw err;\n }\n return workingDirectory;\n}\n\n/**\n * Gets the base URL of the entity location that points to the source location\n * of the entity description within a repo. If there is not source location\n * or if it has an invalid type, undefined will be returned instead.\n *\n * For file locations this will return a `file://` URL.\n */\nexport function getEntityBaseUrl(entity: Entity): string | undefined {\n let location = entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION];\n if (!location) {\n location = entity.metadata.annotations?.[LOCATION_ANNOTATION];\n }\n if (!location) {\n return undefined;\n }\n\n const { type, target } = parseLocationReference(location);\n if (type === 'url') {\n return target;\n } else if (type === 'file') {\n return `file://${target}`;\n }\n\n // Only url and file location are handled, as we otherwise don't know if\n // what the url is pointing to makes sense to use as a baseUrl\n return undefined;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { CatalogEntityClient } from '../lib/catalog';\nimport { validate } from 'jsonschema';\nimport {\n DatabaseTaskStore,\n StorageTaskBroker,\n TaskWorker,\n} from '../scaffolder/tasks';\nimport { TemplateActionRegistry } from '../scaffolder/actions/TemplateActionRegistry';\nimport { getEntityBaseUrl, getWorkingDirectory } from './helpers';\nimport {\n ContainerRunner,\n PluginDatabaseManager,\n UrlReader,\n} from '@backstage/backend-common';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { TemplateAction } from '../scaffolder/actions';\nimport { createBuiltinActions } from '../scaffolder/actions/builtin/createBuiltinActions';\n\nexport interface RouterOptions {\n logger: Logger;\n config: Config;\n reader: UrlReader;\n database: PluginDatabaseManager;\n catalogClient: CatalogApi;\n actions?: TemplateAction<any>[];\n taskWorkers?: number;\n containerRunner: ContainerRunner;\n}\n\nfunction isBeta2Template(\n entity: TemplateEntityV1beta2,\n): entity is TemplateEntityV1beta2 {\n return entity.apiVersion === 'backstage.io/v1beta2';\n}\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n router.use(express.json());\n\n const {\n logger: parentLogger,\n config,\n reader,\n database,\n catalogClient,\n actions,\n containerRunner,\n taskWorkers,\n } = options;\n\n const logger = parentLogger.child({ plugin: 'scaffolder' });\n const workingDirectory = await getWorkingDirectory(config, logger);\n const entityClient = new CatalogEntityClient(catalogClient);\n const integrations = ScmIntegrations.fromConfig(config);\n\n const databaseTaskStore = await DatabaseTaskStore.create(\n await database.getClient(),\n );\n const taskBroker = new StorageTaskBroker(databaseTaskStore, logger);\n const actionRegistry = new TemplateActionRegistry();\n const workers = [];\n for (let i = 0; i < (taskWorkers || 1); i++) {\n const worker = new TaskWorker({\n logger,\n taskBroker,\n actionRegistry,\n workingDirectory,\n integrations,\n });\n workers.push(worker);\n }\n\n const actionsToRegister = Array.isArray(actions)\n ? actions\n : createBuiltinActions({\n integrations,\n catalogClient,\n containerRunner,\n reader,\n config,\n });\n\n actionsToRegister.forEach(action => actionRegistry.register(action));\n workers.forEach(worker => worker.start());\n\n router\n .get(\n '/v2/templates/:namespace/:kind/:name/parameter-schema',\n async (req, res) => {\n const { namespace, kind, name } = req.params;\n\n if (namespace !== 'default') {\n throw new InputError(\n `Invalid namespace, only 'default' namespace is supported`,\n );\n }\n if (kind.toLowerCase() !== 'template') {\n throw new InputError(\n `Invalid kind, only 'Template' kind is supported`,\n );\n }\n\n const template = await entityClient.findTemplate(name, {\n token: getBearerToken(req.headers.authorization),\n });\n if (isBeta2Template(template)) {\n const parameters = [template.spec.parameters ?? []].flat();\n res.json({\n title: template.metadata.title ?? template.metadata.name,\n steps: parameters.map(schema => ({\n title: schema.title ?? 'Fill in template parameters',\n schema,\n })),\n });\n } else {\n throw new InputError(\n `Unsupported apiVersion field in schema entity, ${\n (template as Entity).apiVersion\n }`,\n );\n }\n },\n )\n .get('/v2/actions', async (_req, res) => {\n const actionsList = actionRegistry.list().map(action => {\n return {\n id: action.id,\n description: action.description,\n schema: action.schema,\n };\n });\n res.json(actionsList);\n })\n .post('/v2/tasks', async (req, res) => {\n const templateName: string = req.body.templateName;\n const values = req.body.values;\n const token = getBearerToken(req.headers.authorization);\n const template = await entityClient.findTemplate(templateName, {\n token,\n });\n\n let taskSpec;\n\n if (isBeta2Template(template)) {\n for (const parameters of [template.spec.parameters ?? []].flat()) {\n const result = validate(values, parameters);\n\n if (!result.valid) {\n res.status(400).json({ errors: result.errors });\n return;\n }\n }\n\n const baseUrl = getEntityBaseUrl(template);\n\n taskSpec = {\n baseUrl,\n values,\n steps: template.spec.steps.map((step, index) => ({\n ...step,\n id: step.id ?? `step-${index + 1}`,\n name: step.name ?? step.action,\n })),\n output: template.spec.output ?? {},\n };\n } else {\n throw new InputError(\n `Unsupported apiVersion field in schema entity, ${\n (template as Entity).apiVersion\n }`,\n );\n }\n\n const result = await taskBroker.dispatch(taskSpec, {\n token: token,\n });\n\n res.status(201).json({ id: result.taskId });\n })\n .get('/v2/tasks/:taskId', async (req, res) => {\n const { taskId } = req.params;\n const task = await taskBroker.get(taskId);\n if (!task) {\n throw new NotFoundError(`Task with id ${taskId} does not exist`);\n }\n // Do not disclose secrets\n delete task.secrets;\n res.status(200).json(task);\n })\n .get('/v2/tasks/:taskId/eventstream', async (req, res) => {\n const { taskId } = req.params;\n const after = Number(req.query.after) || undefined;\n logger.debug(`Event stream observing taskId '${taskId}' opened`);\n\n // Mandatory headers and http status to keep connection open\n res.writeHead(200, {\n Connection: 'keep-alive',\n 'Cache-Control': 'no-cache',\n 'Content-Type': 'text/event-stream',\n });\n\n // After client opens connection send all events as string\n const unsubscribe = taskBroker.observe(\n { taskId, after },\n (error, { events }) => {\n if (error) {\n logger.error(\n `Received error from event stream when observing taskId '${taskId}', ${error}`,\n );\n }\n\n let shouldUnsubscribe = false;\n for (const event of events) {\n res.write(\n `event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`,\n );\n if (event.type === 'completion') {\n shouldUnsubscribe = true;\n // Closing the event stream here would cause the frontend\n // to automatically reconnect because it lost connection.\n }\n }\n res.flush();\n if (shouldUnsubscribe) unsubscribe();\n },\n );\n // When client closes connection we update the clients list\n // avoiding the disconnected one\n req.on('close', () => {\n unsubscribe();\n logger.debug(`Event stream observing taskId '${taskId}' closed`);\n });\n });\n\n const app = express();\n app.set('logger', logger);\n app.use('/', router);\n\n return app;\n}\n\nfunction getBearerToken(header?: string): string | undefined {\n return header?.match(/Bearer\\s+(\\S+)/i)?.[1];\n}\n"],"names":["InputError","getEntityName","fs","resolvePath","yaml","relative","readdir","resolve","stat","isAbsolute","resolveSafeChildPath","nunjucks","globby","extname","isBinaryFile","PassThrough","spawn","Git","normalizePath","joinPath","getPersonalAccessTokenHandler","WebApi","fetch","path","dirname","GithubCredentialsProvider","integration","Octokit","CustomErrorBase","createPullRequest","readFile","zipObject","Gitlab","createFetchCookiecutterAction","ConflictError","NotFoundError","resolvePackagePath","uuid","DateTime","isArray","Handlebars","winston","stream","validateJsonSchema","errors","os","SOURCE_LOCATION_ANNOTATION","LOCATION_ANNOTATION","parseLocationReference","Router","express","ScmIntegrations","validate"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/scaffolder/actions/createTemplateAction.ts","../src/scaffolder/actions/builtin/catalog/register.ts","../src/scaffolder/actions/builtin/catalog/write.ts","../src/scaffolder/actions/builtin/debug/log.ts","../src/scaffolder/actions/builtin/fetch/helpers.ts","../src/scaffolder/actions/builtin/fetch/plain.ts","../src/scaffolder/actions/builtin/fetch/template.ts","../src/scaffolder/actions/builtin/filesystem/delete.ts","../src/scaffolder/actions/builtin/filesystem/rename.ts","../src/scaffolder/actions/builtin/helpers.ts","../src/scaffolder/actions/builtin/publish/util.ts","../src/scaffolder/actions/builtin/publish/azure.ts","../src/scaffolder/actions/builtin/publish/bitbucket.ts","../src/scaffolder/actions/builtin/publish/file.ts","../src/scaffolder/actions/builtin/github/OctokitProvider.ts","../src/scaffolder/actions/builtin/publish/github.ts","../src/scaffolder/actions/builtin/publish/githubPullRequest.ts","../src/scaffolder/actions/builtin/publish/gitlab.ts","../src/scaffolder/actions/builtin/github/githubActionsDispatch.ts","../src/scaffolder/actions/builtin/github/githubWebhook.ts","../src/scaffolder/actions/builtin/createBuiltinActions.ts","../src/scaffolder/actions/TemplateActionRegistry.ts","../src/lib/catalog/CatalogEntityClient.ts","../src/scaffolder/tasks/DatabaseTaskStore.ts","../src/scaffolder/tasks/StorageTaskBroker.ts","../src/scaffolder/tasks/helper.ts","../src/scaffolder/tasks/TaskWorker.ts","../src/service/helpers.ts","../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputBase, TemplateAction } from './types';\n\nexport const createTemplateAction = <Input extends InputBase>(\n templateAction: TemplateAction<Input>,\n): TemplateAction<any> => {\n // TODO(blam): Can add some more validation here to validate the action later on\n return templateAction;\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { getEntityName } from '@backstage/catalog-model';\nimport { createTemplateAction } from '../../createTemplateAction';\n\nexport function createCatalogRegisterAction(options: {\n catalogClient: CatalogApi;\n integrations: ScmIntegrations;\n}) {\n const { catalogClient, integrations } = options;\n\n return createTemplateAction<\n | { catalogInfoUrl: string }\n | { repoContentsUrl: string; catalogInfoPath?: string }\n >({\n id: 'catalog:register',\n description:\n 'Registers entities from a catalog descriptor file in the workspace into the software catalog.',\n schema: {\n input: {\n oneOf: [\n {\n type: 'object',\n required: ['catalogInfoUrl'],\n properties: {\n catalogInfoUrl: {\n title: 'Catalog Info URL',\n description:\n 'An absolute URL pointing to the catalog info file location',\n type: 'string',\n },\n },\n },\n {\n type: 'object',\n required: ['repoContentsUrl'],\n properties: {\n repoContentsUrl: {\n title: 'Repository Contents URL',\n description:\n 'An absolute URL pointing to the root of a repository directory tree',\n type: 'string',\n },\n catalogInfoPath: {\n title: 'Fetch URL',\n description:\n 'A relative path from the repo root pointing to the catalog info file, defaults to /catalog-info.yaml',\n type: 'string',\n },\n },\n },\n ],\n },\n },\n async handler(ctx) {\n const { input } = ctx;\n\n let catalogInfoUrl;\n if ('catalogInfoUrl' in input) {\n catalogInfoUrl = input.catalogInfoUrl;\n } else {\n const { repoContentsUrl, catalogInfoPath = '/catalog-info.yaml' } =\n input;\n const integration = integrations.byUrl(repoContentsUrl);\n if (!integration) {\n throw new InputError(\n `No integration found for host ${repoContentsUrl}`,\n );\n }\n\n catalogInfoUrl = integration.resolveUrl({\n base: repoContentsUrl,\n url: catalogInfoPath,\n });\n }\n\n ctx.logger.info(`Registering ${catalogInfoUrl} in the catalog`);\n\n const result = await catalogClient.addLocation(\n {\n type: 'url',\n target: catalogInfoUrl,\n },\n ctx.token ? { token: ctx.token } : {},\n );\n if (result.entities.length >= 1) {\n const { kind, name, namespace } = getEntityName(result.entities[0]);\n ctx.output('entityRef', `${kind}:${namespace}/${name}`);\n ctx.output('catalogInfoUrl', catalogInfoUrl);\n }\n },\n });\n}\n","/*\n * Copyright 2021 Spotify AB\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { resolve as resolvePath } from 'path';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport * as yaml from 'yaml';\nimport { Entity } from '@backstage/catalog-model';\n\nexport function createCatalogWriteAction() {\n return createTemplateAction<{ name?: string; entity: Entity }>({\n id: 'catalog:write',\n description: 'Writes the catalog-info.yaml for your template',\n schema: {\n input: {\n type: 'object',\n properties: {\n entity: {\n title: 'Entity info to write catalog-info.yaml',\n description:\n 'You can provide the same values used in the Entity schema.',\n type: 'object',\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logStream.write(`Writing catalog-info.yaml`);\n const { entity } = ctx.input;\n\n await fs.writeFile(\n resolvePath(ctx.workspacePath, 'catalog-info.yaml'),\n yaml.stringify(entity),\n );\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { readdir, stat } from 'fs-extra';\nimport { relative, resolve } from 'path';\nimport { createTemplateAction } from '../../createTemplateAction';\n\n/**\n * This task is useful for local development and testing of both the scaffolder\n * and scaffolder templates.\n */\nexport function createDebugLogAction() {\n return createTemplateAction<{ message?: string; listWorkspace?: boolean }>({\n id: 'debug:log',\n description:\n 'Writes a message into the log or lists all files in the workspace.',\n schema: {\n input: {\n type: 'object',\n properties: {\n message: {\n title: 'Message to output.',\n type: 'string',\n },\n listWorkspace: {\n title: 'List all files in the workspace, if true.',\n type: 'boolean',\n },\n },\n },\n },\n async handler(ctx) {\n if (ctx.input?.message) {\n ctx.logStream.write(ctx.input.message);\n }\n\n if (ctx.input?.listWorkspace) {\n const files = await recursiveReadDir(ctx.workspacePath);\n ctx.logStream.write(\n `Workspace:\\n${files\n .map(f => ` - ${relative(ctx.workspacePath, f)}`)\n .join('\\n')}`,\n );\n }\n },\n });\n}\n\nexport async function recursiveReadDir(dir: string): Promise<string[]> {\n const subdirs = await readdir(dir);\n const files = await Promise.all(\n subdirs.map(async subdir => {\n const res = resolve(dir, subdir);\n return (await stat(res)).isDirectory() ? recursiveReadDir(res) : [res];\n }),\n );\n return files.reduce((a, f) => a.concat(f), []);\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { resolve as resolvePath, isAbsolute } from 'path';\nimport { UrlReader } from '@backstage/backend-common';\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { JsonValue } from '@backstage/config';\n\nexport async function fetchContents({\n reader,\n integrations,\n baseUrl,\n fetchUrl = '.',\n outputPath,\n}: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n baseUrl?: string;\n fetchUrl?: JsonValue;\n outputPath: string;\n}) {\n if (typeof fetchUrl !== 'string') {\n throw new InputError(\n `Invalid url parameter, expected string, got ${typeof fetchUrl}`,\n );\n }\n\n let fetchUrlIsAbsolute = false;\n try {\n // eslint-disable-next-line no-new\n new URL(fetchUrl);\n fetchUrlIsAbsolute = true;\n } catch {\n /* ignored */\n }\n\n // We handle both file locations and url ones\n if (!fetchUrlIsAbsolute && baseUrl?.startsWith('file://')) {\n const basePath = baseUrl.slice('file://'.length);\n if (isAbsolute(fetchUrl)) {\n throw new InputError(\n `Fetch URL may not be absolute for file locations, ${fetchUrl}`,\n );\n }\n const srcDir = resolvePath(basePath, '..', fetchUrl);\n await fs.copy(srcDir, outputPath);\n } else {\n let readUrl;\n\n if (fetchUrlIsAbsolute) {\n readUrl = fetchUrl;\n } else if (baseUrl) {\n const integration = integrations.byUrl(baseUrl);\n if (!integration) {\n throw new InputError(`No integration found for location ${baseUrl}`);\n }\n\n readUrl = integration.resolveUrl({\n url: fetchUrl,\n base: baseUrl,\n });\n } else {\n throw new InputError(\n `Failed to fetch, template location could not be determined and the fetch URL is relative, ${fetchUrl}`,\n );\n }\n\n const res = await reader.readTree(readUrl);\n await fs.ensureDir(outputPath);\n await res.dir({ targetDir: outputPath });\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { UrlReader, resolveSafeChildPath } from '@backstage/backend-common';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { fetchContents } from './helpers';\nimport { createTemplateAction } from '../../createTemplateAction';\n\nexport function createFetchPlainAction(options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n}) {\n const { reader, integrations } = options;\n\n return createTemplateAction<{ url: string; targetPath?: string }>({\n id: 'fetch:plain',\n description:\n \"Downloads content and places it in the workspace, or optionally in a subdirectory specified by the 'targetPath' input option.\",\n schema: {\n input: {\n type: 'object',\n required: ['url'],\n properties: {\n url: {\n title: 'Fetch URL',\n description:\n 'Relative path or absolute URL pointing to the directory tree to fetch',\n type: 'string',\n },\n targetPath: {\n title: 'Target Path',\n description:\n 'Target path within the working directory to download the contents to.',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logger.info('Fetching plain content from remote URL');\n\n // Finally move the template result into the task workspace\n const targetPath = ctx.input.targetPath ?? './';\n const outputPath = resolveSafeChildPath(ctx.workspacePath, targetPath);\n\n await fetchContents({\n reader,\n integrations,\n baseUrl: ctx.baseUrl,\n fetchUrl: ctx.input.url,\n outputPath,\n });\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { resolve as resolvePath, extname } from 'path';\nimport { resolveSafeChildPath, UrlReader } from '@backstage/backend-common';\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { fetchContents } from './helpers';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport globby from 'globby';\nimport nunjucks from 'nunjucks';\nimport fs from 'fs-extra';\nimport { isBinaryFile } from 'isbinaryfile';\n\n/*\n * Maximise compatibility with Jinja (and therefore cookiecutter)\n * using nunjucks jinja compat mode. Since this method mutates\n * the global nunjucks instance, we can't enable this per-template,\n * or only for templates with cookiecutter compat enabled, so the\n * next best option is to explicitly enable it globally and allow\n * folks to rely on jinja compatibility behaviour in fetch:template\n * templates if they wish.\n *\n * cf. https://mozilla.github.io/nunjucks/api.html#installjinjacompat\n */\nnunjucks.installJinjaCompat();\n\ntype CookieCompatInput = {\n copyWithoutRender?: string[];\n cookiecutterCompat?: boolean;\n};\n\ntype ExtensionInput = {\n templateFileExtension?: string | boolean;\n};\n\nexport type FetchTemplateInput = {\n url: string;\n targetPath?: string;\n values: any;\n} & CookieCompatInput &\n ExtensionInput;\n\nexport function createFetchTemplateAction(options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n}) {\n const { reader, integrations } = options;\n\n return createTemplateAction<FetchTemplateInput>({\n id: 'fetch:template',\n description:\n \"Downloads a skeleton, templates variables into file and directory names and content, and places the result in the workspace, or optionally in a subdirectory specified by the 'targetPath' input option.\",\n schema: {\n input: {\n type: 'object',\n required: ['url'],\n properties: {\n url: {\n title: 'Fetch URL',\n description:\n 'Relative path or absolute URL pointing to the directory tree to fetch',\n type: 'string',\n },\n targetPath: {\n title: 'Target Path',\n description:\n 'Target path within the working directory to download the contents to. Defaults to the working directory root.',\n type: 'string',\n },\n values: {\n title: 'Template Values',\n description: 'Values to pass on to the templating engine',\n type: 'object',\n },\n copyWithoutRender: {\n title: 'Copy Without Render',\n description:\n 'An array of glob patterns. Any files or directories which match are copied without being processed as templates.',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n cookiecutterCompat: {\n title: 'Cookiecutter compatibility mode',\n description:\n 'Enable features to maximise compatibility with templates built for fetch:cookiecutter',\n type: 'boolean',\n },\n templateFileExtension: {\n title: 'Template File Extension',\n description:\n 'If set, only files with the given extension will be templated. If set to `true`, the default extension `.njk` is used.',\n type: ['string', 'boolean'],\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logger.info('Fetching template content from remote URL');\n\n const workDir = await ctx.createTemporaryDirectory();\n const templateDir = resolvePath(workDir, 'template');\n\n const targetPath = ctx.input.targetPath ?? './';\n const outputDir = resolveSafeChildPath(ctx.workspacePath, targetPath);\n\n if (\n ctx.input.copyWithoutRender &&\n !Array.isArray(ctx.input.copyWithoutRender)\n ) {\n throw new InputError(\n 'Fetch action input copyWithoutRender must be an Array',\n );\n }\n\n if (\n ctx.input.templateFileExtension &&\n (ctx.input.copyWithoutRender || ctx.input.cookiecutterCompat)\n ) {\n throw new InputError(\n 'Fetch action input extension incompatible with copyWithoutRender and cookiecutterCompat',\n );\n }\n\n let extension: string | false = false;\n if (ctx.input.templateFileExtension) {\n extension =\n ctx.input.templateFileExtension === true\n ? '.njk'\n : ctx.input.templateFileExtension;\n if (!extension.startsWith('.')) {\n extension = `.${extension}`;\n }\n }\n\n await fetchContents({\n reader,\n integrations,\n baseUrl: ctx.baseUrl,\n fetchUrl: ctx.input.url,\n outputPath: templateDir,\n });\n\n ctx.logger.info('Listing files and directories in template');\n const allEntriesInTemplate = await globby(`**/*`, {\n cwd: templateDir,\n dot: true,\n onlyFiles: false,\n markDirectories: true,\n });\n\n const nonTemplatedEntries = new Set(\n (\n await Promise.all(\n (ctx.input.copyWithoutRender || []).map(pattern =>\n globby(pattern, {\n cwd: templateDir,\n dot: true,\n onlyFiles: false,\n markDirectories: true,\n }),\n ),\n )\n ).flat(),\n );\n\n // Create a templater\n const templater = nunjucks.configure({\n ...(ctx.input.cookiecutterCompat\n ? {}\n : {\n tags: {\n // TODO(mtlewis/orkohunter): Document Why we are changing the literals? Not here, but on scaffolder docs. ADR?\n variableStart: '${{',\n variableEnd: '}}',\n },\n }),\n // We don't want this builtin auto-escaping, since uses HTML escape sequences\n // like `&quot;` - the correct way to escape strings in our case depends on\n // the file type.\n autoescape: false,\n });\n\n if (ctx.input.cookiecutterCompat) {\n // The \"jsonify\" filter built into cookiecutter is common\n // in fetch:cookiecutter templates, so when compat mode\n // is enabled we alias the \"dump\" filter from nunjucks as\n // jsonify. Dump accepts an optional `spaces` parameter\n // which enables indented output, but when this parameter\n // is not supplied it works identically to jsonify.\n //\n // cf. https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html?highlight=jsonify#jsonify-extension\n // cf. https://mozilla.github.io/nunjucks/templating.html#dump\n templater.addFilter('jsonify', templater.getFilter('dump'));\n }\n\n // Cookiecutter prefixes all parameters in templates with\n // `cookiecutter.`. To replicate this, we wrap our parameters\n // in an object with a `cookiecutter` property when compat\n // mode is enabled.\n const { cookiecutterCompat, values } = ctx.input;\n const context = {\n [cookiecutterCompat ? 'cookiecutter' : 'values']: values,\n };\n\n ctx.logger.info(\n `Processing ${allEntriesInTemplate.length} template files/directories with input values`,\n ctx.input.values,\n );\n\n for (const location of allEntriesInTemplate) {\n let renderFilename: boolean;\n let renderContents: boolean;\n\n let localOutputPath = location;\n if (extension) {\n renderFilename = true;\n renderContents = extname(localOutputPath) === extension;\n if (renderContents) {\n localOutputPath = localOutputPath.slice(0, -extension.length);\n }\n } else {\n renderFilename = renderContents = !nonTemplatedEntries.has(location);\n }\n if (renderFilename) {\n localOutputPath = templater.renderString(localOutputPath, context);\n }\n const outputPath = resolvePath(outputDir, localOutputPath);\n\n if (!renderContents && !extension) {\n ctx.logger.info(\n `Copying file/directory ${location} without processing.`,\n );\n }\n\n if (location.endsWith('/')) {\n ctx.logger.info(\n `Writing directory ${location} to template output path.`,\n );\n await fs.ensureDir(outputPath);\n } else {\n const inputFilePath = resolvePath(templateDir, location);\n\n if (await isBinaryFile(inputFilePath)) {\n ctx.logger.info(\n `Copying binary file ${location} to template output path.`,\n );\n await fs.copy(inputFilePath, outputPath);\n } else {\n const statsObj = await fs.stat(inputFilePath);\n ctx.logger.info(\n `Writing file ${location} to template output path with mode ${statsObj.mode}.`,\n );\n const inputFileContents = await fs.readFile(inputFilePath, 'utf-8');\n await fs.outputFile(\n outputPath,\n renderContents\n ? templater.renderString(inputFileContents, context)\n : inputFileContents,\n { mode: statsObj.mode },\n );\n }\n }\n }\n\n ctx.logger.info(`Template result written to ${outputDir}`);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { InputError } from '@backstage/errors';\nimport { resolveSafeChildPath } from '@backstage/backend-common';\nimport fs from 'fs-extra';\n\nexport const createFilesystemDeleteAction = () => {\n return createTemplateAction<{ files: string[] }>({\n id: 'fs:delete',\n description: 'Deletes files and directories from the workspace',\n schema: {\n input: {\n required: ['files'],\n type: 'object',\n properties: {\n files: {\n title: 'Files',\n description: 'A list of files and directories that will be deleted',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n },\n },\n },\n async handler(ctx) {\n if (!Array.isArray(ctx.input?.files)) {\n throw new InputError('files must be an Array');\n }\n\n for (const file of ctx.input.files) {\n const filepath = resolveSafeChildPath(ctx.workspacePath, file);\n\n try {\n await fs.remove(filepath);\n ctx.logger.info(`File ${filepath} deleted successfully`);\n } catch (err) {\n ctx.logger.error(`Failed to delete file ${filepath}:`, err);\n throw err;\n }\n }\n },\n });\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { resolveSafeChildPath } from '@backstage/backend-common';\n\nimport { InputError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/config';\nimport fs from 'fs-extra';\n\ninterface FilesToRename extends JsonObject {\n from: string;\n to: string;\n}\n\nexport const createFilesystemRenameAction = () => {\n return createTemplateAction<{ files: FilesToRename }>({\n id: 'fs:rename',\n description: 'Renames files and directories within the workspace',\n schema: {\n input: {\n required: ['files'],\n type: 'object',\n properties: {\n files: {\n title: 'Files',\n description:\n 'A list of file and directory names that will be renamed',\n type: 'array',\n items: {\n type: 'object',\n required: ['from', 'to'],\n properties: {\n from: {\n type: 'string',\n title: 'The source location of the file to be renamed',\n },\n to: {\n type: 'string',\n title: 'The destination of the new file',\n },\n overwrite: {\n type: 'boolean',\n title:\n 'Overwrite existing file or directory, default is false',\n },\n },\n },\n },\n },\n },\n },\n async handler(ctx) {\n if (!Array.isArray(ctx.input?.files)) {\n throw new InputError('files must be an Array');\n }\n\n for (const file of ctx.input.files) {\n if (!file.from || !file.to) {\n throw new InputError('each file must have a from and to property');\n }\n\n const sourceFilepath = resolveSafeChildPath(\n ctx.workspacePath,\n file.from,\n );\n const destFilepath = resolveSafeChildPath(ctx.workspacePath, file.to);\n\n try {\n await fs.move(sourceFilepath, destFilepath, {\n overwrite: file.overwrite ?? false,\n });\n ctx.logger.info(\n `File ${sourceFilepath} renamed to ${destFilepath} successfully`,\n );\n } catch (err) {\n ctx.logger.error(\n `Failed to rename file ${sourceFilepath} to ${destFilepath}:`,\n err,\n );\n throw err;\n }\n }\n },\n });\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { spawn } from 'child_process';\nimport { PassThrough, Writable } from 'stream';\nimport { Logger } from 'winston';\nimport { Git } from '@backstage/backend-common';\nimport { Octokit } from '@octokit/rest';\n\nexport type RunCommandOptions = {\n command: string;\n args: string[];\n logStream?: Writable;\n};\n\nexport const runCommand = async ({\n command,\n args,\n logStream = new PassThrough(),\n}: RunCommandOptions) => {\n await new Promise<void>((resolve, reject) => {\n const process = spawn(command, args);\n\n process.stdout.on('data', stream => {\n logStream.write(stream);\n });\n\n process.stderr.on('data', stream => {\n logStream.write(stream);\n });\n\n process.on('error', error => {\n return reject(error);\n });\n\n process.on('close', code => {\n if (code !== 0) {\n return reject(`Command ${command} failed, exit code: ${code}`);\n }\n return resolve();\n });\n });\n};\n\nexport async function initRepoAndPush({\n dir,\n remoteUrl,\n auth,\n logger,\n defaultBranch = 'master',\n commitMessage = 'Initial commit',\n gitAuthorInfo,\n}: {\n dir: string;\n remoteUrl: string;\n auth: { username: string; password: string };\n logger: Logger;\n defaultBranch?: string;\n commitMessage?: string;\n gitAuthorInfo?: { name?: string; email?: string };\n}): Promise<void> {\n const git = Git.fromAuth({\n username: auth.username,\n password: auth.password,\n logger,\n });\n\n await git.init({\n dir,\n defaultBranch,\n });\n\n await git.add({ dir, filepath: '.' });\n\n // use provided info if possible, otherwise use fallbacks\n const authorInfo = {\n name: gitAuthorInfo?.name ?? 'Scaffolder',\n email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io',\n };\n\n await git.commit({\n dir,\n message: commitMessage,\n author: authorInfo,\n committer: authorInfo,\n });\n\n await git.addRemote({\n dir,\n url: remoteUrl,\n remote: 'origin',\n });\n\n await git.push({\n dir,\n remote: 'origin',\n });\n}\n\ntype BranchProtectionOptions = {\n client: Octokit;\n owner: string;\n repoName: string;\n logger: Logger;\n requireCodeOwnerReviews: boolean;\n defaultBranch?: string;\n};\n\nexport const enableBranchProtectionOnDefaultRepoBranch = async ({\n repoName,\n client,\n owner,\n logger,\n requireCodeOwnerReviews,\n defaultBranch = 'master',\n}: BranchProtectionOptions): Promise<void> => {\n const tryOnce = async () => {\n try {\n await client.repos.updateBranchProtection({\n mediaType: {\n /**\n * 👇 we need this preview because allowing a custom\n * reviewer count on branch protection is a preview\n * feature\n *\n * More here: https://docs.github.com/en/rest/overview/api-previews#require-multiple-approving-reviews\n */\n previews: ['luke-cage-preview'],\n },\n owner,\n repo: repoName,\n branch: defaultBranch,\n required_status_checks: { strict: true, contexts: [] },\n restrictions: null,\n enforce_admins: true,\n required_pull_request_reviews: {\n required_approving_review_count: 1,\n require_code_owner_reviews: requireCodeOwnerReviews,\n },\n });\n } catch (e) {\n if (\n e.message.includes(\n 'Upgrade to GitHub Pro or make this repository public to enable this feature',\n )\n ) {\n logger.warn(\n 'Branch protection was not enabled as it requires GitHub Pro for private repositories',\n );\n } else {\n throw e;\n }\n }\n };\n\n try {\n await tryOnce();\n } catch (e) {\n if (!e.message.includes('Branch not found')) {\n throw e;\n }\n\n // GitHub has eventual consistency. Fail silently, wait, and try again.\n await new Promise(resolve => setTimeout(resolve, 600));\n await tryOnce();\n }\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { join as joinPath, normalize as normalizePath } from 'path';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\n\nexport const getRepoSourceDirectory = (\n workspacePath: string,\n sourcePath: string | undefined,\n) => {\n if (sourcePath) {\n const safeSuffix = normalizePath(sourcePath).replace(\n /^(\\.\\.(\\/|\\\\|$))+/,\n '',\n );\n return joinPath(workspacePath, safeSuffix);\n }\n return workspacePath;\n};\nexport type RepoSpec = {\n repo: string;\n host: string;\n owner?: string;\n organization?: string;\n workspace?: string;\n project?: string;\n};\n\nexport const parseRepoUrl = (\n repoUrl: string,\n integrations: ScmIntegrationRegistry,\n): RepoSpec => {\n let parsed;\n try {\n parsed = new URL(`https://${repoUrl}`);\n } catch (error) {\n throw new InputError(\n `Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`,\n );\n }\n const host = parsed.host;\n const owner = parsed.searchParams.get('owner') ?? undefined;\n const organization = parsed.searchParams.get('organization') ?? undefined;\n const workspace = parsed.searchParams.get('workspace') ?? undefined;\n const project = parsed.searchParams.get('project') ?? undefined;\n\n const type = integrations.byHost(host)?.type;\n\n if (!type) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n if (type === 'bitbucket') {\n if (host === 'bitbucket.org') {\n if (!workspace) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing workspace`,\n );\n }\n }\n if (!project) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing project`,\n );\n }\n } else {\n if (!owner) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing owner`,\n );\n }\n }\n\n const repo = parsed.searchParams.get('repo');\n if (!repo) {\n throw new InputError(\n `Invalid repo URL passed to publisher: ${repoUrl}, missing repo`,\n );\n }\n\n return { host, owner, repo, organization, workspace, project };\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { initRepoAndPush } from '../helpers';\nimport { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces';\nimport { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Config } from '@backstage/config';\n\nexport function createPublishAzureAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n return createTemplateAction<{\n repoUrl: string;\n description?: string;\n defaultBranch?: string;\n sourcePath?: string;\n }>({\n id: 'publish:azure',\n description:\n 'Initializes a git repository of the content in the workspace, and publishes it to Azure.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n type: 'string',\n },\n description: {\n title: 'Repository Description',\n type: 'string',\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const { repoUrl, defaultBranch = 'master' } = ctx.input;\n\n const { owner, repo, host, organization } = parseRepoUrl(\n repoUrl,\n integrations,\n );\n\n if (!organization) {\n throw new InputError(\n `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing organization`,\n );\n }\n\n const integrationConfig = integrations.azure.byHost(host);\n\n if (!integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n if (!integrationConfig.config.token) {\n throw new InputError(`No token provided for Azure Integration ${host}`);\n }\n const authHandler = getPersonalAccessTokenHandler(\n integrationConfig.config.token,\n );\n\n const webApi = new WebApi(`https://${host}/${organization}`, authHandler);\n const client = await webApi.getGitApi();\n const createOptions: GitRepositoryCreateOptions = { name: repo };\n const returnedRepo = await client.createRepository(createOptions, owner);\n\n if (!returnedRepo) {\n throw new InputError(\n `Unable to create the repository with Organization ${organization}, Project ${owner} and Repo ${repo}.\n Please make sure that both the Org and Project are typed corrected and exist.`,\n );\n }\n const remoteUrl = returnedRepo.remoteUrl;\n\n if (!remoteUrl) {\n throw new InputError(\n 'No remote URL returned from create repository for Azure',\n );\n }\n\n // blam: Repo contents is serialized into the path,\n // so it's just the base path I think\n const repoContentsUrl = remoteUrl;\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl,\n defaultBranch,\n auth: {\n username: 'notempty',\n password: integrationConfig.config.token,\n },\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport {\n BitbucketIntegrationConfig,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport fetch from 'cross-fetch';\nimport { initRepoAndPush } from '../helpers';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { Config } from '@backstage/config';\n\nconst createBitbucketCloudRepository = async (opts: {\n workspace: string;\n project: string;\n repo: string;\n description: string;\n repoVisibility: 'private' | 'public';\n authorization: string;\n}) => {\n const {\n workspace,\n project,\n repo,\n description,\n repoVisibility,\n authorization,\n } = opts;\n\n const options: RequestInit = {\n method: 'POST',\n body: JSON.stringify({\n scm: 'git',\n description: description,\n is_private: repoVisibility === 'private',\n project: { key: project },\n }),\n headers: {\n Authorization: authorization,\n 'Content-Type': 'application/json',\n },\n };\n\n let response: Response;\n try {\n response = await fetch(\n `https://api.bitbucket.org/2.0/repositories/${workspace}/${repo}`,\n options,\n );\n } catch (e) {\n throw new Error(`Unable to create repository, ${e}`);\n }\n\n if (response.status !== 200) {\n throw new Error(\n `Unable to create repository, ${response.status} ${\n response.statusText\n }, ${await response.text()}`,\n );\n }\n\n const r = await response.json();\n let remoteUrl = '';\n for (const link of r.links.clone) {\n if (link.name === 'https') {\n remoteUrl = link.href;\n }\n }\n\n // TODO use the urlReader to get the default branch\n const repoContentsUrl = `${r.links.html.href}/src/master`;\n return { remoteUrl, repoContentsUrl };\n};\n\nconst createBitbucketServerRepository = async (opts: {\n host: string;\n project: string;\n repo: string;\n description: string;\n repoVisibility: 'private' | 'public';\n authorization: string;\n apiBaseUrl?: string;\n}) => {\n const {\n host,\n project,\n repo,\n description,\n authorization,\n repoVisibility,\n apiBaseUrl,\n } = opts;\n\n let response: Response;\n const options: RequestInit = {\n method: 'POST',\n body: JSON.stringify({\n name: repo,\n description: description,\n public: repoVisibility === 'public',\n }),\n headers: {\n Authorization: authorization,\n 'Content-Type': 'application/json',\n },\n };\n\n try {\n const baseUrl = apiBaseUrl ? apiBaseUrl : `https://${host}/rest/api/1.0`;\n response = await fetch(`${baseUrl}/projects/${project}/repos`, options);\n } catch (e) {\n throw new Error(`Unable to create repository, ${e}`);\n }\n\n if (response.status !== 201) {\n throw new Error(\n `Unable to create repository, ${response.status} ${\n response.statusText\n }, ${await response.text()}`,\n );\n }\n\n const r = await response.json();\n let remoteUrl = '';\n for (const link of r.links.clone) {\n if (link.name === 'http') {\n remoteUrl = link.href;\n }\n }\n\n const repoContentsUrl = `${r.links.self[0].href}`;\n return { remoteUrl, repoContentsUrl };\n};\n\nconst getAuthorizationHeader = (config: BitbucketIntegrationConfig) => {\n if (config.username && config.appPassword) {\n const buffer = Buffer.from(\n `${config.username}:${config.appPassword}`,\n 'utf8',\n );\n\n return `Basic ${buffer.toString('base64')}`;\n }\n\n if (config.token) {\n return `Bearer ${config.token}`;\n }\n\n throw new Error(\n `Authorization has not been provided for Bitbucket. Please add either username + appPassword or token to the Integrations config`,\n );\n};\n\nconst performEnableLFS = async (opts: {\n authorization: string;\n host: string;\n project: string;\n repo: string;\n}) => {\n const { authorization, host, project, repo } = opts;\n\n const options: RequestInit = {\n method: 'PUT',\n headers: {\n Authorization: authorization,\n },\n };\n\n const { ok, status, statusText } = await fetch(\n `https://${host}/rest/git-lfs/admin/projects/${project}/repos/${repo}/enabled`,\n options,\n );\n\n if (!ok)\n throw new Error(\n `Failed to enable LFS in the repository, ${status}: ${statusText}`,\n );\n};\n\nexport function createPublishBitbucketAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n return createTemplateAction<{\n repoUrl: string;\n description: string;\n defaultBranch?: string;\n repoVisibility: 'private' | 'public';\n sourcePath?: string;\n enableLFS: boolean;\n }>({\n id: 'publish:bitbucket',\n description:\n 'Initializes a git repository of the content in the workspace, and publishes it to Bitbucket.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n type: 'string',\n },\n description: {\n title: 'Repository Description',\n type: 'string',\n },\n repoVisibility: {\n title: 'Repository Visibility',\n type: 'string',\n enum: ['private', 'public'],\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n enableLFS: {\n title:\n 'Enable LFS for the repository. Only available for hosted Bitbucket.',\n type: 'boolean',\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n description,\n defaultBranch = 'master',\n repoVisibility = 'private',\n enableLFS = false,\n } = ctx.input;\n\n const { workspace, project, repo, host } = parseRepoUrl(\n repoUrl,\n integrations,\n );\n\n // Workspace is only required for bitbucket cloud\n if (host === 'bitbucket.org') {\n if (!workspace) {\n throw new InputError(\n `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`,\n );\n }\n }\n\n // Project is required for both bitbucket cloud and bitbucket server\n if (!project) {\n throw new InputError(\n `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing project`,\n );\n }\n\n const integrationConfig = integrations.bitbucket.byHost(host);\n\n if (!integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n const authorization = getAuthorizationHeader(integrationConfig.config);\n const apiBaseUrl = integrationConfig.config.apiBaseUrl;\n\n const createMethod =\n host === 'bitbucket.org'\n ? createBitbucketCloudRepository\n : createBitbucketServerRepository;\n\n const { remoteUrl, repoContentsUrl } = await createMethod({\n authorization,\n host,\n workspace: workspace || '',\n project,\n repo,\n repoVisibility,\n description,\n apiBaseUrl,\n });\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl,\n auth: {\n username: integrationConfig.config.username\n ? integrationConfig.config.username\n : 'x-token-auth',\n password: integrationConfig.config.appPassword\n ? integrationConfig.config.appPassword\n : integrationConfig.config.token ?? '',\n },\n defaultBranch,\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n if (enableLFS && host !== 'bitbucket.org') {\n await performEnableLFS({ authorization, host, project, repo });\n }\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { dirname } from 'path';\nimport { InputError } from '@backstage/errors';\nimport { createTemplateAction } from '../../createTemplateAction';\n\n/**\n * This task is useful for local development and testing of both the scaffolder\n * and scaffolder templates.\n *\n * This action is not installed by default and should not be installed in\n * production, as it writes the files to the local filesystem of the scaffolder.\n */\nexport function createPublishFileAction() {\n return createTemplateAction<{ path: string }>({\n id: 'publish:file',\n description: 'Writes contents of the workspace to a local directory',\n schema: {\n input: {\n type: 'object',\n required: ['path'],\n properties: {\n path: {\n title: 'Path to a directory where the output will be written',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const { path } = ctx.input;\n\n const exists = await fs.pathExists(path);\n if (exists) {\n throw new InputError('Output path already exists');\n }\n await fs.ensureDir(dirname(path));\n await fs.copy(ctx.workspacePath, path);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport {\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { Octokit } from '@octokit/rest';\nimport { parseRepoUrl } from '../publish/util';\n\nexport type OctokitIntegration = {\n client: Octokit;\n token: string;\n owner: string;\n repo: string;\n};\n/**\n * OctokitProvider provides Octokit client based on ScmIntegrationsRegistry configuration.\n * OctokitProvider supports GitHub credentials caching out of the box.\n */\nexport class OctokitProvider {\n private readonly integrations: ScmIntegrationRegistry;\n private readonly credentialsProviders: Map<string, GithubCredentialsProvider>;\n\n constructor(integrations: ScmIntegrationRegistry) {\n this.integrations = integrations;\n this.credentialsProviders = new Map(\n integrations.github.list().map(integration => {\n const provider = GithubCredentialsProvider.create(integration.config);\n return [integration.config.host, provider];\n }),\n );\n }\n\n /**\n * gets standard Octokit client based on repository URL.\n *\n * @param repoUrl Repository URL\n */\n async getOctokit(repoUrl: string): Promise<OctokitIntegration> {\n const { owner, repo, host } = parseRepoUrl(repoUrl, this.integrations);\n\n if (!owner) {\n throw new InputError(`No owner provided for repo ${repoUrl}`);\n }\n\n const integrationConfig = this.integrations.github.byHost(host)?.config;\n\n if (!integrationConfig) {\n throw new InputError(`No integration for host ${host}`);\n }\n\n const credentialsProvider = this.credentialsProviders.get(host);\n\n if (!credentialsProvider) {\n throw new InputError(\n `No matching credentials for host ${host}, please check your integrations config`,\n );\n }\n\n // TODO(blam): Consider changing this API to have owner, repo interface instead of URL as the it's\n // needless to create URL and then parse again the other side.\n const { token } = await credentialsProvider.getCredentials({\n url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(\n repo,\n )}`,\n });\n\n if (!token) {\n throw new InputError(\n `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,\n );\n }\n\n const client = new Octokit({\n auth: token,\n baseUrl: integrationConfig.apiBaseUrl,\n previews: ['nebula-preview'],\n });\n\n return { client, token, owner, repo };\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport {\n enableBranchProtectionOnDefaultRepoBranch,\n initRepoAndPush,\n} from '../helpers';\nimport { getRepoSourceDirectory } from './util';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Config } from '@backstage/config';\nimport { OctokitProvider } from '../github/OctokitProvider';\n\ntype Permission = 'pull' | 'push' | 'admin' | 'maintain' | 'triage';\ntype Collaborator = { access: Permission; username: string };\n\nexport function createPublishGithubAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n const octokitProvider = new OctokitProvider(integrations);\n\n return createTemplateAction<{\n repoUrl: string;\n description?: string;\n access?: string;\n defaultBranch?: string;\n sourcePath?: string;\n requireCodeOwnerReviews?: boolean;\n repoVisibility: 'private' | 'internal' | 'public';\n collaborators: Collaborator[];\n topics?: string[];\n }>({\n id: 'publish:github',\n description:\n 'Initializes a git repository of contents in workspace and publishes it to GitHub.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n description: {\n title: 'Repository Description',\n type: 'string',\n },\n access: {\n title: 'Repository Access',\n description: `Sets an admin collaborator on the repository. Can either be a user reference different from 'owner' in 'repoUrl' or team reference, eg. 'org/team-name'`,\n type: 'string',\n },\n requireCodeOwnerReviews: {\n title:\n 'Require an approved review in PR including files with a designated Code Owner',\n type: 'boolean',\n },\n repoVisibility: {\n title: 'Repository Visibility',\n type: 'string',\n enum: ['private', 'public', 'internal'],\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n collaborators: {\n title: 'Collaborators',\n description: 'Provide additional users with permissions',\n type: 'array',\n items: {\n type: 'object',\n required: ['username', 'access'],\n properties: {\n access: {\n type: 'string',\n description: 'The type of access for the user',\n enum: ['push', 'pull', 'admin', 'maintain', 'triage'],\n },\n username: {\n type: 'string',\n description: 'The username or group',\n },\n },\n },\n },\n topics: {\n title: 'Topics',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n description,\n access,\n requireCodeOwnerReviews = false,\n repoVisibility = 'private',\n defaultBranch = 'master',\n collaborators,\n topics,\n } = ctx.input;\n\n const { client, token, owner, repo } = await octokitProvider.getOctokit(\n repoUrl,\n );\n\n const user = await client.users.getByUsername({\n username: owner,\n });\n\n const repoCreationPromise =\n user.data.type === 'Organization'\n ? client.repos.createInOrg({\n name: repo,\n org: owner,\n private: repoVisibility === 'private',\n visibility: repoVisibility,\n description: description,\n })\n : client.repos.createForAuthenticatedUser({\n name: repo,\n private: repoVisibility === 'private',\n description: description,\n });\n\n const { data: newRepo } = await repoCreationPromise;\n if (access?.startsWith(`${owner}/`)) {\n const [, team] = access.split('/');\n await client.teams.addOrUpdateRepoPermissionsInOrg({\n org: owner,\n team_slug: team,\n owner,\n repo,\n permission: 'admin',\n });\n // No need to add access if it's the person who owns the personal account\n } else if (access && access !== owner) {\n await client.repos.addCollaborator({\n owner,\n repo,\n username: access,\n permission: 'admin',\n });\n }\n\n if (collaborators) {\n for (const {\n access: permission,\n username: team_slug,\n } of collaborators) {\n try {\n await client.teams.addOrUpdateRepoPermissionsInOrg({\n org: owner,\n team_slug,\n owner,\n repo,\n permission,\n });\n } catch (e) {\n ctx.logger.warn(\n `Skipping ${permission} access for ${team_slug}, ${e.message}`,\n );\n }\n }\n }\n\n if (topics) {\n try {\n await client.repos.replaceAllTopics({\n owner,\n repo,\n names: topics.map(t => t.toLowerCase()),\n });\n } catch (e) {\n ctx.logger.warn(`Skipping topics ${topics.join(' ')}, ${e.message}`);\n }\n }\n\n const remoteUrl = newRepo.clone_url;\n const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl,\n defaultBranch,\n auth: {\n username: 'x-access-token',\n password: token,\n },\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n try {\n await enableBranchProtectionOnDefaultRepoBranch({\n owner,\n client,\n repoName: newRepo.name,\n logger: ctx.logger,\n defaultBranch,\n requireCodeOwnerReviews,\n });\n } catch (e) {\n ctx.logger.warn(\n `Skipping: default branch protection on '${newRepo.name}', ${e.message}`,\n );\n }\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { readFile } from 'fs-extra';\nimport path from 'path';\nimport { parseRepoUrl } from './util';\n\nimport {\n GithubCredentialsProvider,\n ScmIntegrationRegistry,\n} from '@backstage/integration';\nimport { zipObject } from 'lodash';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Octokit } from '@octokit/rest';\nimport { InputError, CustomErrorBase } from '@backstage/errors';\nimport { createPullRequest } from 'octokit-plugin-create-pull-request';\nimport globby from 'globby';\n\nclass GithubResponseError extends CustomErrorBase {}\n\ntype CreatePullRequestResponse = {\n data: { html_url: string };\n};\n\nexport interface PullRequestCreator {\n createPullRequest(\n options: createPullRequest.Options,\n ): Promise<CreatePullRequestResponse | null>;\n}\n\nexport type PullRequestCreatorConstructor = (\n octokit: Octokit,\n) => PullRequestCreator;\n\nexport type GithubPullRequestActionInput = {\n title: string;\n branchName: string;\n description: string;\n repoUrl: string;\n targetPath?: string;\n sourcePath?: string;\n};\n\nexport type ClientFactoryInput = {\n integrations: ScmIntegrationRegistry;\n host: string;\n owner: string;\n repo: string;\n};\n\nexport const defaultClientFactory = async ({\n integrations,\n owner,\n repo,\n host = 'github.com',\n}: ClientFactoryInput): Promise<PullRequestCreator> => {\n const integrationConfig = integrations.github.byHost(host)?.config;\n\n if (!integrationConfig) {\n throw new InputError(`No integration for host ${host}`);\n }\n\n const credentialsProvider =\n GithubCredentialsProvider.create(integrationConfig);\n\n if (!credentialsProvider) {\n throw new InputError(\n `No matching credentials for host ${host}, please check your integrations config`,\n );\n }\n\n const { token } = await credentialsProvider.getCredentials({\n url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(\n repo,\n )}`,\n });\n\n if (!token) {\n throw new InputError(\n `No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,\n );\n }\n\n const OctokitPR = Octokit.plugin(createPullRequest);\n\n return new OctokitPR({\n auth: token,\n baseUrl: integrationConfig.apiBaseUrl,\n });\n};\n\ninterface CreateGithubPullRequestActionOptions {\n integrations: ScmIntegrationRegistry;\n clientFactory?: (input: ClientFactoryInput) => Promise<PullRequestCreator>;\n}\n\nexport const createPublishGithubPullRequestAction = ({\n integrations,\n clientFactory = defaultClientFactory,\n}: CreateGithubPullRequestActionOptions) => {\n return createTemplateAction<GithubPullRequestActionInput>({\n id: 'publish:github:pull-request',\n schema: {\n input: {\n required: ['repoUrl', 'title', 'description', 'branchName'],\n type: 'object',\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n branchName: {\n type: 'string',\n title: 'Branch Name',\n description: 'The name for the branch',\n },\n title: {\n type: 'string',\n title: 'Pull Request Name',\n description: 'The name for the pull request',\n },\n description: {\n type: 'string',\n title: 'Pull Request Description',\n description: 'The description of the pull request',\n },\n sourcePath: {\n type: 'string',\n title: 'Working Subdirectory',\n description:\n 'Subdirectory of working directory to copy changes from',\n },\n targetPath: {\n type: 'string',\n title: 'Repository Subdirectory',\n description: 'Subdirectory of repository to apply changes to',\n },\n },\n },\n output: {\n required: ['remoteUrl'],\n type: 'object',\n properties: {\n remoteUrl: {\n type: 'string',\n title: 'Pull Request URL',\n description: 'Link to the pull request in Github',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n branchName,\n title,\n description,\n targetPath,\n sourcePath,\n } = ctx.input;\n\n const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);\n\n if (!owner) {\n throw new InputError(\n `No owner provided for host: ${host}, and repo ${repo}`,\n );\n }\n\n const client = await clientFactory({ integrations, host, owner, repo });\n const fileRoot = sourcePath\n ? path.resolve(ctx.workspacePath, sourcePath)\n : ctx.workspacePath;\n\n const localFilePaths = await globby(['./**', './**/.*', '!.git'], {\n cwd: fileRoot,\n gitignore: true,\n dot: true,\n });\n\n const fileContents = await Promise.all(\n localFilePaths.map(p => readFile(path.resolve(fileRoot, p))),\n );\n\n const repoFilePaths = localFilePaths.map(repoFilePath => {\n return targetPath ? `${targetPath}/${repoFilePath}` : repoFilePath;\n });\n\n const changes = [\n {\n files: zipObject(\n repoFilePaths,\n fileContents.map(buf => buf.toString()),\n ),\n commit: title,\n },\n ];\n\n try {\n const response = await client.createPullRequest({\n owner,\n repo,\n title,\n changes,\n body: description,\n head: branchName,\n });\n\n if (!response) {\n throw new GithubResponseError('null response from Github');\n }\n\n ctx.output('remoteUrl', response.data.html_url);\n } catch (e) {\n throw new GithubResponseError('Pull request creation failed', e);\n }\n },\n });\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { Gitlab } from '@gitbeaker/node';\nimport { initRepoAndPush } from '../helpers';\nimport { getRepoSourceDirectory, parseRepoUrl } from './util';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { Config } from '@backstage/config';\n\nexport function createPublishGitlabAction(options: {\n integrations: ScmIntegrationRegistry;\n config: Config;\n}) {\n const { integrations, config } = options;\n\n return createTemplateAction<{\n repoUrl: string;\n defaultBranch?: string;\n repoVisibility: 'private' | 'internal' | 'public';\n sourcePath?: string;\n }>({\n id: 'publish:gitlab',\n description:\n 'Initializes a git repository of the content in the workspace, and publishes it to GitLab.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n type: 'string',\n },\n repoVisibility: {\n title: 'Repository Visibility',\n type: 'string',\n enum: ['private', 'public', 'internal'],\n },\n defaultBranch: {\n title: 'Default Branch',\n type: 'string',\n description: `Sets the default branch on the repository. The default value is 'master'`,\n },\n sourcePath: {\n title:\n 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.',\n type: 'string',\n },\n },\n },\n output: {\n type: 'object',\n properties: {\n remoteUrl: {\n title: 'A URL to the repository with the provider',\n type: 'string',\n },\n repoContentsUrl: {\n title: 'A URL to the root of the repository',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n repoVisibility = 'private',\n defaultBranch = 'master',\n } = ctx.input;\n\n const { owner, repo, host } = parseRepoUrl(repoUrl, integrations);\n\n if (!owner) {\n throw new InputError(\n `No owner provided for host: ${host}, and repo ${repo}`,\n );\n }\n\n const integrationConfig = integrations.gitlab.byHost(host);\n\n if (!integrationConfig) {\n throw new InputError(\n `No matching integration configuration for host ${host}, please check your integrations config`,\n );\n }\n\n if (!integrationConfig.config.token) {\n throw new InputError(`No token available for host ${host}`);\n }\n\n const client = new Gitlab({\n host: integrationConfig.config.baseUrl,\n token: integrationConfig.config.token,\n });\n\n let { id: targetNamespace } = (await client.Namespaces.show(owner)) as {\n id: number;\n };\n\n if (!targetNamespace) {\n const { id } = (await client.Users.current()) as {\n id: number;\n };\n targetNamespace = id;\n }\n\n const { http_url_to_repo } = await client.Projects.create({\n namespace_id: targetNamespace,\n name: repo,\n visibility: repoVisibility,\n });\n\n const remoteUrl = (http_url_to_repo as string).replace(/\\.git$/, '');\n const repoContentsUrl = `${remoteUrl}/-/blob/${defaultBranch}`;\n\n const gitAuthorInfo = {\n name: config.getOptionalString('scaffolder.defaultAuthor.name'),\n email: config.getOptionalString('scaffolder.defaultAuthor.email'),\n };\n\n await initRepoAndPush({\n dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),\n remoteUrl: http_url_to_repo as string,\n defaultBranch,\n auth: {\n username: 'oauth2',\n password: integrationConfig.config.token,\n },\n logger: ctx.logger,\n commitMessage: config.getOptionalString(\n 'scaffolder.defaultCommitMessage',\n ),\n gitAuthorInfo,\n });\n\n ctx.output('remoteUrl', remoteUrl);\n ctx.output('repoContentsUrl', repoContentsUrl);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { OctokitProvider } from './OctokitProvider';\n\nexport function createGithubActionsDispatchAction(options: {\n integrations: ScmIntegrationRegistry;\n}) {\n const { integrations } = options;\n const octokitProvider = new OctokitProvider(integrations);\n\n return createTemplateAction<{\n repoUrl: string;\n workflowId: string;\n branchOrTagName: string;\n }>({\n id: 'github:actions:dispatch',\n description:\n 'Dispatches a GitHub Action workflow for a given branch or tag',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl', 'workflowId', 'branchOrTagName'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n workflowId: {\n title: 'Workflow ID',\n description: 'The GitHub Action Workflow filename',\n type: 'string',\n },\n branchOrTagName: {\n title: 'Branch or Tag name',\n description:\n 'The git branch or tag name used to dispatch the workflow',\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n const { repoUrl, workflowId, branchOrTagName } = ctx.input;\n\n ctx.logger.info(\n `Dispatching workflow ${workflowId} for repo ${repoUrl} on ${branchOrTagName}`,\n );\n\n const { client, owner, repo } = await octokitProvider.getOctokit(repoUrl);\n\n await client.rest.actions.createWorkflowDispatch({\n owner,\n repo,\n workflow_id: workflowId,\n ref: branchOrTagName,\n });\n\n ctx.logger.info(`Workflow ${workflowId} dispatched successfully`);\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ScmIntegrationRegistry } from '@backstage/integration';\nimport { createTemplateAction } from '../../createTemplateAction';\nimport { OctokitProvider } from './OctokitProvider';\nimport { emitterEventNames } from '@octokit/webhooks';\n\ntype ContentType = 'form' | 'json';\n\nexport function createGithubWebhookAction(options: {\n integrations: ScmIntegrationRegistry;\n defaultWebhookSecret?: string;\n}) {\n const { integrations, defaultWebhookSecret } = options;\n const octokitProvider = new OctokitProvider(integrations);\n const eventNames = emitterEventNames.filter(event => !event.includes('.'));\n\n return createTemplateAction<{\n repoUrl: string;\n webhookUrl: string;\n webhookSecret?: string;\n events?: string[];\n active?: boolean;\n contentType?: ContentType;\n insecureSsl?: boolean;\n }>({\n id: 'github:webhook',\n description: 'Creates webhook for a repository on GitHub.',\n schema: {\n input: {\n type: 'object',\n required: ['repoUrl', 'webhookUrl'],\n properties: {\n repoUrl: {\n title: 'Repository Location',\n description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the new repository name and 'owner' is an organization or username`,\n type: 'string',\n },\n webhookUrl: {\n title: 'Webhook URL',\n description: 'The URL to which the payloads will be delivered',\n type: 'string',\n },\n webhookSecret: {\n title: 'Webhook Secret',\n description:\n 'Webhook secret value. The default can be provided internally in action creation',\n type: 'string',\n },\n events: {\n title: 'Triggering Events',\n description:\n 'Determines what events the hook is triggered for. Default: push',\n type: 'array',\n oneOf: [\n {\n items: {\n type: 'string',\n enum: eventNames,\n },\n },\n {\n items: {\n type: 'string',\n const: '*',\n },\n },\n ],\n },\n active: {\n title: 'Active',\n type: 'boolean',\n description: `Determines if notifications are sent when the webhook is triggered. Default: true`,\n },\n contentType: {\n title: 'Content Type',\n type: 'string',\n enum: ['form', 'json'],\n description: `The media type used to serialize the payloads. The default is 'form'`,\n },\n insecureSsl: {\n title: 'Insecure SSL',\n type: 'boolean',\n description: `Determines whether the SSL certificate of the host for url will be verified when delivering payloads. Default 'false'`,\n },\n },\n },\n },\n async handler(ctx) {\n const {\n repoUrl,\n webhookUrl,\n webhookSecret = defaultWebhookSecret,\n events = ['push'],\n active = true,\n contentType = 'form',\n insecureSsl = false,\n } = ctx.input;\n\n ctx.logger.info(`Creating webhook ${webhookUrl} for repo ${repoUrl}`);\n\n const { client, owner, repo } = await octokitProvider.getOctokit(repoUrl);\n\n try {\n const insecure_ssl = insecureSsl ? '1' : '0';\n await client.repos.createWebhook({\n owner,\n repo,\n config: {\n url: webhookUrl,\n content_type: contentType,\n secret: webhookSecret,\n insecure_ssl,\n },\n events,\n active,\n });\n ctx.logger.info(`Webhook '${webhookUrl}' created successfully`);\n } catch (e) {\n ctx.logger.warn(\n `Failed: create webhook '${webhookUrl}' on repo: '${repo}', ${e.message}`,\n );\n }\n },\n });\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ContainerRunner, UrlReader } from '@backstage/backend-common';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { Config } from '@backstage/config';\nimport {\n createCatalogWriteAction,\n createCatalogRegisterAction,\n} from './catalog';\n\nimport { createDebugLogAction } from './debug';\nimport { createFetchPlainAction, createFetchTemplateAction } from './fetch';\nimport { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter';\nimport {\n createFilesystemDeleteAction,\n createFilesystemRenameAction,\n} from './filesystem';\nimport {\n createPublishAzureAction,\n createPublishBitbucketAction,\n createPublishGithubAction,\n createPublishGithubPullRequestAction,\n createPublishGitlabAction,\n} from './publish';\nimport {\n createGithubActionsDispatchAction,\n createGithubWebhookAction,\n} from './github';\n\nexport const createBuiltinActions = (options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n catalogClient: CatalogApi;\n containerRunner: ContainerRunner;\n config: Config;\n}) => {\n const { reader, integrations, containerRunner, catalogClient, config } =\n options;\n\n return [\n createFetchPlainAction({\n reader,\n integrations,\n }),\n createFetchCookiecutterAction({\n reader,\n integrations,\n containerRunner,\n }),\n createFetchTemplateAction({\n integrations,\n reader,\n }),\n createPublishGithubAction({\n integrations,\n config,\n }),\n createPublishGithubPullRequestAction({\n integrations,\n }),\n createPublishGitlabAction({\n integrations,\n config,\n }),\n createPublishBitbucketAction({\n integrations,\n config,\n }),\n createPublishAzureAction({\n integrations,\n config,\n }),\n createDebugLogAction(),\n createCatalogRegisterAction({ catalogClient, integrations }),\n createCatalogWriteAction(),\n createFilesystemDeleteAction(),\n createFilesystemRenameAction(),\n createGithubActionsDispatchAction({\n integrations,\n }),\n createGithubWebhookAction({\n integrations,\n }),\n ];\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { InputBase, TemplateAction } from './types';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\n\nexport class TemplateActionRegistry {\n private readonly actions = new Map<string, TemplateAction<any>>();\n\n register<Parameters extends InputBase>(action: TemplateAction<Parameters>) {\n if (this.actions.has(action.id)) {\n throw new ConflictError(\n `Template action with ID '${action.id}' has already been registered`,\n );\n }\n this.actions.set(action.id, action);\n }\n\n get(actionId: string): TemplateAction<any> {\n const action = this.actions.get(actionId);\n if (!action) {\n throw new NotFoundError(\n `Template action with ID '${actionId}' is not registered.`,\n );\n }\n return action;\n }\n\n list(): TemplateAction<any>[] {\n return [...this.actions.values()];\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { TemplateEntityV1beta2 } from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\n\n/**\n * A catalog client tailored for reading out entity data from the catalog.\n */\nexport class CatalogEntityClient {\n constructor(private readonly catalogClient: CatalogApi) {}\n\n /**\n * Looks up a single template using a template name.\n *\n * Throws a NotFoundError or ConflictError if 0 or multiple templates are found.\n */\n async findTemplate(\n templateName: string,\n options?: { token?: string },\n ): Promise<TemplateEntityV1beta2> {\n const { items: templates } = (await this.catalogClient.getEntities(\n {\n filter: {\n kind: 'template',\n 'metadata.name': templateName,\n },\n },\n options,\n )) as { items: TemplateEntityV1beta2[] };\n\n if (templates.length !== 1) {\n if (templates.length > 1) {\n throw new ConflictError(\n 'Templates lookup resulted in multiple matches',\n );\n } else {\n throw new NotFoundError('Template not found');\n }\n }\n\n return templates[0];\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject } from '@backstage/config';\nimport { resolvePackagePath } from '@backstage/backend-common';\nimport { ConflictError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { v4 as uuid } from 'uuid';\nimport {\n DbTaskEventRow,\n DbTaskRow,\n Status,\n TaskEventType,\n TaskSecrets,\n TaskSpec,\n TaskStore,\n TaskStoreEmitOptions,\n TaskStoreGetEventsOptions,\n} from './types';\nimport { DateTime } from 'luxon';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-scaffolder-backend',\n 'migrations',\n);\n\nexport type RawDbTaskRow = {\n id: string;\n spec: string;\n status: Status;\n last_heartbeat_at?: string;\n created_at: string;\n secrets?: string;\n};\n\nexport type RawDbTaskEventRow = {\n id: number;\n task_id: string;\n body: string;\n event_type: TaskEventType;\n created_at: string;\n};\n\nexport class DatabaseTaskStore implements TaskStore {\n static async create(knex: Knex): Promise<DatabaseTaskStore> {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n return new DatabaseTaskStore(knex);\n }\n\n constructor(private readonly db: Knex) {}\n\n async getTask(taskId: string): Promise<DbTaskRow> {\n const [result] = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId })\n .select();\n if (!result) {\n throw new NotFoundError(`No task with id '${taskId}' found`);\n }\n try {\n const spec = JSON.parse(result.spec);\n const secrets = result.secrets ? JSON.parse(result.secrets) : undefined;\n return {\n id: result.id,\n spec,\n status: result.status,\n lastHeartbeatAt: result.last_heartbeat_at,\n createdAt: result.created_at,\n secrets,\n };\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${taskId}', ${error}`);\n }\n }\n\n async createTask(\n spec: TaskSpec,\n secrets?: TaskSecrets,\n ): Promise<{ taskId: string }> {\n const taskId = uuid();\n await this.db<RawDbTaskRow>('tasks').insert({\n id: taskId,\n spec: JSON.stringify(spec),\n secrets: secrets ? JSON.stringify(secrets) : undefined,\n status: 'open',\n });\n return { taskId };\n }\n\n async claimTask(): Promise<DbTaskRow | undefined> {\n return this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n status: 'open',\n })\n .limit(1)\n .select();\n\n if (!task) {\n return undefined;\n }\n\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({ id: task.id, status: 'open' })\n .update({\n status: 'processing',\n last_heartbeat_at: this.db.fn.now(),\n });\n\n if (updateCount < 1) {\n return undefined;\n }\n\n try {\n const spec = JSON.parse(task.spec);\n const secrets = task.secrets ? JSON.parse(task.secrets) : undefined;\n return {\n id: task.id,\n spec,\n status: 'processing',\n lastHeartbeatAt: task.last_heartbeat_at,\n createdAt: task.created_at,\n secrets,\n };\n } catch (error) {\n throw new Error(`Failed to parse spec of task '${task.id}', ${error}`);\n }\n });\n }\n\n async heartbeatTask(taskId: string): Promise<void> {\n const updateCount = await this.db<RawDbTaskRow>('tasks')\n .where({ id: taskId, status: 'processing' })\n .update({\n last_heartbeat_at: this.db.fn.now(),\n });\n if (updateCount === 0) {\n throw new ConflictError(`No running task with taskId ${taskId} found`);\n }\n }\n\n async listStaleTasks({ timeoutS }: { timeoutS: number }): Promise<{\n tasks: { taskId: string }[];\n }> {\n const rawRows = await this.db<RawDbTaskRow>('tasks')\n .where('status', 'processing')\n .andWhere(\n 'last_heartbeat_at',\n '<=',\n this.db.client.config.client === 'sqlite3'\n ? this.db.raw(`datetime('now', ?)`, [`-${timeoutS} seconds`])\n : this.db.raw(`dateadd('second', ?, ?)`, [\n `-${timeoutS}`,\n this.db.fn.now(),\n ]),\n );\n const tasks = rawRows.map(row => ({\n taskId: row.id,\n }));\n return { tasks };\n }\n\n async completeTask({\n taskId,\n status,\n eventBody,\n }: {\n taskId: string;\n status: Status;\n eventBody: JsonObject;\n }): Promise<void> {\n let oldStatus: string;\n if (status === 'failed' || status === 'completed') {\n oldStatus = 'processing';\n } else {\n throw new Error(\n `Invalid status update of run '${taskId}' to status '${status}'`,\n );\n }\n await this.db.transaction(async tx => {\n const [task] = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n })\n .limit(1)\n .select();\n\n if (!task) {\n throw new Error(`No task with taskId ${taskId} found`);\n }\n if (task.status !== oldStatus) {\n throw new ConflictError(\n `Refusing to update status of run '${taskId}' to status '${status}' ` +\n `as it is currently '${task.status}', expected '${oldStatus}'`,\n );\n }\n const updateCount = await tx<RawDbTaskRow>('tasks')\n .where({\n id: taskId,\n status: oldStatus,\n })\n .update({\n status,\n secrets: null as any,\n });\n if (updateCount !== 1) {\n throw new ConflictError(\n `Failed to update status to '${status}' for taskId ${taskId}`,\n );\n }\n\n await tx<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'completion',\n body: JSON.stringify(eventBody),\n });\n });\n }\n\n async emitLogEvent({ taskId, body }: TaskStoreEmitOptions): Promise<void> {\n const serliazedBody = JSON.stringify(body);\n await this.db<RawDbTaskEventRow>('task_events').insert({\n task_id: taskId,\n event_type: 'log',\n body: serliazedBody,\n });\n }\n\n async listEvents({\n taskId,\n after,\n }: TaskStoreGetEventsOptions): Promise<{ events: DbTaskEventRow[] }> {\n const rawEvents = await this.db<RawDbTaskEventRow>('task_events')\n .where({\n task_id: taskId,\n })\n .andWhere(builder => {\n if (typeof after === 'number') {\n builder.where('id', '>', after).orWhere('event_type', 'completion');\n }\n })\n .orderBy('id')\n .select();\n\n const events = rawEvents.map(event => {\n try {\n const body = JSON.parse(event.body) as JsonObject;\n return {\n id: Number(event.id),\n taskId,\n body,\n type: event.event_type,\n createdAt:\n typeof event.created_at === 'string'\n ? DateTime.fromSQL(event.created_at, { zone: 'UTC' }).toISO()\n : event.created_at,\n };\n } catch (error) {\n throw new Error(\n `Failed to parse event body from event taskId=${taskId} id=${event.id}, ${error}`,\n );\n }\n });\n return { events };\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { JsonObject } from '@backstage/config';\nimport { Logger } from 'winston';\nimport {\n CompletedTaskState,\n Task,\n TaskSecrets,\n TaskSpec,\n TaskStore,\n TaskBroker,\n DispatchResult,\n DbTaskEventRow,\n DbTaskRow,\n} from './types';\n\nexport class TaskAgent implements Task {\n private isDone = false;\n\n private heartbeatTimeoutId?: ReturnType<typeof setInterval>;\n\n static create(state: TaskState, storage: TaskStore, logger: Logger) {\n const agent = new TaskAgent(state, storage, logger);\n agent.startTimeout();\n return agent;\n }\n\n // Runs heartbeat internally\n private constructor(\n private readonly state: TaskState,\n private readonly storage: TaskStore,\n private readonly logger: Logger,\n ) {}\n\n get spec() {\n return this.state.spec;\n }\n\n get secrets() {\n return this.state.secrets;\n }\n\n async getWorkspaceName() {\n return this.state.taskId;\n }\n\n get done() {\n return this.isDone;\n }\n\n async emitLog(message: string, metadata?: JsonObject): Promise<void> {\n await this.storage.emitLogEvent({\n taskId: this.state.taskId,\n body: { message, ...metadata },\n });\n }\n\n async complete(\n result: CompletedTaskState,\n metadata?: JsonObject,\n ): Promise<void> {\n await this.storage.completeTask({\n taskId: this.state.taskId,\n status: result === 'failed' ? 'failed' : 'completed',\n eventBody: {\n message: `Run completed with status: ${result}`,\n ...metadata,\n },\n });\n this.isDone = true;\n if (this.heartbeatTimeoutId) {\n clearTimeout(this.heartbeatTimeoutId);\n }\n }\n\n private startTimeout() {\n this.heartbeatTimeoutId = setTimeout(async () => {\n try {\n await this.storage.heartbeatTask(this.state.taskId);\n this.startTimeout();\n } catch (error) {\n this.isDone = true;\n\n this.logger.error(\n `Heartbeat for task ${this.state.taskId} failed`,\n error,\n );\n }\n }, 1000);\n }\n}\n\ninterface TaskState {\n spec: TaskSpec;\n taskId: string;\n secrets?: TaskSecrets;\n}\n\nfunction defer() {\n let resolve = () => {};\n const promise = new Promise<void>(_resolve => {\n resolve = _resolve;\n });\n return { promise, resolve };\n}\n\nexport class StorageTaskBroker implements TaskBroker {\n constructor(\n private readonly storage: TaskStore,\n private readonly logger: Logger,\n ) {}\n private deferredDispatch = defer();\n\n async claim(): Promise<Task> {\n for (;;) {\n const pendingTask = await this.storage.claimTask();\n if (pendingTask) {\n return TaskAgent.create(\n {\n taskId: pendingTask.id,\n spec: pendingTask.spec,\n secrets: pendingTask.secrets,\n },\n this.storage,\n this.logger,\n );\n }\n\n await this.waitForDispatch();\n }\n }\n\n async dispatch(\n spec: TaskSpec,\n secrets?: TaskSecrets,\n ): Promise<DispatchResult> {\n const taskRow = await this.storage.createTask(spec, secrets);\n this.signalDispatch();\n return {\n taskId: taskRow.taskId,\n };\n }\n\n async get(taskId: string): Promise<DbTaskRow> {\n return this.storage.getTask(taskId);\n }\n\n observe(\n options: {\n taskId: string;\n after: number | undefined;\n },\n callback: (\n error: Error | undefined,\n result: { events: DbTaskEventRow[] },\n ) => void,\n ): () => void {\n const { taskId } = options;\n\n let cancelled = false;\n const unsubscribe = () => {\n cancelled = true;\n };\n\n (async () => {\n let after = options.after;\n while (!cancelled) {\n const result = await this.storage.listEvents({ taskId, after: after });\n const { events } = result;\n if (events.length) {\n after = events[events.length - 1].id;\n try {\n callback(undefined, result);\n } catch (error) {\n callback(error, { events: [] });\n }\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000));\n }\n })();\n\n return unsubscribe;\n }\n\n async vacuumTasks(timeoutS: { timeoutS: number }): Promise<void> {\n const { tasks } = await this.storage.listStaleTasks(timeoutS);\n await Promise.all(\n tasks.map(async task => {\n try {\n await this.storage.completeTask({\n taskId: task.taskId,\n status: 'failed',\n eventBody: {\n message:\n 'The task was cancelled because the task worker lost connection to the task broker',\n },\n });\n } catch (error) {\n this.logger.warn(`Failed to cancel task '${task.taskId}', ${error}`);\n }\n }),\n );\n }\n\n private waitForDispatch() {\n return this.deferredDispatch.promise;\n }\n\n private signalDispatch() {\n this.deferredDispatch.resolve();\n this.deferredDispatch = defer();\n }\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { isArray } from 'lodash';\n\n/**\n * Returns true if the input is not `false`, `undefined`, `null`, `\"\"`, `0`, or\n * `[]`. This behavior is based on the behavior of handlebars, see\n * https://handlebarsjs.com/guide/builtin-helpers.html#if\n */\nexport function isTruthy(value: any): boolean {\n return isArray(value) ? value.length > 0 : !!value;\n}\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { JsonObject, JsonValue } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\nimport fs from 'fs-extra';\nimport * as Handlebars from 'handlebars';\nimport { validate as validateJsonSchema } from 'jsonschema';\nimport path from 'path';\nimport { PassThrough } from 'stream';\nimport * as winston from 'winston';\nimport { Logger } from 'winston';\nimport { parseRepoUrl } from '../actions/builtin/publish/util';\nimport { TemplateActionRegistry } from '../actions/TemplateActionRegistry';\nimport { isTruthy } from './helper';\nimport { Task, TaskBroker } from './types';\nimport { ScmIntegrations } from '@backstage/integration';\n\ntype Options = {\n logger: Logger;\n taskBroker: TaskBroker;\n workingDirectory: string;\n actionRegistry: TemplateActionRegistry;\n integrations: ScmIntegrations;\n};\n\nexport class TaskWorker {\n private readonly handlebars: typeof Handlebars;\n\n constructor(private readonly options: Options) {\n this.handlebars = Handlebars.create();\n\n // TODO(blam): this should be a public facing API but it's a little\n // scary right now, so we're going to lock it off like the component API is\n // in the frontend until we can work out a nice way to do it.\n this.handlebars.registerHelper('parseRepoUrl', repoUrl => {\n return JSON.stringify(parseRepoUrl(repoUrl, options.integrations));\n });\n\n this.handlebars.registerHelper('projectSlug', repoUrl => {\n const { owner, repo } = parseRepoUrl(repoUrl, options.integrations);\n return `${owner}/${repo}`;\n });\n\n this.handlebars.registerHelper('json', obj => JSON.stringify(obj));\n\n this.handlebars.registerHelper('not', value => !isTruthy(value));\n\n this.handlebars.registerHelper('eq', (a, b) => a === b);\n }\n\n start() {\n (async () => {\n for (;;) {\n const task = await this.options.taskBroker.claim();\n await this.runOneTask(task);\n }\n })();\n }\n\n async runOneTask(task: Task) {\n let workspacePath: string | undefined = undefined;\n try {\n const { actionRegistry } = this.options;\n\n workspacePath = path.join(\n this.options.workingDirectory,\n await task.getWorkspaceName(),\n );\n await fs.ensureDir(workspacePath);\n await task.emitLog(\n `Starting up task with ${task.spec.steps.length} steps`,\n );\n\n const templateCtx: {\n parameters: JsonObject;\n steps: {\n [stepName: string]: { output: { [outputName: string]: JsonValue } };\n };\n } = { parameters: task.spec.values, steps: {} };\n\n for (const step of task.spec.steps) {\n const metadata = { stepId: step.id };\n try {\n const taskLogger = winston.createLogger({\n level: process.env.LOG_LEVEL || 'info',\n format: winston.format.combine(\n winston.format.colorize(),\n winston.format.timestamp(),\n winston.format.simple(),\n ),\n defaultMeta: {},\n });\n\n const stream = new PassThrough();\n stream.on('data', async data => {\n const message = data.toString().trim();\n if (message?.length > 1) {\n await task.emitLog(message, metadata);\n }\n });\n\n taskLogger.add(new winston.transports.Stream({ stream }));\n\n if (step.if !== undefined) {\n // Support passing values like false to disable steps\n let skip = !step.if;\n\n // Evaluate strings as handlebar templates\n if (typeof step.if === 'string') {\n const condition = JSON.parse(\n JSON.stringify(step.if),\n (_key, value) => {\n if (typeof value === 'string') {\n const templated = this.handlebars.compile(value, {\n noEscape: true,\n data: false,\n preventIndent: true,\n })(templateCtx);\n\n // If it's just an empty string, treat it as undefined\n if (templated === '') {\n return undefined;\n }\n\n try {\n return JSON.parse(templated);\n } catch {\n return templated;\n }\n }\n\n return value;\n },\n );\n\n skip = !isTruthy(condition);\n }\n\n if (skip) {\n await task.emitLog(`Skipped step ${step.name}`, {\n ...metadata,\n status: 'skipped',\n });\n continue;\n }\n }\n\n await task.emitLog(`Beginning step ${step.name}`, {\n ...metadata,\n status: 'processing',\n });\n\n const action = actionRegistry.get(step.action);\n if (!action) {\n throw new Error(`Action '${step.action}' does not exist`);\n }\n\n const input =\n step.input &&\n JSON.parse(JSON.stringify(step.input), (_key, value) => {\n if (typeof value === 'string') {\n const templated = this.handlebars.compile(value, {\n noEscape: true,\n data: false,\n preventIndent: true,\n })(templateCtx);\n\n // If it smells like a JSON object then give it a parse as an object and if it fails return the string\n if (\n (templated.startsWith('\"') && templated.endsWith('\"')) ||\n (templated.startsWith('{') && templated.endsWith('}')) ||\n (templated.startsWith('[') && templated.endsWith(']'))\n ) {\n try {\n // Don't recursively JSON parse the values of this string.\n // Shouldn't need to, don't want to encourage the use of returning handlebars from somewhere else\n return JSON.parse(templated);\n } catch {\n return templated;\n }\n }\n return templated;\n }\n\n return value;\n });\n\n if (action.schema?.input) {\n const validateResult = validateJsonSchema(\n input,\n action.schema.input,\n );\n if (!validateResult.valid) {\n const errors = validateResult.errors.join(', ');\n throw new InputError(\n `Invalid input passed to action ${action.id}, ${errors}`,\n );\n }\n }\n\n const stepOutputs: { [name: string]: JsonValue } = {};\n\n // Keep track of all tmp dirs that are created by the action so we can remove them after\n const tmpDirs = new Array<string>();\n\n this.options.logger.debug(`Running ${action.id} with input`, {\n input: JSON.stringify(input, null, 2),\n });\n\n await action.handler({\n baseUrl: task.spec.baseUrl,\n logger: taskLogger,\n logStream: stream,\n input,\n token: task.secrets?.token,\n workspacePath,\n async createTemporaryDirectory() {\n const tmpDir = await fs.mkdtemp(\n `${workspacePath}_step-${step.id}-`,\n );\n tmpDirs.push(tmpDir);\n return tmpDir;\n },\n output(name: string, value: JsonValue) {\n stepOutputs[name] = value;\n },\n });\n\n // Remove all temporary directories that were created when executing the action\n for (const tmpDir of tmpDirs) {\n await fs.remove(tmpDir);\n }\n\n templateCtx.steps[step.id] = { output: stepOutputs };\n\n await task.emitLog(`Finished step ${step.name}`, {\n ...metadata,\n status: 'completed',\n });\n } catch (error) {\n await task.emitLog(String(error.stack), {\n ...metadata,\n status: 'failed',\n });\n throw error;\n }\n }\n\n const output = JSON.parse(\n JSON.stringify(task.spec.output),\n (_key, value) => {\n if (typeof value === 'string') {\n const templated = this.handlebars.compile(value, {\n noEscape: true,\n data: false,\n preventIndent: true,\n })(templateCtx);\n\n // If it's just an empty string, treat it as undefined\n if (templated === '') {\n return undefined;\n }\n\n // If it smells like a JSON object then give it a parse as an object and if it fails return the string\n if (\n (templated.startsWith('\"') && templated.endsWith('\"')) ||\n (templated.startsWith('{') && templated.endsWith('}')) ||\n (templated.startsWith('[') && templated.endsWith(']'))\n ) {\n try {\n // Don't recursively JSON parse the values of this string.\n // Shouldn't need to, don't want to encourage the use of returning handlebars from somewhere else\n return JSON.parse(templated);\n } catch {\n return templated;\n }\n }\n return templated;\n }\n return value;\n },\n );\n\n await task.complete('completed', { output });\n } catch (error) {\n await task.complete('failed', {\n error: { name: error.name, message: error.message },\n });\n } finally {\n if (workspacePath) {\n await fs.remove(workspacePath);\n }\n }\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n Entity,\n LOCATION_ANNOTATION,\n parseLocationReference,\n SOURCE_LOCATION_ANNOTATION,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport fs from 'fs-extra';\nimport os from 'os';\nimport { Logger } from 'winston';\n\nexport async function getWorkingDirectory(\n config: Config,\n logger: Logger,\n): Promise<string> {\n if (!config.has('backend.workingDirectory')) {\n return os.tmpdir();\n }\n\n const workingDirectory = config.getString('backend.workingDirectory');\n try {\n // Check if working directory exists and is writable\n await fs.access(workingDirectory, fs.constants.F_OK | fs.constants.W_OK);\n logger.info(`using working directory: ${workingDirectory}`);\n } catch (err) {\n logger.error(\n `working directory ${workingDirectory} ${\n err.code === 'ENOENT' ? 'does not exist' : 'is not writable'\n }`,\n );\n throw err;\n }\n return workingDirectory;\n}\n\n/**\n * Gets the base URL of the entity location that points to the source location\n * of the entity description within a repo. If there is not source location\n * or if it has an invalid type, undefined will be returned instead.\n *\n * For file locations this will return a `file://` URL.\n */\nexport function getEntityBaseUrl(entity: Entity): string | undefined {\n let location = entity.metadata.annotations?.[SOURCE_LOCATION_ANNOTATION];\n if (!location) {\n location = entity.metadata.annotations?.[LOCATION_ANNOTATION];\n }\n if (!location) {\n return undefined;\n }\n\n const { type, target } = parseLocationReference(location);\n if (type === 'url') {\n return target;\n } else if (type === 'file') {\n return `file://${target}`;\n }\n\n // Only url and file location are handled, as we otherwise don't know if\n // what the url is pointing to makes sense to use as a baseUrl\n return undefined;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport express from 'express';\nimport Router from 'express-promise-router';\nimport { Logger } from 'winston';\nimport { CatalogEntityClient } from '../lib/catalog';\nimport { validate } from 'jsonschema';\nimport {\n DatabaseTaskStore,\n StorageTaskBroker,\n TaskWorker,\n} from '../scaffolder/tasks';\nimport { TemplateActionRegistry } from '../scaffolder/actions/TemplateActionRegistry';\nimport { getEntityBaseUrl, getWorkingDirectory } from './helpers';\nimport {\n ContainerRunner,\n PluginDatabaseManager,\n UrlReader,\n} from '@backstage/backend-common';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { TemplateEntityV1beta2, Entity } from '@backstage/catalog-model';\nimport { ScmIntegrations } from '@backstage/integration';\nimport { TemplateAction } from '../scaffolder/actions';\nimport { createBuiltinActions } from '../scaffolder/actions/builtin/createBuiltinActions';\n\nexport interface RouterOptions {\n logger: Logger;\n config: Config;\n reader: UrlReader;\n database: PluginDatabaseManager;\n catalogClient: CatalogApi;\n actions?: TemplateAction<any>[];\n taskWorkers?: number;\n containerRunner: ContainerRunner;\n}\n\nfunction isBeta2Template(\n entity: TemplateEntityV1beta2,\n): entity is TemplateEntityV1beta2 {\n return entity.apiVersion === 'backstage.io/v1beta2';\n}\n\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const router = Router();\n router.use(express.json());\n\n const {\n logger: parentLogger,\n config,\n reader,\n database,\n catalogClient,\n actions,\n containerRunner,\n taskWorkers,\n } = options;\n\n const logger = parentLogger.child({ plugin: 'scaffolder' });\n const workingDirectory = await getWorkingDirectory(config, logger);\n const entityClient = new CatalogEntityClient(catalogClient);\n const integrations = ScmIntegrations.fromConfig(config);\n\n const databaseTaskStore = await DatabaseTaskStore.create(\n await database.getClient(),\n );\n const taskBroker = new StorageTaskBroker(databaseTaskStore, logger);\n const actionRegistry = new TemplateActionRegistry();\n const workers = [];\n for (let i = 0; i < (taskWorkers || 1); i++) {\n const worker = new TaskWorker({\n logger,\n taskBroker,\n actionRegistry,\n workingDirectory,\n integrations,\n });\n workers.push(worker);\n }\n\n const actionsToRegister = Array.isArray(actions)\n ? actions\n : createBuiltinActions({\n integrations,\n catalogClient,\n containerRunner,\n reader,\n config,\n });\n\n actionsToRegister.forEach(action => actionRegistry.register(action));\n workers.forEach(worker => worker.start());\n\n router\n .get(\n '/v2/templates/:namespace/:kind/:name/parameter-schema',\n async (req, res) => {\n const { namespace, kind, name } = req.params;\n\n if (namespace !== 'default') {\n throw new InputError(\n `Invalid namespace, only 'default' namespace is supported`,\n );\n }\n if (kind.toLowerCase() !== 'template') {\n throw new InputError(\n `Invalid kind, only 'Template' kind is supported`,\n );\n }\n\n const template = await entityClient.findTemplate(name, {\n token: getBearerToken(req.headers.authorization),\n });\n if (isBeta2Template(template)) {\n const parameters = [template.spec.parameters ?? []].flat();\n res.json({\n title: template.metadata.title ?? template.metadata.name,\n steps: parameters.map(schema => ({\n title: schema.title ?? 'Fill in template parameters',\n schema,\n })),\n });\n } else {\n throw new InputError(\n `Unsupported apiVersion field in schema entity, ${\n (template as Entity).apiVersion\n }`,\n );\n }\n },\n )\n .get('/v2/actions', async (_req, res) => {\n const actionsList = actionRegistry.list().map(action => {\n return {\n id: action.id,\n description: action.description,\n schema: action.schema,\n };\n });\n res.json(actionsList);\n })\n .post('/v2/tasks', async (req, res) => {\n const templateName: string = req.body.templateName;\n const values = req.body.values;\n const token = getBearerToken(req.headers.authorization);\n const template = await entityClient.findTemplate(templateName, {\n token,\n });\n\n let taskSpec;\n\n if (isBeta2Template(template)) {\n for (const parameters of [template.spec.parameters ?? []].flat()) {\n const result = validate(values, parameters);\n\n if (!result.valid) {\n res.status(400).json({ errors: result.errors });\n return;\n }\n }\n\n const baseUrl = getEntityBaseUrl(template);\n\n taskSpec = {\n baseUrl,\n values,\n steps: template.spec.steps.map((step, index) => ({\n ...step,\n id: step.id ?? `step-${index + 1}`,\n name: step.name ?? step.action,\n })),\n output: template.spec.output ?? {},\n };\n } else {\n throw new InputError(\n `Unsupported apiVersion field in schema entity, ${\n (template as Entity).apiVersion\n }`,\n );\n }\n\n const result = await taskBroker.dispatch(taskSpec, {\n token: token,\n });\n\n res.status(201).json({ id: result.taskId });\n })\n .get('/v2/tasks/:taskId', async (req, res) => {\n const { taskId } = req.params;\n const task = await taskBroker.get(taskId);\n if (!task) {\n throw new NotFoundError(`Task with id ${taskId} does not exist`);\n }\n // Do not disclose secrets\n delete task.secrets;\n res.status(200).json(task);\n })\n .get('/v2/tasks/:taskId/eventstream', async (req, res) => {\n const { taskId } = req.params;\n const after = Number(req.query.after) || undefined;\n logger.debug(`Event stream observing taskId '${taskId}' opened`);\n\n // Mandatory headers and http status to keep connection open\n res.writeHead(200, {\n Connection: 'keep-alive',\n 'Cache-Control': 'no-cache',\n 'Content-Type': 'text/event-stream',\n });\n\n // After client opens connection send all events as string\n const unsubscribe = taskBroker.observe(\n { taskId, after },\n (error, { events }) => {\n if (error) {\n logger.error(\n `Received error from event stream when observing taskId '${taskId}', ${error}`,\n );\n }\n\n let shouldUnsubscribe = false;\n for (const event of events) {\n res.write(\n `event: ${event.type}\\ndata: ${JSON.stringify(event)}\\n\\n`,\n );\n if (event.type === 'completion') {\n shouldUnsubscribe = true;\n // Closing the event stream here would cause the frontend\n // to automatically reconnect because it lost connection.\n }\n }\n res.flush();\n if (shouldUnsubscribe) unsubscribe();\n },\n );\n // When client closes connection we update the clients list\n // avoiding the disconnected one\n req.on('close', () => {\n unsubscribe();\n logger.debug(`Event stream observing taskId '${taskId}' closed`);\n });\n });\n\n const app = express();\n app.set('logger', logger);\n app.use('/', router);\n\n return app;\n}\n\nfunction getBearerToken(header?: string): string | undefined {\n return header?.match(/Bearer\\s+(\\S+)/i)?.[1];\n}\n"],"names":["InputError","getEntityName","fs","resolvePath","yaml","relative","readdir","resolve","stat","isAbsolute","resolveSafeChildPath","nunjucks","globby","extname","isBinaryFile","PassThrough","spawn","Git","normalizePath","joinPath","getPersonalAccessTokenHandler","WebApi","fetch","path","dirname","GithubCredentialsProvider","integration","Octokit","CustomErrorBase","createPullRequest","readFile","zipObject","Gitlab","emitterEventNames","createFetchCookiecutterAction","ConflictError","NotFoundError","resolvePackagePath","uuid","DateTime","isArray","Handlebars","winston","stream","validateJsonSchema","errors","os","SOURCE_LOCATION_ANNOTATION","LOCATION_ANNOTATION","parseLocationReference","Router","express","ScmIntegrations","validate"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAkBa,uBAAuB,CAClC,mBACwB;AAExB,SAAO;AAAA;;qCCAmC,SAGzC;AACD,QAAM,CAAE,eAAe,gBAAiB;AAExC,SAAO,qBAGL;AAAA,IACA,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,YACE,MAAM;AAAA,YACN,UAAU,CAAC;AAAA,YACX,YAAY;AAAA,cACV,gBAAgB;AAAA,gBACd,OAAO;AAAA,gBACP,aACE;AAAA,gBACF,MAAM;AAAA;AAAA;AAAA;AAAA,UAIZ;AAAA,YACE,MAAM;AAAA,YACN,UAAU,CAAC;AAAA,YACX,YAAY;AAAA,cACV,iBAAiB;AAAA,gBACf,OAAO;AAAA,gBACP,aACE;AAAA,gBACF,MAAM;AAAA;AAAA,cAER,iBAAiB;AAAA,gBACf,OAAO;AAAA,gBACP,aACE;AAAA,gBACF,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAOZ,QAAQ,KAAK;AACjB,YAAM,CAAE,SAAU;AAElB,UAAI;AACJ,UAAI,oBAAoB,OAAO;AAC7B,yBAAiB,MAAM;AAAA,aAClB;AACL,cAAM,CAAE,iBAAiB,kBAAkB,wBACzC;AACF,cAAM,cAAc,aAAa,MAAM;AACvC,YAAI,CAAC,aAAa;AAChB,gBAAM,IAAIA,kBACR,iCAAiC;AAAA;AAIrC,yBAAiB,YAAY,WAAW;AAAA,UACtC,MAAM;AAAA,UACN,KAAK;AAAA;AAAA;AAIT,UAAI,OAAO,KAAK,eAAe;AAE/B,YAAM,SAAS,MAAM,cAAc,YACjC;AAAA,QACE,MAAM;AAAA,QACN,QAAQ;AAAA,SAEV,IAAI,QAAQ,CAAE,OAAO,IAAI,SAAU;AAErC,UAAI,OAAO,SAAS,UAAU,GAAG;AAC/B,cAAM,CAAE,MAAM,MAAM,aAAcC,2BAAc,OAAO,SAAS;AAChE,YAAI,OAAO,aAAa,GAAG,QAAQ,aAAa;AAChD,YAAI,OAAO,kBAAkB;AAAA;AAAA;AAAA;AAAA;;oCCnFM;AACzC,SAAO,qBAAwD;AAAA,IAC7D,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY;AAAA,UACV,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AACjB,UAAI,UAAU,MAAM;AACpB,YAAM,CAAE,UAAW,IAAI;AAEvB,YAAMC,uBAAG,UACPC,aAAY,IAAI,eAAe,sBAC/BC,gBAAK,UAAU;AAAA;AAAA;AAAA;;gCCrBgB;AACrC,SAAO,qBAAoE;AAAA,IACzE,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AA5CvB;AA6CM,UAAI,UAAI,UAAJ,mBAAW,SAAS;AACtB,YAAI,UAAU,MAAM,IAAI,MAAM;AAAA;AAGhC,UAAI,UAAI,UAAJ,mBAAW,eAAe;AAC5B,cAAM,QAAQ,MAAM,iBAAiB,IAAI;AACzC,YAAI,UAAU,MACZ;AAAA,EAAe,MACZ,IAAI,OAAK,OAAOC,cAAS,IAAI,eAAe,MAC5C,KAAK;AAAA;AAAA;AAAA;AAAA;gCAOqB,KAAgC;AACrE,QAAM,UAAU,MAAMC,WAAQ;AAC9B,QAAM,QAAQ,MAAM,QAAQ,IAC1B,QAAQ,IAAI,OAAM,WAAU;AAC1B,UAAM,MAAMC,aAAQ,KAAK;AACzB,WAAQ,OAAMC,QAAK,MAAM,gBAAgB,iBAAiB,OAAO,CAAC;AAAA;AAGtE,SAAO,MAAM,OAAO,CAAC,GAAG,MAAM,EAAE,OAAO,IAAI;AAAA;;6BC9CT;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,GAOC;AACD,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,IAAIR,kBACR,+CAA+C,OAAO;AAAA;AAI1D,MAAI,qBAAqB;AACzB,MAAI;AAEF,QAAI,IAAI;AACR,yBAAqB;AAAA,UACrB;AAAA;AAKF,MAAI,CAAC,0DAA+B,WAAW,aAAY;AACzD,UAAM,WAAW,QAAQ,MAAM,UAAU;AACzC,QAAIS,gBAAW,WAAW;AACxB,YAAM,IAAIT,kBACR,qDAAqD;AAAA;AAGzD,UAAM,SAASG,aAAY,UAAU,MAAM;AAC3C,UAAMD,uBAAG,KAAK,QAAQ;AAAA,SACjB;AACL,QAAI;AAEJ,QAAI,oBAAoB;AACtB,gBAAU;AAAA,eACD,SAAS;AAClB,YAAM,cAAc,aAAa,MAAM;AACvC,UAAI,CAAC,aAAa;AAChB,cAAM,IAAIF,kBAAW,qCAAqC;AAAA;AAG5D,gBAAU,YAAY,WAAW;AAAA,QAC/B,KAAK;AAAA,QACL,MAAM;AAAA;AAAA,WAEH;AACL,YAAM,IAAIA,kBACR,6FAA6F;AAAA;AAIjG,UAAM,MAAM,MAAM,OAAO,SAAS;AAClC,UAAME,uBAAG,UAAU;AACnB,UAAM,IAAI,IAAI,CAAE,WAAW;AAAA;AAAA;;gCC/DQ,SAGpC;AACD,QAAM,CAAE,QAAQ,gBAAiB;AAEjC,SAAO,qBAA2D;AAAA,IAChE,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,KAAK;AAAA,YACH,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,YAAY;AAAA,YACV,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AAnDvB;AAoDM,UAAI,OAAO,KAAK;AAGhB,YAAM,aAAa,UAAI,MAAM,eAAV,YAAwB;AAC3C,YAAM,aAAaQ,mCAAqB,IAAI,eAAe;AAE3D,YAAM,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,QACA,SAAS,IAAI;AAAA,QACb,UAAU,IAAI,MAAM;AAAA,QACpB;AAAA;AAAA;AAAA;AAAA;;ACzBRC,6BAAS;mCAkBiC,SAGvC;AACD,QAAM,CAAE,QAAQ,gBAAiB;AAEjC,SAAO,qBAAyC;AAAA,IAC9C,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,KAAK;AAAA,YACH,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,YAAY;AAAA,YACV,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,mBAAmB;AAAA,YACjB,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA,YACN,OAAO;AAAA,cACL,MAAM;AAAA;AAAA;AAAA,UAGV,oBAAoB;AAAA,YAClB,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,uBAAuB;AAAA,YACrB,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM,CAAC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,UAKnB,QAAQ,KAAK;AAhHvB;AAiHM,UAAI,OAAO,KAAK;AAEhB,YAAM,UAAU,MAAM,IAAI;AAC1B,YAAM,cAAcR,aAAY,SAAS;AAEzC,YAAM,aAAa,UAAI,MAAM,eAAV,YAAwB;AAC3C,YAAM,YAAYO,mCAAqB,IAAI,eAAe;AAE1D,UACE,IAAI,MAAM,qBACV,CAAC,MAAM,QAAQ,IAAI,MAAM,oBACzB;AACA,cAAM,IAAIV,kBACR;AAAA;AAIJ,UACE,IAAI,MAAM,8BACL,MAAM,qBAAqB,IAAI,MAAM,qBAC1C;AACA,cAAM,IAAIA,kBACR;AAAA;AAIJ,UAAI,YAA4B;AAChC,UAAI,IAAI,MAAM,uBAAuB;AACnC,oBACE,IAAI,MAAM,0BAA0B,OAChC,SACA,IAAI,MAAM;AAChB,YAAI,CAAC,UAAU,WAAW,MAAM;AAC9B,sBAAY,IAAI;AAAA;AAAA;AAIpB,YAAM,cAAc;AAAA,QAClB;AAAA,QACA;AAAA,QACA,SAAS,IAAI;AAAA,QACb,UAAU,IAAI,MAAM;AAAA,QACpB,YAAY;AAAA;AAGd,UAAI,OAAO,KAAK;AAChB,YAAM,uBAAuB,MAAMY,2BAAO,QAAQ;AAAA,QAChD,KAAK;AAAA,QACL,KAAK;AAAA,QACL,WAAW;AAAA,QACX,iBAAiB;AAAA;AAGnB,YAAM,sBAAsB,IAAI,IAE5B,OAAM,QAAQ,IACX,KAAI,MAAM,qBAAqB,IAAI,IAAI,aACtCA,2BAAO,SAAS;AAAA,QACd,KAAK;AAAA,QACL,KAAK;AAAA,QACL,WAAW;AAAA,QACX,iBAAiB;AAAA,YAIvB;AAIJ,YAAM,YAAYD,6BAAS,UAAU;AAAA,WAC/B,IAAI,MAAM,qBACV,KACA;AAAA,UACE,MAAM;AAAA,YAEJ,eAAe;AAAA,YACf,aAAa;AAAA;AAAA;AAAA,QAMrB,YAAY;AAAA;AAGd,UAAI,IAAI,MAAM,oBAAoB;AAUhC,kBAAU,UAAU,WAAW,UAAU,UAAU;AAAA;AAOrD,YAAM,CAAE,oBAAoB,UAAW,IAAI;AAC3C,YAAM,UAAU;AAAA,SACb,qBAAqB,iBAAiB,WAAW;AAAA;AAGpD,UAAI,OAAO,KACT,cAAc,qBAAqB,uDACnC,IAAI,MAAM;AAGZ,iBAAW,YAAY,sBAAsB;AAC3C,YAAI;AACJ,YAAI;AAEJ,YAAI,kBAAkB;AACtB,YAAI,WAAW;AACb,2BAAiB;AACjB,2BAAiBE,aAAQ,qBAAqB;AAC9C,cAAI,gBAAgB;AAClB,8BAAkB,gBAAgB,MAAM,GAAG,CAAC,UAAU;AAAA;AAAA,eAEnD;AACL,2BAAiB,iBAAiB,CAAC,oBAAoB,IAAI;AAAA;AAE7D,YAAI,gBAAgB;AAClB,4BAAkB,UAAU,aAAa,iBAAiB;AAAA;AAE5D,cAAM,aAAaV,aAAY,WAAW;AAE1C,YAAI,CAAC,kBAAkB,CAAC,WAAW;AACjC,cAAI,OAAO,KACT,0BAA0B;AAAA;AAI9B,YAAI,SAAS,SAAS,MAAM;AAC1B,cAAI,OAAO,KACT,qBAAqB;AAEvB,gBAAMD,uBAAG,UAAU;AAAA,eACd;AACL,gBAAM,gBAAgBC,aAAY,aAAa;AAE/C,cAAI,MAAMW,0BAAa,gBAAgB;AACrC,gBAAI,OAAO,KACT,uBAAuB;AAEzB,kBAAMZ,uBAAG,KAAK,eAAe;AAAA,iBACxB;AACL,kBAAM,WAAW,MAAMA,uBAAG,KAAK;AAC/B,gBAAI,OAAO,KACT,gBAAgB,8CAA8C,SAAS;AAEzE,kBAAM,oBAAoB,MAAMA,uBAAG,SAAS,eAAe;AAC3D,kBAAMA,uBAAG,WACP,YACA,iBACI,UAAU,aAAa,mBAAmB,WAC1C,mBACJ,CAAE,MAAM,SAAS;AAAA;AAAA;AAAA;AAMzB,UAAI,OAAO,KAAK,8BAA8B;AAAA;AAAA;AAAA;;MCpQvC,+BAA+B,MAAM;AAChD,SAAO,qBAA0C;AAAA,IAC/C,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,UAAU,CAAC;AAAA,QACX,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA,YACN,OAAO;AAAA,cACL,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMV,QAAQ,KAAK;AAxCvB;AAyCM,UAAI,CAAC,MAAM,QAAQ,UAAI,UAAJ,mBAAW,QAAQ;AACpC,cAAM,IAAIF,kBAAW;AAAA;AAGvB,iBAAW,QAAQ,IAAI,MAAM,OAAO;AAClC,cAAM,WAAWU,mCAAqB,IAAI,eAAe;AAEzD,YAAI;AACF,gBAAMR,uBAAG,OAAO;AAChB,cAAI,OAAO,KAAK,QAAQ;AAAA,iBACjB,KAAP;AACA,cAAI,OAAO,MAAM,yBAAyB,aAAa;AACvD,gBAAM;AAAA;AAAA;AAAA;AAAA;AAAA;;MC1BH,+BAA+B,MAAM;AAChD,SAAO,qBAA+C;AAAA,IACpD,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,UAAU,CAAC;AAAA,QACX,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO;AAAA,YACL,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA,YACN,OAAO;AAAA,cACL,MAAM;AAAA,cACN,UAAU,CAAC,QAAQ;AAAA,cACnB,YAAY;AAAA,gBACV,MAAM;AAAA,kBACJ,MAAM;AAAA,kBACN,OAAO;AAAA;AAAA,gBAET,IAAI;AAAA,kBACF,MAAM;AAAA,kBACN,OAAO;AAAA;AAAA,gBAET,WAAW;AAAA,kBACT,MAAM;AAAA,kBACN,OACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAQV,QAAQ,KAAK;AAhEvB;AAiEM,UAAI,CAAC,MAAM,QAAQ,UAAI,UAAJ,mBAAW,QAAQ;AACpC,cAAM,IAAIF,kBAAW;AAAA;AAGvB,iBAAW,QAAQ,IAAI,MAAM,OAAO;AAClC,YAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,IAAI;AAC1B,gBAAM,IAAIA,kBAAW;AAAA;AAGvB,cAAM,iBAAiBU,mCACrB,IAAI,eACJ,KAAK;AAEP,cAAM,eAAeA,mCAAqB,IAAI,eAAe,KAAK;AAElE,YAAI;AACF,gBAAMR,uBAAG,KAAK,gBAAgB,cAAc;AAAA,YAC1C,WAAW,WAAK,cAAL,YAAkB;AAAA;AAE/B,cAAI,OAAO,KACT,QAAQ,6BAA6B;AAAA,iBAEhC,KAAP;AACA,cAAI,OAAO,MACT,yBAAyB,qBAAqB,iBAC9C;AAEF,gBAAM;AAAA;AAAA;AAAA;AAAA;AAAA;;MChEH,aAAa,OAAO;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,YAAY,IAAIa;AAAA,MACO;AACvB,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,UAAUC,oBAAM,SAAS;AAE/B,YAAQ,OAAO,GAAG,QAAQ,YAAU;AAClC,gBAAU,MAAM;AAAA;AAGlB,YAAQ,OAAO,GAAG,QAAQ,YAAU;AAClC,gBAAU,MAAM;AAAA;AAGlB,YAAQ,GAAG,SAAS,WAAS;AAC3B,aAAO,OAAO;AAAA;AAGhB,YAAQ,GAAG,SAAS,UAAQ;AAC1B,UAAI,SAAS,GAAG;AACd,eAAO,OAAO,WAAW,8BAA8B;AAAA;AAEzD,aAAO;AAAA;AAAA;AAAA;+BAKyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB;AAAA,GASgB;AAzElB;AA0EE,QAAM,MAAMC,kBAAI,SAAS;AAAA,IACvB,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf;AAAA;AAGF,QAAM,IAAI,KAAK;AAAA,IACb;AAAA,IACA;AAAA;AAGF,QAAM,IAAI,IAAI,CAAE,KAAK,UAAU;AAG/B,QAAM,aAAa;AAAA,IACjB,MAAM,qDAAe,SAAf,YAAuB;AAAA,IAC7B,OAAO,qDAAe,UAAf,YAAwB;AAAA;AAGjC,QAAM,IAAI,OAAO;AAAA,IACf;AAAA,IACA,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,WAAW;AAAA;AAGb,QAAM,IAAI,UAAU;AAAA,IAClB;AAAA,IACA,KAAK;AAAA,IACL,QAAQ;AAAA;AAGV,QAAM,IAAI,KAAK;AAAA,IACb;AAAA,IACA,QAAQ;AAAA;AAAA;MAaC,4CAA4C,OAAO;AAAA,EAC9D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,MAC4B;AAC5C,QAAM,UAAU,YAAY;AAC1B,QAAI;AACF,YAAM,OAAO,MAAM,uBAAuB;AAAA,QACxC,WAAW;AAAA,UAQT,UAAU,CAAC;AAAA;AAAA,QAEb;AAAA,QACA,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,wBAAwB,CAAE,QAAQ,MAAM,UAAU;AAAA,QAClD,cAAc;AAAA,QACd,gBAAgB;AAAA,QAChB,+BAA+B;AAAA,UAC7B,iCAAiC;AAAA,UACjC,4BAA4B;AAAA;AAAA;AAAA,aAGzB,GAAP;AACA,UACE,EAAE,QAAQ,SACR,gFAEF;AACA,eAAO,KACL;AAAA,aAEG;AACL,cAAM;AAAA;AAAA;AAAA;AAKZ,MAAI;AACF,UAAM;AAAA,WACC,GAAP;AACA,QAAI,CAAC,EAAE,QAAQ,SAAS,qBAAqB;AAC3C,YAAM;AAAA;AAIR,UAAM,IAAI,QAAQ,aAAW,WAAW,SAAS;AACjD,UAAM;AAAA;AAAA;;MC7JG,yBAAyB,CACpC,eACA,eACG;AACH,MAAI,YAAY;AACd,UAAM,aAAaC,eAAc,YAAY,QAC3C,qBACA;AAEF,WAAOC,UAAS,eAAe;AAAA;AAEjC,SAAO;AAAA;MAWI,eAAe,CAC1B,SACA,iBACa;AA7Cf;AA8CE,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,WAAW;AAAA,WACrB,OAAP;AACA,UAAM,IAAInB,kBACR,6CAA6C,YAAY;AAAA;AAG7D,QAAM,OAAO,OAAO;AACpB,QAAM,QAAQ,aAAO,aAAa,IAAI,aAAxB,YAAoC;AAClD,QAAM,eAAe,aAAO,aAAa,IAAI,oBAAxB,YAA2C;AAChE,QAAM,YAAY,aAAO,aAAa,IAAI,iBAAxB,YAAwC;AAC1D,QAAM,UAAU,aAAO,aAAa,IAAI,eAAxB,YAAsC;AAEtD,QAAM,OAAO,mBAAa,OAAO,UAApB,mBAA2B;AAExC,MAAI,CAAC,MAAM;AACT,UAAM,IAAIA,kBACR,kDAAkD;AAAA;AAItD,MAAI,SAAS,aAAa;AACxB,QAAI,SAAS,iBAAiB;AAC5B,UAAI,CAAC,WAAW;AACd,cAAM,IAAIA,kBACR,yCAAyC;AAAA;AAAA;AAI/C,QAAI,CAAC,SAAS;AACZ,YAAM,IAAIA,kBACR,yCAAyC;AAAA;AAAA,SAGxC;AACL,QAAI,CAAC,OAAO;AACV,YAAM,IAAIA,kBACR,yCAAyC;AAAA;AAAA;AAK/C,QAAM,OAAO,OAAO,aAAa,IAAI;AACrC,MAAI,CAAC,MAAM;AACT,UAAM,IAAIA,kBACR,yCAAyC;AAAA;AAI7C,SAAO,CAAE,MAAM,OAAO,MAAM,cAAc,WAAW;AAAA;;kCCvEd,SAGtC;AACD,QAAM,CAAE,cAAc,UAAW;AAEjC,SAAO,qBAKJ;AAAA,IACD,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA;AAAA,UAEf,YAAY;AAAA,YACV,OACE;AAAA,YACF,MAAM;AAAA;AAAA;AAAA;AAAA,MAIZ,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,UACV,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AACjB,YAAM,CAAE,SAAS,gBAAgB,YAAa,IAAI;AAElD,YAAM,CAAE,OAAO,MAAM,MAAM,gBAAiB,aAC1C,SACA;AAGF,UAAI,CAAC,cAAc;AACjB,cAAM,IAAIA,kBACR,+DAA+D,IAAI,MAAM;AAAA;AAI7E,YAAM,oBAAoB,aAAa,MAAM,OAAO;AAEpD,UAAI,CAAC,mBAAmB;AACtB,cAAM,IAAIA,kBACR,kDAAkD;AAAA;AAGtD,UAAI,CAAC,kBAAkB,OAAO,OAAO;AACnC,cAAM,IAAIA,kBAAW,2CAA2C;AAAA;AAElE,YAAM,cAAcoB,iDAClB,kBAAkB,OAAO;AAG3B,YAAM,SAAS,IAAIC,0BAAO,WAAW,QAAQ,gBAAgB;AAC7D,YAAM,SAAS,MAAM,OAAO;AAC5B,YAAM,gBAA4C,CAAE,MAAM;AAC1D,YAAM,eAAe,MAAM,OAAO,iBAAiB,eAAe;AAElE,UAAI,CAAC,cAAc;AACjB,cAAM,IAAIrB,kBACR,qDAAqD,yBAAyB,kBAAkB;AAAA;AAAA;AAIpG,YAAM,YAAY,aAAa;AAE/B,UAAI,CAAC,WAAW;AACd,cAAM,IAAIA,kBACR;AAAA;AAMJ,YAAM,kBAAkB;AAExB,YAAM,gBAAgB;AAAA,QACpB,MAAM,OAAO,kBAAkB;AAAA,QAC/B,OAAO,OAAO,kBAAkB;AAAA;AAGlC,YAAM,gBAAgB;AAAA,QACpB,KAAK,uBAAuB,IAAI,eAAe,IAAI,MAAM;AAAA,QACzD;AAAA,QACA;AAAA,QACA,MAAM;AAAA,UACJ,UAAU;AAAA,UACV,UAAU,kBAAkB,OAAO;AAAA;AAAA,QAErC,QAAQ,IAAI;AAAA,QACZ,eAAe,OAAO,kBACpB;AAAA,QAEF;AAAA;AAGF,UAAI,OAAO,aAAa;AACxB,UAAI,OAAO,mBAAmB;AAAA;AAAA;AAAA;;AC5HpC,MAAM,iCAAiC,OAAO,SAOxC;AACJ,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAEJ,QAAM,UAAuB;AAAA,IAC3B,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACnB,KAAK;AAAA,MACL;AAAA,MACA,YAAY,mBAAmB;AAAA,MAC/B,SAAS,CAAE,KAAK;AAAA;AAAA,IAElB,SAAS;AAAA,MACP,eAAe;AAAA,MACf,gBAAgB;AAAA;AAAA;AAIpB,MAAI;AACJ,MAAI;AACF,eAAW,MAAMsB,0BACf,8CAA8C,aAAa,QAC3D;AAAA,WAEK,GAAP;AACA,UAAM,IAAI,MAAM,gCAAgC;AAAA;AAGlD,MAAI,SAAS,WAAW,KAAK;AAC3B,UAAM,IAAI,MACR,gCAAgC,SAAS,UACvC,SAAS,eACN,MAAM,SAAS;AAAA;AAIxB,QAAM,IAAI,MAAM,SAAS;AACzB,MAAI,YAAY;AAChB,aAAW,QAAQ,EAAE,MAAM,OAAO;AAChC,QAAI,KAAK,SAAS,SAAS;AACzB,kBAAY,KAAK;AAAA;AAAA;AAKrB,QAAM,kBAAkB,GAAG,EAAE,MAAM,KAAK;AACxC,SAAO,CAAE,WAAW;AAAA;AAGtB,MAAM,kCAAkC,OAAO,SAQzC;AACJ,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAEJ,MAAI;AACJ,QAAM,UAAuB;AAAA,IAC3B,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACnB,MAAM;AAAA,MACN;AAAA,MACA,QAAQ,mBAAmB;AAAA;AAAA,IAE7B,SAAS;AAAA,MACP,eAAe;AAAA,MACf,gBAAgB;AAAA;AAAA;AAIpB,MAAI;AACF,UAAM,UAAU,aAAa,aAAa,WAAW;AACrD,eAAW,MAAMA,0BAAM,GAAG,oBAAoB,iBAAiB;AAAA,WACxD,GAAP;AACA,UAAM,IAAI,MAAM,gCAAgC;AAAA;AAGlD,MAAI,SAAS,WAAW,KAAK;AAC3B,UAAM,IAAI,MACR,gCAAgC,SAAS,UACvC,SAAS,eACN,MAAM,SAAS;AAAA;AAIxB,QAAM,IAAI,MAAM,SAAS;AACzB,MAAI,YAAY;AAChB,aAAW,QAAQ,EAAE,MAAM,OAAO;AAChC,QAAI,KAAK,SAAS,QAAQ;AACxB,kBAAY,KAAK;AAAA;AAAA;AAIrB,QAAM,kBAAkB,GAAG,EAAE,MAAM,KAAK,GAAG;AAC3C,SAAO,CAAE,WAAW;AAAA;AAGtB,MAAM,yBAAyB,CAAC,WAAuC;AACrE,MAAI,OAAO,YAAY,OAAO,aAAa;AACzC,UAAM,SAAS,OAAO,KACpB,GAAG,OAAO,YAAY,OAAO,eAC7B;AAGF,WAAO,SAAS,OAAO,SAAS;AAAA;AAGlC,MAAI,OAAO,OAAO;AAChB,WAAO,UAAU,OAAO;AAAA;AAG1B,QAAM,IAAI,MACR;AAAA;AAIJ,MAAM,mBAAmB,OAAO,SAK1B;AACJ,QAAM,CAAE,eAAe,MAAM,SAAS,QAAS;AAE/C,QAAM,UAAuB;AAAA,IAC3B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,eAAe;AAAA;AAAA;AAInB,QAAM,CAAE,IAAI,QAAQ,cAAe,MAAMA,0BACvC,WAAW,oCAAoC,iBAAiB,gBAChE;AAGF,MAAI,CAAC;AACH,UAAM,IAAI,MACR,2CAA2C,WAAW;AAAA;sCAIf,SAG1C;AACD,QAAM,CAAE,cAAc,UAAW;AAEjC,SAAO,qBAOJ;AAAA,IACD,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,gBAAgB;AAAA,YACd,OAAO;AAAA,YACP,MAAM;AAAA,YACN,MAAM,CAAC,WAAW;AAAA;AAAA,UAEpB,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA;AAAA,UAEf,YAAY;AAAA,YACV,OACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,WAAW;AAAA,YACT,OACE;AAAA,YACF,MAAM;AAAA;AAAA;AAAA;AAAA,MAIZ,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,UACV,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AApQvB;AAqQM,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,YAAY;AAAA,UACV,IAAI;AAER,YAAM,CAAE,WAAW,SAAS,MAAM,QAAS,aACzC,SACA;AAIF,UAAI,SAAS,iBAAiB;AAC5B,YAAI,CAAC,WAAW;AACd,gBAAM,IAAItB,kBACR,+DAA+D,IAAI,MAAM;AAAA;AAAA;AAM/E,UAAI,CAAC,SAAS;AACZ,cAAM,IAAIA,kBACR,+DAA+D,IAAI,MAAM;AAAA;AAI7E,YAAM,oBAAoB,aAAa,UAAU,OAAO;AAExD,UAAI,CAAC,mBAAmB;AACtB,cAAM,IAAIA,kBACR,kDAAkD;AAAA;AAItD,YAAM,gBAAgB,uBAAuB,kBAAkB;AAC/D,YAAM,aAAa,kBAAkB,OAAO;AAE5C,YAAM,eACJ,SAAS,kBACL,iCACA;AAEN,YAAM,CAAE,WAAW,mBAAoB,MAAM,aAAa;AAAA,QACxD;AAAA,QACA;AAAA,QACA,WAAW,aAAa;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAGF,YAAM,gBAAgB;AAAA,QACpB,MAAM,OAAO,kBAAkB;AAAA,QAC/B,OAAO,OAAO,kBAAkB;AAAA;AAGlC,YAAM,gBAAgB;AAAA,QACpB,KAAK,uBAAuB,IAAI,eAAe,IAAI,MAAM;AAAA,QACzD;AAAA,QACA,MAAM;AAAA,UACJ,UAAU,kBAAkB,OAAO,WAC/B,kBAAkB,OAAO,WACzB;AAAA,UACJ,UAAU,kBAAkB,OAAO,cAC/B,kBAAkB,OAAO,cACzB,wBAAkB,OAAO,UAAzB,YAAkC;AAAA;AAAA,QAExC;AAAA,QACA,QAAQ,IAAI;AAAA,QACZ,eAAe,OAAO,kBACpB;AAAA,QAEF;AAAA;AAGF,UAAI,aAAa,SAAS,iBAAiB;AACzC,cAAM,iBAAiB,CAAE,eAAe,MAAM,SAAS;AAAA;AAGzD,UAAI,OAAO,aAAa;AACxB,UAAI,OAAO,mBAAmB;AAAA;AAAA;AAAA;;mCC9TM;AACxC,SAAO,qBAAuC;AAAA,IAC5C,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,MAAM;AAAA,YACJ,OAAO;AAAA,YACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AACjB,YAAM,OAAEuB,UAAS,IAAI;AAErB,YAAM,SAAS,MAAMrB,uBAAG,WAAWqB;AACnC,UAAI,QAAQ;AACV,cAAM,IAAIvB,kBAAW;AAAA;AAEvB,YAAME,uBAAG,UAAUsB,aAAQD;AAC3B,YAAMrB,uBAAG,KAAK,IAAI,eAAeqB;AAAA;AAAA;AAAA;;sBClBV;AAAA,EAI3B,YAAY,cAAsC;AAChD,SAAK,eAAe;AACpB,SAAK,uBAAuB,IAAI,IAC9B,aAAa,OAAO,OAAO,IAAI,mBAAe;AAC5C,YAAM,WAAWE,sCAA0B,OAAOC,cAAY;AAC9D,aAAO,CAACA,cAAY,OAAO,MAAM;AAAA;AAAA;AAAA,QAUjC,WAAW,SAA8C;AArDjE;AAsDI,UAAM,CAAE,OAAO,MAAM,QAAS,aAAa,SAAS,KAAK;AAEzD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI1B,kBAAW,8BAA8B;AAAA;AAGrD,UAAM,oBAAoB,WAAK,aAAa,OAAO,OAAO,UAAhC,mBAAuC;AAEjE,QAAI,CAAC,mBAAmB;AACtB,YAAM,IAAIA,kBAAW,2BAA2B;AAAA;AAGlD,UAAM,sBAAsB,KAAK,qBAAqB,IAAI;AAE1D,QAAI,CAAC,qBAAqB;AACxB,YAAM,IAAIA,kBACR,oCAAoC;AAAA;AAMxC,UAAM,CAAE,SAAU,MAAM,oBAAoB,eAAe;AAAA,MACzD,KAAK,WAAW,QAAQ,mBAAmB,UAAU,mBACnD;AAAA;AAIJ,QAAI,CAAC,OAAO;AACV,YAAM,IAAIA,kBACR,gCAAgC,oBAAoB,mBAAmB;AAAA;AAI3E,UAAM,SAAS,IAAI2B,aAAQ;AAAA,MACzB,MAAM;AAAA,MACN,SAAS,kBAAkB;AAAA,MAC3B,UAAU,CAAC;AAAA;AAGb,WAAO,CAAE,QAAQ,OAAO,OAAO;AAAA;AAAA;;mCClEO,SAGvC;AACD,QAAM,CAAE,cAAc,UAAW;AACjC,QAAM,kBAAkB,IAAI,gBAAgB;AAE5C,SAAO,qBAUJ;AAAA,IACD,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,yBAAyB;AAAA,YACvB,OACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,gBAAgB;AAAA,YACd,OAAO;AAAA,YACP,MAAM;AAAA,YACN,MAAM,CAAC,WAAW,UAAU;AAAA;AAAA,UAE9B,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA;AAAA,UAEf,YAAY;AAAA,YACV,OACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,eAAe;AAAA,YACb,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA,YACN,OAAO;AAAA,cACL,MAAM;AAAA,cACN,UAAU,CAAC,YAAY;AAAA,cACvB,YAAY;AAAA,gBACV,QAAQ;AAAA,kBACN,MAAM;AAAA,kBACN,aAAa;AAAA,kBACb,MAAM,CAAC,QAAQ,QAAQ,SAAS,YAAY;AAAA;AAAA,gBAE9C,UAAU;AAAA,kBACR,MAAM;AAAA,kBACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,UAKrB,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,YACN,OAAO;AAAA,cACL,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAKd,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,UACV,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AACjB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA,0BAA0B;AAAA,QAC1B,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,QAChB;AAAA,QACA;AAAA,UACE,IAAI;AAER,YAAM,CAAE,QAAQ,OAAO,OAAO,QAAS,MAAM,gBAAgB,WAC3D;AAGF,YAAM,OAAO,MAAM,OAAO,MAAM,cAAc;AAAA,QAC5C,UAAU;AAAA;AAGZ,YAAM,sBACJ,KAAK,KAAK,SAAS,iBACf,OAAO,MAAM,YAAY;AAAA,QACvB,MAAM;AAAA,QACN,KAAK;AAAA,QACL,SAAS,mBAAmB;AAAA,QAC5B,YAAY;AAAA,QACZ;AAAA,WAEF,OAAO,MAAM,2BAA2B;AAAA,QACtC,MAAM;AAAA,QACN,SAAS,mBAAmB;AAAA,QAC5B;AAAA;AAGR,YAAM,CAAE,MAAM,WAAY,MAAM;AAChC,UAAI,iCAAQ,WAAW,GAAG,WAAW;AACnC,cAAM,GAAG,QAAQ,OAAO,MAAM;AAC9B,cAAM,OAAO,MAAM,gCAAgC;AAAA,UACjD,KAAK;AAAA,UACL,WAAW;AAAA,UACX;AAAA,UACA;AAAA,UACA,YAAY;AAAA;AAAA,iBAGL,UAAU,WAAW,OAAO;AACrC,cAAM,OAAO,MAAM,gBAAgB;AAAA,UACjC;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV,YAAY;AAAA;AAAA;AAIhB,UAAI,eAAe;AACjB,mBAAW;AAAA,UACT,QAAQ;AAAA,UACR,UAAU;AAAA,aACP,eAAe;AAClB,cAAI;AACF,kBAAM,OAAO,MAAM,gCAAgC;AAAA,cACjD,KAAK;AAAA,cACL;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA;AAAA,mBAEK,GAAP;AACA,gBAAI,OAAO,KACT,YAAY,yBAAyB,cAAc,EAAE;AAAA;AAAA;AAAA;AAM7D,UAAI,QAAQ;AACV,YAAI;AACF,gBAAM,OAAO,MAAM,iBAAiB;AAAA,YAClC;AAAA,YACA;AAAA,YACA,OAAO,OAAO,IAAI,OAAK,EAAE;AAAA;AAAA,iBAEpB,GAAP;AACA,cAAI,OAAO,KAAK,mBAAmB,OAAO,KAAK,SAAS,EAAE;AAAA;AAAA;AAI9D,YAAM,YAAY,QAAQ;AAC1B,YAAM,kBAAkB,GAAG,QAAQ,iBAAiB;AAEpD,YAAM,gBAAgB;AAAA,QACpB,MAAM,OAAO,kBAAkB;AAAA,QAC/B,OAAO,OAAO,kBAAkB;AAAA;AAGlC,YAAM,gBAAgB;AAAA,QACpB,KAAK,uBAAuB,IAAI,eAAe,IAAI,MAAM;AAAA,QACzD;AAAA,QACA;AAAA,QACA,MAAM;AAAA,UACJ,UAAU;AAAA,UACV,UAAU;AAAA;AAAA,QAEZ,QAAQ,IAAI;AAAA,QACZ,eAAe,OAAO,kBACpB;AAAA,QAEF;AAAA;AAGF,UAAI;AACF,cAAM,0CAA0C;AAAA,UAC9C;AAAA,UACA;AAAA,UACA,UAAU,QAAQ;AAAA,UAClB,QAAQ,IAAI;AAAA,UACZ;AAAA,UACA;AAAA;AAAA,eAEK,GAAP;AACA,YAAI,OAAO,KACT,2CAA2C,QAAQ,UAAU,EAAE;AAAA;AAInE,UAAI,OAAO,aAAa;AACxB,UAAI,OAAO,mBAAmB;AAAA;AAAA;AAAA;;ACnOpC,kCAAkCC,uBAAgB;AAAA;MAgCrC,uBAAuB,OAAO;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,MAC8C;AApEvD;AAqEE,QAAM,oBAAoB,mBAAa,OAAO,OAAO,UAA3B,mBAAkC;AAE5D,MAAI,CAAC,mBAAmB;AACtB,UAAM,IAAI5B,kBAAW,2BAA2B;AAAA;AAGlD,QAAM,sBACJyB,sCAA0B,OAAO;AAEnC,MAAI,CAAC,qBAAqB;AACxB,UAAM,IAAIzB,kBACR,oCAAoC;AAAA;AAIxC,QAAM,CAAE,SAAU,MAAM,oBAAoB,eAAe;AAAA,IACzD,KAAK,WAAW,QAAQ,mBAAmB,UAAU,mBACnD;AAAA;AAIJ,MAAI,CAAC,OAAO;AACV,UAAM,IAAIA,kBACR,gCAAgC,oBAAoB,mBAAmB;AAAA;AAI3E,QAAM,YAAY2B,aAAQ,OAAOE;AAEjC,SAAO,IAAI,UAAU;AAAA,IACnB,MAAM;AAAA,IACN,SAAS,kBAAkB;AAAA;AAAA;MASlB,uCAAuC,CAAC;AAAA,EACnD;AAAA,EACA,gBAAgB;AAAA,MAC0B;AAC1C,SAAO,qBAAmD;AAAA,IACxD,IAAI;AAAA,IACJ,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,UAAU,CAAC,WAAW,SAAS,eAAe;AAAA,QAC9C,MAAM;AAAA,QACN,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,YAAY;AAAA,YACV,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA;AAAA,UAEf,OAAO;AAAA,YACL,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA;AAAA,UAEf,aAAa;AAAA,YACX,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA;AAAA,UAEf,YAAY;AAAA,YACV,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aACE;AAAA;AAAA,UAEJ,YAAY;AAAA,YACV,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA;AAAA;AAAA;AAAA,MAInB,QAAQ;AAAA,QACN,UAAU,CAAC;AAAA,QACX,MAAM;AAAA,QACN,YAAY;AAAA,UACV,WAAW;AAAA,YACT,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,UAKf,QAAQ,KAAK;AACjB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,UACE,IAAI;AAER,YAAM,CAAE,OAAO,MAAM,QAAS,aAAa,SAAS;AAEpD,UAAI,CAAC,OAAO;AACV,cAAM,IAAI7B,kBACR,+BAA+B,kBAAkB;AAAA;AAIrD,YAAM,SAAS,MAAM,cAAc,CAAE,cAAc,MAAM,OAAO;AAChE,YAAM,WAAW,aACbuB,yBAAK,QAAQ,IAAI,eAAe,cAChC,IAAI;AAER,YAAM,iBAAiB,MAAMX,2BAAO,CAAC,QAAQ,WAAW,UAAU;AAAA,QAChE,KAAK;AAAA,QACL,WAAW;AAAA,QACX,KAAK;AAAA;AAGP,YAAM,eAAe,MAAM,QAAQ,IACjC,eAAe,IAAI,OAAKkB,YAASP,yBAAK,QAAQ,UAAU;AAG1D,YAAM,gBAAgB,eAAe,IAAI,kBAAgB;AACvD,eAAO,aAAa,GAAG,cAAc,iBAAiB;AAAA;AAGxD,YAAM,UAAU;AAAA,QACd;AAAA,UACE,OAAOQ,iBACL,eACA,aAAa,IAAI,SAAO,IAAI;AAAA,UAE9B,QAAQ;AAAA;AAAA;AAIZ,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,kBAAkB;AAAA,UAC9C;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,MAAM;AAAA;AAGR,YAAI,CAAC,UAAU;AACb,gBAAM,IAAI,oBAAoB;AAAA;AAGhC,YAAI,OAAO,aAAa,SAAS,KAAK;AAAA,eAC/B,GAAP;AACA,cAAM,IAAI,oBAAoB,gCAAgC;AAAA;AAAA;AAAA;AAAA;;mCC5M5B,SAGvC;AACD,QAAM,CAAE,cAAc,UAAW;AAEjC,SAAO,qBAKJ;AAAA,IACD,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC;AAAA,QACX,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,gBAAgB;AAAA,YACd,OAAO;AAAA,YACP,MAAM;AAAA,YACN,MAAM,CAAC,WAAW,UAAU;AAAA;AAAA,UAE9B,eAAe;AAAA,YACb,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA;AAAA,UAEf,YAAY;AAAA,YACV,OACE;AAAA,YACF,MAAM;AAAA;AAAA;AAAA;AAAA,MAIZ,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,UACV,WAAW;AAAA,YACT,OAAO;AAAA,YACP,MAAM;AAAA;AAAA,UAER,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AACjB,YAAM;AAAA,QACJ;AAAA,QACA,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,UACd,IAAI;AAER,YAAM,CAAE,OAAO,MAAM,QAAS,aAAa,SAAS;AAEpD,UAAI,CAAC,OAAO;AACV,cAAM,IAAI/B,kBACR,+BAA+B,kBAAkB;AAAA;AAIrD,YAAM,oBAAoB,aAAa,OAAO,OAAO;AAErD,UAAI,CAAC,mBAAmB;AACtB,cAAM,IAAIA,kBACR,kDAAkD;AAAA;AAItD,UAAI,CAAC,kBAAkB,OAAO,OAAO;AACnC,cAAM,IAAIA,kBAAW,+BAA+B;AAAA;AAGtD,YAAM,SAAS,IAAIgC,YAAO;AAAA,QACxB,MAAM,kBAAkB,OAAO;AAAA,QAC/B,OAAO,kBAAkB,OAAO;AAAA;AAGlC,UAAI,CAAE,IAAI,mBAAqB,MAAM,OAAO,WAAW,KAAK;AAI5D,UAAI,CAAC,iBAAiB;AACpB,cAAM,CAAE,MAAQ,MAAM,OAAO,MAAM;AAGnC,0BAAkB;AAAA;AAGpB,YAAM,CAAE,oBAAqB,MAAM,OAAO,SAAS,OAAO;AAAA,QACxD,cAAc;AAAA,QACd,MAAM;AAAA,QACN,YAAY;AAAA;AAGd,YAAM,YAAa,iBAA4B,QAAQ,UAAU;AACjE,YAAM,kBAAkB,GAAG,oBAAoB;AAE/C,YAAM,gBAAgB;AAAA,QACpB,MAAM,OAAO,kBAAkB;AAAA,QAC/B,OAAO,OAAO,kBAAkB;AAAA;AAGlC,YAAM,gBAAgB;AAAA,QACpB,KAAK,uBAAuB,IAAI,eAAe,IAAI,MAAM;AAAA,QACzD,WAAW;AAAA,QACX;AAAA,QACA,MAAM;AAAA,UACJ,UAAU;AAAA,UACV,UAAU,kBAAkB,OAAO;AAAA;AAAA,QAErC,QAAQ,IAAI;AAAA,QACZ,eAAe,OAAO,kBACpB;AAAA,QAEF;AAAA;AAGF,UAAI,OAAO,aAAa;AACxB,UAAI,OAAO,mBAAmB;AAAA;AAAA;AAAA;;2CCrIc,SAE/C;AACD,QAAM,CAAE,gBAAiB;AACzB,QAAM,kBAAkB,IAAI,gBAAgB;AAE5C,SAAO,qBAIJ;AAAA,IACD,IAAI;AAAA,IACJ,aACE;AAAA,IACF,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC,WAAW,cAAc;AAAA,QACpC,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,YAAY;AAAA,YACV,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,iBAAiB;AAAA,YACf,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,UAKR,QAAQ,KAAK;AACjB,YAAM,CAAE,SAAS,YAAY,mBAAoB,IAAI;AAErD,UAAI,OAAO,KACT,wBAAwB,uBAAuB,cAAc;AAG/D,YAAM,CAAE,QAAQ,OAAO,QAAS,MAAM,gBAAgB,WAAW;AAEjE,YAAM,OAAO,KAAK,QAAQ,uBAAuB;AAAA,QAC/C;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb,KAAK;AAAA;AAGP,UAAI,OAAO,KAAK,YAAY;AAAA;AAAA;AAAA;;mCCnDQ,SAGvC;AACD,QAAM,CAAE,cAAc,wBAAyB;AAC/C,QAAM,kBAAkB,IAAI,gBAAgB;AAC5C,QAAM,aAAaC,2BAAkB,OAAO,WAAS,CAAC,MAAM,SAAS;AAErE,SAAO,qBAQJ;AAAA,IACD,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,CAAC,WAAW;AAAA,QACtB,YAAY;AAAA,UACV,SAAS;AAAA,YACP,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,YAAY;AAAA,YACV,OAAO;AAAA,YACP,aAAa;AAAA,YACb,MAAM;AAAA;AAAA,UAER,eAAe;AAAA,YACb,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA;AAAA,UAER,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,aACE;AAAA,YACF,MAAM;AAAA,YACN,OAAO;AAAA,cACL;AAAA,gBACE,OAAO;AAAA,kBACL,MAAM;AAAA,kBACN,MAAM;AAAA;AAAA;AAAA,cAGV;AAAA,gBACE,OAAO;AAAA,kBACL,MAAM;AAAA,kBACN,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,UAKf,QAAQ;AAAA,YACN,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA;AAAA,UAEf,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,YACN,MAAM,CAAC,QAAQ;AAAA,YACf,aAAa;AAAA;AAAA,UAEf,aAAa;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,UAKf,QAAQ,KAAK;AACjB,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,gBAAgB;AAAA,QAChB,SAAS,CAAC;AAAA,QACV,SAAS;AAAA,QACT,cAAc;AAAA,QACd,cAAc;AAAA,UACZ,IAAI;AAER,UAAI,OAAO,KAAK,oBAAoB,uBAAuB;AAE3D,YAAM,CAAE,QAAQ,OAAO,QAAS,MAAM,gBAAgB,WAAW;AAEjE,UAAI;AACF,cAAM,eAAe,cAAc,MAAM;AACzC,cAAM,OAAO,MAAM,cAAc;AAAA,UAC/B;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,YACN,KAAK;AAAA,YACL,cAAc;AAAA,YACd,QAAQ;AAAA,YACR;AAAA;AAAA,UAEF;AAAA,UACA;AAAA;AAEF,YAAI,OAAO,KAAK,YAAY;AAAA,eACrB,GAAP;AACA,YAAI,OAAO,KACT,2BAA2B,yBAAyB,UAAU,EAAE;AAAA;AAAA;AAAA;AAAA;;MCzF7D,uBAAuB,CAAC,YAM/B;AACJ,QAAM,CAAE,QAAQ,cAAc,iBAAiB,eAAe,UAC5D;AAEF,SAAO;AAAA,IACL,uBAAuB;AAAA,MACrB;AAAA,MACA;AAAA;AAAA,IAEFC,wEAA8B;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA;AAAA,IAEF,0BAA0B;AAAA,MACxB;AAAA,MACA;AAAA;AAAA,IAEF,0BAA0B;AAAA,MACxB;AAAA,MACA;AAAA;AAAA,IAEF,qCAAqC;AAAA,MACnC;AAAA;AAAA,IAEF,0BAA0B;AAAA,MACxB;AAAA,MACA;AAAA;AAAA,IAEF,6BAA6B;AAAA,MAC3B;AAAA,MACA;AAAA;AAAA,IAEF,yBAAyB;AAAA,MACvB;AAAA,MACA;AAAA;AAAA,IAEF;AAAA,IACA,4BAA4B,CAAE,eAAe;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA,kCAAkC;AAAA,MAChC;AAAA;AAAA,IAEF,0BAA0B;AAAA,MACxB;AAAA;AAAA;AAAA;;6BC7E8B;AAAA,EAA7B,cAnBP;AAoBmB,mBAAU,IAAI;AAAA;AAAA,EAE/B,SAAuC,QAAoC;AACzE,QAAI,KAAK,QAAQ,IAAI,OAAO,KAAK;AAC/B,YAAM,IAAIC,qBACR,4BAA4B,OAAO;AAAA;AAGvC,SAAK,QAAQ,IAAI,OAAO,IAAI;AAAA;AAAA,EAG9B,IAAI,UAAuC;AACzC,UAAM,SAAS,KAAK,QAAQ,IAAI;AAChC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAIC,qBACR,4BAA4B;AAAA;AAGhC,WAAO;AAAA;AAAA,EAGT,OAA8B;AAC5B,WAAO,CAAC,GAAG,KAAK,QAAQ;AAAA;AAAA;;0BCnBK;AAAA,EAC/B,YAA6B,eAA2B;AAA3B;AAAA;AAAA,QAOvB,aACJ,cACA,SACgC;AAChC,UAAM,CAAE,OAAO,aAAe,MAAM,KAAK,cAAc,YACrD;AAAA,MACE,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,iBAAiB;AAAA;AAAA,OAGrB;AAGF,QAAI,UAAU,WAAW,GAAG;AAC1B,UAAI,UAAU,SAAS,GAAG;AACxB,cAAM,IAAID,qBACR;AAAA,aAEG;AACL,cAAM,IAAIC,qBAAc;AAAA;AAAA;AAI5B,WAAO,UAAU;AAAA;AAAA;;ACrBrB,MAAM,gBAAgBC,iCACpB,wCACA;wBAoBkD;AAAA,EAQlD,YAA6B,IAAU;AAAV;AAAA;AAAA,eAPhB,OAAO,MAAwC;AAC1D,UAAM,KAAK,QAAQ,OAAO;AAAA,MACxB,WAAW;AAAA;AAEb,WAAO,IAAI,kBAAkB;AAAA;AAAA,QAKzB,QAAQ,QAAoC;AAChD,UAAM,CAAC,UAAU,MAAM,KAAK,GAAiB,SAC1C,MAAM,CAAE,IAAI,SACZ;AACH,QAAI,CAAC,QAAQ;AACX,YAAM,IAAID,qBAAc,oBAAoB;AAAA;AAE9C,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,YAAM,UAAU,OAAO,UAAU,KAAK,MAAM,OAAO,WAAW;AAC9D,aAAO;AAAA,QACL,IAAI,OAAO;AAAA,QACX;AAAA,QACA,QAAQ,OAAO;AAAA,QACf,iBAAiB,OAAO;AAAA,QACxB,WAAW,OAAO;AAAA,QAClB;AAAA;AAAA,aAEK,OAAP;AACA,YAAM,IAAI,MAAM,iCAAiC,YAAY;AAAA;AAAA;AAAA,QAI3D,WACJ,MACA,SAC6B;AAC7B,UAAM,SAASE;AACf,UAAM,KAAK,GAAiB,SAAS,OAAO;AAAA,MAC1C,IAAI;AAAA,MACJ,MAAM,KAAK,UAAU;AAAA,MACrB,SAAS,UAAU,KAAK,UAAU,WAAW;AAAA,MAC7C,QAAQ;AAAA;AAEV,WAAO,CAAE;AAAA;AAAA,QAGL,YAA4C;AAChD,WAAO,KAAK,GAAG,YAAY,OAAM,OAAM;AACrC,YAAM,CAAC,QAAQ,MAAM,GAAiB,SACnC,MAAM;AAAA,QACL,QAAQ;AAAA,SAET,MAAM,GACN;AAEH,UAAI,CAAC,MAAM;AACT,eAAO;AAAA;AAGT,YAAM,cAAc,MAAM,GAAiB,SACxC,MAAM,CAAE,IAAI,KAAK,IAAI,QAAQ,SAC7B,OAAO;AAAA,QACN,QAAQ;AAAA,QACR,mBAAmB,KAAK,GAAG,GAAG;AAAA;AAGlC,UAAI,cAAc,GAAG;AACnB,eAAO;AAAA;AAGT,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,cAAM,UAAU,KAAK,UAAU,KAAK,MAAM,KAAK,WAAW;AAC1D,eAAO;AAAA,UACL,IAAI,KAAK;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,UACR,iBAAiB,KAAK;AAAA,UACtB,WAAW,KAAK;AAAA,UAChB;AAAA;AAAA,eAEK,OAAP;AACA,cAAM,IAAI,MAAM,iCAAiC,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA,QAK9D,cAAc,QAA+B;AACjD,UAAM,cAAc,MAAM,KAAK,GAAiB,SAC7C,MAAM,CAAE,IAAI,QAAQ,QAAQ,eAC5B,OAAO;AAAA,MACN,mBAAmB,KAAK,GAAG,GAAG;AAAA;AAElC,QAAI,gBAAgB,GAAG;AACrB,YAAM,IAAIH,qBAAc,+BAA+B;AAAA;AAAA;AAAA,QAIrD,eAAe,CAAE,WAEpB;AACD,UAAM,UAAU,MAAM,KAAK,GAAiB,SACzC,MAAM,UAAU,cAChB,SACC,qBACA,MACA,KAAK,GAAG,OAAO,OAAO,WAAW,YAC7B,KAAK,GAAG,IAAI,sBAAsB,CAAC,IAAI,uBACvC,KAAK,GAAG,IAAI,2BAA2B;AAAA,MACrC,IAAI;AAAA,MACJ,KAAK,GAAG,GAAG;AAAA;AAGrB,UAAM,QAAQ,QAAQ,IAAI;AAAQ,MAChC,QAAQ,IAAI;AAAA;AAEd,WAAO,CAAE;AAAA;AAAA,QAGL,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,KAKgB;AAChB,QAAI;AACJ,QAAI,WAAW,YAAY,WAAW,aAAa;AACjD,kBAAY;AAAA,WACP;AACL,YAAM,IAAI,MACR,iCAAiC,sBAAsB;AAAA;AAG3D,UAAM,KAAK,GAAG,YAAY,OAAM,OAAM;AACpC,YAAM,CAAC,QAAQ,MAAM,GAAiB,SACnC,MAAM;AAAA,QACL,IAAI;AAAA,SAEL,MAAM,GACN;AAEH,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,uBAAuB;AAAA;AAEzC,UAAI,KAAK,WAAW,WAAW;AAC7B,cAAM,IAAIA,qBACR,qCAAqC,sBAAsB,+BAClC,KAAK,sBAAsB;AAAA;AAGxD,YAAM,cAAc,MAAM,GAAiB,SACxC,MAAM;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,SAET,OAAO;AAAA,QACN;AAAA,QACA,SAAS;AAAA;AAEb,UAAI,gBAAgB,GAAG;AACrB,cAAM,IAAIA,qBACR,+BAA+B,sBAAsB;AAAA;AAIzD,YAAM,GAAsB,eAAe,OAAO;AAAA,QAChD,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,MAAM,KAAK,UAAU;AAAA;AAAA;AAAA;AAAA,QAKrB,aAAa,CAAE,QAAQ,OAA6C;AACxE,UAAM,gBAAgB,KAAK,UAAU;AACrC,UAAM,KAAK,GAAsB,eAAe,OAAO;AAAA,MACrD,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,MAAM;AAAA;AAAA;AAAA,QAIJ,WAAW;AAAA,IACf;AAAA,IACA;AAAA,KACmE;AACnE,UAAM,YAAY,MAAM,KAAK,GAAsB,eAChD,MAAM;AAAA,MACL,SAAS;AAAA,OAEV,SAAS,aAAW;AACnB,UAAI,OAAO,UAAU,UAAU;AAC7B,gBAAQ,MAAM,MAAM,KAAK,OAAO,QAAQ,cAAc;AAAA;AAAA,OAGzD,QAAQ,MACR;AAEH,UAAM,SAAS,UAAU,IAAI,WAAS;AACpC,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,eAAO;AAAA,UACL,IAAI,OAAO,MAAM;AAAA,UACjB;AAAA,UACA;AAAA,UACA,MAAM,MAAM;AAAA,UACZ,WACE,OAAO,MAAM,eAAe,WACxBI,eAAS,QAAQ,MAAM,YAAY,CAAE,MAAM,QAAS,UACpD,MAAM;AAAA;AAAA,eAEP,OAAP;AACA,cAAM,IAAI,MACR,gDAAgD,aAAa,MAAM,OAAO;AAAA;AAAA;AAIhF,WAAO,CAAE;AAAA;AAAA;;gBCxP0B;AAAA,EAY7B,YACW,OACA,SACA,QACjB;AAHiB;AACA;AACA;AAdX,kBAAS;AAAA;AAAA,SAIV,OAAO,OAAkB,SAAoB,QAAgB;AAClE,UAAM,QAAQ,IAAI,UAAU,OAAO,SAAS;AAC5C,UAAM;AACN,WAAO;AAAA;AAAA,MAUL,OAAO;AACT,WAAO,KAAK,MAAM;AAAA;AAAA,MAGhB,UAAU;AACZ,WAAO,KAAK,MAAM;AAAA;AAAA,QAGd,mBAAmB;AACvB,WAAO,KAAK,MAAM;AAAA;AAAA,MAGhB,OAAO;AACT,WAAO,KAAK;AAAA;AAAA,QAGR,QAAQ,SAAiB,UAAsC;AACnE,UAAM,KAAK,QAAQ,aAAa;AAAA,MAC9B,QAAQ,KAAK,MAAM;AAAA,MACnB,MAAM,CAAE,YAAY;AAAA;AAAA;AAAA,QAIlB,SACJ,QACA,UACe;AACf,UAAM,KAAK,QAAQ,aAAa;AAAA,MAC9B,QAAQ,KAAK,MAAM;AAAA,MACnB,QAAQ,WAAW,WAAW,WAAW;AAAA,MACzC,WAAW;AAAA,QACT,SAAS,8BAA8B;AAAA,WACpC;AAAA;AAAA;AAGP,SAAK,SAAS;AACd,QAAI,KAAK,oBAAoB;AAC3B,mBAAa,KAAK;AAAA;AAAA;AAAA,EAId,eAAe;AACrB,SAAK,qBAAqB,WAAW,YAAY;AAC/C,UAAI;AACF,cAAM,KAAK,QAAQ,cAAc,KAAK,MAAM;AAC5C,aAAK;AAAA,eACE,OAAP;AACA,aAAK,SAAS;AAEd,aAAK,OAAO,MACV,sBAAsB,KAAK,MAAM,iBACjC;AAAA;AAAA,OAGH;AAAA;AAAA;AAUP,iBAAiB;AACf,MAAI,UAAU,MAAM;AAAA;AACpB,QAAM,UAAU,IAAI,QAAc,cAAY;AAC5C,cAAU;AAAA;AAEZ,SAAO,CAAE,SAAS;AAAA;wBAGiC;AAAA,EACnD,YACmB,SACA,QACjB;AAFiB;AACA;AAEX,4BAAmB;AAAA;AAAA,QAErB,QAAuB;AAC3B,eAAS;AACP,YAAM,cAAc,MAAM,KAAK,QAAQ;AACvC,UAAI,aAAa;AACf,eAAO,UAAU,OACf;AAAA,UACE,QAAQ,YAAY;AAAA,UACpB,MAAM,YAAY;AAAA,UAClB,SAAS,YAAY;AAAA,WAEvB,KAAK,SACL,KAAK;AAAA;AAIT,YAAM,KAAK;AAAA;AAAA;AAAA,QAIT,SACJ,MACA,SACyB;AACzB,UAAM,UAAU,MAAM,KAAK,QAAQ,WAAW,MAAM;AACpD,SAAK;AACL,WAAO;AAAA,MACL,QAAQ,QAAQ;AAAA;AAAA;AAAA,QAId,IAAI,QAAoC;AAC5C,WAAO,KAAK,QAAQ,QAAQ;AAAA;AAAA,EAG9B,QACE,SAIA,UAIY;AACZ,UAAM,CAAE,UAAW;AAEnB,QAAI,YAAY;AAChB,UAAM,cAAc,MAAM;AACxB,kBAAY;AAAA;AAGd,IAAC,aAAY;AACX,UAAI,QAAQ,QAAQ;AACpB,aAAO,CAAC,WAAW;AACjB,cAAM,SAAS,MAAM,KAAK,QAAQ,WAAW,CAAE,QAAQ;AACvD,cAAM,CAAE,UAAW;AACnB,YAAI,OAAO,QAAQ;AACjB,kBAAQ,OAAO,OAAO,SAAS,GAAG;AAClC,cAAI;AACF,qBAAS,QAAW;AAAA,mBACb,OAAP;AACA,qBAAS,OAAO,CAAE,QAAQ;AAAA;AAAA;AAI9B,cAAM,IAAI,QAAQ,aAAW,WAAW,SAAS;AAAA;AAAA;AAIrD,WAAO;AAAA;AAAA,QAGH,YAAY,UAA+C;AAC/D,UAAM,CAAE,SAAU,MAAM,KAAK,QAAQ,eAAe;AACpD,UAAM,QAAQ,IACZ,MAAM,IAAI,OAAM,SAAQ;AACtB,UAAI;AACF,cAAM,KAAK,QAAQ,aAAa;AAAA,UAC9B,QAAQ,KAAK;AAAA,UACb,QAAQ;AAAA,UACR,WAAW;AAAA,YACT,SACE;AAAA;AAAA;AAAA,eAGC,OAAP;AACA,aAAK,OAAO,KAAK,0BAA0B,KAAK,YAAY;AAAA;AAAA;AAAA;AAAA,EAM5D,kBAAkB;AACxB,WAAO,KAAK,iBAAiB;AAAA;AAAA,EAGvB,iBAAiB;AACvB,SAAK,iBAAiB;AACtB,SAAK,mBAAmB;AAAA;AAAA;;kBCzMH,OAAqB;AAC5C,SAAOC,eAAQ,SAAS,MAAM,SAAS,IAAI,CAAC,CAAC;AAAA;;iBCevB;AAAA,EAGtB,YAA6B,SAAkB;AAAlB;AAC3B,SAAK,aAAaC,sBAAW;AAK7B,SAAK,WAAW,eAAe,gBAAgB,aAAW;AACxD,aAAO,KAAK,UAAU,aAAa,SAAS,QAAQ;AAAA;AAGtD,SAAK,WAAW,eAAe,eAAe,aAAW;AACvD,YAAM,CAAE,OAAO,QAAS,aAAa,SAAS,QAAQ;AACtD,aAAO,GAAG,SAAS;AAAA;AAGrB,SAAK,WAAW,eAAe,QAAQ,SAAO,KAAK,UAAU;AAE7D,SAAK,WAAW,eAAe,OAAO,WAAS,CAAC,SAAS;AAEzD,SAAK,WAAW,eAAe,MAAM,CAAC,GAAG,MAAM,MAAM;AAAA;AAAA,EAGvD,QAAQ;AACN,IAAC,aAAY;AACX,iBAAS;AACP,cAAM,OAAO,MAAM,KAAK,QAAQ,WAAW;AAC3C,cAAM,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA,QAKtB,WAAW,MAAY;AAzE/B;AA0EI,QAAI,gBAAoC;AACxC,QAAI;AACF,YAAM,CAAE,kBAAmB,KAAK;AAEhC,sBAAgBlB,yBAAK,KACnB,KAAK,QAAQ,kBACb,MAAM,KAAK;AAEb,YAAMrB,uBAAG,UAAU;AACnB,YAAM,KAAK,QACT,yBAAyB,KAAK,KAAK,MAAM;AAG3C,YAAM,cAKF,CAAE,YAAY,KAAK,KAAK,QAAQ,OAAO;AAE3C,iBAAW,QAAQ,KAAK,KAAK,OAAO;AAClC,cAAM,WAAW,CAAE,QAAQ,KAAK;AAChC,YAAI;AACF,gBAAM,aAAawC,mBAAQ,aAAa;AAAA,YACtC,OAAO,QAAQ,IAAI,aAAa;AAAA,YAChC,QAAQA,mBAAQ,OAAO,QACrBA,mBAAQ,OAAO,YACfA,mBAAQ,OAAO,aACfA,mBAAQ,OAAO;AAAA,YAEjB,aAAa;AAAA;AAGf,gBAAMC,WAAS,IAAI5B;AACnB,mBAAO,GAAG,QAAQ,OAAM,SAAQ;AAC9B,kBAAM,UAAU,KAAK,WAAW;AAChC,gBAAI,oCAAS,UAAS,GAAG;AACvB,oBAAM,KAAK,QAAQ,SAAS;AAAA;AAAA;AAIhC,qBAAW,IAAI,IAAI2B,mBAAQ,WAAW,OAAO,SAAEC;AAE/C,cAAI,KAAK,OAAO,QAAW;AAEzB,gBAAI,OAAO,CAAC,KAAK;AAGjB,gBAAI,OAAO,KAAK,OAAO,UAAU;AAC/B,oBAAM,YAAY,KAAK,MACrB,KAAK,UAAU,KAAK,KACpB,CAAC,MAAM,UAAU;AACf,oBAAI,OAAO,UAAU,UAAU;AAC7B,wBAAM,YAAY,KAAK,WAAW,QAAQ,OAAO;AAAA,oBAC/C,UAAU;AAAA,oBACV,MAAM;AAAA,oBACN,eAAe;AAAA,qBACd;AAGH,sBAAI,cAAc,IAAI;AACpB,2BAAO;AAAA;AAGT,sBAAI;AACF,2BAAO,KAAK,MAAM;AAAA,0BAClB;AACA,2BAAO;AAAA;AAAA;AAIX,uBAAO;AAAA;AAIX,qBAAO,CAAC,SAAS;AAAA;AAGnB,gBAAI,MAAM;AACR,oBAAM,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;AAAA,mBAC3C;AAAA,gBACH,QAAQ;AAAA;AAEV;AAAA;AAAA;AAIJ,gBAAM,KAAK,QAAQ,kBAAkB,KAAK,QAAQ;AAAA,eAC7C;AAAA,YACH,QAAQ;AAAA;AAGV,gBAAM,SAAS,eAAe,IAAI,KAAK;AACvC,cAAI,CAAC,QAAQ;AACX,kBAAM,IAAI,MAAM,WAAW,KAAK;AAAA;AAGlC,gBAAM,QACJ,KAAK,SACL,KAAK,MAAM,KAAK,UAAU,KAAK,QAAQ,CAAC,MAAM,UAAU;AACtD,gBAAI,OAAO,UAAU,UAAU;AAC7B,oBAAM,YAAY,KAAK,WAAW,QAAQ,OAAO;AAAA,gBAC/C,UAAU;AAAA,gBACV,MAAM;AAAA,gBACN,eAAe;AAAA,iBACd;AAGH,kBACG,UAAU,WAAW,QAAQ,UAAU,SAAS,QAChD,UAAU,WAAW,QAAQ,UAAU,SAAS,QAChD,UAAU,WAAW,QAAQ,UAAU,SAAS,MACjD;AACA,oBAAI;AAGF,yBAAO,KAAK,MAAM;AAAA,wBAClB;AACA,yBAAO;AAAA;AAAA;AAGX,qBAAO;AAAA;AAGT,mBAAO;AAAA;AAGX,cAAI,aAAO,WAAP,mBAAe,OAAO;AACxB,kBAAM,iBAAiBC,oBACrB,OACA,OAAO,OAAO;AAEhB,gBAAI,CAAC,eAAe,OAAO;AACzB,oBAAMC,WAAS,eAAe,OAAO,KAAK;AAC1C,oBAAM,IAAI7C,kBACR,kCAAkC,OAAO,OAAO6C;AAAA;AAAA;AAKtD,gBAAM,cAA6C;AAGnD,gBAAM,UAAU,IAAI;AAEpB,eAAK,QAAQ,OAAO,MAAM,WAAW,OAAO,iBAAiB;AAAA,YAC3D,OAAO,KAAK,UAAU,OAAO,MAAM;AAAA;AAGrC,gBAAM,OAAO,QAAQ;AAAA,YACnB,SAAS,KAAK,KAAK;AAAA,YACnB,QAAQ;AAAA,YACR,WAAWF;AAAA,YACX;AAAA,YACA,OAAO,WAAK,YAAL,mBAAc;AAAA,YACrB;AAAA,kBACM,2BAA2B;AAC/B,oBAAM,SAAS,MAAMzC,uBAAG,QACtB,GAAG,sBAAsB,KAAK;AAEhC,sBAAQ,KAAK;AACb,qBAAO;AAAA;AAAA,YAET,OAAO,MAAc,OAAkB;AACrC,0BAAY,QAAQ;AAAA;AAAA;AAKxB,qBAAW,UAAU,SAAS;AAC5B,kBAAMA,uBAAG,OAAO;AAAA;AAGlB,sBAAY,MAAM,KAAK,MAAM,CAAE,QAAQ;AAEvC,gBAAM,KAAK,QAAQ,iBAAiB,KAAK,QAAQ;AAAA,eAC5C;AAAA,YACH,QAAQ;AAAA;AAAA,iBAEH,OAAP;AACA,gBAAM,KAAK,QAAQ,OAAO,MAAM,QAAQ;AAAA,eACnC;AAAA,YACH,QAAQ;AAAA;AAEV,gBAAM;AAAA;AAAA;AAIV,YAAM,SAAS,KAAK,MAClB,KAAK,UAAU,KAAK,KAAK,SACzB,CAAC,MAAM,UAAU;AACf,YAAI,OAAO,UAAU,UAAU;AAC7B,gBAAM,YAAY,KAAK,WAAW,QAAQ,OAAO;AAAA,YAC/C,UAAU;AAAA,YACV,MAAM;AAAA,YACN,eAAe;AAAA,aACd;AAGH,cAAI,cAAc,IAAI;AACpB,mBAAO;AAAA;AAIT,cACG,UAAU,WAAW,QAAQ,UAAU,SAAS,QAChD,UAAU,WAAW,QAAQ,UAAU,SAAS,QAChD,UAAU,WAAW,QAAQ,UAAU,SAAS,MACjD;AACA,gBAAI;AAGF,qBAAO,KAAK,MAAM;AAAA,oBAClB;AACA,qBAAO;AAAA;AAAA;AAGX,iBAAO;AAAA;AAET,eAAO;AAAA;AAIX,YAAM,KAAK,SAAS,aAAa,CAAE;AAAA,aAC5B,OAAP;AACA,YAAM,KAAK,SAAS,UAAU;AAAA,QAC5B,OAAO,CAAE,MAAM,MAAM,MAAM,SAAS,MAAM;AAAA;AAAA,cAE5C;AACA,UAAI,eAAe;AACjB,cAAMA,uBAAG,OAAO;AAAA;AAAA;AAAA;AAAA;;mCCpRtB,QACA,QACiB;AACjB,MAAI,CAAC,OAAO,IAAI,6BAA6B;AAC3C,WAAO4C,uBAAG;AAAA;AAGZ,QAAM,mBAAmB,OAAO,UAAU;AAC1C,MAAI;AAEF,UAAM5C,uBAAG,OAAO,kBAAkBA,uBAAG,UAAU,OAAOA,uBAAG,UAAU;AACnE,WAAO,KAAK,4BAA4B;AAAA,WACjC,KAAP;AACA,WAAO,MACL,qBAAqB,oBACnB,IAAI,SAAS,WAAW,mBAAmB;AAG/C,UAAM;AAAA;AAER,SAAO;AAAA;0BAUwB,QAAoC;AA1DrE;AA2DE,MAAI,WAAW,aAAO,SAAS,gBAAhB,mBAA8B6C;AAC7C,MAAI,CAAC,UAAU;AACb,eAAW,aAAO,SAAS,gBAAhB,mBAA8BC;AAAA;AAE3C,MAAI,CAAC,UAAU;AACb,WAAO;AAAA;AAGT,QAAM,CAAE,MAAM,UAAWC,oCAAuB;AAChD,MAAI,SAAS,OAAO;AAClB,WAAO;AAAA,aACE,SAAS,QAAQ;AAC1B,WAAO,UAAU;AAAA;AAKnB,SAAO;AAAA;;ACxBT,yBACE,QACiC;AACjC,SAAO,OAAO,eAAe;AAAA;4BAI7B,SACyB;AACzB,QAAM,SAASC;AACf,SAAO,IAAIC,4BAAQ;AAEnB,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,MACE;AAEJ,QAAM,SAAS,aAAa,MAAM,CAAE,QAAQ;AAC5C,QAAM,mBAAmB,MAAM,oBAAoB,QAAQ;AAC3D,QAAM,eAAe,IAAI,oBAAoB;AAC7C,QAAM,eAAeC,4BAAgB,WAAW;AAEhD,QAAM,oBAAoB,MAAM,kBAAkB,OAChD,MAAM,SAAS;AAEjB,QAAM,aAAa,IAAI,kBAAkB,mBAAmB;AAC5D,QAAM,iBAAiB,IAAI;AAC3B,QAAM,UAAU;AAChB,WAAS,IAAI,GAAG,oBAAoB,IAAI,KAAK;AAC3C,UAAM,SAAS,IAAI,WAAW;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAEF,YAAQ,KAAK;AAAA;AAGf,QAAM,oBAAoB,MAAM,QAAQ,WACpC,UACA,qBAAqB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGN,oBAAkB,QAAQ,YAAU,eAAe,SAAS;AAC5D,UAAQ,QAAQ,YAAU,OAAO;AAEjC,SACG,IACC,yDACA,OAAO,KAAK,QAAQ;AAjH1B;AAkHQ,UAAM,CAAE,WAAW,MAAM,QAAS,IAAI;AAEtC,QAAI,cAAc,WAAW;AAC3B,YAAM,IAAIpD,kBACR;AAAA;AAGJ,QAAI,KAAK,kBAAkB,YAAY;AACrC,YAAM,IAAIA,kBACR;AAAA;AAIJ,UAAM,WAAW,MAAM,aAAa,aAAa,MAAM;AAAA,MACrD,OAAO,eAAe,IAAI,QAAQ;AAAA;AAEpC,QAAI,gBAAgB,WAAW;AAC7B,YAAM,aAAa,CAAC,eAAS,KAAK,eAAd,YAA4B,IAAI;AACpD,UAAI,KAAK;AAAA,QACP,OAAO,eAAS,SAAS,UAAlB,YAA2B,SAAS,SAAS;AAAA,QACpD,OAAO,WAAW,IAAI,YAAO;AAtIzC;AAsI6C;AAAA,YAC/B,OAAO,cAAO,UAAP,aAAgB;AAAA,YACvB;AAAA;AAAA;AAAA;AAAA,WAGC;AACL,YAAM,IAAIA,kBACR,kDACG,SAAoB;AAAA;AAAA,KAM9B,IAAI,eAAe,OAAO,MAAM,QAAQ;AACvC,UAAM,cAAc,eAAe,OAAO,IAAI,YAAU;AACtD,aAAO;AAAA,QACL,IAAI,OAAO;AAAA,QACX,aAAa,OAAO;AAAA,QACpB,QAAQ,OAAO;AAAA;AAAA;AAGnB,QAAI,KAAK;AAAA,KAEV,KAAK,aAAa,OAAO,KAAK,QAAQ;AA9J3C;AA+JM,UAAM,eAAuB,IAAI,KAAK;AACtC,UAAM,SAAS,IAAI,KAAK;AACxB,UAAM,QAAQ,eAAe,IAAI,QAAQ;AACzC,UAAM,WAAW,MAAM,aAAa,aAAa,cAAc;AAAA,MAC7D;AAAA;AAGF,QAAI;AAEJ,QAAI,gBAAgB,WAAW;AAC7B,iBAAW,cAAc,CAAC,eAAS,KAAK,eAAd,YAA4B,IAAI,QAAQ;AAChE,cAAM,UAASqD,oBAAS,QAAQ;AAEhC,YAAI,CAAC,QAAO,OAAO;AACjB,cAAI,OAAO,KAAK,KAAK,CAAE,QAAQ,QAAO;AACtC;AAAA;AAAA;AAIJ,YAAM,UAAU,iBAAiB;AAEjC,iBAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,OAAO,SAAS,KAAK,MAAM,IAAI,CAAC,MAAM,UAAO;AAvLvD;AAuL2D;AAAA,eAC5C;AAAA,YACH,IAAI,YAAK,OAAL,aAAW,QAAQ,QAAQ;AAAA,YAC/B,MAAM,YAAK,SAAL,aAAa,KAAK;AAAA;AAAA;AAAA,QAE1B,QAAQ,eAAS,KAAK,WAAd,YAAwB;AAAA;AAAA,WAE7B;AACL,YAAM,IAAIrD,kBACR,kDACG,SAAoB;AAAA;AAK3B,UAAM,SAAS,MAAM,WAAW,SAAS,UAAU;AAAA,MACjD;AAAA;AAGF,QAAI,OAAO,KAAK,KAAK,CAAE,IAAI,OAAO;AAAA,KAEnC,IAAI,qBAAqB,OAAO,KAAK,QAAQ;AAC5C,UAAM,CAAE,UAAW,IAAI;AACvB,UAAM,OAAO,MAAM,WAAW,IAAI;AAClC,QAAI,CAAC,MAAM;AACT,YAAM,IAAIoC,qBAAc,gBAAgB;AAAA;AAG1C,WAAO,KAAK;AACZ,QAAI,OAAO,KAAK,KAAK;AAAA,KAEtB,IAAI,iCAAiC,OAAO,KAAK,QAAQ;AACxD,UAAM,CAAE,UAAW,IAAI;AACvB,UAAM,QAAQ,OAAO,IAAI,MAAM,UAAU;AACzC,WAAO,MAAM,kCAAkC;AAG/C,QAAI,UAAU,KAAK;AAAA,MACjB,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,gBAAgB;AAAA;AAIlB,UAAM,cAAc,WAAW,QAC7B,CAAE,QAAQ,QACV,CAAC,OAAO,CAAE,YAAa;AACrB,UAAI,OAAO;AACT,eAAO,MACL,2DAA2D,YAAY;AAAA;AAI3E,UAAI,oBAAoB;AACxB,iBAAW,SAAS,QAAQ;AAC1B,YAAI,MACF,UAAU,MAAM;AAAA,QAAe,KAAK,UAAU;AAAA;AAAA;AAEhD,YAAI,MAAM,SAAS,cAAc;AAC/B,8BAAoB;AAAA;AAAA;AAKxB,UAAI;AACJ,UAAI;AAAmB;AAAA;AAK3B,QAAI,GAAG,SAAS,MAAM;AACpB;AACA,aAAO,MAAM,kCAAkC;AAAA;AAAA;AAIrD,QAAM,MAAMe;AACZ,MAAI,IAAI,UAAU;AAClB,MAAI,IAAI,KAAK;AAEb,SAAO;AAAA;AAGT,wBAAwB,QAAqC;AA1Q7D;AA2QE,SAAO,uCAAQ,MAAM,uBAAd,mBAAmC;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}