Skip to content

Commit ccc649a

Browse files
authored
Merge pull request backstage#6385 from backstage/blam/move-out-cookiecutter-action
Move Cookiecutter to it's own module
2 parents 366547f + 0a7e3d2 commit ccc649a

15 files changed

Lines changed: 305 additions & 21 deletions

File tree

.changeset/spotty-pandas-deny.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@backstage/plugin-scaffolder-backend': patch
3+
---
4+
5+
- Move out the `cookiecutter` templating to its own module that is depended on by the `scaffolder-backend` plugin. No breaking change yet, but we will drop first class support for `cookiecutter` in the future and it will become an opt-in feature.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
3+
};
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# scaffolder-backend-module-cookiecutter
2+
3+
Welcome to the `fetch:cookiecutter` action for the `scaffolder-backend`.
4+
5+
## Getting started
6+
7+
You need to configure the action in your backend:
8+
9+
## From your Backstage root directory
10+
11+
```
12+
cd packages/backend
13+
yarn add @backstage/plugin-scaffolder-backend-module-cookiecutter
14+
```
15+
16+
Configure the action:
17+
(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options):
18+
19+
```typescript
20+
// packages/backend/src/plugins/scaffolder.ts
21+
22+
const actions = [
23+
createFetchCookiecutterAction({
24+
integrations,
25+
reader,
26+
containerRunner,
27+
}),
28+
...createBuiltInActions({
29+
...
30+
})
31+
];
32+
33+
return await createRouter({
34+
containerRunner,
35+
logger,
36+
config,
37+
database,
38+
catalogClient,
39+
reader,
40+
actions,
41+
});
42+
```
43+
44+
After that you can use the action in your template:
45+
46+
```yaml
47+
apiVersion: backstage.io/v1beta2
48+
kind: Template
49+
metadata:
50+
name: cookiecutter-demo
51+
title: Cookiecutter Test
52+
description: Cookiecutter example
53+
spec:
54+
owner: backstage/techdocs-core
55+
type: service
56+
57+
parameters:
58+
- title: Fill in some steps
59+
required:
60+
- name
61+
- owner
62+
properties:
63+
name:
64+
title: Name
65+
type: string
66+
description: Unique name of the component
67+
ui:autofocus: true
68+
ui:options:
69+
rows: 5
70+
owner:
71+
title: Owner
72+
type: string
73+
description: Owner of the component
74+
ui:field: OwnerPicker
75+
ui:options:
76+
allowedKinds:
77+
- Group
78+
system:
79+
title: System
80+
type: string
81+
description: System of the component
82+
ui:field: EntityPicker
83+
ui:options:
84+
allowedKinds:
85+
- System
86+
defaultKind: System
87+
88+
- title: Choose a location
89+
required:
90+
- repoUrl
91+
- dryRun
92+
properties:
93+
repoUrl:
94+
title: Repository Location
95+
type: string
96+
ui:field: RepoUrlPicker
97+
ui:options:
98+
allowedHosts:
99+
- github.com
100+
dryRun:
101+
title: Only perform a dry run, don't publish anything
102+
type: boolean
103+
default: false
104+
105+
steps:
106+
- id: fetch-base
107+
name: Fetch Base
108+
action: fetch:cookiecutter
109+
input:
110+
url: ./template
111+
values:
112+
name: '{{ parameters.name }}'
113+
owner: '{{ parameters.owner }}'
114+
system: '{{ parameters.system }}'
115+
destination: '{{ parseRepoUrl parameters.repoUrl }}'
116+
117+
- id: publish
118+
if: '{{ not parameters.dryRun }}'
119+
name: Publish
120+
action: publish:github
121+
input:
122+
allowedHosts: ['github.com']
123+
description: 'This is {{ parameters.name }}'
124+
repoUrl: '{{ parameters.repoUrl }}'
125+
126+
- id: register
127+
if: '{{ not parameters.dryRun }}'
128+
name: Register
129+
action: catalog:register
130+
input:
131+
repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}'
132+
catalogInfoPath: '/catalog-info.yaml'
133+
134+
- name: Results
135+
if: '{{ parameters.dryRun }}'
136+
action: debug:log
137+
input:
138+
listWorkspace: true
139+
140+
output:
141+
links:
142+
- title: Repository
143+
url: '{{ steps.publish.output.remoteUrl }}'
144+
- title: Open in catalog
145+
icon: 'catalog'
146+
entityRef: '{{ steps.register.output.entityRef }}'
147+
```
148+
149+
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.
150+
151+
### Environment setup
152+
153+
The environment needs to have either `cookiecutter` installed and be available in the `PATH` or access to a `docker` daemon so it can spin up a docker container with `cookiecutter` available.
154+
155+
If you are running Backstage from a Docker container and you want to avoid calling a container inside a container, you can set up `cookiecutter` in your own image, this will use the local installation instead.
156+
157+
You can do so by including the following lines in the last step of your Dockerfile:
158+
159+
```dockerfile
160+
RUN apt-get update && apt-get install -y python3 python3-pip
161+
RUN pip3 install cookiecutter
162+
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## API Report File for "@backstage/plugin-scaffolder-backend-module-cookiecutter"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
/// <reference types="node" />
7+
8+
import { ContainerRunner } from '@backstage/backend-common';
9+
import { ScmIntegrations } from '@backstage/integration';
10+
import { TemplateAction } from '@backstage/plugin-scaffolder-backend';
11+
import { UrlReader } from '@backstage/backend-common';
12+
13+
// Warning: (ae-missing-release-tag) "createFetchCookiecutterAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
14+
//
15+
// @public (undocumented)
16+
export function createFetchCookiecutterAction(options: {
17+
reader: UrlReader;
18+
integrations: ScmIntegrations;
19+
containerRunner: ContainerRunner;
20+
}): TemplateAction<any>;
21+
22+
// (No @packageDocumentation comment for this package)
23+
```
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "@backstage/plugin-scaffolder-backend-module-cookiecutter",
3+
"version": "0.1.0",
4+
"main": "src/index.ts",
5+
"types": "src/index.ts",
6+
"license": "Apache-2.0",
7+
"publishConfig": {
8+
"access": "public",
9+
"main": "dist/index.cjs.js",
10+
"types": "dist/index.d.ts"
11+
},
12+
"scripts": {
13+
"start": "backstage-cli backend:dev",
14+
"build": "backstage-cli backend:build",
15+
"lint": "backstage-cli lint",
16+
"test": "backstage-cli test",
17+
"prepack": "backstage-cli prepack",
18+
"postpack": "backstage-cli postpack",
19+
"clean": "backstage-cli clean"
20+
},
21+
"dependencies": {
22+
"@backstage/backend-common": "^0.8.6",
23+
"@backstage/errors": "^0.1.1",
24+
"@backstage/integration": "^0.5.7",
25+
"@backstage/plugin-scaffolder-backend": "^0.14.0",
26+
"@backstage/config": "^0.1.5",
27+
"command-exists": "^1.2.9",
28+
"fs-extra": "10.0.0",
29+
"winston": "^3.2.1",
30+
"cross-fetch": "^3.0.6",
31+
"yn": "^4.0.0"
32+
},
33+
"devDependencies": {
34+
"@backstage/cli": "^0.7.3",
35+
"@types/fs-extra": "^9.0.1",
36+
"@types/mock-fs": "^4.13.0",
37+
"@types/jest": "^26.0.7",
38+
"@types/command-exists": "^1.2.0",
39+
"mock-fs": "^4.13.0",
40+
"msw": "^0.29.0"
41+
},
42+
"files": [
43+
"dist"
44+
]
45+
}

plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts renamed to plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ const runCommand = jest.fn();
1717
const commandExists = jest.fn();
1818
const fetchContents = jest.fn();
1919

20-
jest.mock('./helpers', () => ({ fetchContents }));
20+
jest.mock('@backstage/plugin-scaffolder-backend', () => ({
21+
...jest.requireActual('@backstage/plugin-scaffolder-backend'),
22+
fetchContents,
23+
runCommand,
24+
}));
2125
jest.mock('command-exists', () => commandExists);
22-
jest.mock('../helpers', () => ({ runCommand }));
2326

2427
import {
2528
getVoidLogger,
@@ -33,7 +36,7 @@ import os from 'os';
3336
import { PassThrough } from 'stream';
3437
import { createFetchCookiecutterAction } from './cookiecutter';
3538
import { join } from 'path';
36-
import { ActionContext } from '../../types';
39+
import type { ActionContext } from '@backstage/plugin-scaffolder-backend';
3740

3841
describe('fetch:cookiecutter', () => {
3942
const integrations = ScmIntegrations.fromConfig(

plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts renamed to plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ import commandExists from 'command-exists';
2626
import fs from 'fs-extra';
2727
import path, { resolve as resolvePath } from 'path';
2828
import { Writable } from 'stream';
29-
import { runCommand } from '../helpers';
30-
import { createTemplateAction } from '../../createTemplateAction';
31-
import { fetchContents } from './helpers';
29+
import {
30+
runCommand,
31+
createTemplateAction,
32+
fetchContents,
33+
} from '@backstage/plugin-scaffolder-backend';
3234

3335
export class CookiecutterRunner {
3436
private readonly containerRunner: ContainerRunner;
@@ -136,7 +138,7 @@ export function createFetchCookiecutterAction(options: {
136138
}>({
137139
id: 'fetch:cookiecutter',
138140
description:
139-
"Downloads a template from the given URL into the workspace, and runs cookiecutter on it. This action is deprecated in favor of 'fetch:template'. See https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetch-cookiecutter-to-fetch-template for more details.",
141+
'Downloads a template from the given URL into the workspace, and runs cookiecutter on it.',
140142
schema: {
141143
input: {
142144
type: 'object',
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2021 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
export { createFetchCookiecutterAction } from './cookiecutter';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2021 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
export * from './fetch';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2021 The Backstage Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
export * from './actions';

0 commit comments

Comments
 (0)