@backstage/plugin-scaffolder-backend-module-cookiecutter 0.2.2 → 0.2.4

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @backstage/plugin-scaffolder-backend-module-cookiecutter
2
2
 
3
+ ## 0.2.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 8122e27717: Updating documentation for supporting `apiVersion: scaffolder.backstage.io/v1beta3`
8
+ - e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1
9
+ - Updated dependencies
10
+ - @backstage/backend-common@0.13.0
11
+ - @backstage/plugin-scaffolder-backend@0.18.0
12
+
13
+ ## 0.2.4-next.0
14
+
15
+ ### Patch Changes
16
+
17
+ - 8122e27717: Updating documentation for supporting `apiVersion: scaffolder.backstage.io/v1beta3`
18
+ - e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1
19
+ - Updated dependencies
20
+ - @backstage/backend-common@0.13.0-next.0
21
+ - @backstage/plugin-scaffolder-backend@0.18.0-next.0
22
+
23
+ ## 0.2.3
24
+
25
+ ### Patch Changes
26
+
27
+ - 6c7a879660: Fixed bug where existing cookiecutter.json file is not used.
28
+ - Updated dependencies
29
+ - @backstage/backend-common@0.12.0
30
+ - @backstage/plugin-scaffolder-backend@0.17.3
31
+ - @backstage/integration@0.8.0
32
+
3
33
  ## 0.2.2
4
34
 
5
35
  ### Patch Changes
package/README.md CHANGED
@@ -44,7 +44,7 @@ return await createRouter({
44
44
  After that you can use the action in your template:
45
45
 
46
46
  ```yaml
47
- apiVersion: backstage.io/v1beta2
47
+ apiVersion: scaffolder.backstage.io/v1beta3
48
48
  kind: Template
49
49
  metadata:
50
50
  name: cookiecutter-demo
@@ -109,30 +109,31 @@ spec:
109
109
  input:
110
110
  url: ./template
111
111
  values:
112
- name: '{{ parameters.name }}'
113
- owner: '{{ parameters.owner }}'
114
- system: '{{ parameters.system }}'
115
- destination: '{{ parseRepoUrl parameters.repoUrl }}'
112
+ name: ${{ parameters.name }}
113
+ owner: ${{ parameters.owner }}
114
+ system: ${{ parameters.system }}
115
+ destination: ${{ parameters.repoUrl | parseRepoUrl }}
116
116
 
117
117
  - id: publish
118
- if: '{{ not parameters.dryRun }}'
118
+ if: ${{ parameters.dryRun !== true }}
119
119
  name: Publish
120
120
  action: publish:github
121
121
  input:
122
- allowedHosts: ['github.com']
123
- description: 'This is {{ parameters.name }}'
124
- repoUrl: '{{ parameters.repoUrl }}'
122
+ allowedHosts:
123
+ - github.com
124
+ description: This is ${{ parameters.name }}
125
+ repoUrl: ${{ parameters.repoUrl }}
125
126
 
126
127
  - id: register
127
- if: '{{ not parameters.dryRun }}'
128
+ if: ${{ parameters.dryRun !== true }}
128
129
  name: Register
129
130
  action: catalog:register
130
131
  input:
131
- repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}'
132
+ repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
132
133
  catalogInfoPath: '/catalog-info.yaml'
133
134
 
134
135
  - name: Results
135
- if: '{{ parameters.dryRun }}'
136
+ if: ${{ parameters.dryRun }}
136
137
  action: debug:log
137
138
  input:
138
139
  listWorkspace: true
@@ -140,10 +141,10 @@ spec:
140
141
  output:
141
142
  links:
142
143
  - title: Repository
143
- url: '{{ steps.publish.output.remoteUrl }}'
144
+ url: ${{ steps.publish.output.remoteUrl }}
144
145
  - title: Open in catalog
145
- icon: 'catalog'
146
- entityRef: '{{ steps.register.output.entityRef }}'
146
+ icon: catalog
147
+ entityRef: ${{ steps.register.output.entityRef }}
147
148
  ```
148
149
 
149
150
  You can also visit the `/create/actions` route in your Backstage application to find out more about the parameters this action accepts when it's installed to configure how you like.
package/dist/index.cjs.js CHANGED
@@ -32,17 +32,18 @@ class CookiecutterRunner {
32
32
  async run({
33
33
  workspacePath,
34
34
  values,
35
- logStream
35
+ logStream,
36
+ imageName,
37
+ templateDir,
38
+ templateContentsDir
36
39
  }) {
37
- const templateDir = path__default["default"].join(workspacePath, "template");
38
40
  const intermediateDir = path__default["default"].join(workspacePath, "intermediate");
39
41
  await fs__default["default"].ensureDir(intermediateDir);
40
42
  const resultDir = path__default["default"].join(workspacePath, "result");
41
- const cookieCutterJson = await this.fetchTemplateCookieCutter(templateDir);
42
- const { imageName, ...valuesForCookieCutterJson } = values;
43
+ const cookieCutterJson = await this.fetchTemplateCookieCutter(templateContentsDir);
43
44
  const cookieInfo = {
44
45
  ...cookieCutterJson,
45
- ...valuesForCookieCutterJson
46
+ ...values
46
47
  };
47
48
  await fs__default["default"].writeJSON(path__default["default"].join(templateDir, "cookiecutter.json"), cookieInfo);
48
49
  const mountDirs = {
@@ -147,13 +148,15 @@ function createFetchCookiecutterAction(options) {
147
148
  const values = {
148
149
  ...ctx.input.values,
149
150
  _copy_without_render: ctx.input.copyWithoutRender,
150
- _extensions: ctx.input.extensions,
151
- imageName: ctx.input.imageName
151
+ _extensions: ctx.input.extensions
152
152
  };
153
153
  await cookiecutter.run({
154
154
  workspacePath: workDir,
155
155
  logStream: ctx.logStream,
156
- values
156
+ values,
157
+ imageName: ctx.input.imageName,
158
+ templateDir,
159
+ templateContentsDir
157
160
  });
158
161
  const targetPath = (_b = ctx.input.targetPath) != null ? _b : "./";
159
162
  const outputPath = backendCommon.resolveSafeChildPath(ctx.workspacePath, targetPath);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/actions/fetch/cookiecutter.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 {\n ContainerRunner,\n UrlReader,\n resolveSafeChildPath,\n} from '@backstage/backend-common';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport commandExists from 'command-exists';\nimport fs from 'fs-extra';\nimport path, { resolve as resolvePath } from 'path';\nimport { Writable } from 'stream';\nimport {\n createTemplateAction,\n fetchContents,\n executeShellCommand,\n} from '@backstage/plugin-scaffolder-backend';\n\nexport class CookiecutterRunner {\n private readonly containerRunner: ContainerRunner;\n\n constructor({ containerRunner }: { containerRunner: ContainerRunner }) {\n this.containerRunner = containerRunner;\n }\n\n private async fetchTemplateCookieCutter(\n directory: string,\n ): Promise<Record<string, JsonValue>> {\n try {\n return await fs.readJSON(path.join(directory, 'cookiecutter.json'));\n } catch (ex) {\n if (ex.code !== 'ENOENT') {\n throw ex;\n }\n\n return {};\n }\n }\n\n public async run({\n workspacePath,\n values,\n logStream,\n }: {\n workspacePath: string;\n values: JsonObject;\n logStream: Writable;\n }): Promise<void> {\n const templateDir = path.join(workspacePath, 'template');\n const intermediateDir = path.join(workspacePath, 'intermediate');\n await fs.ensureDir(intermediateDir);\n const resultDir = path.join(workspacePath, 'result');\n\n // First lets grab the default cookiecutter.json file\n const cookieCutterJson = await this.fetchTemplateCookieCutter(templateDir);\n\n const { imageName, ...valuesForCookieCutterJson } = values;\n const cookieInfo = {\n ...cookieCutterJson,\n ...valuesForCookieCutterJson,\n };\n\n await fs.writeJSON(path.join(templateDir, 'cookiecutter.json'), cookieInfo);\n\n // Directories to bind on container\n const mountDirs = {\n [templateDir]: '/input',\n [intermediateDir]: '/output',\n };\n\n // the command-exists package returns `true` or throws an error\n const cookieCutterInstalled = await commandExists('cookiecutter').catch(\n () => false,\n );\n if (cookieCutterInstalled) {\n await executeShellCommand({\n command: 'cookiecutter',\n args: ['--no-input', '-o', intermediateDir, templateDir, '--verbose'],\n logStream,\n });\n } else {\n await this.containerRunner.runContainer({\n imageName: (imageName as string) ?? 'spotify/backstage-cookiecutter',\n command: 'cookiecutter',\n args: ['--no-input', '-o', '/output', '/input', '--verbose'],\n mountDirs,\n workingDir: '/input',\n // Set the home directory inside the container as something that applications can\n // write to, otherwise they will just fail trying to write to /\n envVars: { HOME: '/tmp' },\n logStream,\n });\n }\n\n // if cookiecutter was successful, intermediateDir will contain\n // exactly one directory.\n\n const [generated] = await fs.readdir(intermediateDir);\n\n if (generated === undefined) {\n throw new Error('No data generated by cookiecutter');\n }\n\n await fs.move(path.join(intermediateDir, generated), resultDir);\n }\n}\n\n/**\n * Creates a `fetch:cookiecutter` Scaffolder action.\n *\n * @remarks\n *\n * See {@link https://cookiecutter.readthedocs.io/} and {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}.\n * @param options - Templating configuration.\n * @public\n */\nexport function createFetchCookiecutterAction(options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n containerRunner: ContainerRunner;\n}) {\n const { reader, containerRunner, integrations } = options;\n\n return createTemplateAction<{\n url: string;\n targetPath?: string;\n values: JsonObject;\n copyWithoutRender?: string[];\n extensions?: string[];\n imageName?: string;\n }>({\n id: 'fetch:cookiecutter',\n description:\n 'Downloads a template from the given URL into the workspace, and runs cookiecutter on it.',\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 values: {\n title: 'Template Values',\n description: 'Values to pass on to cookiecutter for templating',\n type: 'object',\n },\n copyWithoutRender: {\n title: 'Copy Without Render',\n description:\n 'Avoid rendering directories and files in the template',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n extensions: {\n title: 'Template Extensions',\n description:\n \"Jinja2 extensions to add filters, tests, globals or extend the parser. Extensions must be installed in the container or on the host where Cookiecutter executes. See the contrib directory in Backstage's repo for more information\",\n type: 'array',\n items: {\n type: 'string',\n },\n },\n imageName: {\n title: 'Cookiecutter Docker image',\n description:\n \"Specify a custom Docker image to run cookiecutter, to override the default: 'spotify/backstage-cookiecutter'. This can be used to execute cookiecutter with Template Extensions. Used only when a local cookiecutter is not found.\",\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logger.info('Fetching and then templating using cookiecutter');\n const workDir = await ctx.createTemporaryDirectory();\n const templateDir = resolvePath(workDir, 'template');\n const templateContentsDir = resolvePath(\n templateDir,\n \"{{cookiecutter and 'contents'}}\",\n );\n const resultDir = resolvePath(workDir, 'result');\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 if (ctx.input.extensions && !Array.isArray(ctx.input.extensions)) {\n throw new InputError('Fetch action input extensions must be an Array');\n }\n\n await fetchContents({\n reader,\n integrations,\n baseUrl: ctx.templateInfo?.baseUrl,\n fetchUrl: ctx.input.url,\n outputPath: templateContentsDir,\n });\n\n const cookiecutter = new CookiecutterRunner({ containerRunner });\n const values = {\n ...ctx.input.values,\n _copy_without_render: ctx.input.copyWithoutRender,\n _extensions: ctx.input.extensions,\n imageName: ctx.input.imageName,\n };\n\n // Will execute the template in ./template and put the result in ./result\n await cookiecutter.run({\n workspacePath: workDir,\n logStream: ctx.logStream,\n values,\n });\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 await fs.copy(resultDir, outputPath);\n },\n });\n}\n"],"names":["fs","path","commandExists","executeShellCommand","createTemplateAction","resolvePath","InputError","fetchContents","resolveSafeChildPath"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/actions/fetch/cookiecutter.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 {\n ContainerRunner,\n UrlReader,\n resolveSafeChildPath,\n} from '@backstage/backend-common';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport { InputError } from '@backstage/errors';\nimport { ScmIntegrations } from '@backstage/integration';\nimport commandExists from 'command-exists';\nimport fs from 'fs-extra';\nimport path, { resolve as resolvePath } from 'path';\nimport { Writable } from 'stream';\nimport {\n createTemplateAction,\n fetchContents,\n executeShellCommand,\n} from '@backstage/plugin-scaffolder-backend';\n\nexport class CookiecutterRunner {\n private readonly containerRunner: ContainerRunner;\n\n constructor({ containerRunner }: { containerRunner: ContainerRunner }) {\n this.containerRunner = containerRunner;\n }\n\n private async fetchTemplateCookieCutter(\n directory: string,\n ): Promise<Record<string, JsonValue>> {\n try {\n return await fs.readJSON(path.join(directory, 'cookiecutter.json'));\n } catch (ex) {\n if (ex.code !== 'ENOENT') {\n throw ex;\n }\n\n return {};\n }\n }\n\n public async run({\n workspacePath,\n values,\n logStream,\n imageName,\n templateDir,\n templateContentsDir,\n }: {\n workspacePath: string;\n values: JsonObject;\n logStream: Writable;\n imageName?: string;\n templateDir: string;\n templateContentsDir: string;\n }): Promise<void> {\n const intermediateDir = path.join(workspacePath, 'intermediate');\n await fs.ensureDir(intermediateDir);\n const resultDir = path.join(workspacePath, 'result');\n\n // First lets grab the default cookiecutter.json file\n const cookieCutterJson = await this.fetchTemplateCookieCutter(\n templateContentsDir,\n );\n\n const cookieInfo = {\n ...cookieCutterJson,\n ...values,\n };\n\n await fs.writeJSON(path.join(templateDir, 'cookiecutter.json'), cookieInfo);\n\n // Directories to bind on container\n const mountDirs = {\n [templateDir]: '/input',\n [intermediateDir]: '/output',\n };\n\n // the command-exists package returns `true` or throws an error\n const cookieCutterInstalled = await commandExists('cookiecutter').catch(\n () => false,\n );\n if (cookieCutterInstalled) {\n await executeShellCommand({\n command: 'cookiecutter',\n args: ['--no-input', '-o', intermediateDir, templateDir, '--verbose'],\n logStream,\n });\n } else {\n await this.containerRunner.runContainer({\n imageName: imageName ?? 'spotify/backstage-cookiecutter',\n command: 'cookiecutter',\n args: ['--no-input', '-o', '/output', '/input', '--verbose'],\n mountDirs,\n workingDir: '/input',\n // Set the home directory inside the container as something that applications can\n // write to, otherwise they will just fail trying to write to /\n envVars: { HOME: '/tmp' },\n logStream,\n });\n }\n\n // if cookiecutter was successful, intermediateDir will contain\n // exactly one directory.\n\n const [generated] = await fs.readdir(intermediateDir);\n\n if (generated === undefined) {\n throw new Error('No data generated by cookiecutter');\n }\n\n await fs.move(path.join(intermediateDir, generated), resultDir);\n }\n}\n\n/**\n * Creates a `fetch:cookiecutter` Scaffolder action.\n *\n * @remarks\n *\n * See {@link https://cookiecutter.readthedocs.io/} and {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}.\n * @param options - Templating configuration.\n * @public\n */\nexport function createFetchCookiecutterAction(options: {\n reader: UrlReader;\n integrations: ScmIntegrations;\n containerRunner: ContainerRunner;\n}) {\n const { reader, containerRunner, integrations } = options;\n\n return createTemplateAction<{\n url: string;\n targetPath?: string;\n values: JsonObject;\n copyWithoutRender?: string[];\n extensions?: string[];\n imageName?: string;\n }>({\n id: 'fetch:cookiecutter',\n description:\n 'Downloads a template from the given URL into the workspace, and runs cookiecutter on it.',\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 values: {\n title: 'Template Values',\n description: 'Values to pass on to cookiecutter for templating',\n type: 'object',\n },\n copyWithoutRender: {\n title: 'Copy Without Render',\n description:\n 'Avoid rendering directories and files in the template',\n type: 'array',\n items: {\n type: 'string',\n },\n },\n extensions: {\n title: 'Template Extensions',\n description:\n \"Jinja2 extensions to add filters, tests, globals or extend the parser. Extensions must be installed in the container or on the host where Cookiecutter executes. See the contrib directory in Backstage's repo for more information\",\n type: 'array',\n items: {\n type: 'string',\n },\n },\n imageName: {\n title: 'Cookiecutter Docker image',\n description:\n \"Specify a custom Docker image to run cookiecutter, to override the default: 'spotify/backstage-cookiecutter'. This can be used to execute cookiecutter with Template Extensions. Used only when a local cookiecutter is not found.\",\n type: 'string',\n },\n },\n },\n },\n async handler(ctx) {\n ctx.logger.info('Fetching and then templating using cookiecutter');\n const workDir = await ctx.createTemporaryDirectory();\n const templateDir = resolvePath(workDir, 'template');\n const templateContentsDir = resolvePath(\n templateDir,\n \"{{cookiecutter and 'contents'}}\",\n );\n const resultDir = resolvePath(workDir, 'result');\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 if (ctx.input.extensions && !Array.isArray(ctx.input.extensions)) {\n throw new InputError('Fetch action input extensions must be an Array');\n }\n\n await fetchContents({\n reader,\n integrations,\n baseUrl: ctx.templateInfo?.baseUrl,\n fetchUrl: ctx.input.url,\n outputPath: templateContentsDir,\n });\n\n const cookiecutter = new CookiecutterRunner({ containerRunner });\n const values = {\n ...ctx.input.values,\n _copy_without_render: ctx.input.copyWithoutRender,\n _extensions: ctx.input.extensions,\n };\n\n // Will execute the template in ./template and put the result in ./result\n await cookiecutter.run({\n workspacePath: workDir,\n logStream: ctx.logStream,\n values: values,\n imageName: ctx.input.imageName,\n templateDir: templateDir,\n templateContentsDir: templateContentsDir,\n });\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 await fs.copy(resultDir, outputPath);\n },\n });\n}\n"],"names":["fs","path","commandExists","executeShellCommand","createTemplateAction","resolvePath","InputError","fetchContents","resolveSafeChildPath"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter",
3
3
  "description": "A module for the scaffolder backend that lets you template projects using cookiecutter",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -23,19 +23,19 @@
23
23
  "clean": "backstage-cli package clean"
24
24
  },
25
25
  "dependencies": {
26
- "@backstage/backend-common": "^0.11.0",
26
+ "@backstage/backend-common": "^0.13.0",
27
27
  "@backstage/config": "^0.1.15",
28
28
  "@backstage/errors": "^0.2.2",
29
- "@backstage/integration": "^0.7.5",
30
- "@backstage/plugin-scaffolder-backend": "^0.17.0",
29
+ "@backstage/integration": "^0.8.0",
30
+ "@backstage/plugin-scaffolder-backend": "^0.18.0",
31
31
  "@backstage/types": "^0.1.3",
32
32
  "command-exists": "^1.2.9",
33
- "fs-extra": "10.0.0",
33
+ "fs-extra": "10.0.1",
34
34
  "winston": "^3.2.1",
35
35
  "yn": "^4.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "@backstage/cli": "^0.14.1",
38
+ "@backstage/cli": "^0.15.2",
39
39
  "@types/command-exists": "^1.2.0",
40
40
  "@types/fs-extra": "^9.0.1",
41
41
  "@types/jest": "^26.0.7",
@@ -46,5 +46,5 @@
46
46
  "files": [
47
47
  "dist"
48
48
  ],
49
- "gitHead": "a15da6ea1e3e8adc37be98be064071c8b5279b4a"
49
+ "gitHead": "60c4e39d1fcaeb10d6488ada1d907f34dc2a105a"
50
50
  }