@backstage/create-app 0.4.6 → 0.4.10
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 +213 -0
- package/README.md +1 -1
- package/dist/index.cjs.js +71 -68
- package/dist/index.cjs.js.map +1 -1
- package/package.json +4 -3
- package/templates/default-app/app-config.yaml.hbs +1 -3
- package/templates/default-app/package.json.hbs +2 -2
- package/templates/default-app/packages/app/package.json.hbs +3 -3
- package/templates/default-app/packages/app/public/index.html +8 -11
- package/templates/default-app/packages/app/src/components/search/SearchPage.tsx +25 -7
- package/templates/default-app/packages/backend/package.json.hbs +1 -0
- package/templates/default-app/packages/backend/src/index.ts +17 -4
- package/templates/default-app/packages/backend/src/plugins/techdocs.ts +2 -0
- package/templates/default-app/packages/backend/src/types.ts +2 -0
- package/templates/serve_index.html +0 -27
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,218 @@
|
|
|
1
1
|
# @backstage/create-app
|
|
2
2
|
|
|
3
|
+
## 0.4.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 79b342bd36: removed inline and internal CSS from index.html
|
|
8
|
+
|
|
9
|
+
To make this change to an existing app, apply the following changes to the `packages/app/public/index.html` file:
|
|
10
|
+
|
|
11
|
+
Remove internal style
|
|
12
|
+
|
|
13
|
+
```diff
|
|
14
|
+
- <style>
|
|
15
|
+
- #root {
|
|
16
|
+
- min-height: 100%;
|
|
17
|
+
- }
|
|
18
|
+
- </style>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Remove inline style from the body tag
|
|
22
|
+
|
|
23
|
+
```diff
|
|
24
|
+
- <body style="margin: 0">
|
|
25
|
+
+ <body>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- d33b65dc52: Removed unused templating asset.
|
|
29
|
+
- 613ad12960: Add a comment to the default backend about the fallback 404 handler.
|
|
30
|
+
- 20af5a701f: The `<SearchType />` filter in the composed `SearchPage.tsx` was replaced with the `<SearchType.Accordion />` variant.
|
|
31
|
+
|
|
32
|
+
This is an entirely optional change; if you wish to display a control surface for search `types` as a single-select accordion (as opposed to the current multi-select of checkboxes), you can make the following (or similar) changes to your search page layout:
|
|
33
|
+
|
|
34
|
+
```diff
|
|
35
|
+
--- a/packages/app/src/components/search/SearchPage.tsx
|
|
36
|
+
+++ b/packages/app/src/components/search/SearchPage.tsx
|
|
37
|
+
@@ -11,7 +11,7 @@ import {
|
|
38
|
+
SearchType,
|
|
39
|
+
DefaultResultListItem,
|
|
40
|
+
} from '@backstage/plugin-search';
|
|
41
|
+
-import { Content, Header, Page } from '@backstage/core-components';
|
|
42
|
+
+import { CatalogIcon, Content, DocsIcon, Header, Page } from '@backstage/core-components';
|
|
43
|
+
|
|
44
|
+
const useStyles = makeStyles((theme: Theme) => ({
|
|
45
|
+
bar: {
|
|
46
|
+
@@ -19,6 +19,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|
47
|
+
},
|
|
48
|
+
filters: {
|
|
49
|
+
padding: theme.spacing(2),
|
|
50
|
+
+ marginTop: theme.spacing(2),
|
|
51
|
+
},
|
|
52
|
+
filter: {
|
|
53
|
+
'& + &': {
|
|
54
|
+
@@ -41,12 +42,23 @@ const SearchPage = () => {
|
|
55
|
+
</Paper>
|
|
56
|
+
</Grid>
|
|
57
|
+
<Grid item xs={3}>
|
|
58
|
+
+ <SearchType.Accordion
|
|
59
|
+
+ name="Result Type"
|
|
60
|
+
+ defaultValue="software-catalog"
|
|
61
|
+
+ types={[
|
|
62
|
+
+ {
|
|
63
|
+
+ value: 'software-catalog',
|
|
64
|
+
+ name: 'Software Catalog',
|
|
65
|
+
+ icon: <CatalogIcon />,
|
|
66
|
+
+ },
|
|
67
|
+
+ {
|
|
68
|
+
+ value: 'techdocs',
|
|
69
|
+
+ name: 'Documentation',
|
|
70
|
+
+ icon: <DocsIcon />,
|
|
71
|
+
+ },
|
|
72
|
+
+ ]}
|
|
73
|
+
+ />
|
|
74
|
+
<Paper className={classes.filters}>
|
|
75
|
+
- <SearchType
|
|
76
|
+
- values={['techdocs', 'software-catalog']}
|
|
77
|
+
- name="type"
|
|
78
|
+
- defaultValue="software-catalog"
|
|
79
|
+
- />
|
|
80
|
+
<SearchFilter.Select
|
|
81
|
+
className={classes.filter}
|
|
82
|
+
name="kind"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- 0dcd1dd64f: Add a `scheduler` to the plugin environment, which can schedule collaborative tasks across backends. To apply the same change in your backend, follow the steps below.
|
|
86
|
+
|
|
87
|
+
First install the package:
|
|
88
|
+
|
|
89
|
+
```shell
|
|
90
|
+
# From the Backstage repository root
|
|
91
|
+
cd packages/backend
|
|
92
|
+
yarn add @backstage/backend-tasks
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Add the scheduler to your plugin environment type:
|
|
96
|
+
|
|
97
|
+
```diff
|
|
98
|
+
// In packages/backend/src/types.ts
|
|
99
|
+
+import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
|
100
|
+
|
|
101
|
+
export type PluginEnvironment = {
|
|
102
|
+
+ scheduler: PluginTaskScheduler;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
And finally make sure to add such an instance to each plugin's environment:
|
|
106
|
+
|
|
107
|
+
```diff
|
|
108
|
+
// In packages/backend/src/index.ts
|
|
109
|
+
+import { TaskScheduler } from '@backstage/backend-tasks';
|
|
110
|
+
|
|
111
|
+
function makeCreateEnv(config: Config) {
|
|
112
|
+
// ...
|
|
113
|
+
+ const taskScheduler = TaskScheduler.fromConfig(config);
|
|
114
|
+
|
|
115
|
+
return (plugin: string): PluginEnvironment => {
|
|
116
|
+
// ...
|
|
117
|
+
+ const scheduler = taskScheduler.forPlugin(plugin);
|
|
118
|
+
return {
|
|
119
|
+
+ scheduler,
|
|
120
|
+
// ...
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 0.4.9
|
|
124
|
+
|
|
125
|
+
### Patch Changes
|
|
126
|
+
|
|
127
|
+
- 49a696d720: debounceTime prop is removed from the SearchBar component in the SearchPage as the default is set to 200. The prop is safe to remove and makes it easier to stay up to date with any changes in the future.
|
|
128
|
+
- 5fdc8df0e8: The `index.html` template of the app has been updated to use the new `config` global provided by the Backstage CLI.
|
|
129
|
+
|
|
130
|
+
To apply this change to an existing app, make the following changes to `packages/app/public/index.html`:
|
|
131
|
+
|
|
132
|
+
```diff
|
|
133
|
+
- <title><%= app.title %></title>
|
|
134
|
+
+ <title><%= config.getString('app.title') %></title>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```diff
|
|
138
|
+
- <% if (app.googleAnalyticsTrackingId && typeof app.googleAnalyticsTrackingId === 'string') { %>
|
|
139
|
+
+ <% if (config.has('app.googleAnalyticsTrackingId')) { %>
|
|
140
|
+
<script
|
|
141
|
+
async
|
|
142
|
+
- src="https://www.googletagmanager.com/gtag/js?id=<%= app.googleAnalyticsTrackingId %>"
|
|
143
|
+
+ src="https://www.googletagmanager.com/gtag/js?id=<%= config.getString('app.googleAnalyticsTrackingId') %>"
|
|
144
|
+
></script>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```diff
|
|
148
|
+
- gtag('config', '<%= app.googleAnalyticsTrackingId %>');
|
|
149
|
+
+ gtag(
|
|
150
|
+
+ 'config',
|
|
151
|
+
+ '<%= config.getString("app.googleAnalyticsTrackingId") %>',
|
|
152
|
+
+ );
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 0.4.8
|
|
156
|
+
|
|
157
|
+
### Patch Changes
|
|
158
|
+
|
|
159
|
+
- 25dfc2d483: Updated the root `package.json` to include files with `.cjs` and `.mjs` extensions in the `"lint-staged"` configuration.
|
|
160
|
+
|
|
161
|
+
To make this change to an existing app, apply the following changes to the `package.json` file:
|
|
162
|
+
|
|
163
|
+
```diff
|
|
164
|
+
"lint-staged": {
|
|
165
|
+
- "*.{js,jsx,ts,tsx}": [
|
|
166
|
+
+ "*.{js,jsx,ts,tsx,mjs,cjs}": [
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## 0.4.7
|
|
170
|
+
|
|
171
|
+
### Patch Changes
|
|
172
|
+
|
|
173
|
+
- 9603827bb5: Addressed some peer dependency warnings
|
|
174
|
+
- 1bada775a9: TechDocs Backend may now (optionally) leverage a cache store to improve
|
|
175
|
+
performance when reading content from a cloud storage provider.
|
|
176
|
+
|
|
177
|
+
To apply this change to an existing app, pass the cache manager from the plugin
|
|
178
|
+
environment to the `createRouter` function in your backend:
|
|
179
|
+
|
|
180
|
+
```diff
|
|
181
|
+
// packages/backend/src/plugins/techdocs.ts
|
|
182
|
+
|
|
183
|
+
export default async function createPlugin({
|
|
184
|
+
logger,
|
|
185
|
+
config,
|
|
186
|
+
discovery,
|
|
187
|
+
reader,
|
|
188
|
+
+ cache,
|
|
189
|
+
}: PluginEnvironment): Promise<Router> {
|
|
190
|
+
|
|
191
|
+
// ...
|
|
192
|
+
|
|
193
|
+
return await createRouter({
|
|
194
|
+
preparers,
|
|
195
|
+
generators,
|
|
196
|
+
publisher,
|
|
197
|
+
logger,
|
|
198
|
+
config,
|
|
199
|
+
discovery,
|
|
200
|
+
+ cache,
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
If your `PluginEnvironment` does not include a cache manager, be sure you've
|
|
205
|
+
applied [the cache management change][cm-change] to your backend as well.
|
|
206
|
+
|
|
207
|
+
[Additional configuration][td-rec-arch] is required if you wish to enable
|
|
208
|
+
caching in TechDocs.
|
|
209
|
+
|
|
210
|
+
[cm-change]: https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md#patch-changes-6
|
|
211
|
+
[td-rec-arch]: https://backstage.io/docs/features/techdocs/architecture#recommended-deployment
|
|
212
|
+
|
|
213
|
+
- 4862fbc64f: Bump @spotify/prettier-config
|
|
214
|
+
- 36bb4fb2e9: Removed the `scaffolder.github.visibility` configuration that is no longer used from the default app template.
|
|
215
|
+
|
|
3
216
|
## 0.4.6
|
|
4
217
|
|
|
5
218
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -22,4 +22,4 @@ yarn backstage-create-app
|
|
|
22
22
|
## Documentation
|
|
23
23
|
|
|
24
24
|
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
|
|
25
|
-
- [Backstage Documentation](https://
|
|
25
|
+
- [Backstage Documentation](https://backstage.io/docs/)
|
package/dist/index.cjs.js
CHANGED
|
@@ -42,96 +42,99 @@ class ExitCodeError extends CustomError {
|
|
|
42
42
|
function exitWithError(error) {
|
|
43
43
|
if (error instanceof ExitCodeError) {
|
|
44
44
|
process.stderr.write(`
|
|
45
|
-
${chalk__default[
|
|
45
|
+
${chalk__default["default"].red(error.message)}
|
|
46
46
|
|
|
47
47
|
`);
|
|
48
48
|
process.exit(error.code);
|
|
49
49
|
} else {
|
|
50
50
|
process.stderr.write(`
|
|
51
|
-
${chalk__default[
|
|
51
|
+
${chalk__default["default"].red(`${error}`)}
|
|
52
52
|
|
|
53
53
|
`);
|
|
54
54
|
process.exit(1);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
var version$
|
|
58
|
+
var version$B = "0.4.10";
|
|
59
59
|
|
|
60
|
-
var version$
|
|
60
|
+
var version$A = "0.1.3";
|
|
61
61
|
|
|
62
|
-
var version$
|
|
62
|
+
var version$z = "0.10.1";
|
|
63
63
|
|
|
64
|
-
var version$
|
|
64
|
+
var version$y = "0.1.1";
|
|
65
65
|
|
|
66
|
-
var version$
|
|
66
|
+
var version$x = "0.5.3";
|
|
67
67
|
|
|
68
|
-
var version$
|
|
68
|
+
var version$w = "0.9.8";
|
|
69
|
+
|
|
70
|
+
var version$v = "0.10.4";
|
|
69
71
|
|
|
70
72
|
var version$u = "0.1.11";
|
|
71
73
|
|
|
72
|
-
var version$t = "0.
|
|
74
|
+
var version$t = "0.3.0";
|
|
73
75
|
|
|
74
|
-
var version$s = "0.
|
|
76
|
+
var version$s = "0.8.2";
|
|
75
77
|
|
|
76
|
-
var version$r = "0.
|
|
78
|
+
var version$r = "0.4.0";
|
|
77
79
|
|
|
78
80
|
var version$q = "0.1.5";
|
|
79
81
|
|
|
80
|
-
var version$p = "0.1.
|
|
82
|
+
var version$p = "0.1.17";
|
|
81
83
|
|
|
82
|
-
var version$o = "0.1
|
|
84
|
+
var version$o = "0.2.1";
|
|
83
85
|
|
|
84
86
|
var version$n = "0.2.14";
|
|
85
87
|
|
|
86
|
-
var version$m = "0.6.
|
|
88
|
+
var version$m = "0.6.20";
|
|
87
89
|
|
|
88
|
-
var version$l = "0.3.
|
|
90
|
+
var version$l = "0.3.21";
|
|
89
91
|
|
|
90
|
-
var version$k = "0.
|
|
92
|
+
var version$k = "0.6.0";
|
|
91
93
|
|
|
92
|
-
var version$j = "0.7.
|
|
94
|
+
var version$j = "0.7.6";
|
|
93
95
|
|
|
94
|
-
var version$i = "0.6.
|
|
96
|
+
var version$i = "0.6.9";
|
|
95
97
|
|
|
96
|
-
var version$h = "0.19.
|
|
98
|
+
var version$h = "0.19.4";
|
|
97
99
|
|
|
98
|
-
var version$g = "0.7.
|
|
100
|
+
var version$g = "0.7.7";
|
|
99
101
|
|
|
100
|
-
var version$f = "0.2.
|
|
102
|
+
var version$f = "0.2.32";
|
|
101
103
|
|
|
102
|
-
var version$e = "0.3.
|
|
104
|
+
var version$e = "0.3.23";
|
|
103
105
|
|
|
104
|
-
var version$d = "0.4.
|
|
106
|
+
var version$d = "0.4.29";
|
|
105
107
|
|
|
106
|
-
var version$c = "0.2.
|
|
108
|
+
var version$c = "0.2.32";
|
|
107
109
|
|
|
108
|
-
var version$b = "0.3.
|
|
110
|
+
var version$b = "0.3.32";
|
|
109
111
|
|
|
110
|
-
var version$a = "0.2.
|
|
112
|
+
var version$a = "0.2.15";
|
|
111
113
|
|
|
112
|
-
var version$9 = "0.1.
|
|
114
|
+
var version$9 = "0.1.18";
|
|
113
115
|
|
|
114
|
-
var version$8 = "0.11.
|
|
116
|
+
var version$8 = "0.11.16";
|
|
115
117
|
|
|
116
|
-
var version$7 = "0.15.
|
|
118
|
+
var version$7 = "0.15.19";
|
|
117
119
|
|
|
118
|
-
var version$6 = "0.5.
|
|
120
|
+
var version$6 = "0.5.3";
|
|
119
121
|
|
|
120
|
-
var version$5 = "0.
|
|
122
|
+
var version$5 = "0.3.0";
|
|
121
123
|
|
|
122
124
|
var version$4 = "0.4.3";
|
|
123
125
|
|
|
124
|
-
var version$3 = "0.
|
|
126
|
+
var version$3 = "0.5.0";
|
|
125
127
|
|
|
126
|
-
var version$2 = "0.12.
|
|
128
|
+
var version$2 = "0.12.12";
|
|
127
129
|
|
|
128
|
-
var version$1 = "0.
|
|
130
|
+
var version$1 = "0.12.2";
|
|
129
131
|
|
|
130
|
-
var version = "0.3.
|
|
132
|
+
var version = "0.3.14";
|
|
131
133
|
|
|
132
134
|
const packageVersions = {
|
|
133
|
-
"@backstage/app-defaults": version$
|
|
134
|
-
"@backstage/backend-common": version$
|
|
135
|
+
"@backstage/app-defaults": version$A,
|
|
136
|
+
"@backstage/backend-common": version$z,
|
|
137
|
+
"@backstage/backend-tasks": version$y,
|
|
135
138
|
"@backstage/catalog-client": version$x,
|
|
136
139
|
"@backstage/catalog-model": version$w,
|
|
137
140
|
"@backstage/cli": version$v,
|
|
@@ -172,17 +175,17 @@ const TASK_NAME_MAX_LENGTH = 14;
|
|
|
172
175
|
const exec = util.promisify(child_process.exec);
|
|
173
176
|
class Task {
|
|
174
177
|
static log(name = "") {
|
|
175
|
-
process.stdout.write(`${chalk__default[
|
|
178
|
+
process.stdout.write(`${chalk__default["default"].green(name)}
|
|
176
179
|
`);
|
|
177
180
|
}
|
|
178
181
|
static error(message = "") {
|
|
179
182
|
process.stdout.write(`
|
|
180
|
-
${chalk__default[
|
|
183
|
+
${chalk__default["default"].red(message)}
|
|
181
184
|
|
|
182
185
|
`);
|
|
183
186
|
}
|
|
184
187
|
static section(name) {
|
|
185
|
-
const title = chalk__default[
|
|
188
|
+
const title = chalk__default["default"].green(`${name}:`);
|
|
186
189
|
process.stdout.write(`
|
|
187
190
|
${title}
|
|
188
191
|
`);
|
|
@@ -191,9 +194,9 @@ ${chalk__default['default'].red(message)}
|
|
|
191
194
|
process.exit(code);
|
|
192
195
|
}
|
|
193
196
|
static async forItem(task, item, taskFunc) {
|
|
194
|
-
const paddedTask = chalk__default[
|
|
195
|
-
const spinner = ora__default[
|
|
196
|
-
prefixText: chalk__default[
|
|
197
|
+
const paddedTask = chalk__default["default"].green(task.padEnd(TASK_NAME_MAX_LENGTH));
|
|
198
|
+
const spinner = ora__default["default"]({
|
|
199
|
+
prefixText: chalk__default["default"].green(` ${paddedTask}${chalk__default["default"].cyan(item)}`),
|
|
197
200
|
spinner: "arc",
|
|
198
201
|
color: "green"
|
|
199
202
|
}).start();
|
|
@@ -207,18 +210,18 @@ ${chalk__default['default'].red(message)}
|
|
|
207
210
|
}
|
|
208
211
|
}
|
|
209
212
|
async function templatingTask(templateDir, destinationDir, context, version) {
|
|
210
|
-
const files = await recursive__default[
|
|
213
|
+
const files = await recursive__default["default"](templateDir).catch((error) => {
|
|
211
214
|
throw new Error(`Failed to read template directory: ${error.message}`);
|
|
212
215
|
});
|
|
213
216
|
for (const file of files) {
|
|
214
217
|
const destinationFile = path.resolve(destinationDir, path.relative(templateDir, file));
|
|
215
|
-
await fs__default[
|
|
218
|
+
await fs__default["default"].ensureDir(path.dirname(destinationFile));
|
|
216
219
|
if (file.endsWith(".hbs")) {
|
|
217
220
|
await Task.forItem("templating", path.basename(file), async () => {
|
|
218
221
|
const destination = destinationFile.replace(/\.hbs$/, "");
|
|
219
|
-
const template = await fs__default[
|
|
220
|
-
const compiled = handlebars__default[
|
|
221
|
-
const contents = compiled({name: path.basename(destination), ...context}, {
|
|
222
|
+
const template = await fs__default["default"].readFile(file);
|
|
223
|
+
const compiled = handlebars__default["default"].compile(template.toString());
|
|
224
|
+
const contents = compiled({ name: path.basename(destination), ...context }, {
|
|
222
225
|
helpers: {
|
|
223
226
|
version(name) {
|
|
224
227
|
if (name in packageVersions) {
|
|
@@ -228,20 +231,20 @@ async function templatingTask(templateDir, destinationDir, context, version) {
|
|
|
228
231
|
}
|
|
229
232
|
}
|
|
230
233
|
});
|
|
231
|
-
await fs__default[
|
|
234
|
+
await fs__default["default"].writeFile(destination, contents).catch((error) => {
|
|
232
235
|
throw new Error(`Failed to create file: ${destination}: ${error.message}`);
|
|
233
236
|
});
|
|
234
237
|
});
|
|
235
238
|
} else {
|
|
236
239
|
await Task.forItem("copying", path.basename(file), async () => {
|
|
237
|
-
await fs__default[
|
|
240
|
+
await fs__default["default"].copyFile(file, destinationFile).catch((error) => {
|
|
238
241
|
const destination = destinationFile;
|
|
239
242
|
throw new Error(`Failed to copy file to ${destination} : ${error.message}`);
|
|
240
243
|
});
|
|
241
244
|
});
|
|
242
245
|
}
|
|
243
246
|
}
|
|
244
|
-
await Task.forItem("creating", cliCommon.BACKSTAGE_JSON, () => fs__default[
|
|
247
|
+
await Task.forItem("creating", cliCommon.BACKSTAGE_JSON, () => fs__default["default"].writeFile(path.join(destinationDir, cliCommon.BACKSTAGE_JSON), `{
|
|
245
248
|
"version": ${JSON.stringify(version)}
|
|
246
249
|
}
|
|
247
250
|
`));
|
|
@@ -249,8 +252,8 @@ async function templatingTask(templateDir, destinationDir, context, version) {
|
|
|
249
252
|
async function checkAppExistsTask(rootDir, name) {
|
|
250
253
|
await Task.forItem("checking", name, async () => {
|
|
251
254
|
const destination = path.resolve(rootDir, name);
|
|
252
|
-
if (await fs__default[
|
|
253
|
-
const existing = chalk__default[
|
|
255
|
+
if (await fs__default["default"].pathExists(destination)) {
|
|
256
|
+
const existing = chalk__default["default"].cyan(destination.replace(`${rootDir}/`, ""));
|
|
254
257
|
throw new Error(`A directory with the same name already exists: ${existing}
|
|
255
258
|
Please try again with a different app name`);
|
|
256
259
|
}
|
|
@@ -259,7 +262,7 @@ Please try again with a different app name`);
|
|
|
259
262
|
async function checkPathExistsTask(path) {
|
|
260
263
|
await Task.forItem("checking", path, async () => {
|
|
261
264
|
try {
|
|
262
|
-
await fs__default[
|
|
265
|
+
await fs__default["default"].mkdirs(path);
|
|
263
266
|
} catch (error) {
|
|
264
267
|
throw new Error(`Failed to create app directory: ${error.message}`);
|
|
265
268
|
}
|
|
@@ -268,7 +271,7 @@ async function checkPathExistsTask(path) {
|
|
|
268
271
|
async function createTemporaryAppFolderTask(tempDir) {
|
|
269
272
|
await Task.forItem("creating", "temporary directory", async () => {
|
|
270
273
|
try {
|
|
271
|
-
await fs__default[
|
|
274
|
+
await fs__default["default"].mkdir(tempDir);
|
|
272
275
|
} catch (error) {
|
|
273
276
|
throw new Error(`Failed to create temporary app directory, ${error}`);
|
|
274
277
|
}
|
|
@@ -281,7 +284,7 @@ async function buildAppTask(appDir) {
|
|
|
281
284
|
await exec(cmd).catch((error) => {
|
|
282
285
|
process.stdout.write(error.stderr);
|
|
283
286
|
process.stdout.write(error.stdout);
|
|
284
|
-
throw new Error(`Could not execute command ${chalk__default[
|
|
287
|
+
throw new Error(`Could not execute command ${chalk__default["default"].cyan(cmd)}`);
|
|
285
288
|
});
|
|
286
289
|
});
|
|
287
290
|
};
|
|
@@ -290,10 +293,10 @@ async function buildAppTask(appDir) {
|
|
|
290
293
|
}
|
|
291
294
|
async function moveAppTask(tempDir, destination, id) {
|
|
292
295
|
await Task.forItem("moving", id, async () => {
|
|
293
|
-
await fs__default[
|
|
296
|
+
await fs__default["default"].move(tempDir, destination).catch((error) => {
|
|
294
297
|
throw new Error(`Failed to move app from ${tempDir} to ${destination}: ${error.message}`);
|
|
295
298
|
}).finally(() => {
|
|
296
|
-
fs__default[
|
|
299
|
+
fs__default["default"].removeSync(tempDir);
|
|
297
300
|
});
|
|
298
301
|
});
|
|
299
302
|
}
|
|
@@ -304,12 +307,12 @@ var createApp = async (cmd, version) => {
|
|
|
304
307
|
{
|
|
305
308
|
type: "input",
|
|
306
309
|
name: "name",
|
|
307
|
-
message: chalk__default[
|
|
310
|
+
message: chalk__default["default"].blue("Enter a name for the app [required]"),
|
|
308
311
|
validate: (value) => {
|
|
309
312
|
if (!value) {
|
|
310
|
-
return chalk__default[
|
|
313
|
+
return chalk__default["default"].red("Please enter a name for the app");
|
|
311
314
|
} else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
|
|
312
|
-
return chalk__default[
|
|
315
|
+
return chalk__default["default"].red("App name must be lowercase and contain only letters, digits, and dashes.");
|
|
313
316
|
}
|
|
314
317
|
return true;
|
|
315
318
|
}
|
|
@@ -317,15 +320,15 @@ var createApp = async (cmd, version) => {
|
|
|
317
320
|
{
|
|
318
321
|
type: "list",
|
|
319
322
|
name: "dbType",
|
|
320
|
-
message: chalk__default[
|
|
323
|
+
message: chalk__default["default"].blue("Select database for the backend [required]"),
|
|
321
324
|
choices: ["SQLite", "PostgreSQL"]
|
|
322
325
|
}
|
|
323
326
|
];
|
|
324
|
-
const answers = await inquirer__default[
|
|
327
|
+
const answers = await inquirer__default["default"].prompt(questions);
|
|
325
328
|
answers.dbTypePG = answers.dbType === "PostgreSQL";
|
|
326
329
|
answers.dbTypeSqlite = answers.dbType === "SQLite";
|
|
327
330
|
const templateDir = paths.resolveOwn("templates/default-app");
|
|
328
|
-
const tempDir = path.resolve(os__default[
|
|
331
|
+
const tempDir = path.resolve(os__default["default"].tmpdir(), answers.name);
|
|
329
332
|
const appDir = cmd.path ? path.resolve(paths.targetDir, cmd.path) : path.resolve(paths.targetDir, answers.name);
|
|
330
333
|
Task.log();
|
|
331
334
|
Task.log("Creating the app...");
|
|
@@ -350,10 +353,10 @@ var createApp = async (cmd, version) => {
|
|
|
350
353
|
await buildAppTask(appDir);
|
|
351
354
|
}
|
|
352
355
|
Task.log();
|
|
353
|
-
Task.log(chalk__default[
|
|
356
|
+
Task.log(chalk__default["default"].green(`\u{1F947} Successfully created ${chalk__default["default"].cyan(answers.name)}`));
|
|
354
357
|
Task.log();
|
|
355
358
|
Task.section("All set! Now you might want to");
|
|
356
|
-
Task.log(` Run the app: ${chalk__default[
|
|
359
|
+
Task.log(` Run the app: ${chalk__default["default"].cyan(`cd ${answers.name} && yarn dev`)}`);
|
|
357
360
|
Task.log(" Set up the software catalog: https://backstage.io/docs/features/software-catalog/configuration");
|
|
358
361
|
Task.log(" Add authentication: https://backstage.io/docs/auth/");
|
|
359
362
|
Task.log();
|
|
@@ -367,8 +370,8 @@ var createApp = async (cmd, version) => {
|
|
|
367
370
|
};
|
|
368
371
|
|
|
369
372
|
const main = (argv) => {
|
|
370
|
-
program__default[
|
|
371
|
-
program__default[
|
|
373
|
+
program__default["default"].name("backstage-create-app").version(version$B).description("Creates a new app in a new directory or specified path").option("--path [directory]", "Location to store the app defaulting to a new folder with the app name").option("--skip-install", "Skip the install and builds steps after creating the app").action((cmd) => createApp(cmd, version$B));
|
|
374
|
+
program__default["default"].parse(argv);
|
|
372
375
|
};
|
|
373
376
|
process.on("unhandledRejection", (rejection) => {
|
|
374
377
|
if (rejection instanceof Error) {
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/lib/errors.ts","../src/lib/versions.ts","../src/lib/tasks.ts","../src/createApp.ts","../src/index.ts"],"sourcesContent":["/*\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 chalk from 'chalk';\n\nexport class CustomError extends Error {\n get name(): string {\n return this.constructor.name;\n }\n}\n\nexport class ExitCodeError extends CustomError {\n readonly code: number;\n\n constructor(code: number, command?: string) {\n if (command) {\n super(`Command '${command}' exited with code ${code}`);\n } else {\n super(`Child exited with code ${code}`);\n }\n this.code = code;\n }\n}\n\nexport function exitWithError(error: Error): never {\n if (error instanceof ExitCodeError) {\n process.stderr.write(`\\n${chalk.red(error.message)}\\n\\n`);\n process.exit(error.code);\n } else {\n process.stderr.write(`\\n${chalk.red(`${error}`)}\\n\\n`);\n process.exit(1);\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\n/* eslint-disable monorepo/no-relative-import */\n\n/*\nThis is a list of all packages used by the template. If dependencies are added or removed,\nthis list should be updated as well.\n\nThe list, and the accompanying peerDependencies entries, are here to ensure correct versioning\nand bumping of this package. Without this list the version would not be bumped unless we\nmanually trigger a release.\n\nThis does not create an actual dependency on these packages and does not bring in any code.\nRelative imports are used rather than package imports to make sure the packages aren't externalized.\nRollup will extract the value of the version field in each package at build time without\nleaving any imports in place.\n*/\n\nimport { version as appDefaults } from '../../../app-defaults/package.json';\nimport { version as backendCommon } from '../../../backend-common/package.json';\nimport { version as catalogClient } from '../../../catalog-client/package.json';\nimport { version as catalogModel } from '../../../catalog-model/package.json';\nimport { version as cli } from '../../../cli/package.json';\nimport { version as config } from '../../../config/package.json';\nimport { version as coreAppApi } from '../../../core-app-api/package.json';\nimport { version as coreComponents } from '../../../core-components/package.json';\nimport { version as corePluginApi } from '../../../core-plugin-api/package.json';\nimport { version as errors } from '../../../errors/package.json';\nimport { version as integrationReact } from '../../../integration-react/package.json';\nimport { version as testUtils } from '../../../test-utils/package.json';\nimport { version as theme } from '../../../theme/package.json';\n\nimport { version as pluginApiDocs } from '../../../../plugins/api-docs/package.json';\nimport { version as pluginAppBackend } from '../../../../plugins/app-backend/package.json';\nimport { version as pluginAuthBackend } from '../../../../plugins/auth-backend/package.json';\nimport { version as pluginCatalog } from '../../../../plugins/catalog/package.json';\nimport { version as pluginCatalogReact } from '../../../../plugins/catalog-react/package.json';\nimport { version as pluginCatalogBackend } from '../../../../plugins/catalog-backend/package.json';\nimport { version as pluginCatalogImport } from '../../../../plugins/catalog-import/package.json';\nimport { version as pluginCircleci } from '../../../../plugins/circleci/package.json';\nimport { version as pluginExplore } from '../../../../plugins/explore/package.json';\nimport { version as pluginGithubActions } from '../../../../plugins/github-actions/package.json';\nimport { version as pluginLighthouse } from '../../../../plugins/lighthouse/package.json';\nimport { version as pluginOrg } from '../../../../plugins/org/package.json';\nimport { version as pluginProxyBackend } from '../../../../plugins/proxy-backend/package.json';\nimport { version as pluginRollbarBackend } from '../../../../plugins/rollbar-backend/package.json';\nimport { version as pluginScaffolder } from '../../../../plugins/scaffolder/package.json';\nimport { version as pluginScaffolderBackend } from '../../../../plugins/scaffolder-backend/package.json';\nimport { version as pluginSearch } from '../../../../plugins/search/package.json';\nimport { version as pluginSearchBackend } from '../../../../plugins/search-backend/package.json';\nimport { version as pluginSearchBackendNode } from '../../../../plugins/search-backend-node/package.json';\nimport { version as pluginTechRadar } from '../../../../plugins/tech-radar/package.json';\nimport { version as pluginTechdocs } from '../../../../plugins/techdocs/package.json';\nimport { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-backend/package.json';\nimport { version as pluginUserSettings } from '../../../../plugins/user-settings/package.json';\n\nexport const packageVersions = {\n '@backstage/app-defaults': appDefaults,\n '@backstage/backend-common': backendCommon,\n '@backstage/catalog-client': catalogClient,\n '@backstage/catalog-model': catalogModel,\n '@backstage/cli': cli,\n '@backstage/config': config,\n '@backstage/core-app-api': coreAppApi,\n '@backstage/core-components': coreComponents,\n '@backstage/core-plugin-api': corePluginApi,\n '@backstage/errors': errors,\n '@backstage/integration-react': integrationReact,\n '@backstage/plugin-api-docs': pluginApiDocs,\n '@backstage/plugin-app-backend': pluginAppBackend,\n '@backstage/plugin-auth-backend': pluginAuthBackend,\n '@backstage/plugin-catalog': pluginCatalog,\n '@backstage/plugin-catalog-react': pluginCatalogReact,\n '@backstage/plugin-catalog-backend': pluginCatalogBackend,\n '@backstage/plugin-catalog-import': pluginCatalogImport,\n '@backstage/plugin-circleci': pluginCircleci,\n '@backstage/plugin-explore': pluginExplore,\n '@backstage/plugin-github-actions': pluginGithubActions,\n '@backstage/plugin-lighthouse': pluginLighthouse,\n '@backstage/plugin-org': pluginOrg,\n '@backstage/plugin-proxy-backend': pluginProxyBackend,\n '@backstage/plugin-rollbar-backend': pluginRollbarBackend,\n '@backstage/plugin-scaffolder': pluginScaffolder,\n '@backstage/plugin-scaffolder-backend': pluginScaffolderBackend,\n '@backstage/plugin-search': pluginSearch,\n '@backstage/plugin-search-backend': pluginSearchBackend,\n '@backstage/plugin-search-backend-node': pluginSearchBackendNode,\n '@backstage/plugin-tech-radar': pluginTechRadar,\n '@backstage/plugin-techdocs': pluginTechdocs,\n '@backstage/plugin-techdocs-backend': pluginTechdocsBackend,\n '@backstage/plugin-user-settings': pluginUserSettings,\n '@backstage/test-utils': testUtils,\n '@backstage/theme': theme,\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 { BACKSTAGE_JSON } from '@backstage/cli-common';\nimport chalk from 'chalk';\nimport fs from 'fs-extra';\nimport handlebars from 'handlebars';\nimport ora from 'ora';\nimport recursive from 'recursive-readdir';\nimport {\n basename,\n dirname,\n join,\n resolve as resolvePath,\n relative as relativePath,\n} from 'path';\nimport { exec as execCb } from 'child_process';\nimport { packageVersions } from './versions';\nimport { promisify } from 'util';\n\nconst TASK_NAME_MAX_LENGTH = 14;\nconst exec = promisify(execCb);\n\nexport class Task {\n static log(name: string = '') {\n process.stdout.write(`${chalk.green(name)}\\n`);\n }\n\n static error(message: string = '') {\n process.stdout.write(`\\n${chalk.red(message)}\\n\\n`);\n }\n\n static section(name: string) {\n const title = chalk.green(`${name}:`);\n process.stdout.write(`\\n ${title}\\n`);\n }\n\n static exit(code: number = 0) {\n process.exit(code);\n }\n\n static async forItem(\n task: string,\n item: string,\n taskFunc: () => Promise<void>,\n ): Promise<void> {\n const paddedTask = chalk.green(task.padEnd(TASK_NAME_MAX_LENGTH));\n\n const spinner = ora({\n prefixText: chalk.green(` ${paddedTask}${chalk.cyan(item)}`),\n spinner: 'arc',\n color: 'green',\n }).start();\n\n try {\n await taskFunc();\n spinner.succeed();\n } catch (error) {\n spinner.fail();\n throw error;\n }\n }\n}\n\n/**\n * Generate a templated backstage project\n *\n * @param templateDir - location containing template files\n * @param destinationDir - location to save templated project\n * @param context - template parameters\n */\nexport async function templatingTask(\n templateDir: string,\n destinationDir: string,\n context: any,\n version: string,\n) {\n const files = await recursive(templateDir).catch(error => {\n throw new Error(`Failed to read template directory: ${error.message}`);\n });\n\n for (const file of files) {\n const destinationFile = resolvePath(\n destinationDir,\n relativePath(templateDir, file),\n );\n await fs.ensureDir(dirname(destinationFile));\n\n if (file.endsWith('.hbs')) {\n await Task.forItem('templating', basename(file), async () => {\n const destination = destinationFile.replace(/\\.hbs$/, '');\n\n const template = await fs.readFile(file);\n const compiled = handlebars.compile(template.toString());\n const contents = compiled(\n { name: basename(destination), ...context },\n {\n helpers: {\n version(name: keyof typeof packageVersions) {\n if (name in packageVersions) {\n return packageVersions[name];\n }\n throw new Error(`No version available for package ${name}`);\n },\n },\n },\n );\n\n await fs.writeFile(destination, contents).catch(error => {\n throw new Error(\n `Failed to create file: ${destination}: ${error.message}`,\n );\n });\n });\n } else {\n await Task.forItem('copying', basename(file), async () => {\n await fs.copyFile(file, destinationFile).catch(error => {\n const destination = destinationFile;\n throw new Error(\n `Failed to copy file to ${destination} : ${error.message}`,\n );\n });\n });\n }\n }\n await Task.forItem('creating', BACKSTAGE_JSON, () =>\n fs.writeFile(\n join(destinationDir, BACKSTAGE_JSON),\n `{\\n \"version\": ${JSON.stringify(version)}\\n}\\n`,\n ),\n );\n}\n\n/**\n * Verify that application target does not already exist\n *\n * @param rootDir - The directory to create application folder `name`\n * @param name - The specified name of the application\n * @Throws Error - If directory with name of `destination` already exists\n */\nexport async function checkAppExistsTask(rootDir: string, name: string) {\n await Task.forItem('checking', name, async () => {\n const destination = resolvePath(rootDir, name);\n\n if (await fs.pathExists(destination)) {\n const existing = chalk.cyan(destination.replace(`${rootDir}/`, ''));\n throw new Error(\n `A directory with the same name already exists: ${existing}\\nPlease try again with a different app name`,\n );\n }\n });\n}\n\n/**\n * Verify that application `path` exists, otherwise create the directory\n *\n * @param {string} path - target to create directory\n * @throws {Error} if `path` is a file, or `fs.mkdir` fails\n */\nexport async function checkPathExistsTask(path: string) {\n await Task.forItem('checking', path, async () => {\n try {\n await fs.mkdirs(path);\n } catch (error) {\n // will fail if a file already exists at given `path`\n throw new Error(`Failed to create app directory: ${error.message}`);\n }\n });\n}\n\n/**\n * Create a folder to store templated files\n *\n * @param {string} tempDir - target temporary directory\n * @throws {Error} if `fs.mkdir` fails\n */\nexport async function createTemporaryAppFolderTask(tempDir: string) {\n await Task.forItem('creating', 'temporary directory', async () => {\n try {\n await fs.mkdir(tempDir);\n } catch (error) {\n throw new Error(`Failed to create temporary app directory, ${error}`);\n }\n });\n}\n\n/**\n * Run `yarn install` and `run tsc` in application directory\n *\n * @param {string} appDir - location of application to build\n */\nexport async function buildAppTask(appDir: string) {\n const runCmd = async (cmd: string) => {\n await Task.forItem('executing', cmd, async () => {\n process.chdir(appDir);\n await exec(cmd).catch(error => {\n process.stdout.write(error.stderr);\n process.stdout.write(error.stdout);\n throw new Error(`Could not execute command ${chalk.cyan(cmd)}`);\n });\n });\n };\n\n await runCmd('yarn install');\n await runCmd('yarn tsc');\n}\n\n/**\n * Move temporary directory to destination application folder\n *\n * @param {string} tempDir source path to copy files from\n * @param {string} destination target path to copy files\n * @param {string} id\n * @throws {Error} if `fs.move` fails\n */\nexport async function moveAppTask(\n tempDir: string,\n destination: string,\n id: string,\n) {\n await Task.forItem('moving', id, async () => {\n await fs\n .move(tempDir, destination)\n .catch(error => {\n throw new Error(\n `Failed to move app from ${tempDir} to ${destination}: ${error.message}`,\n );\n })\n .finally(() => {\n // remove temporary files on both success and failure\n fs.removeSync(tempDir);\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 chalk from 'chalk';\nimport { Command } from 'commander';\nimport inquirer, { Answers, Question } from 'inquirer';\nimport { resolve as resolvePath } from 'path';\nimport { findPaths } from '@backstage/cli-common';\nimport os from 'os';\nimport {\n Task,\n buildAppTask,\n checkAppExistsTask,\n checkPathExistsTask,\n createTemporaryAppFolderTask,\n moveAppTask,\n templatingTask,\n} from './lib/tasks';\n\nexport default async (cmd: Command, version: string): Promise<void> => {\n /* eslint-disable-next-line no-restricted-syntax */\n const paths = findPaths(__dirname);\n\n const questions: Question[] = [\n {\n type: 'input',\n name: 'name',\n message: chalk.blue('Enter a name for the app [required]'),\n validate: (value: any) => {\n if (!value) {\n return chalk.red('Please enter a name for the app');\n } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {\n return chalk.red(\n 'App name must be lowercase and contain only letters, digits, and dashes.',\n );\n }\n return true;\n },\n },\n {\n type: 'list',\n name: 'dbType',\n message: chalk.blue('Select database for the backend [required]'),\n // @ts-ignore\n choices: ['SQLite', 'PostgreSQL'],\n },\n ];\n const answers: Answers = await inquirer.prompt(questions);\n answers.dbTypePG = answers.dbType === 'PostgreSQL';\n answers.dbTypeSqlite = answers.dbType === 'SQLite';\n\n const templateDir = paths.resolveOwn('templates/default-app');\n const tempDir = resolvePath(os.tmpdir(), answers.name);\n\n // Use `--path` argument as applicaiton directory when specified, otherwise\n // create a directory using `answers.name`\n const appDir = cmd.path\n ? resolvePath(paths.targetDir, cmd.path)\n : resolvePath(paths.targetDir, answers.name);\n\n Task.log();\n Task.log('Creating the app...');\n\n try {\n if (cmd.path) {\n // Template directly to specified path\n\n Task.section('Checking that supplied path exists');\n await checkPathExistsTask(appDir);\n\n Task.section('Preparing files');\n await templatingTask(templateDir, cmd.path, answers, version);\n } else {\n // Template to temporary location, and then move files\n\n Task.section('Checking if the directory is available');\n await checkAppExistsTask(paths.targetDir, answers.name);\n\n Task.section('Creating a temporary app directory');\n await createTemporaryAppFolderTask(tempDir);\n\n Task.section('Preparing files');\n await templatingTask(templateDir, tempDir, answers, version);\n\n Task.section('Moving to final location');\n await moveAppTask(tempDir, appDir, answers.name);\n }\n\n if (!cmd.skipInstall) {\n Task.section('Building the app');\n await buildAppTask(appDir);\n }\n\n Task.log();\n Task.log(\n chalk.green(`🥇 Successfully created ${chalk.cyan(answers.name)}`),\n );\n Task.log();\n Task.section('All set! Now you might want to');\n Task.log(` Run the app: ${chalk.cyan(`cd ${answers.name} && yarn dev`)}`);\n Task.log(\n ' Set up the software catalog: https://backstage.io/docs/features/software-catalog/configuration',\n );\n Task.log(' Add authentication: https://backstage.io/docs/auth/');\n Task.log();\n Task.exit();\n } catch (error) {\n Task.error(String(error));\n\n Task.log('It seems that something went wrong when creating the app 🤔');\n\n Task.error('🔥 Failed to create app!');\n Task.exit(1);\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\n/**\n * A CLI that helps you create your own Backstage app\n *\n * @packageDocumentation\n */\n\nimport program from 'commander';\nimport { exitWithError } from './lib/errors';\nimport { version } from '../package.json';\nimport createApp from './createApp';\n\nconst main = (argv: string[]) => {\n program\n .name('backstage-create-app')\n .version(version)\n .description('Creates a new app in a new directory or specified path')\n .option(\n '--path [directory]',\n 'Location to store the app defaulting to a new folder with the app name',\n )\n .option(\n '--skip-install',\n 'Skip the install and builds steps after creating the app',\n )\n .action(cmd => createApp(cmd, version));\n\n program.parse(argv);\n};\n\nprocess.on('unhandledRejection', rejection => {\n if (rejection instanceof Error) {\n exitWithError(rejection);\n } else {\n exitWithError(new Error(`Unknown rejection: '${rejection}'`));\n }\n});\n\nmain(process.argv);\n"],"names":["chalk","appDefaults","backendCommon","catalogClient","catalogModel","cli","config","coreAppApi","coreComponents","corePluginApi","errors","integrationReact","pluginApiDocs","pluginAppBackend","pluginAuthBackend","pluginCatalog","pluginCatalogReact","pluginCatalogBackend","pluginCatalogImport","pluginCircleci","pluginExplore","pluginGithubActions","pluginLighthouse","pluginOrg","pluginProxyBackend","pluginRollbarBackend","pluginScaffolder","pluginScaffolderBackend","pluginSearch","pluginSearchBackend","pluginSearchBackendNode","pluginTechRadar","pluginTechdocs","pluginTechdocsBackend","pluginUserSettings","testUtils","theme","promisify","execCb","ora","recursive","resolvePath","relativePath","fs","dirname","basename","handlebars","BACKSTAGE_JSON","join","findPaths","inquirer","os","version"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/lib/errors.ts","../src/lib/versions.ts","../src/lib/tasks.ts","../src/createApp.ts","../src/index.ts"],"sourcesContent":["/*\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 chalk from 'chalk';\n\nexport class CustomError extends Error {\n get name(): string {\n return this.constructor.name;\n }\n}\n\nexport class ExitCodeError extends CustomError {\n readonly code: number;\n\n constructor(code: number, command?: string) {\n if (command) {\n super(`Command '${command}' exited with code ${code}`);\n } else {\n super(`Child exited with code ${code}`);\n }\n this.code = code;\n }\n}\n\nexport function exitWithError(error: Error): never {\n if (error instanceof ExitCodeError) {\n process.stderr.write(`\\n${chalk.red(error.message)}\\n\\n`);\n process.exit(error.code);\n } else {\n process.stderr.write(`\\n${chalk.red(`${error}`)}\\n\\n`);\n process.exit(1);\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\n/* eslint-disable monorepo/no-relative-import */\n\n/*\nThis is a list of all packages used by the template. If dependencies are added or removed,\nthis list should be updated as well.\n\nThe list, and the accompanying peerDependencies entries, are here to ensure correct versioning\nand bumping of this package. Without this list the version would not be bumped unless we\nmanually trigger a release.\n\nThis does not create an actual dependency on these packages and does not bring in any code.\nRelative imports are used rather than package imports to make sure the packages aren't externalized.\nRollup will extract the value of the version field in each package at build time without\nleaving any imports in place.\n*/\n\nimport { version as appDefaults } from '../../../app-defaults/package.json';\nimport { version as backendCommon } from '../../../backend-common/package.json';\nimport { version as backendTasks } from '../../../backend-tasks/package.json';\nimport { version as catalogClient } from '../../../catalog-client/package.json';\nimport { version as catalogModel } from '../../../catalog-model/package.json';\nimport { version as cli } from '../../../cli/package.json';\nimport { version as config } from '../../../config/package.json';\nimport { version as coreAppApi } from '../../../core-app-api/package.json';\nimport { version as coreComponents } from '../../../core-components/package.json';\nimport { version as corePluginApi } from '../../../core-plugin-api/package.json';\nimport { version as errors } from '../../../errors/package.json';\nimport { version as integrationReact } from '../../../integration-react/package.json';\nimport { version as testUtils } from '../../../test-utils/package.json';\nimport { version as theme } from '../../../theme/package.json';\n\nimport { version as pluginApiDocs } from '../../../../plugins/api-docs/package.json';\nimport { version as pluginAppBackend } from '../../../../plugins/app-backend/package.json';\nimport { version as pluginAuthBackend } from '../../../../plugins/auth-backend/package.json';\nimport { version as pluginCatalog } from '../../../../plugins/catalog/package.json';\nimport { version as pluginCatalogReact } from '../../../../plugins/catalog-react/package.json';\nimport { version as pluginCatalogBackend } from '../../../../plugins/catalog-backend/package.json';\nimport { version as pluginCatalogImport } from '../../../../plugins/catalog-import/package.json';\nimport { version as pluginCircleci } from '../../../../plugins/circleci/package.json';\nimport { version as pluginExplore } from '../../../../plugins/explore/package.json';\nimport { version as pluginGithubActions } from '../../../../plugins/github-actions/package.json';\nimport { version as pluginLighthouse } from '../../../../plugins/lighthouse/package.json';\nimport { version as pluginOrg } from '../../../../plugins/org/package.json';\nimport { version as pluginProxyBackend } from '../../../../plugins/proxy-backend/package.json';\nimport { version as pluginRollbarBackend } from '../../../../plugins/rollbar-backend/package.json';\nimport { version as pluginScaffolder } from '../../../../plugins/scaffolder/package.json';\nimport { version as pluginScaffolderBackend } from '../../../../plugins/scaffolder-backend/package.json';\nimport { version as pluginSearch } from '../../../../plugins/search/package.json';\nimport { version as pluginSearchBackend } from '../../../../plugins/search-backend/package.json';\nimport { version as pluginSearchBackendNode } from '../../../../plugins/search-backend-node/package.json';\nimport { version as pluginTechRadar } from '../../../../plugins/tech-radar/package.json';\nimport { version as pluginTechdocs } from '../../../../plugins/techdocs/package.json';\nimport { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-backend/package.json';\nimport { version as pluginUserSettings } from '../../../../plugins/user-settings/package.json';\n\nexport const packageVersions = {\n '@backstage/app-defaults': appDefaults,\n '@backstage/backend-common': backendCommon,\n '@backstage/backend-tasks': backendTasks,\n '@backstage/catalog-client': catalogClient,\n '@backstage/catalog-model': catalogModel,\n '@backstage/cli': cli,\n '@backstage/config': config,\n '@backstage/core-app-api': coreAppApi,\n '@backstage/core-components': coreComponents,\n '@backstage/core-plugin-api': corePluginApi,\n '@backstage/errors': errors,\n '@backstage/integration-react': integrationReact,\n '@backstage/plugin-api-docs': pluginApiDocs,\n '@backstage/plugin-app-backend': pluginAppBackend,\n '@backstage/plugin-auth-backend': pluginAuthBackend,\n '@backstage/plugin-catalog': pluginCatalog,\n '@backstage/plugin-catalog-react': pluginCatalogReact,\n '@backstage/plugin-catalog-backend': pluginCatalogBackend,\n '@backstage/plugin-catalog-import': pluginCatalogImport,\n '@backstage/plugin-circleci': pluginCircleci,\n '@backstage/plugin-explore': pluginExplore,\n '@backstage/plugin-github-actions': pluginGithubActions,\n '@backstage/plugin-lighthouse': pluginLighthouse,\n '@backstage/plugin-org': pluginOrg,\n '@backstage/plugin-proxy-backend': pluginProxyBackend,\n '@backstage/plugin-rollbar-backend': pluginRollbarBackend,\n '@backstage/plugin-scaffolder': pluginScaffolder,\n '@backstage/plugin-scaffolder-backend': pluginScaffolderBackend,\n '@backstage/plugin-search': pluginSearch,\n '@backstage/plugin-search-backend': pluginSearchBackend,\n '@backstage/plugin-search-backend-node': pluginSearchBackendNode,\n '@backstage/plugin-tech-radar': pluginTechRadar,\n '@backstage/plugin-techdocs': pluginTechdocs,\n '@backstage/plugin-techdocs-backend': pluginTechdocsBackend,\n '@backstage/plugin-user-settings': pluginUserSettings,\n '@backstage/test-utils': testUtils,\n '@backstage/theme': theme,\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 { BACKSTAGE_JSON } from '@backstage/cli-common';\nimport chalk from 'chalk';\nimport fs from 'fs-extra';\nimport handlebars from 'handlebars';\nimport ora from 'ora';\nimport recursive from 'recursive-readdir';\nimport {\n basename,\n dirname,\n join,\n resolve as resolvePath,\n relative as relativePath,\n} from 'path';\nimport { exec as execCb } from 'child_process';\nimport { packageVersions } from './versions';\nimport { promisify } from 'util';\n\nconst TASK_NAME_MAX_LENGTH = 14;\nconst exec = promisify(execCb);\n\nexport class Task {\n static log(name: string = '') {\n process.stdout.write(`${chalk.green(name)}\\n`);\n }\n\n static error(message: string = '') {\n process.stdout.write(`\\n${chalk.red(message)}\\n\\n`);\n }\n\n static section(name: string) {\n const title = chalk.green(`${name}:`);\n process.stdout.write(`\\n ${title}\\n`);\n }\n\n static exit(code: number = 0) {\n process.exit(code);\n }\n\n static async forItem(\n task: string,\n item: string,\n taskFunc: () => Promise<void>,\n ): Promise<void> {\n const paddedTask = chalk.green(task.padEnd(TASK_NAME_MAX_LENGTH));\n\n const spinner = ora({\n prefixText: chalk.green(` ${paddedTask}${chalk.cyan(item)}`),\n spinner: 'arc',\n color: 'green',\n }).start();\n\n try {\n await taskFunc();\n spinner.succeed();\n } catch (error) {\n spinner.fail();\n throw error;\n }\n }\n}\n\n/**\n * Generate a templated backstage project\n *\n * @param templateDir - location containing template files\n * @param destinationDir - location to save templated project\n * @param context - template parameters\n */\nexport async function templatingTask(\n templateDir: string,\n destinationDir: string,\n context: any,\n version: string,\n) {\n const files = await recursive(templateDir).catch(error => {\n throw new Error(`Failed to read template directory: ${error.message}`);\n });\n\n for (const file of files) {\n const destinationFile = resolvePath(\n destinationDir,\n relativePath(templateDir, file),\n );\n await fs.ensureDir(dirname(destinationFile));\n\n if (file.endsWith('.hbs')) {\n await Task.forItem('templating', basename(file), async () => {\n const destination = destinationFile.replace(/\\.hbs$/, '');\n\n const template = await fs.readFile(file);\n const compiled = handlebars.compile(template.toString());\n const contents = compiled(\n { name: basename(destination), ...context },\n {\n helpers: {\n version(name: keyof typeof packageVersions) {\n if (name in packageVersions) {\n return packageVersions[name];\n }\n throw new Error(`No version available for package ${name}`);\n },\n },\n },\n );\n\n await fs.writeFile(destination, contents).catch(error => {\n throw new Error(\n `Failed to create file: ${destination}: ${error.message}`,\n );\n });\n });\n } else {\n await Task.forItem('copying', basename(file), async () => {\n await fs.copyFile(file, destinationFile).catch(error => {\n const destination = destinationFile;\n throw new Error(\n `Failed to copy file to ${destination} : ${error.message}`,\n );\n });\n });\n }\n }\n await Task.forItem('creating', BACKSTAGE_JSON, () =>\n fs.writeFile(\n join(destinationDir, BACKSTAGE_JSON),\n `{\\n \"version\": ${JSON.stringify(version)}\\n}\\n`,\n ),\n );\n}\n\n/**\n * Verify that application target does not already exist\n *\n * @param rootDir - The directory to create application folder `name`\n * @param name - The specified name of the application\n * @Throws Error - If directory with name of `destination` already exists\n */\nexport async function checkAppExistsTask(rootDir: string, name: string) {\n await Task.forItem('checking', name, async () => {\n const destination = resolvePath(rootDir, name);\n\n if (await fs.pathExists(destination)) {\n const existing = chalk.cyan(destination.replace(`${rootDir}/`, ''));\n throw new Error(\n `A directory with the same name already exists: ${existing}\\nPlease try again with a different app name`,\n );\n }\n });\n}\n\n/**\n * Verify that application `path` exists, otherwise create the directory\n *\n * @param {string} path - target to create directory\n * @throws {Error} if `path` is a file, or `fs.mkdir` fails\n */\nexport async function checkPathExistsTask(path: string) {\n await Task.forItem('checking', path, async () => {\n try {\n await fs.mkdirs(path);\n } catch (error) {\n // will fail if a file already exists at given `path`\n throw new Error(`Failed to create app directory: ${error.message}`);\n }\n });\n}\n\n/**\n * Create a folder to store templated files\n *\n * @param {string} tempDir - target temporary directory\n * @throws {Error} if `fs.mkdir` fails\n */\nexport async function createTemporaryAppFolderTask(tempDir: string) {\n await Task.forItem('creating', 'temporary directory', async () => {\n try {\n await fs.mkdir(tempDir);\n } catch (error) {\n throw new Error(`Failed to create temporary app directory, ${error}`);\n }\n });\n}\n\n/**\n * Run `yarn install` and `run tsc` in application directory\n *\n * @param {string} appDir - location of application to build\n */\nexport async function buildAppTask(appDir: string) {\n const runCmd = async (cmd: string) => {\n await Task.forItem('executing', cmd, async () => {\n process.chdir(appDir);\n await exec(cmd).catch(error => {\n process.stdout.write(error.stderr);\n process.stdout.write(error.stdout);\n throw new Error(`Could not execute command ${chalk.cyan(cmd)}`);\n });\n });\n };\n\n await runCmd('yarn install');\n await runCmd('yarn tsc');\n}\n\n/**\n * Move temporary directory to destination application folder\n *\n * @param {string} tempDir source path to copy files from\n * @param {string} destination target path to copy files\n * @param {string} id\n * @throws {Error} if `fs.move` fails\n */\nexport async function moveAppTask(\n tempDir: string,\n destination: string,\n id: string,\n) {\n await Task.forItem('moving', id, async () => {\n await fs\n .move(tempDir, destination)\n .catch(error => {\n throw new Error(\n `Failed to move app from ${tempDir} to ${destination}: ${error.message}`,\n );\n })\n .finally(() => {\n // remove temporary files on both success and failure\n fs.removeSync(tempDir);\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 chalk from 'chalk';\nimport { Command } from 'commander';\nimport inquirer, { Answers, Question } from 'inquirer';\nimport { resolve as resolvePath } from 'path';\nimport { findPaths } from '@backstage/cli-common';\nimport os from 'os';\nimport {\n Task,\n buildAppTask,\n checkAppExistsTask,\n checkPathExistsTask,\n createTemporaryAppFolderTask,\n moveAppTask,\n templatingTask,\n} from './lib/tasks';\n\nexport default async (cmd: Command, version: string): Promise<void> => {\n /* eslint-disable-next-line no-restricted-syntax */\n const paths = findPaths(__dirname);\n\n const questions: Question[] = [\n {\n type: 'input',\n name: 'name',\n message: chalk.blue('Enter a name for the app [required]'),\n validate: (value: any) => {\n if (!value) {\n return chalk.red('Please enter a name for the app');\n } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {\n return chalk.red(\n 'App name must be lowercase and contain only letters, digits, and dashes.',\n );\n }\n return true;\n },\n },\n {\n type: 'list',\n name: 'dbType',\n message: chalk.blue('Select database for the backend [required]'),\n // @ts-ignore\n choices: ['SQLite', 'PostgreSQL'],\n },\n ];\n const answers: Answers = await inquirer.prompt(questions);\n answers.dbTypePG = answers.dbType === 'PostgreSQL';\n answers.dbTypeSqlite = answers.dbType === 'SQLite';\n\n const templateDir = paths.resolveOwn('templates/default-app');\n const tempDir = resolvePath(os.tmpdir(), answers.name);\n\n // Use `--path` argument as application directory when specified, otherwise\n // create a directory using `answers.name`\n const appDir = cmd.path\n ? resolvePath(paths.targetDir, cmd.path)\n : resolvePath(paths.targetDir, answers.name);\n\n Task.log();\n Task.log('Creating the app...');\n\n try {\n if (cmd.path) {\n // Template directly to specified path\n\n Task.section('Checking that supplied path exists');\n await checkPathExistsTask(appDir);\n\n Task.section('Preparing files');\n await templatingTask(templateDir, cmd.path, answers, version);\n } else {\n // Template to temporary location, and then move files\n\n Task.section('Checking if the directory is available');\n await checkAppExistsTask(paths.targetDir, answers.name);\n\n Task.section('Creating a temporary app directory');\n await createTemporaryAppFolderTask(tempDir);\n\n Task.section('Preparing files');\n await templatingTask(templateDir, tempDir, answers, version);\n\n Task.section('Moving to final location');\n await moveAppTask(tempDir, appDir, answers.name);\n }\n\n if (!cmd.skipInstall) {\n Task.section('Building the app');\n await buildAppTask(appDir);\n }\n\n Task.log();\n Task.log(\n chalk.green(`🥇 Successfully created ${chalk.cyan(answers.name)}`),\n );\n Task.log();\n Task.section('All set! Now you might want to');\n Task.log(` Run the app: ${chalk.cyan(`cd ${answers.name} && yarn dev`)}`);\n Task.log(\n ' Set up the software catalog: https://backstage.io/docs/features/software-catalog/configuration',\n );\n Task.log(' Add authentication: https://backstage.io/docs/auth/');\n Task.log();\n Task.exit();\n } catch (error) {\n Task.error(String(error));\n\n Task.log('It seems that something went wrong when creating the app 🤔');\n\n Task.error('🔥 Failed to create app!');\n Task.exit(1);\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\n/**\n * A CLI that helps you create your own Backstage app\n *\n * @packageDocumentation\n */\n\nimport program from 'commander';\nimport { exitWithError } from './lib/errors';\nimport { version } from '../package.json';\nimport createApp from './createApp';\n\nconst main = (argv: string[]) => {\n program\n .name('backstage-create-app')\n .version(version)\n .description('Creates a new app in a new directory or specified path')\n .option(\n '--path [directory]',\n 'Location to store the app defaulting to a new folder with the app name',\n )\n .option(\n '--skip-install',\n 'Skip the install and builds steps after creating the app',\n )\n .action(cmd => createApp(cmd, version));\n\n program.parse(argv);\n};\n\nprocess.on('unhandledRejection', rejection => {\n if (rejection instanceof Error) {\n exitWithError(rejection);\n } else {\n exitWithError(new Error(`Unknown rejection: '${rejection}'`));\n }\n});\n\nmain(process.argv);\n"],"names":["chalk","appDefaults","backendCommon","backendTasks","catalogClient","catalogModel","cli","config","coreAppApi","coreComponents","corePluginApi","errors","integrationReact","pluginApiDocs","pluginAppBackend","pluginAuthBackend","pluginCatalog","pluginCatalogReact","pluginCatalogBackend","pluginCatalogImport","pluginCircleci","pluginExplore","pluginGithubActions","pluginLighthouse","pluginOrg","pluginProxyBackend","pluginRollbarBackend","pluginScaffolder","pluginScaffolderBackend","pluginSearch","pluginSearchBackend","pluginSearchBackendNode","pluginTechRadar","pluginTechdocs","pluginTechdocsBackend","pluginUserSettings","testUtils","theme","promisify","execCb","ora","recursive","resolvePath","relativePath","fs","dirname","basename","handlebars","BACKSTAGE_JSON","join","findPaths","inquirer","os","version"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/create-app",
|
|
3
3
|
"description": "A CLI that helps you create your own Backstage app",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.10",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/fs-extra": "^9.0.1",
|
|
44
|
-
"@types/inquirer": "^
|
|
44
|
+
"@types/inquirer": "^8.1.3",
|
|
45
|
+
"@types/node": "^14.14.32",
|
|
45
46
|
"@types/recursive-readdir": "^2.2.0",
|
|
46
47
|
"mock-fs": "^5.1.1",
|
|
47
48
|
"ts-node": "^10.0.0"
|
|
@@ -56,5 +57,5 @@
|
|
|
56
57
|
"dist",
|
|
57
58
|
"templates"
|
|
58
59
|
],
|
|
59
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "4b2a8ed96ff427735c872a72c1864321ef698436"
|
|
60
61
|
}
|
|
@@ -77,9 +77,7 @@ auth:
|
|
|
77
77
|
providers: {}
|
|
78
78
|
|
|
79
79
|
scaffolder:
|
|
80
|
-
|
|
81
|
-
token: ${GITHUB_TOKEN}
|
|
82
|
-
visibility: public # or 'internal' or 'private'
|
|
80
|
+
# see https://backstage.io/docs/features/software-templates/configuration for software template options
|
|
83
81
|
|
|
84
82
|
catalog:
|
|
85
83
|
rules:
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@backstage/cli": "^{{version '@backstage/cli'}}",
|
|
34
|
-
"@spotify/prettier-config": "^
|
|
34
|
+
"@spotify/prettier-config": "^12.0.0",
|
|
35
35
|
"concurrently": "^6.0.0",
|
|
36
36
|
"lerna": "^4.0.0",
|
|
37
37
|
"prettier": "^2.3.2"
|
|
38
38
|
},
|
|
39
39
|
"prettier": "@spotify/prettier-config",
|
|
40
40
|
"lint-staged": {
|
|
41
|
-
"*.{js,jsx,ts,tsx}": [
|
|
41
|
+
"*.{js,jsx,ts,tsx,mjs,cjs}": [
|
|
42
42
|
"eslint --fix",
|
|
43
43
|
"prettier --write"
|
|
44
44
|
],
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"@backstage/plugin-tech-radar": "^{{version '@backstage/plugin-tech-radar'}}",
|
|
23
23
|
"@backstage/plugin-techdocs": "^{{version '@backstage/plugin-techdocs'}}",
|
|
24
24
|
"@backstage/plugin-user-settings": "^{{version '@backstage/plugin-user-settings'}}",
|
|
25
|
-
"@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
|
|
26
25
|
"@backstage/theme": "^{{version '@backstage/theme'}}",
|
|
27
26
|
"@material-ui/core": "^4.12.2",
|
|
28
27
|
"@material-ui/icons": "^4.9.1",
|
|
@@ -34,6 +33,7 @@
|
|
|
34
33
|
"react-use": "^15.3.3"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
36
|
+
"@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
|
|
37
37
|
"@testing-library/jest-dom": "^5.10.1",
|
|
38
38
|
"@testing-library/react": "^10.4.1",
|
|
39
39
|
"@testing-library/user-event": "^12.0.7",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
"scripts": {
|
|
49
49
|
"start": "backstage-cli app:serve",
|
|
50
50
|
"build": "backstage-cli app:build",
|
|
51
|
-
"test": "backstage-cli test",
|
|
52
|
-
"lint": "backstage-cli lint",
|
|
53
51
|
"clean": "backstage-cli clean",
|
|
52
|
+
"test": "backstage-cli test",
|
|
54
53
|
"test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev",
|
|
55
54
|
"test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run",
|
|
55
|
+
"lint": "backstage-cli lint",
|
|
56
56
|
"cy:dev": "cypress open",
|
|
57
57
|
"cy:run": "cypress run"
|
|
58
58
|
},
|
|
@@ -42,17 +42,11 @@
|
|
|
42
42
|
href="<%= publicPath %>/safari-pinned-tab.svg"
|
|
43
43
|
color="#5bbad5"
|
|
44
44
|
/>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
min-height: 100%;
|
|
48
|
-
}
|
|
49
|
-
</style>
|
|
50
|
-
<title><%= app.title %></title>
|
|
51
|
-
<% if (app.googleAnalyticsTrackingId && typeof app.googleAnalyticsTrackingId
|
|
52
|
-
=== 'string') { %>
|
|
45
|
+
<title><%= config.getString('app.title') %></title>
|
|
46
|
+
<% if (config.has('app.googleAnalyticsTrackingId')) { %>
|
|
53
47
|
<script
|
|
54
48
|
async
|
|
55
|
-
src="https://www.googletagmanager.com/gtag/js?id=<%= app.googleAnalyticsTrackingId %>"
|
|
49
|
+
src="https://www.googletagmanager.com/gtag/js?id=<%= config.getString('app.googleAnalyticsTrackingId') %>"
|
|
56
50
|
></script>
|
|
57
51
|
<script>
|
|
58
52
|
window.dataLayer = window.dataLayer || [];
|
|
@@ -61,11 +55,14 @@
|
|
|
61
55
|
}
|
|
62
56
|
gtag('js', new Date());
|
|
63
57
|
|
|
64
|
-
gtag(
|
|
58
|
+
gtag(
|
|
59
|
+
'config',
|
|
60
|
+
'<%= config.getString("app.googleAnalyticsTrackingId") %>',
|
|
61
|
+
);
|
|
65
62
|
</script>
|
|
66
63
|
<% } %>
|
|
67
64
|
</head>
|
|
68
|
-
<body
|
|
65
|
+
<body>
|
|
69
66
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
70
67
|
<div id="root"></div>
|
|
71
68
|
<!--
|
|
@@ -11,7 +11,13 @@ import {
|
|
|
11
11
|
SearchType,
|
|
12
12
|
DefaultResultListItem,
|
|
13
13
|
} from '@backstage/plugin-search';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
CatalogIcon,
|
|
16
|
+
Content,
|
|
17
|
+
DocsIcon,
|
|
18
|
+
Header,
|
|
19
|
+
Page,
|
|
20
|
+
} from '@backstage/core-components';
|
|
15
21
|
|
|
16
22
|
const useStyles = makeStyles((theme: Theme) => ({
|
|
17
23
|
bar: {
|
|
@@ -19,6 +25,7 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|
|
19
25
|
},
|
|
20
26
|
filters: {
|
|
21
27
|
padding: theme.spacing(2),
|
|
28
|
+
marginTop: theme.spacing(2),
|
|
22
29
|
},
|
|
23
30
|
filter: {
|
|
24
31
|
'& + &': {
|
|
@@ -37,16 +44,27 @@ const SearchPage = () => {
|
|
|
37
44
|
<Grid container direction="row">
|
|
38
45
|
<Grid item xs={12}>
|
|
39
46
|
<Paper className={classes.bar}>
|
|
40
|
-
<SearchBar
|
|
47
|
+
<SearchBar />
|
|
41
48
|
</Paper>
|
|
42
49
|
</Grid>
|
|
43
50
|
<Grid item xs={3}>
|
|
51
|
+
<SearchType.Accordion
|
|
52
|
+
name="Result Type"
|
|
53
|
+
defaultValue="software-catalog"
|
|
54
|
+
types={[
|
|
55
|
+
{
|
|
56
|
+
value: 'software-catalog',
|
|
57
|
+
name: 'Software Catalog',
|
|
58
|
+
icon: <CatalogIcon />,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
value: 'techdocs',
|
|
62
|
+
name: 'Documentation',
|
|
63
|
+
icon: <DocsIcon />,
|
|
64
|
+
},
|
|
65
|
+
]}
|
|
66
|
+
/>
|
|
44
67
|
<Paper className={classes.filters}>
|
|
45
|
-
<SearchType
|
|
46
|
-
values={['techdocs', 'software-catalog']}
|
|
47
|
-
name="type"
|
|
48
|
-
defaultValue="software-catalog"
|
|
49
|
-
/>
|
|
50
68
|
<SearchFilter.Select
|
|
51
69
|
className={classes.filter}
|
|
52
70
|
name="kind"
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"app": "0.0.0",
|
|
18
18
|
"@backstage/backend-common": "^{{version '@backstage/backend-common'}}",
|
|
19
|
+
"@backstage/backend-tasks": "^{{version '@backstage/backend-tasks'}}",
|
|
19
20
|
"@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}",
|
|
20
21
|
"@backstage/catalog-client": "^{{version '@backstage/catalog-client'}}",
|
|
21
22
|
"@backstage/config": "^{{version '@backstage/config'}}",
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
UrlReaders,
|
|
20
20
|
ServerTokenManager,
|
|
21
21
|
} from '@backstage/backend-common';
|
|
22
|
+
import { TaskScheduler } from '@backstage/backend-tasks';
|
|
22
23
|
import { Config } from '@backstage/config';
|
|
23
24
|
import app from './plugins/app';
|
|
24
25
|
import auth from './plugins/auth';
|
|
@@ -33,18 +34,28 @@ function makeCreateEnv(config: Config) {
|
|
|
33
34
|
const root = getRootLogger();
|
|
34
35
|
const reader = UrlReaders.default({ logger: root, config });
|
|
35
36
|
const discovery = SingleHostDiscovery.fromConfig(config);
|
|
36
|
-
|
|
37
|
-
root.info(`Created UrlReader ${reader}`);
|
|
38
|
-
|
|
39
37
|
const cacheManager = CacheManager.fromConfig(config);
|
|
40
38
|
const databaseManager = DatabaseManager.fromConfig(config);
|
|
41
39
|
const tokenManager = ServerTokenManager.noop();
|
|
40
|
+
const taskScheduler = TaskScheduler.fromConfig(config);
|
|
41
|
+
|
|
42
|
+
root.info(`Created UrlReader ${reader}`);
|
|
42
43
|
|
|
43
44
|
return (plugin: string): PluginEnvironment => {
|
|
44
45
|
const logger = root.child({ type: 'plugin', plugin });
|
|
45
46
|
const database = databaseManager.forPlugin(plugin);
|
|
46
47
|
const cache = cacheManager.forPlugin(plugin);
|
|
47
|
-
|
|
48
|
+
const scheduler = taskScheduler.forPlugin(plugin);
|
|
49
|
+
return {
|
|
50
|
+
logger,
|
|
51
|
+
database,
|
|
52
|
+
cache,
|
|
53
|
+
config,
|
|
54
|
+
reader,
|
|
55
|
+
discovery,
|
|
56
|
+
tokenManager,
|
|
57
|
+
scheduler,
|
|
58
|
+
};
|
|
48
59
|
};
|
|
49
60
|
}
|
|
50
61
|
|
|
@@ -70,6 +81,8 @@ async function main() {
|
|
|
70
81
|
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
|
|
71
82
|
apiRouter.use('/proxy', await proxy(proxyEnv));
|
|
72
83
|
apiRouter.use('/search', await search(searchEnv));
|
|
84
|
+
|
|
85
|
+
// Add backends ABOVE this line; this 404 handler is the catch-all fallback
|
|
73
86
|
apiRouter.use(notFoundHandler());
|
|
74
87
|
|
|
75
88
|
const service = createServiceBuilder(module)
|
|
@@ -14,6 +14,7 @@ export default async function createPlugin({
|
|
|
14
14
|
config,
|
|
15
15
|
discovery,
|
|
16
16
|
reader,
|
|
17
|
+
cache,
|
|
17
18
|
}: PluginEnvironment): Promise<Router> {
|
|
18
19
|
// Preparers are responsible for fetching source files for documentation.
|
|
19
20
|
const preparers = await Preparers.fromConfig(config, {
|
|
@@ -49,5 +50,6 @@ export default async function createPlugin({
|
|
|
49
50
|
logger,
|
|
50
51
|
config,
|
|
51
52
|
discovery,
|
|
53
|
+
cache,
|
|
52
54
|
});
|
|
53
55
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
TokenManager,
|
|
8
8
|
UrlReader,
|
|
9
9
|
} from '@backstage/backend-common';
|
|
10
|
+
import { PluginTaskScheduler } from '@backstage/backend-tasks';
|
|
10
11
|
|
|
11
12
|
export type PluginEnvironment = {
|
|
12
13
|
logger: Logger;
|
|
@@ -16,4 +17,5 @@ export type PluginEnvironment = {
|
|
|
16
17
|
reader: UrlReader;
|
|
17
18
|
discovery: PluginEndpointDiscovery;
|
|
18
19
|
tokenManager: TokenManager;
|
|
20
|
+
scheduler: PluginTaskScheduler;
|
|
19
21
|
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<meta name="theme-color" content="#000000" />
|
|
7
|
-
<meta
|
|
8
|
-
name="description"
|
|
9
|
-
content="Backstage is an open platform for building developer portals"
|
|
10
|
-
/>
|
|
11
|
-
<title>Backstage</title>
|
|
12
|
-
</head>
|
|
13
|
-
<body style="margin: 0">
|
|
14
|
-
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
15
|
-
<div id="root"></div>
|
|
16
|
-
<!--
|
|
17
|
-
This HTML file is a template.
|
|
18
|
-
If you open it directly in the browser, you will see an empty page.
|
|
19
|
-
|
|
20
|
-
You can add webfonts, meta tags, or analytics to this file.
|
|
21
|
-
The build step will place the bundled scripts into the <body> tag.
|
|
22
|
-
|
|
23
|
-
To begin the development, run `npm start` or `yarn start`.
|
|
24
|
-
To create a production bundle, use `npm run build` or `yarn build`.
|
|
25
|
-
-->
|
|
26
|
-
</body>
|
|
27
|
-
</html>
|