@backstage/repo-tools 0.16.6-next.0 → 0.17.0-next.2

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,31 @@
1
1
  # @backstage/repo-tools
2
2
 
3
+ ## 0.17.0-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/backend-plugin-api@1.8.0-next.1
9
+ - @backstage/cli-common@0.2.0-next.2
10
+
11
+ ## 0.17.0-next.1
12
+
13
+ ### Minor Changes
14
+
15
+ - 0fbcf23: Added support for OpenAPI 3.1 to all `schema openapi` commands. The commands now auto-detect the OpenAPI version from the spec file and use the appropriate generator, supporting both OpenAPI 3.0.x and 3.1.x specifications.
16
+
17
+ ### Patch Changes
18
+
19
+ - 426edbe: Fixed `generate-catalog-info` command failing with "too many arguments" when invoked by lint-staged via the pre-commit hook.
20
+ - d5779e5: Updated the CLI report parser to support cleye-style help output, and strip ANSI escape codes from captured output.
21
+ - Updated dependencies
22
+ - @backstage/cli-common@0.2.0-next.1
23
+ - @backstage/cli-node@0.2.19-next.1
24
+ - @backstage/backend-plugin-api@1.7.1-next.0
25
+ - @backstage/catalog-model@1.7.6
26
+ - @backstage/config-loader@1.10.9-next.0
27
+ - @backstage/errors@1.2.7
28
+
3
29
  ## 0.16.6-next.0
4
30
 
5
31
  ### Patch Changes
@@ -12,7 +12,11 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
12
12
  var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
13
13
 
14
14
  function parseHelpPage(helpPageContent) {
15
- const [, usage] = helpPageContent.match(/^\s*Usage: (.*)$/im) ?? [];
15
+ let usage;
16
+ const commanderUsage = helpPageContent.match(/^\s*Usage: (.*)$/im);
17
+ if (commanderUsage) {
18
+ usage = commanderUsage[1];
19
+ }
16
20
  const lines = helpPageContent.split(/\r?\n/);
17
21
  let options = new Array();
18
22
  let commands = new Array();
@@ -22,19 +26,24 @@ function parseHelpPage(helpPageContent) {
22
26
  lines.shift();
23
27
  }
24
28
  if (lines.length > 0) {
25
- const sectionName = lines.shift();
29
+ const sectionName = lines.shift()?.toLocaleLowerCase("en-US");
26
30
  const sectionEndIndex = lines.findIndex(
27
31
  (line) => line && !line.match(/^\s/)
28
32
  );
29
33
  const sectionLines = lines.slice(0, sectionEndIndex);
30
34
  lines.splice(0, sectionLines.length);
31
35
  const sectionItems = sectionLines.map((line) => line.match(/^\s{1,8}(.*?)\s\s+/)?.[1]).filter(Boolean);
32
- if (sectionName?.toLocaleLowerCase("en-US") === "options:") {
36
+ if (sectionName === "options:" || sectionName === "flags:") {
33
37
  options = sectionItems;
34
- } else if (sectionName?.toLocaleLowerCase("en-US") === "commands:") {
38
+ } else if (sectionName === "commands:") {
35
39
  commands = sectionItems;
36
- } else if (sectionName?.toLocaleLowerCase("en-US") === "arguments:") {
40
+ } else if (sectionName === "arguments:") {
37
41
  commandArguments = sectionItems;
42
+ } else if (sectionName === "usage:") {
43
+ const usageLine = sectionLines.find((l) => l.trim().length > 0)?.trim();
44
+ if (usageLine) {
45
+ usage = usageLine;
46
+ }
38
47
  } else {
39
48
  throw new Error(`Unknown CLI section: ${sectionName}`);
40
49
  }
@@ -108,7 +108,7 @@ function registerCommands(program) {
108
108
  ).option(
109
109
  "--ci",
110
110
  "CI run checks that there are no changes to catalog-info.yaml files"
111
- ).description("Create or fix info yaml files for all backstage packages").action(
111
+ ).description("Create or fix info yaml files for all backstage packages").allowExcessArguments(true).action(
112
112
  lazy(
113
113
  () => import('./generate-catalog-info/generate-catalog-info.cjs.js'),
114
114
  "default"
@@ -24,6 +24,7 @@ async function generate(outputDirectory, clientAdditionalProperties, abortSignal
24
24
  const additionalProperties = helpers.toGeneratorAdditionalProperties({
25
25
  initialValue: clientAdditionalProperties
26
26
  });
27
+ const generatorKey = await helpers.getOpenApiGeneratorKey(resolvedOpenapiPath);
27
28
  await fs__default.default.emptyDir(resolvedOutputDirectory);
28
29
  await fs__default.default.writeFile(
29
30
  path.resolve(resolvedOutputDirectory, ".openapi-generator-ignore"),
@@ -46,7 +47,7 @@ async function generate(outputDirectory, clientAdditionalProperties, abortSignal
46
47
  "templates/typescript-backstage-client.yaml"
47
48
  ),
48
49
  "--generator-key",
49
- "v3.0",
50
+ generatorKey,
50
51
  additionalProperties ? `--additional-properties=${additionalProperties}` : ""
51
52
  ],
52
53
  {
@@ -80,6 +81,11 @@ async function generate(outputDirectory, clientAdditionalProperties, abortSignal
80
81
  }
81
82
  }
82
83
  fs__default.default.removeSync(path.resolve(resolvedOutputDirectory, ".openapi-generator-ignore"));
84
+ fs__default.default.removeSync(path.resolve(resolvedOutputDirectory, ".gitattributes"));
85
+ fs__default.default.rmSync(path.resolve(resolvedOutputDirectory, "docs"), {
86
+ recursive: true,
87
+ force: true
88
+ });
83
89
  fs__default.default.rmSync(path.resolve(resolvedOutputDirectory, ".openapi-generator"), {
84
90
  recursive: true,
85
91
  force: true
@@ -67,6 +67,7 @@ async function generate(serverAdditionalProperties, abortSignal) {
67
67
  const additionalProperties = helpers.toGeneratorAdditionalProperties({
68
68
  initialValue: serverAdditionalProperties
69
69
  });
70
+ const generatorKey = await helpers.getOpenApiGeneratorKey(resolvedOpenapiPath);
70
71
  await exec.exec(
71
72
  "node",
72
73
  [
@@ -84,7 +85,7 @@ async function generate(serverAdditionalProperties, abortSignal) {
84
85
  "templates/typescript-backstage-server.yaml"
85
86
  ),
86
87
  "--generator-key",
87
- "v3.0",
88
+ generatorKey,
88
89
  additionalProperties ? `--additional-properties=${additionalProperties}` : ""
89
90
  ],
90
91
  {
@@ -116,6 +117,11 @@ async function generate(serverAdditionalProperties, abortSignal) {
116
117
  });
117
118
  }
118
119
  fs__default.default.removeSync(path.resolve(resolvedOutputDirectory, ".openapi-generator-ignore"));
120
+ fs__default.default.removeSync(path.resolve(resolvedOutputDirectory, ".gitattributes"));
121
+ fs__default.default.rmSync(path.resolve(resolvedOutputDirectory, "docs"), {
122
+ recursive: true,
123
+ force: true
124
+ });
119
125
  fs__default.default.rmSync(path.resolve(resolvedOutputDirectory, ".openapi-generator"), {
120
126
  recursive: true,
121
127
  force: true
@@ -33,13 +33,14 @@ async function lint(directoryPath, config) {
33
33
  {
34
34
  extends: [spectralRulesets.oas, ruleset__default.default],
35
35
  rules: {
36
- "allow-reserved-in-params": {
37
- given: "$.paths..parameters[*]",
36
+ "allow-reserved-in-query-params": {
37
+ given: '$.paths..parameters[?(@.in == "query")]',
38
38
  then: {
39
39
  field: "allowReserved",
40
40
  function: spectralFunctions.truthy
41
41
  },
42
- severity: "error"
42
+ severity: "error",
43
+ message: "Query parameters must specify allowReserved (true or false)"
43
44
  }
44
45
  },
45
46
  overrides: [
@@ -1,53 +1,48 @@
1
1
  'use strict';
2
2
 
3
3
  var node_child_process = require('node:child_process');
4
+ var crypto = require('node:crypto');
5
+ var fs = require('node:fs');
4
6
  var os = require('node:os');
5
- var pLimit = require('p-limit');
7
+ var path = require('node:path');
6
8
 
7
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
8
-
9
- var os__default = /*#__PURE__*/_interopDefaultCompat(os);
10
- var pLimit__default = /*#__PURE__*/_interopDefaultCompat(pLimit);
11
-
12
- const limiter = pLimit__default.default(os__default.default.cpus().length);
13
- function createBinRunner(cwd, path) {
14
- return async (...command) => limiter(
15
- () => new Promise((resolve, reject) => {
16
- const args = path ? [path, ...command] : command;
17
- const child = node_child_process.spawn("node", args, {
9
+ const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
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, {
18
17
  cwd,
19
- stdio: ["ignore", "pipe", "pipe"]
20
- });
21
- let stdout = "";
22
- let stderr = "";
23
- child.stdout?.on("data", (data) => {
24
- stdout += data.toString();
18
+ env: { ...process.env, NO_COLOR: "1" },
19
+ stdio: ["ignore", outFd, "pipe"]
25
20
  });
26
- child.stderr?.on("data", (data) => {
27
- stderr += data.toString();
28
- });
29
- child.on("error", (err) => {
30
- reject(new Error(`Process error: ${err.message}`));
31
- });
32
- child.on("close", (code, signal) => {
33
- if (signal) {
34
- reject(
35
- new Error(
36
- `Process was killed with signal ${signal}
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}
37
30
  ${stderr}`
38
- )
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
- });
49
- })
50
- );
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
+ }
45
+ };
51
46
  }
52
47
 
53
48
  exports.createBinRunner = createBinRunner;
@@ -51,8 +51,31 @@ function toGeneratorAdditionalProperties({
51
51
  );
52
52
  return Object.entries(parsed).map(([key, value]) => `${key}=${value}`).join(",");
53
53
  }
54
+ async function getOpenApiGeneratorKey(specPath) {
55
+ const yaml = await loadAndValidateOpenApiYaml(specPath);
56
+ const version = yaml.openapi;
57
+ if (!version) {
58
+ throw new Error(`Could not determine OpenAPI version from ${specPath}`);
59
+ }
60
+ const semver = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(version);
61
+ if (!semver) {
62
+ throw new Error(`Invalid OpenAPI version format ${version} in ${specPath}`);
63
+ }
64
+ const [, major, minor] = semver;
65
+ const supportedVersions = ["3.0", "3.1"];
66
+ const majorMinor = `${major}.${minor}`;
67
+ if (!supportedVersions.includes(majorMinor)) {
68
+ throw new Error(
69
+ `Unsupported OpenAPI version ${version} in ${specPath}. Supported versions are: ${supportedVersions.join(
70
+ ", "
71
+ )}`
72
+ );
73
+ }
74
+ return `v${majorMinor}`;
75
+ }
54
76
 
55
77
  exports.assertExists = assertExists;
78
+ exports.getOpenApiGeneratorKey = getOpenApiGeneratorKey;
56
79
  exports.getPathToCurrentOpenApiSpec = getPathToCurrentOpenApiSpec;
57
80
  exports.getPathToFile = getPathToFile;
58
81
  exports.getPathToOpenApiSpec = getPathToOpenApiSpec;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.16.6-next.0";
3
+ var version = "0.17.0-next.2";
4
4
 
5
5
  exports.version = version;
6
6
  //# sourceMappingURL=package.json.cjs.js.map
package/openapitools.json CHANGED
@@ -2,6 +2,6 @@
2
2
  "$schema": "../../node_modules/@openapitools/openapi-generator-cli/config.schema.json",
3
3
  "spaces": 2,
4
4
  "generator-cli": {
5
- "version": "6.5.0"
5
+ "version": "7.18.0"
6
6
  }
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/repo-tools",
3
- "version": "0.16.6-next.0",
3
+ "version": "0.17.0-next.2",
4
4
  "description": "CLI for Backstage repo tooling ",
5
5
  "backstage": {
6
6
  "role": "cli"
@@ -43,10 +43,10 @@
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.7.1-next.0",
46
+ "@backstage/backend-plugin-api": "1.8.0-next.1",
47
47
  "@backstage/catalog-model": "1.7.6",
48
- "@backstage/cli-common": "0.2.0-next.0",
49
- "@backstage/cli-node": "0.2.19-next.0",
48
+ "@backstage/cli-common": "0.2.0-next.2",
49
+ "@backstage/cli-node": "0.2.19-next.1",
50
50
  "@backstage/config-loader": "1.10.9-next.0",
51
51
  "@backstage/errors": "1.2.7",
52
52
  "@electric-sql/pglite": "^0.3.0",
@@ -87,8 +87,8 @@
87
87
  "zod": "^3.25.76"
88
88
  },
89
89
  "devDependencies": {
90
- "@backstage/backend-test-utils": "1.11.1-next.0",
91
- "@backstage/cli": "0.35.5-next.0",
90
+ "@backstage/backend-test-utils": "1.11.1-next.2",
91
+ "@backstage/cli": "0.36.0-next.2",
92
92
  "@backstage/types": "1.2.2",
93
93
  "@types/is-glob": "^4.0.2",
94
94
  "@types/node": "^22.13.14",