@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 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://github.com/backstage/backstage/blob/master/docs/README.md)
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['default'].red(error.message)}
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['default'].red(`${error}`)}
51
+ ${chalk__default["default"].red(`${error}`)}
52
52
 
53
53
  `);
54
54
  process.exit(1);
55
55
  }
56
56
  }
57
57
 
58
- var version$A = "0.4.6";
58
+ var version$B = "0.4.10";
59
59
 
60
- var version$z = "0.1.1";
60
+ var version$A = "0.1.3";
61
61
 
62
- var version$y = "0.9.12";
62
+ var version$z = "0.10.1";
63
63
 
64
- var version$x = "0.5.2";
64
+ var version$y = "0.1.1";
65
65
 
66
- var version$w = "0.9.7";
66
+ var version$x = "0.5.3";
67
67
 
68
- var version$v = "0.10.0";
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.1.24";
74
+ var version$t = "0.3.0";
73
75
 
74
- var version$s = "0.7.6";
76
+ var version$s = "0.8.2";
75
77
 
76
- var version$r = "0.2.2";
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.14";
82
+ var version$p = "0.1.17";
81
83
 
82
- var version$o = "0.1.23";
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.16";
88
+ var version$m = "0.6.20";
87
89
 
88
- var version$l = "0.3.19";
90
+ var version$l = "0.3.21";
89
91
 
90
- var version$k = "0.4.10";
92
+ var version$k = "0.6.0";
91
93
 
92
- var version$j = "0.7.3";
94
+ var version$j = "0.7.6";
93
95
 
94
- var version$i = "0.6.4";
96
+ var version$i = "0.6.9";
95
97
 
96
- var version$h = "0.19.0";
98
+ var version$h = "0.19.4";
97
99
 
98
- var version$g = "0.7.4";
100
+ var version$g = "0.7.7";
99
101
 
100
- var version$f = "0.2.30";
102
+ var version$f = "0.2.32";
101
103
 
102
- var version$e = "0.3.21";
104
+ var version$e = "0.3.23";
103
105
 
104
- var version$d = "0.4.25";
106
+ var version$d = "0.4.29";
105
107
 
106
- var version$c = "0.2.30";
108
+ var version$c = "0.2.32";
107
109
 
108
- var version$b = "0.3.29";
110
+ var version$b = "0.3.32";
109
111
 
110
- var version$a = "0.2.14";
112
+ var version$a = "0.2.15";
111
113
 
112
- var version$9 = "0.1.16";
114
+ var version$9 = "0.1.18";
113
115
 
114
- var version$8 = "0.11.13";
116
+ var version$8 = "0.11.16";
115
117
 
116
- var version$7 = "0.15.15";
118
+ var version$7 = "0.15.19";
117
119
 
118
- var version$6 = "0.5.0";
120
+ var version$6 = "0.5.3";
119
121
 
120
- var version$5 = "0.2.7";
122
+ var version$5 = "0.3.0";
121
123
 
122
124
  var version$4 = "0.4.3";
123
125
 
124
- var version$3 = "0.4.12";
126
+ var version$3 = "0.5.0";
125
127
 
126
- var version$2 = "0.12.8";
128
+ var version$2 = "0.12.12";
127
129
 
128
- var version$1 = "0.11.0";
130
+ var version$1 = "0.12.2";
129
131
 
130
- var version = "0.3.12";
132
+ var version = "0.3.14";
131
133
 
132
134
  const packageVersions = {
133
- "@backstage/app-defaults": version$z,
134
- "@backstage/backend-common": version$y,
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['default'].green(name)}
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['default'].red(message)}
183
+ ${chalk__default["default"].red(message)}
181
184
 
182
185
  `);
183
186
  }
184
187
  static section(name) {
185
- const title = chalk__default['default'].green(`${name}:`);
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['default'].green(task.padEnd(TASK_NAME_MAX_LENGTH));
195
- const spinner = ora__default['default']({
196
- prefixText: chalk__default['default'].green(` ${paddedTask}${chalk__default['default'].cyan(item)}`),
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['default'](templateDir).catch((error) => {
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['default'].ensureDir(path.dirname(destinationFile));
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['default'].readFile(file);
220
- const compiled = handlebars__default['default'].compile(template.toString());
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['default'].writeFile(destination, contents).catch((error) => {
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['default'].copyFile(file, destinationFile).catch((error) => {
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['default'].writeFile(path.join(destinationDir, cliCommon.BACKSTAGE_JSON), `{
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['default'].pathExists(destination)) {
253
- const existing = chalk__default['default'].cyan(destination.replace(`${rootDir}/`, ""));
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['default'].mkdirs(path);
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['default'].mkdir(tempDir);
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['default'].cyan(cmd)}`);
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['default'].move(tempDir, destination).catch((error) => {
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['default'].removeSync(tempDir);
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['default'].blue("Enter a name for the app [required]"),
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['default'].red("Please enter a name for the app");
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['default'].red("App name must be lowercase and contain only letters, digits, and dashes.");
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['default'].blue("Select database for the backend [required]"),
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['default'].prompt(questions);
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['default'].tmpdir(), answers.name);
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['default'].green(`\u{1F947} Successfully created ${chalk__default['default'].cyan(answers.name)}`));
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['default'].cyan(`cd ${answers.name} && yarn dev`)}`);
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['default'].name("backstage-create-app").version(version$A).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$A));
371
- program__default['default'].parse(argv);
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) {
@@ -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.6",
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": "^7.3.1",
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": "a05e7081b805006e3f0b2960a08a7753357f532f"
60
+ "gitHead": "4b2a8ed96ff427735c872a72c1864321ef698436"
60
61
  }
@@ -77,9 +77,7 @@ auth:
77
77
  providers: {}
78
78
 
79
79
  scaffolder:
80
- github:
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": "^11.0.0",
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
- <style>
46
- #root {
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('config', '<%= app.googleAnalyticsTrackingId %>');
58
+ gtag(
59
+ 'config',
60
+ '<%= config.getString("app.googleAnalyticsTrackingId") %>',
61
+ );
65
62
  </script>
66
63
  <% } %>
67
64
  </head>
68
- <body style="margin: 0">
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 { Content, Header, Page } from '@backstage/core-components';
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 debounceTime={100} />
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
- return { logger, database, cache, config, reader, discovery, tokenManager };
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>