@backstage/create-app 0.4.3 → 0.4.7

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,168 @@
1
1
  # @backstage/create-app
2
2
 
3
+ ## 0.4.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 9603827bb5: Addressed some peer dependency warnings
8
+ - 1bada775a9: TechDocs Backend may now (optionally) leverage a cache store to improve
9
+ performance when reading content from a cloud storage provider.
10
+
11
+ To apply this change to an existing app, pass the cache manager from the plugin
12
+ environment to the `createRouter` function in your backend:
13
+
14
+ ```diff
15
+ // packages/backend/src/plugins/techdocs.ts
16
+
17
+ export default async function createPlugin({
18
+ logger,
19
+ config,
20
+ discovery,
21
+ reader,
22
+ + cache,
23
+ }: PluginEnvironment): Promise<Router> {
24
+
25
+ // ...
26
+
27
+ return await createRouter({
28
+ preparers,
29
+ generators,
30
+ publisher,
31
+ logger,
32
+ config,
33
+ discovery,
34
+ + cache,
35
+ });
36
+ ```
37
+
38
+ If your `PluginEnvironment` does not include a cache manager, be sure you've
39
+ applied [the cache management change][cm-change] to your backend as well.
40
+
41
+ [Additional configuration][td-rec-arch] is required if you wish to enable
42
+ caching in TechDocs.
43
+
44
+ [cm-change]: https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md#patch-changes-6
45
+ [td-rec-arch]: https://backstage.io/docs/features/techdocs/architecture#recommended-deployment
46
+
47
+ - 4862fbc64f: Bump @spotify/prettier-config
48
+ - 36bb4fb2e9: Removed the `scaffolder.github.visibility` configuration that is no longer used from the default app template.
49
+
50
+ ## 0.4.6
51
+
52
+ ### Patch Changes
53
+
54
+ - 24d2ce03f3: Search Modal now relies on the Search Context to access state and state setter. If you use the SidebarSearchModal as described in the [getting started documentation](https://backstage.io/docs/features/search/getting-started#using-the-search-modal), make sure to update your code with the SearchContextProvider.
55
+
56
+ ```diff
57
+ export const Root = ({ children }: PropsWithChildren<{}>) => (
58
+ <SidebarPage>
59
+ <Sidebar>
60
+ <SidebarLogo />
61
+ - <SidebarSearchModal />
62
+ + <SearchContextProvider>
63
+ + <SidebarSearchModal />
64
+ + </SearchContextProvider>
65
+ <SidebarDivider />
66
+ ...
67
+ ```
68
+
69
+ - 905dd952ac: Incorporate usage of the tokenManager into the backend created using `create-app`.
70
+
71
+ In existing backends, update the `PluginEnvironment` to include a `tokenManager`:
72
+
73
+ ```diff
74
+ // packages/backend/src/types.ts
75
+
76
+ ...
77
+ import {
78
+ ...
79
+ + TokenManager,
80
+ } from '@backstage/backend-common';
81
+
82
+ export type PluginEnvironment = {
83
+ ...
84
+ + tokenManager: TokenManager;
85
+ };
86
+ ```
87
+
88
+ Then, create a `ServerTokenManager`. This can either be a `noop` that requires no secret and validates all requests by default, or one that uses a secret from your `app-config.yaml` to generate and validate tokens.
89
+
90
+ ```diff
91
+ // packages/backend/src/index.ts
92
+
93
+ ...
94
+ import {
95
+ ...
96
+ + ServerTokenManager,
97
+ } from '@backstage/backend-common';
98
+ ...
99
+
100
+ function makeCreateEnv(config: Config) {
101
+ ...
102
+ // CHOOSE ONE
103
+ // TokenManager not requiring a secret
104
+ + const tokenManager = ServerTokenManager.noop();
105
+ // OR TokenManager requiring a secret
106
+ + const tokenManager = ServerTokenManager.fromConfig(config);
107
+
108
+ ...
109
+ return (plugin: string): PluginEnvironment => {
110
+ ...
111
+ - return { logger, cache, database, config, reader, discovery };
112
+ + return { logger, cache, database, config, reader, discovery, tokenManager };
113
+ };
114
+ }
115
+ ```
116
+
117
+ ## 0.4.5
118
+
119
+ ### Patch Changes
120
+
121
+ - dcaeaac174: Cleaned out the `peerDependencies` in the published version of the package, making it much quicker to run `npx @backstage/create-app` as it no longer needs to install a long list of unnecessary.
122
+ - a5a5d7e1f1: DefaultTechDocsCollator is now included in the search backend, and the Search Page updated with the SearchType component that includes the techdocs type
123
+ - bab752e2b3: Change default port of backend from 7000 to 7007.
124
+
125
+ This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
126
+
127
+ You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
128
+
129
+ ```
130
+ backend:
131
+ listen: 0.0.0.0:7123
132
+ baseUrl: http://localhost:7123
133
+ ```
134
+
135
+ More information can be found here: https://backstage.io/docs/conf/writing
136
+
137
+ - 42ebbc18c0: Bump gitbeaker to the latest version
138
+
139
+ ## 0.4.4
140
+
141
+ ### Patch Changes
142
+
143
+ - 4ebc9fd277: Create backstage.json file
144
+
145
+ `@backstage/create-app` will create a new `backstage.json` file. At this point, the file will contain a `version` property, representing the version of `@backstage/create-app` used for creating the application. If the backstage's application has been bootstrapped using an older version of `@backstage/create-app`, the `backstage.json` file can be created and kept in sync, together with all the changes of the latest version of backstage, by running the following script:
146
+
147
+ ```bash
148
+ yarn backstage-cli versions:bump
149
+ ```
150
+
151
+ - e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers`
152
+ - 014cbf8cb9: Migrated the app template use the new `@backstage/app-defaults` for the `createApp` import, since the `createApp` exported by `@backstage/app-core-api` will be removed in the future.
153
+
154
+ To migrate an existing application, add the latest version of `@backstage/app-defaults` as a dependency in `packages/app/package.json`, and make the following change to `packages/app/src/App.tsx`:
155
+
156
+ ```diff
157
+ -import { createApp, FlatRoutes } from '@backstage/core-app-api';
158
+ +import { createApp } from '@backstage/app-defaults';
159
+ +import { FlatRoutes } from '@backstage/core-app-api';
160
+ ```
161
+
162
+ - 2163e83fa2: Refactor and add regression tests for create-app tasks
163
+ - Updated dependencies
164
+ - @backstage/cli-common@0.1.6
165
+
3
166
  ## 0.4.3
4
167
 
5
168
  ### 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
@@ -2,24 +2,24 @@
2
2
 
3
3
  var program = require('commander');
4
4
  var chalk = require('chalk');
5
- var fs = require('fs-extra');
6
- var util = require('util');
7
5
  var inquirer = require('inquirer');
8
- var child_process = require('child_process');
9
6
  var path = require('path');
10
7
  var cliCommon = require('@backstage/cli-common');
11
8
  var os = require('os');
9
+ var fs = require('fs-extra');
12
10
  var handlebars = require('handlebars');
13
11
  var ora = require('ora');
14
12
  var recursive = require('recursive-readdir');
13
+ var child_process = require('child_process');
14
+ var util = require('util');
15
15
 
16
16
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
17
17
 
18
18
  var program__default = /*#__PURE__*/_interopDefaultLegacy(program);
19
19
  var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
20
- var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
21
20
  var inquirer__default = /*#__PURE__*/_interopDefaultLegacy(inquirer);
22
21
  var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
22
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
23
23
  var handlebars__default = /*#__PURE__*/_interopDefaultLegacy(handlebars);
24
24
  var ora__default = /*#__PURE__*/_interopDefaultLegacy(ora);
25
25
  var recursive__default = /*#__PURE__*/_interopDefaultLegacy(recursive);
@@ -42,92 +42,95 @@ 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$z = "0.4.3";
58
+ var version$A = "0.4.7";
59
+
60
+ var version$z = "0.1.2";
59
61
 
60
- var version$y = "0.9.9";
62
+ var version$y = "0.9.13";
61
63
 
62
- var version$x = "0.5.1";
64
+ var version$x = "0.5.2";
63
65
 
64
- var version$w = "0.9.6";
66
+ var version$w = "0.9.7";
65
67
 
66
- var version$v = "0.8.2";
68
+ var version$v = "0.10.1";
67
69
 
68
70
  var version$u = "0.1.11";
69
71
 
70
- var version$t = "0.1.20";
72
+ var version$t = "0.2.0";
71
73
 
72
- var version$s = "0.7.3";
74
+ var version$s = "0.8.0";
73
75
 
74
- var version$r = "0.1.13";
76
+ var version$r = "0.3.0";
75
77
 
76
- var version$q = "0.1.4";
78
+ var version$q = "0.1.5";
77
79
 
78
- var version$p = "0.1.13";
80
+ var version$p = "0.1.15";
79
81
 
80
- var version$o = "0.1.21";
82
+ var version$o = "0.1.24";
81
83
 
82
- var version$n = "0.2.13";
84
+ var version$n = "0.2.14";
83
85
 
84
- var version$m = "0.6.13";
86
+ var version$m = "0.6.18";
85
87
 
86
- var version$l = "0.3.18";
88
+ var version$l = "0.3.19";
87
89
 
88
- var version$k = "0.4.7";
90
+ var version$k = "0.5.0";
89
91
 
90
- var version$j = "0.7.2";
92
+ var version$j = "0.7.4";
91
93
 
92
- var version$i = "0.6.3";
94
+ var version$i = "0.6.5";
93
95
 
94
- var version$h = "0.17.3";
96
+ var version$h = "0.19.1";
95
97
 
96
- var version$g = "0.7.3";
98
+ var version$g = "0.7.5";
97
99
 
98
- var version$f = "0.2.28";
100
+ var version$f = "0.2.31";
99
101
 
100
- var version$e = "0.3.20";
102
+ var version$e = "0.3.22";
101
103
 
102
- var version$d = "0.4.23";
104
+ var version$d = "0.4.26";
103
105
 
104
- var version$c = "0.2.29";
106
+ var version$c = "0.2.31";
105
107
 
106
- var version$b = "0.3.27";
108
+ var version$b = "0.3.30";
107
109
 
108
- var version$a = "0.2.13";
110
+ var version$a = "0.2.14";
109
111
 
110
- var version$9 = "0.1.15";
112
+ var version$9 = "0.1.16";
111
113
 
112
- var version$8 = "0.11.10";
114
+ var version$8 = "0.11.14";
113
115
 
114
- var version$7 = "0.15.12";
116
+ var version$7 = "0.15.16";
115
117
 
116
- var version$6 = "0.4.17";
118
+ var version$6 = "0.5.1";
117
119
 
118
- var version$5 = "0.2.6";
120
+ var version$5 = "0.2.8";
119
121
 
120
122
  var version$4 = "0.4.3";
121
123
 
122
- var version$3 = "0.4.11";
124
+ var version$3 = "0.4.13";
123
125
 
124
- var version$2 = "0.12.5";
126
+ var version$2 = "0.12.9";
125
127
 
126
- var version$1 = "0.10.7";
128
+ var version$1 = "0.12.0";
127
129
 
128
- var version = "0.3.10";
130
+ var version = "0.3.13";
129
131
 
130
132
  const packageVersions = {
133
+ "@backstage/app-defaults": version$z,
131
134
  "@backstage/backend-common": version$y,
132
135
  "@backstage/catalog-client": version$x,
133
136
  "@backstage/catalog-model": version$w,
@@ -166,19 +169,20 @@ const packageVersions = {
166
169
  };
167
170
 
168
171
  const TASK_NAME_MAX_LENGTH = 14;
172
+ const exec = util.promisify(child_process.exec);
169
173
  class Task {
170
174
  static log(name = "") {
171
- process.stdout.write(`${chalk__default['default'].green(name)}
175
+ process.stdout.write(`${chalk__default["default"].green(name)}
172
176
  `);
173
177
  }
174
178
  static error(message = "") {
175
179
  process.stdout.write(`
176
- ${chalk__default['default'].red(message)}
180
+ ${chalk__default["default"].red(message)}
177
181
 
178
182
  `);
179
183
  }
180
184
  static section(name) {
181
- const title = chalk__default['default'].green(`${name}:`);
185
+ const title = chalk__default["default"].green(`${name}:`);
182
186
  process.stdout.write(`
183
187
  ${title}
184
188
  `);
@@ -187,9 +191,9 @@ ${chalk__default['default'].red(message)}
187
191
  process.exit(code);
188
192
  }
189
193
  static async forItem(task, item, taskFunc) {
190
- const paddedTask = chalk__default['default'].green(task.padEnd(TASK_NAME_MAX_LENGTH));
191
- const spinner = ora__default['default']({
192
- prefixText: chalk__default['default'].green(` ${paddedTask}${chalk__default['default'].cyan(item)}`),
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)}`),
193
197
  spinner: "arc",
194
198
  color: "green"
195
199
  }).start();
@@ -202,19 +206,19 @@ ${chalk__default['default'].red(message)}
202
206
  }
203
207
  }
204
208
  }
205
- async function templatingTask(templateDir, destinationDir, context) {
206
- const files = await recursive__default['default'](templateDir).catch((error) => {
209
+ async function templatingTask(templateDir, destinationDir, context, version) {
210
+ const files = await recursive__default["default"](templateDir).catch((error) => {
207
211
  throw new Error(`Failed to read template directory: ${error.message}`);
208
212
  });
209
213
  for (const file of files) {
210
- const destinationFile = file.replace(templateDir, destinationDir);
211
- await fs__default['default'].ensureDir(path.dirname(destinationFile));
214
+ const destinationFile = path.resolve(destinationDir, path.relative(templateDir, file));
215
+ await fs__default["default"].ensureDir(path.dirname(destinationFile));
212
216
  if (file.endsWith(".hbs")) {
213
217
  await Task.forItem("templating", path.basename(file), async () => {
214
218
  const destination = destinationFile.replace(/\.hbs$/, "");
215
- const template = await fs__default['default'].readFile(file);
216
- const compiled = handlebars__default['default'].compile(template.toString());
217
- const contents = compiled({name: path.basename(destination), ...context}, {
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 }, {
218
222
  helpers: {
219
223
  version(name) {
220
224
  if (name in packageVersions) {
@@ -224,85 +228,88 @@ async function templatingTask(templateDir, destinationDir, context) {
224
228
  }
225
229
  }
226
230
  });
227
- await fs__default['default'].writeFile(destination, contents).catch((error) => {
231
+ await fs__default["default"].writeFile(destination, contents).catch((error) => {
228
232
  throw new Error(`Failed to create file: ${destination}: ${error.message}`);
229
233
  });
230
234
  });
231
235
  } else {
232
236
  await Task.forItem("copying", path.basename(file), async () => {
233
- await fs__default['default'].copyFile(file, destinationFile).catch((error) => {
237
+ await fs__default["default"].copyFile(file, destinationFile).catch((error) => {
234
238
  const destination = destinationFile;
235
239
  throw new Error(`Failed to copy file to ${destination} : ${error.message}`);
236
240
  });
237
241
  });
238
242
  }
239
243
  }
244
+ await Task.forItem("creating", cliCommon.BACKSTAGE_JSON, () => fs__default["default"].writeFile(path.join(destinationDir, cliCommon.BACKSTAGE_JSON), `{
245
+ "version": ${JSON.stringify(version)}
240
246
  }
241
-
242
- const exec = util.promisify(child_process.exec);
243
- async function checkAppExists(rootDir, name) {
247
+ `));
248
+ }
249
+ async function checkAppExistsTask(rootDir, name) {
244
250
  await Task.forItem("checking", name, async () => {
245
251
  const destination = path.resolve(rootDir, name);
246
- if (await fs__default['default'].pathExists(destination)) {
247
- const existing = chalk__default['default'].cyan(destination.replace(`${rootDir}/`, ""));
252
+ if (await fs__default["default"].pathExists(destination)) {
253
+ const existing = chalk__default["default"].cyan(destination.replace(`${rootDir}/`, ""));
248
254
  throw new Error(`A directory with the same name already exists: ${existing}
249
255
  Please try again with a different app name`);
250
256
  }
251
257
  });
252
258
  }
253
- async function checkPathExists(path) {
259
+ async function checkPathExistsTask(path) {
254
260
  await Task.forItem("checking", path, async () => {
255
261
  try {
256
- await fs__default['default'].mkdirs(path);
262
+ await fs__default["default"].mkdirs(path);
257
263
  } catch (error) {
258
264
  throw new Error(`Failed to create app directory: ${error.message}`);
259
265
  }
260
266
  });
261
267
  }
262
- async function createTemporaryAppFolder(tempDir) {
268
+ async function createTemporaryAppFolderTask(tempDir) {
263
269
  await Task.forItem("creating", "temporary directory", async () => {
264
270
  try {
265
- await fs__default['default'].mkdir(tempDir);
271
+ await fs__default["default"].mkdir(tempDir);
266
272
  } catch (error) {
267
273
  throw new Error(`Failed to create temporary app directory, ${error}`);
268
274
  }
269
275
  });
270
276
  }
271
- async function buildApp(appDir) {
277
+ async function buildAppTask(appDir) {
272
278
  const runCmd = async (cmd) => {
273
279
  await Task.forItem("executing", cmd, async () => {
274
280
  process.chdir(appDir);
275
281
  await exec(cmd).catch((error) => {
276
282
  process.stdout.write(error.stderr);
277
283
  process.stdout.write(error.stdout);
278
- throw new Error(`Could not execute command ${chalk__default['default'].cyan(cmd)}`);
284
+ throw new Error(`Could not execute command ${chalk__default["default"].cyan(cmd)}`);
279
285
  });
280
286
  });
281
287
  };
282
288
  await runCmd("yarn install");
283
289
  await runCmd("yarn tsc");
284
290
  }
285
- async function moveApp(tempDir, destination, id) {
291
+ async function moveAppTask(tempDir, destination, id) {
286
292
  await Task.forItem("moving", id, async () => {
287
- await fs__default['default'].move(tempDir, destination).catch((error) => {
293
+ await fs__default["default"].move(tempDir, destination).catch((error) => {
288
294
  throw new Error(`Failed to move app from ${tempDir} to ${destination}: ${error.message}`);
289
295
  }).finally(() => {
290
- fs__default['default'].removeSync(tempDir);
296
+ fs__default["default"].removeSync(tempDir);
291
297
  });
292
298
  });
293
299
  }
294
- var createApp = async (cmd) => {
300
+
301
+ var createApp = async (cmd, version) => {
295
302
  const paths = cliCommon.findPaths(__dirname);
296
303
  const questions = [
297
304
  {
298
305
  type: "input",
299
306
  name: "name",
300
- message: chalk__default['default'].blue("Enter a name for the app [required]"),
307
+ message: chalk__default["default"].blue("Enter a name for the app [required]"),
301
308
  validate: (value) => {
302
309
  if (!value) {
303
- return chalk__default['default'].red("Please enter a name for the app");
310
+ return chalk__default["default"].red("Please enter a name for the app");
304
311
  } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(value)) {
305
- return chalk__default['default'].red("App name must be lowercase and contain only letters, digits, and dashes.");
312
+ return chalk__default["default"].red("App name must be lowercase and contain only letters, digits, and dashes.");
306
313
  }
307
314
  return true;
308
315
  }
@@ -310,43 +317,43 @@ var createApp = async (cmd) => {
310
317
  {
311
318
  type: "list",
312
319
  name: "dbType",
313
- message: chalk__default['default'].blue("Select database for the backend [required]"),
320
+ message: chalk__default["default"].blue("Select database for the backend [required]"),
314
321
  choices: ["SQLite", "PostgreSQL"]
315
322
  }
316
323
  ];
317
- const answers = await inquirer__default['default'].prompt(questions);
324
+ const answers = await inquirer__default["default"].prompt(questions);
318
325
  answers.dbTypePG = answers.dbType === "PostgreSQL";
319
326
  answers.dbTypeSqlite = answers.dbType === "SQLite";
320
327
  const templateDir = paths.resolveOwn("templates/default-app");
321
- const tempDir = path.resolve(os__default['default'].tmpdir(), answers.name);
328
+ const tempDir = path.resolve(os__default["default"].tmpdir(), answers.name);
322
329
  const appDir = cmd.path ? path.resolve(paths.targetDir, cmd.path) : path.resolve(paths.targetDir, answers.name);
323
330
  Task.log();
324
331
  Task.log("Creating the app...");
325
332
  try {
326
333
  if (cmd.path) {
327
334
  Task.section("Checking that supplied path exists");
328
- await checkPathExists(appDir);
335
+ await checkPathExistsTask(appDir);
329
336
  Task.section("Preparing files");
330
- await templatingTask(templateDir, cmd.path, answers);
337
+ await templatingTask(templateDir, cmd.path, answers, version);
331
338
  } else {
332
339
  Task.section("Checking if the directory is available");
333
- await checkAppExists(paths.targetDir, answers.name);
340
+ await checkAppExistsTask(paths.targetDir, answers.name);
334
341
  Task.section("Creating a temporary app directory");
335
- await createTemporaryAppFolder(tempDir);
342
+ await createTemporaryAppFolderTask(tempDir);
336
343
  Task.section("Preparing files");
337
- await templatingTask(templateDir, tempDir, answers);
344
+ await templatingTask(templateDir, tempDir, answers, version);
338
345
  Task.section("Moving to final location");
339
- await moveApp(tempDir, appDir, answers.name);
346
+ await moveAppTask(tempDir, appDir, answers.name);
340
347
  }
341
348
  if (!cmd.skipInstall) {
342
349
  Task.section("Building the app");
343
- await buildApp(appDir);
350
+ await buildAppTask(appDir);
344
351
  }
345
352
  Task.log();
346
- Task.log(chalk__default['default'].green(`\u{1F947} Successfully created ${chalk__default['default'].cyan(answers.name)}`));
353
+ Task.log(chalk__default["default"].green(`\u{1F947} Successfully created ${chalk__default["default"].cyan(answers.name)}`));
347
354
  Task.log();
348
355
  Task.section("All set! Now you might want to");
349
- Task.log(` Run the app: ${chalk__default['default'].cyan(`cd ${answers.name} && yarn dev`)}`);
356
+ Task.log(` Run the app: ${chalk__default["default"].cyan(`cd ${answers.name} && yarn dev`)}`);
350
357
  Task.log(" Set up the software catalog: https://backstage.io/docs/features/software-catalog/configuration");
351
358
  Task.log(" Add authentication: https://backstage.io/docs/auth/");
352
359
  Task.log();
@@ -360,8 +367,8 @@ var createApp = async (cmd) => {
360
367
  };
361
368
 
362
369
  const main = (argv) => {
363
- program__default['default'].name("backstage-create-app").version(version$z).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(createApp);
364
- program__default['default'].parse(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);
365
372
  };
366
373
  process.on("unhandledRejection", (rejection) => {
367
374
  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 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/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 chalk from 'chalk';\nimport fs from 'fs-extra';\nimport handlebars from 'handlebars';\nimport ora from 'ora';\nimport { basename, dirname } from 'path';\nimport recursive from 'recursive-readdir';\nimport { packageVersions } from './versions';\n\nconst TASK_NAME_MAX_LENGTH = 14;\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\nexport async function templatingTask(\n templateDir: string,\n destinationDir: string,\n context: any,\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 = file.replace(templateDir, destinationDir);\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}\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 fs from 'fs-extra';\nimport { promisify } from 'util';\nimport chalk from 'chalk';\nimport { Command } from 'commander';\nimport inquirer, { Answers, Question } from 'inquirer';\nimport { exec as execCb } from 'child_process';\nimport { resolve as resolvePath } from 'path';\nimport { findPaths } from '@backstage/cli-common';\nimport os from 'os';\nimport { Task, templatingTask } from './lib/tasks';\n\nconst exec = promisify(execCb);\n\nasync function checkAppExists(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\nasync function checkPathExists(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\nasync function createTemporaryAppFolder(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\nasync function buildApp(appDir: string) {\n const runCmd = async (cmd: string) => {\n await Task.forItem('executing', cmd, async () => {\n process.chdir(appDir);\n\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\nasync function moveApp(tempDir: string, destination: string, id: string) {\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\nexport default async (cmd: Command): 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 checkPathExists(appDir);\n\n Task.section('Preparing files');\n await templatingTask(templateDir, cmd.path, answers);\n } else {\n // Template to temporary location, and then move files\n\n Task.section('Checking if the directory is available');\n await checkAppExists(paths.targetDir, answers.name);\n\n Task.section('Creating a temporary app directory');\n await createTemporaryAppFolder(tempDir);\n\n Task.section('Preparing files');\n await templatingTask(templateDir, tempDir, answers);\n\n Task.section('Moving to final location');\n await moveApp(tempDir, appDir, answers.name);\n }\n\n if (!cmd.skipInstall) {\n Task.section('Building the app');\n await buildApp(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(createApp);\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","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","ora","recursive","fs","dirname","basename","handlebars","promisify","execCb","resolvePath","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 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 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","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.3",
4
+ "version": "0.4.7",
5
5
  "private": false,
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -23,11 +23,14 @@
23
23
  "scripts": {
24
24
  "build": "backstage-cli build --outputs cjs",
25
25
  "lint": "backstage-cli lint",
26
+ "test": "backstage-cli test",
26
27
  "clean": "backstage-cli clean",
28
+ "prepack": "node scripts/prepack.js",
29
+ "postpack": "node scripts/postpack.js",
27
30
  "start": "nodemon --"
28
31
  },
29
32
  "dependencies": {
30
- "@backstage/cli-common": "^0.1.5",
33
+ "@backstage/cli-common": "^0.1.6",
31
34
  "chalk": "^4.0.0",
32
35
  "commander": "^6.1.0",
33
36
  "fs-extra": "9.1.0",
@@ -39,43 +42,11 @@
39
42
  "devDependencies": {
40
43
  "@types/fs-extra": "^9.0.1",
41
44
  "@types/inquirer": "^7.3.1",
45
+ "@types/node": "^14.14.32",
42
46
  "@types/recursive-readdir": "^2.2.0",
47
+ "mock-fs": "^5.1.1",
43
48
  "ts-node": "^10.0.0"
44
49
  },
45
- "peerDependencies": {
46
- "@backstage/backend-common": "*",
47
- "@backstage/catalog-client": "*",
48
- "@backstage/catalog-model": "*",
49
- "@backstage/cli": "*",
50
- "@backstage/config": "*",
51
- "@backstage/core-app-api": "*",
52
- "@backstage/core-components": "*",
53
- "@backstage/core-plugin-api": "*",
54
- "@backstage/errors": "*",
55
- "@backstage/integration-react": "*",
56
- "@backstage/plugin-api-docs": "*",
57
- "@backstage/plugin-app-backend": "*",
58
- "@backstage/plugin-auth-backend": "*",
59
- "@backstage/plugin-catalog": "*",
60
- "@backstage/plugin-catalog-backend": "*",
61
- "@backstage/plugin-catalog-import": "*",
62
- "@backstage/plugin-explore": "*",
63
- "@backstage/plugin-github-actions": "*",
64
- "@backstage/plugin-lighthouse": "*",
65
- "@backstage/plugin-proxy-backend": "*",
66
- "@backstage/plugin-rollbar-backend": "*",
67
- "@backstage/plugin-scaffolder": "*",
68
- "@backstage/plugin-scaffolder-backend": "*",
69
- "@backstage/plugin-search": "*",
70
- "@backstage/plugin-search-backend": "*",
71
- "@backstage/plugin-search-backend-node": "*",
72
- "@backstage/plugin-tech-radar": "*",
73
- "@backstage/plugin-techdocs": "*",
74
- "@backstage/plugin-techdocs-backend": "*",
75
- "@backstage/plugin-user-settings": "*",
76
- "@backstage/test-utils": "*",
77
- "@backstage/theme": "*"
78
- },
79
50
  "nodemonConfig": {
80
51
  "watch": "./src",
81
52
  "exec": "bin/backstage-create-app",
@@ -86,5 +57,5 @@
86
57
  "dist",
87
58
  "templates"
88
59
  ],
89
- "gitHead": "5bdaccc40b4a814cf0b45d429f15a3afacc2f60b"
60
+ "gitHead": "562be0b43016294e27af3ad024191bb86b13b1c1"
90
61
  }
@@ -1,8 +1,8 @@
1
1
  app:
2
2
  # Should be the same as backend.baseUrl when using the `app-backend` plugin
3
- baseUrl: http://localhost:7000
3
+ baseUrl: http://localhost:7007
4
4
 
5
5
  backend:
6
- baseUrl: http://localhost:7000
6
+ baseUrl: http://localhost:7007
7
7
  listen:
8
- port: 7000
8
+ port: 7007
@@ -6,9 +6,14 @@ organization:
6
6
  name: My Company
7
7
 
8
8
  backend:
9
- baseUrl: http://localhost:7000
9
+ # Used for enabling authentication, secret is shared by all backend plugins
10
+ # See backend-to-backend-auth.md in the docs for information on the format
11
+ # auth:
12
+ # keys:
13
+ # - secret: ${BACKEND_SECRET}
14
+ baseUrl: http://localhost:7007
10
15
  listen:
11
- port: 7000
16
+ port: 7007
12
17
  csp:
13
18
  connect-src: ["'self'", 'http:', 'https:']
14
19
  # Content-Security-Policy directives follow the Helmet format: https://helmetjs.github.io/#reference
@@ -32,7 +37,9 @@ backend:
32
37
  user: ${POSTGRES_USER}
33
38
  password: ${POSTGRES_PASSWORD}
34
39
  # https://node-postgres.com/features/ssl
35
- # ssl: require # see https://www.postgresql.org/docs/current/libpq-ssl.html Table 33.1. SSL Mode Descriptions (e.g. require)
40
+ # you can set the sslmode configuration option via the `PGSSLMODE` environment variable
41
+ # see https://www.postgresql.org/docs/current/libpq-ssl.html Table 33.1. SSL Mode Descriptions (e.g. require)
42
+ # ssl:
36
43
  # ca: # if you have a CA file and want to verify it you can uncomment this section
37
44
  # $file: <file-path>/ca/server.crt
38
45
  {{/if}}
@@ -70,9 +77,7 @@ auth:
70
77
  providers: {}
71
78
 
72
79
  scaffolder:
73
- github:
74
- token: ${GITHUB_TOKEN}
75
- visibility: public # or 'internal' or 'private'
80
+ # see https://backstage.io/docs/features/software-templates/configuration for software template options
76
81
 
77
82
  catalog:
78
83
  rules:
@@ -31,7 +31,7 @@
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"
@@ -4,6 +4,7 @@
4
4
  "private": true,
5
5
  "bundled": true,
6
6
  "dependencies": {
7
+ "@backstage/app-defaults": "^{{version '@backstage/app-defaults'}}",
7
8
  "@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}",
8
9
  "@backstage/cli": "^{{version '@backstage/cli'}}",
9
10
  "@backstage/core-app-api": "^{{version '@backstage/core-app-api'}}",
@@ -21,7 +22,6 @@
21
22
  "@backstage/plugin-tech-radar": "^{{version '@backstage/plugin-tech-radar'}}",
22
23
  "@backstage/plugin-techdocs": "^{{version '@backstage/plugin-techdocs'}}",
23
24
  "@backstage/plugin-user-settings": "^{{version '@backstage/plugin-user-settings'}}",
24
- "@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
25
25
  "@backstage/theme": "^{{version '@backstage/theme'}}",
26
26
  "@material-ui/core": "^4.12.2",
27
27
  "@material-ui/icons": "^4.9.1",
@@ -33,6 +33,7 @@
33
33
  "react-use": "^15.3.3"
34
34
  },
35
35
  "devDependencies": {
36
+ "@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
36
37
  "@testing-library/jest-dom": "^5.10.1",
37
38
  "@testing-library/react": "^10.4.1",
38
39
  "@testing-library/user-event": "^12.0.7",
@@ -47,11 +48,11 @@
47
48
  "scripts": {
48
49
  "start": "backstage-cli app:serve",
49
50
  "build": "backstage-cli app:build",
50
- "test": "backstage-cli test",
51
- "lint": "backstage-cli lint",
52
51
  "clean": "backstage-cli clean",
52
+ "test": "backstage-cli test",
53
53
  "test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev",
54
54
  "test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run",
55
+ "lint": "backstage-cli lint",
55
56
  "cy:dev": "cypress open",
56
57
  "cy:run": "cypress run"
57
58
  },
@@ -10,9 +10,9 @@ describe('App', () => {
10
10
  {
11
11
  data: {
12
12
  app: { title: 'Test' },
13
- backend: { baseUrl: 'http://localhost:7000' },
13
+ backend: { baseUrl: 'http://localhost:7007' },
14
14
  techdocs: {
15
- storageUrl: 'http://localhost:7000/api/techdocs/static/docs',
15
+ storageUrl: 'http://localhost:7007/api/techdocs/static/docs',
16
16
  },
17
17
  },
18
18
  context: 'test',
@@ -26,7 +26,8 @@ import { searchPage } from './components/search/SearchPage';
26
26
  import { Root } from './components/Root';
27
27
 
28
28
  import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components';
29
- import { createApp, FlatRoutes } from '@backstage/core-app-api';
29
+ import { createApp } from '@backstage/app-defaults';
30
+ import { FlatRoutes } from '@backstage/core-app-api';
30
31
 
31
32
  const app = createApp({
32
33
  apis,
@@ -25,7 +25,10 @@ import LogoFull from './LogoFull';
25
25
  import LogoIcon from './LogoIcon';
26
26
  import { NavLink } from 'react-router-dom';
27
27
  import { Settings as SidebarSettings } from '@backstage/plugin-user-settings';
28
- import { SidebarSearchModal } from '@backstage/plugin-search';
28
+ import {
29
+ SidebarSearchModal,
30
+ SearchContextProvider,
31
+ } from '@backstage/plugin-search';
29
32
  import {
30
33
  Sidebar,
31
34
  SidebarPage,
@@ -74,7 +77,9 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
74
77
  <SidebarPage>
75
78
  <Sidebar>
76
79
  <SidebarLogo />
77
- <SidebarSearchModal />
80
+ <SearchContextProvider>
81
+ <SidebarSearchModal />
82
+ </SearchContextProvider>
78
83
  <SidebarDivider />
79
84
  {/* Global nav, not org-specific */}
80
85
  <SidebarItem icon={HomeIcon} to="catalog" text="Home" />
@@ -2,10 +2,13 @@ import React from 'react';
2
2
  import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core';
3
3
 
4
4
  import { CatalogResultListItem } from '@backstage/plugin-catalog';
5
+ import { DocsResultListItem } from '@backstage/plugin-techdocs';
6
+
5
7
  import {
6
8
  SearchBar,
7
9
  SearchFilter,
8
10
  SearchResult,
11
+ SearchType,
9
12
  DefaultResultListItem,
10
13
  } from '@backstage/plugin-search';
11
14
  import { Content, Header, Page } from '@backstage/core-components';
@@ -39,6 +42,11 @@ const SearchPage = () => {
39
42
  </Grid>
40
43
  <Grid item xs={3}>
41
44
  <Paper className={classes.filters}>
45
+ <SearchType
46
+ values={['techdocs', 'software-catalog']}
47
+ name="type"
48
+ defaultValue="software-catalog"
49
+ />
42
50
  <SearchFilter.Select
43
51
  className={classes.filter}
44
52
  name="kind"
@@ -64,6 +72,13 @@ const SearchPage = () => {
64
72
  result={document}
65
73
  />
66
74
  );
75
+ case 'techdocs':
76
+ return (
77
+ <DocsResultListItem
78
+ key={document.location}
79
+ result={document}
80
+ />
81
+ );
67
82
  default:
68
83
  return (
69
84
  <DefaultResultListItem
@@ -36,7 +36,7 @@ yarn start
36
36
  Substitute `x` for actual values, or leave them as dummy values just to try out
37
37
  the backend without using the auth or sentry features.
38
38
 
39
- The backend starts up on port 7000 per default.
39
+ The backend starts up on port 7007 per default.
40
40
 
41
41
  ## Populating The Catalog
42
42
 
@@ -27,9 +27,9 @@
27
27
  "@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
28
28
  "@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
29
29
  "@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
30
- "@gitbeaker/node": "^30.2.0",
30
+ "@gitbeaker/node": "^34.6.0",
31
31
  "@octokit/rest": "^18.5.3",
32
- "dockerode": "^3.2.1",
32
+ "dockerode": "^3.3.1",
33
33
  "express": "^4.17.1",
34
34
  "express-promise-router": "^4.1.0",
35
35
  "knex": "^0.21.6",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@backstage/cli": "^{{version '@backstage/cli'}}",
46
- "@types/dockerode": "^3.2.1",
46
+ "@types/dockerode": "^3.3.0",
47
47
  "@types/express": "^4.17.6",
48
48
  "@types/express-serve-static-core": "^4.17.5"
49
49
  },
@@ -17,6 +17,7 @@ import {
17
17
  DatabaseManager,
18
18
  SingleHostDiscovery,
19
19
  UrlReaders,
20
+ ServerTokenManager,
20
21
  } from '@backstage/backend-common';
21
22
  import { Config } from '@backstage/config';
22
23
  import app from './plugins/app';
@@ -37,12 +38,13 @@ function makeCreateEnv(config: Config) {
37
38
 
38
39
  const cacheManager = CacheManager.fromConfig(config);
39
40
  const databaseManager = DatabaseManager.fromConfig(config);
41
+ const tokenManager = ServerTokenManager.noop();
40
42
 
41
43
  return (plugin: string): PluginEnvironment => {
42
44
  const logger = root.child({ type: 'plugin', plugin });
43
45
  const database = databaseManager.forPlugin(plugin);
44
46
  const cache = cacheManager.forPlugin(plugin);
45
- return { logger, database, cache, config, reader, discovery };
47
+ return { logger, database, cache, config, reader, discovery, tokenManager };
46
48
  };
47
49
  }
48
50
 
@@ -6,21 +6,36 @@ import {
6
6
  } from '@backstage/plugin-search-backend-node';
7
7
  import { PluginEnvironment } from '../types';
8
8
  import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
9
+ import { DefaultTechDocsCollator } from '@backstage/plugin-techdocs-backend';
9
10
 
10
11
  export default async function createPlugin({
11
12
  logger,
12
13
  discovery,
13
14
  config,
15
+ tokenManager,
14
16
  }: PluginEnvironment) {
15
17
  // Initialize a connection to a search engine.
16
18
  const searchEngine = new LunrSearchEngine({ logger });
17
19
  const indexBuilder = new IndexBuilder({ logger, searchEngine });
18
20
 
19
21
  // Collators are responsible for gathering documents known to plugins. This
20
- // particular collator gathers entities from the software catalog.
22
+ // collator gathers entities from the software catalog.
21
23
  indexBuilder.addCollator({
22
24
  defaultRefreshIntervalSeconds: 600,
23
- collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
25
+ collator: DefaultCatalogCollator.fromConfig(config, {
26
+ discovery,
27
+ tokenManager,
28
+ }),
29
+ });
30
+
31
+ // collator gathers entities from techdocs.
32
+ indexBuilder.addCollator({
33
+ defaultRefreshIntervalSeconds: 600,
34
+ collator: DefaultTechDocsCollator.fromConfig(config, {
35
+ discovery,
36
+ logger,
37
+ tokenManager,
38
+ }),
24
39
  });
25
40
 
26
41
  // The scheduler controls when documents are gathered from collators and sent
@@ -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
  }
@@ -4,6 +4,7 @@ import {
4
4
  PluginCacheManager,
5
5
  PluginDatabaseManager,
6
6
  PluginEndpointDiscovery,
7
+ TokenManager,
7
8
  UrlReader,
8
9
  } from '@backstage/backend-common';
9
10
 
@@ -14,4 +15,5 @@ export type PluginEnvironment = {
14
15
  config: Config;
15
16
  reader: UrlReader;
16
17
  discovery: PluginEndpointDiscovery;
18
+ tokenManager: TokenManager;
17
19
  };