@backstage/repo-tools 0.17.1-next.0 → 0.17.1-next.1

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,14 @@
1
1
  # @backstage/repo-tools
2
2
 
3
+ ## 0.17.1-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 2e5c5f8: Bumped `glob` dependency from v7/v8/v11 to v13 to address security vulnerabilities in older versions. Bumped `rollup` from v4.27 to v4.59+ to fix a high severity path traversal vulnerability (GHSA-mw96-cpmx-2vgc).
8
+ - 8e9679b: Parallelized CLI report generation, reducing wall-clock time by ~4x.
9
+ - Updated dependencies
10
+ - @backstage/backend-plugin-api@1.9.0-next.1
11
+
3
12
  ## 0.17.1-next.0
4
13
 
5
14
  ### Patch Changes
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var path = require('node:path');
4
+ var os = require('node:os');
4
5
  var fs = require('fs-extra');
6
+ var pLimit = require('p-limit');
5
7
  var util = require('../../util.cjs.js');
6
8
  var cliCommon = require('@backstage/cli-common');
7
9
  var generateCliReport = require('./generateCliReport.cjs.js');
@@ -9,7 +11,9 @@ var logApiReportInstructions = require('../common/logApiReportInstructions.cjs.j
9
11
 
10
12
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
11
13
 
14
+ var os__default = /*#__PURE__*/_interopDefaultCompat(os);
12
15
  var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
16
+ var pLimit__default = /*#__PURE__*/_interopDefaultCompat(pLimit);
13
17
 
14
18
  function parseHelpPage(helpPageContent) {
15
19
  let usage;
@@ -59,10 +63,10 @@ function parseHelpPage(helpPageContent) {
59
63
  commandArguments
60
64
  };
61
65
  }
62
- async function exploreCliHelpPages(run) {
66
+ async function exploreCliHelpPages(run, limit) {
63
67
  const helpPages = new Array();
64
68
  async function exploreHelpPage(...path) {
65
- const content = await run(...path, "--help");
69
+ const content = await limit(() => run(...path, "--help"));
66
70
  const parsed = parseHelpPage(content);
67
71
  helpPages.push({ path, ...parsed });
68
72
  await Promise.all(
@@ -82,65 +86,68 @@ async function runCliExtraction({
82
86
  packageDirs,
83
87
  isLocalBuild
84
88
  }) {
85
- for (const packageDir of packageDirs) {
86
- console.log(`## Processing ${packageDir}`);
87
- const fullDir = cliCommon.targetPaths.resolveRoot(packageDir);
88
- const pkgJson = await fs__default.default.readJson(path.resolve(fullDir, "package.json"));
89
- if (!pkgJson.bin) {
90
- if (pkgJson.backstage?.role === "cli") {
91
- throw new Error(
92
- `CLI package ${pkgJson.name} is missing a "bin" field in its package.json`
93
- );
94
- }
95
- continue;
96
- }
97
- const models = new Array();
98
- if (typeof pkgJson.bin === "string") {
99
- const run = util.createBinRunner(fullDir, pkgJson.bin);
100
- const helpPages = await exploreCliHelpPages(run);
101
- models.push({ name: path.basename(pkgJson.bin), helpPages });
102
- } else {
103
- for (const [name, path] of Object.entries(pkgJson.bin)) {
104
- const run = util.createBinRunner(fullDir, path);
105
- const helpPages = await exploreCliHelpPages(run);
106
- models.push({ name, helpPages });
89
+ const limit = pLimit__default.default(os__default.default.cpus().length);
90
+ await Promise.all(
91
+ packageDirs.map(async (packageDir) => {
92
+ console.log(`## Processing ${packageDir}`);
93
+ const fullDir = cliCommon.targetPaths.resolveRoot(packageDir);
94
+ const pkgJson = await fs__default.default.readJson(path.resolve(fullDir, "package.json"));
95
+ if (!pkgJson.bin) {
96
+ if (pkgJson.backstage?.role === "cli") {
97
+ throw new Error(
98
+ `CLI package ${pkgJson.name} is missing a "bin" field in its package.json`
99
+ );
100
+ }
101
+ return;
107
102
  }
108
- }
109
- for (const model of models) {
110
- const report = generateCliReport.generateCliReport({ packageName: pkgJson.name, model });
111
- const reportPath = path.resolve(
112
- fullDir,
113
- `cli-report.${models.length === 1 ? "" : `${model.name}.`}md`
114
- );
115
- const existingReport = await fs__default.default.readFile(reportPath, "utf8").catch((error) => {
116
- if (error.code === "ENOENT") {
117
- return void 0;
103
+ const models = new Array();
104
+ if (typeof pkgJson.bin === "string") {
105
+ const run = util.createBinRunner(fullDir, pkgJson.bin);
106
+ const helpPages = await exploreCliHelpPages(run, limit);
107
+ models.push({ name: path.basename(pkgJson.bin), helpPages });
108
+ } else {
109
+ for (const [name, path] of Object.entries(pkgJson.bin)) {
110
+ const run = util.createBinRunner(fullDir, path);
111
+ const helpPages = await exploreCliHelpPages(run, limit);
112
+ models.push({ name, helpPages });
118
113
  }
119
- throw error;
120
- });
121
- if (existingReport !== report) {
122
- if (isLocalBuild) {
123
- console.warn(`CLI report changed for ${packageDir}`);
124
- await fs__default.default.writeFile(reportPath, report);
125
- } else {
126
- logApiReportInstructions.logApiReportInstructions();
127
- if (existingReport) {
128
- console.log("");
129
- console.log(
130
- `The conflicting file is ${path.relative(
131
- cliCommon.targetPaths.rootDir,
132
- reportPath
133
- )}, expecting the following content:`
134
- );
135
- console.log("");
136
- console.log(report);
114
+ }
115
+ for (const model of models) {
116
+ const report = generateCliReport.generateCliReport({ packageName: pkgJson.name, model });
117
+ const reportPath = path.resolve(
118
+ fullDir,
119
+ `cli-report.${models.length === 1 ? "" : `${model.name}.`}md`
120
+ );
121
+ const existingReport = await fs__default.default.readFile(reportPath, "utf8").catch((error) => {
122
+ if (error.code === "ENOENT") {
123
+ return void 0;
124
+ }
125
+ throw error;
126
+ });
127
+ if (existingReport !== report) {
128
+ if (isLocalBuild) {
129
+ console.warn(`CLI report changed for ${packageDir}`);
130
+ await fs__default.default.writeFile(reportPath, report);
131
+ } else {
137
132
  logApiReportInstructions.logApiReportInstructions();
133
+ if (existingReport) {
134
+ console.log("");
135
+ console.log(
136
+ `The conflicting file is ${path.relative(
137
+ cliCommon.targetPaths.rootDir,
138
+ reportPath
139
+ )}, expecting the following content:`
140
+ );
141
+ console.log("");
142
+ console.log(report);
143
+ logApiReportInstructions.logApiReportInstructions();
144
+ }
145
+ throw new Error(`CLI report changed for ${packageDir}`);
138
146
  }
139
- throw new Error(`CLI report changed for ${packageDir}, `);
140
147
  }
141
148
  }
142
- }
143
- }
149
+ })
150
+ );
144
151
  }
145
152
 
146
153
  exports.runCliExtraction = runCliExtraction;
@@ -97,7 +97,8 @@ async function generateDocJson(pkg) {
97
97
  async function packageDocs(paths$1 = [], opts) {
98
98
  console.warn("!!! This is an experimental command !!!");
99
99
  const existingDocsJsonPaths = glob.glob.sync(
100
- cliCommon.targetPaths.resolveRoot("dist-types/**/docs.json")
100
+ cliCommon.targetPaths.resolveRoot("dist-types/**/docs.json"),
101
+ { windowsPathsNoEscape: true }
101
102
  );
102
103
  if (existingDocsJsonPaths.length > 0) {
103
104
  console.warn(
@@ -8,40 +8,51 @@ var path = require('node:path');
8
8
 
9
9
  const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
10
10
  function createBinRunner(cwd, path$1) {
11
- return async (...command) => {
12
- const args = path$1 ? [path$1, ...command] : command;
13
- const outPath = path.join(os.tmpdir(), `backstage-cli-out-${crypto.randomUUID()}.txt`);
14
- const outFd = fs.openSync(outPath, "w");
15
- try {
16
- const result = node_child_process.spawnSync("node", args, {
11
+ return (...command) => {
12
+ return new Promise((resolve, reject) => {
13
+ const args = path$1 ? [path$1, ...command] : command;
14
+ const outPath = path.join(os.tmpdir(), `backstage-cli-out-${crypto.randomUUID()}.txt`);
15
+ const outFd = fs.openSync(outPath, "w");
16
+ const child = node_child_process.spawn("node", args, {
17
17
  cwd,
18
18
  env: { ...process.env, NO_COLOR: "1" },
19
19
  stdio: ["ignore", outFd, "pipe"]
20
20
  });
21
21
  fs.closeSync(outFd);
22
- const stdout = fs.readFileSync(outPath, "utf8").replace(ansiPattern, "");
23
- if (result.error) {
24
- throw new Error(`Process error: ${result.error.message}`);
25
- }
26
- const stderr = result.stderr?.toString() ?? "";
27
- if (result.signal) {
28
- throw new Error(
29
- `Process was killed with signal ${result.signal}
30
- ${stderr}`
31
- );
32
- } else if (result.status !== 0) {
33
- throw new Error(`Process exited with code ${result.status}
34
- ${stderr}`);
35
- } else if (stderr.trim()) {
36
- throw new Error(`Command printed error output: ${stderr}`);
37
- }
38
- return stdout;
39
- } finally {
40
- try {
41
- fs.unlinkSync(outPath);
42
- } catch {
43
- }
44
- }
22
+ const stderrChunks = [];
23
+ child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
24
+ child.on("error", (err) => {
25
+ try {
26
+ fs.unlinkSync(outPath);
27
+ } catch {
28
+ }
29
+ reject(new Error(`Process error: ${err.message}`));
30
+ });
31
+ child.on("close", (code, signal) => {
32
+ try {
33
+ const stdout = fs.readFileSync(outPath, "utf8").replace(ansiPattern, "");
34
+ const stderr = Buffer.concat(stderrChunks).toString();
35
+ if (signal) {
36
+ reject(
37
+ new Error(`Process was killed with signal ${signal}
38
+ ${stderr}`)
39
+ );
40
+ } else if (code !== 0) {
41
+ reject(new Error(`Process exited with code ${code}
42
+ ${stderr}`));
43
+ } else if (stderr.trim()) {
44
+ reject(new Error(`Command printed error output: ${stderr}`));
45
+ } else {
46
+ resolve(stdout);
47
+ }
48
+ } finally {
49
+ try {
50
+ fs.unlinkSync(outPath);
51
+ } catch {
52
+ }
53
+ }
54
+ });
55
+ });
45
56
  };
46
57
  }
47
58
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.17.1-next.0";
3
+ var version = "0.17.1-next.1";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/repo-tools",
3
- "version": "0.17.1-next.0",
3
+ "version": "0.17.1-next.1",
4
4
  "description": "CLI for Backstage repo tooling ",
5
5
  "backstage": {
6
6
  "role": "cli"
@@ -43,7 +43,7 @@
43
43
  "dependencies": {
44
44
  "@apidevtools/swagger-parser": "^10.1.0",
45
45
  "@apisyouwonthate/style-guide": "^1.4.0",
46
- "@backstage/backend-plugin-api": "1.8.1-next.0",
46
+ "@backstage/backend-plugin-api": "1.9.0-next.1",
47
47
  "@backstage/catalog-model": "1.7.7",
48
48
  "@backstage/cli-common": "0.2.1-next.0",
49
49
  "@backstage/cli-node": "0.3.1-next.0",
@@ -69,7 +69,7 @@
69
69
  "command-exists": "^1.2.9",
70
70
  "commander": "^14.0.3",
71
71
  "fs-extra": "^11.2.0",
72
- "glob": "^8.0.3",
72
+ "glob": "^13.0.0",
73
73
  "globby": "^11.0.0",
74
74
  "is-glob": "^4.0.3",
75
75
  "js-yaml": "^4.1.0",
@@ -87,8 +87,8 @@
87
87
  "zod": "^3.25.76 || ^4.0.0"
88
88
  },
89
89
  "devDependencies": {
90
- "@backstage/backend-test-utils": "1.11.2-next.0",
91
- "@backstage/cli": "0.36.1-next.0",
90
+ "@backstage/backend-test-utils": "1.11.2-next.1",
91
+ "@backstage/cli": "0.36.1-next.1",
92
92
  "@backstage/types": "1.2.2",
93
93
  "@types/is-glob": "^4.0.2",
94
94
  "@types/node": "^22.13.14",