@backstage/repo-tools 0.17.4-next.0 → 0.18.0

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,50 @@
1
1
  # @backstage/repo-tools
2
2
 
3
+ ## 0.18.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 84171b3: **BREAKING**: Replaced `@useoptic/optic` and `@useoptic/openapi-utilities` with `oasdiff` for OpenAPI breaking change detection.
8
+
9
+ To migrate, remove `@useoptic/optic` from your root `package.json` and install the `oasdiff` CLI on your system — see https://github.com/oasdiff/oasdiff#installation for instructions.
10
+
11
+ The `package schema openapi diff` command now uses `oasdiff` under the hood. The `--since`, `--json`, and `--ignore` flags continue to work, but the JSON and text output formats have changed to match `oasdiff`'s native output.
12
+
13
+ The `repo schema openapi diff` command now automatically detects all packages with a changed `src/schema/openapi.yaml` and runs `oasdiff` against them directly. Packages no longer need a `"diff"` script in their `package.json` to be included in the check.
14
+
15
+ Removed the `package schema openapi init` and `repo schema openapi test` commands, which depended on the Optic `capture` workflow and have no equivalent with `oasdiff`. Runtime validation of your API against its OpenAPI spec is still available via `wrapServer` from `@backstage/backend-openapi-utils/testUtils`.
16
+
17
+ ### Patch Changes
18
+
19
+ - 120e7c3: chore(deps): bump `js-yaml` from 4.1.1 to 4.2.0
20
+ - Updated dependencies
21
+ - @backstage/config-loader@1.11.0
22
+ - @backstage/cli-common@0.3.0
23
+ - @backstage/backend-plugin-api@1.9.3
24
+ - @backstage/cli-node@0.3.4
25
+
26
+ ## 0.18.0-next.1
27
+
28
+ ### Minor Changes
29
+
30
+ - 84171b3: **BREAKING**: Replaced `@useoptic/optic` and `@useoptic/openapi-utilities` with `oasdiff` for OpenAPI breaking change detection.
31
+
32
+ To migrate, remove `@useoptic/optic` from your root `package.json` and install the `oasdiff` CLI on your system — see https://github.com/oasdiff/oasdiff#installation for instructions.
33
+
34
+ The `package schema openapi diff` command now uses `oasdiff` under the hood. The `--since`, `--json`, and `--ignore` flags continue to work, but the JSON and text output formats have changed to match `oasdiff`'s native output.
35
+
36
+ The `repo schema openapi diff` command now automatically detects all packages with a changed `src/schema/openapi.yaml` and runs `oasdiff` against them directly. Packages no longer need a `"diff"` script in their `package.json` to be included in the check.
37
+
38
+ Removed the `package schema openapi init` and `repo schema openapi test` commands, which depended on the Optic `capture` workflow and have no equivalent with `oasdiff`. Runtime validation of your API against its OpenAPI spec is still available via `wrapServer` from `@backstage/backend-openapi-utils/testUtils`.
39
+
40
+ ### Patch Changes
41
+
42
+ - Updated dependencies
43
+ - @backstage/cli-common@0.3.0-next.0
44
+ - @backstage/backend-plugin-api@1.9.3-next.1
45
+ - @backstage/cli-node@0.3.4-next.0
46
+ - @backstage/config-loader@1.11.0-next.2
47
+
3
48
  ## 0.17.4-next.0
4
49
 
5
50
  ### Patch Changes
@@ -2,6 +2,7 @@
2
2
 
3
3
  var errors$1 = require('@backstage/errors');
4
4
  var errors = require('../lib/errors.cjs.js');
5
+ var constants = require('../lib/openapi/constants.cjs.js');
5
6
 
6
7
  function registerPackageCommand(program) {
7
8
  const command = program.command("package [command]").description("Various tools for working with specific packages.");
@@ -9,11 +10,6 @@ function registerPackageCommand(program) {
9
10
  "Various tools for working with specific packages' API schema"
10
11
  );
11
12
  const openApiCommand = schemaCommand.command("openapi [command]").description("Tooling for OpenAPI schema");
12
- openApiCommand.command("init").description(
13
- "Initialize any required files to use the OpenAPI tooling for this package."
14
- ).action(
15
- lazy(() => import('./package/schema/openapi/init.cjs.js'), "singleCommand")
16
- );
17
13
  openApiCommand.command("generate").description(
18
14
  "Command to generate a client and/or a server stub from an OpenAPI spec."
19
15
  ).option(
@@ -51,17 +47,16 @@ function registerRepoCommand(program) {
51
47
  "--strict",
52
48
  "Fail on any linting severity messages, not just errors."
53
49
  ).action(lazy(() => import('./repo/schema/openapi/lint.cjs.js'), "bulkCommand"));
54
- openApiCommand.command("test [paths...]").description("Test OpenAPI schemas against written tests").option("--update", "Update the spec on failure.").action(lazy(() => import('./repo/schema/openapi/test.cjs.js'), "bulkCommand"));
55
50
  openApiCommand.command("fuzz").description("Fuzz all packages").option(
56
51
  "--since <ref>",
57
52
  "Only fuzz packages that have changed since the given ref"
58
53
  ).action(lazy(() => import('./repo/schema/openapi/fuzz.cjs.js'), "command"));
59
54
  openApiCommand.command("diff").description(
60
- "Diff the repository against a specific ref, will run all package `diff` scripts."
55
+ "Diff all OpenAPI specs in the repository against a specific ref."
61
56
  ).option(
62
57
  "--since <ref>",
63
58
  "Diff the API against a specific ref",
64
- "origin/master"
59
+ constants.DEFAULT_BASE_REF
65
60
  ).action(lazy(() => import('./repo/schema/openapi/diff.cjs.js'), "command"));
66
61
  }
67
62
  function registerLintCommand(program) {
@@ -1,64 +1,51 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
+ var path = require('node:path');
4
5
  var exec = require('../../../../lib/exec.cjs.js');
6
+ var constants = require('../../../../lib/openapi/constants.cjs.js');
5
7
  var helpers = require('../../../../lib/openapi/helpers.cjs.js');
8
+ var util = require('../../../util.cjs.js');
6
9
  var cliCommon = require('@backstage/cli-common');
7
- var node_process = require('node:process');
8
- var promises = require('node:fs/promises');
9
- var path = require('node:path');
10
10
 
11
11
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
12
12
 
13
13
  var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
14
14
 
15
- const reduceOpticOutput = (output) => {
16
- return output.split("\n").filter((e) => !e.startsWith("Rerun") && e.trim()).join("\n");
17
- };
18
15
  async function check(opts) {
16
+ await util.ensureOasdiffInstalled();
19
17
  const resolvedOpenapiPath = await helpers.getPathToCurrentOpenApiSpec();
18
+ const relativeSpecPath = path.relative(
19
+ cliCommon.targetPaths.rootDir,
20
+ resolvedOpenapiPath
21
+ ).split(path.sep).join(path.posix.sep);
20
22
  let baseRef = opts.since;
21
23
  if (!baseRef) {
22
24
  const { stdout: branch } = await exec.exec(
23
- "git merge-base --fork-point origin/master"
25
+ `git merge-base --fork-point ${constants.DEFAULT_BASE_REF}`
24
26
  );
25
27
  baseRef = branch.toString().trim();
26
28
  }
27
- let failed = false;
29
+ const baseSpec = `${baseRef}:${relativeSpecPath}`;
30
+ const subcommand = opts.json ? "changelog" : "breaking";
31
+ const formatArgs = opts.json ? ["--format", "json"] : [];
32
+ const failArgs = !opts.ignore && !opts.json ? ["--fail-on", "ERR"] : [];
28
33
  let output = "";
34
+ let failed = false;
29
35
  try {
30
36
  const { stdout } = await exec.exec(
31
- "yarn optic diff",
32
- [
33
- resolvedOpenapiPath,
34
- "--check",
35
- opts.json ? "--json" : "",
36
- "--base",
37
- baseRef
38
- ],
39
- {
40
- cwd: cliCommon.targetPaths.rootDir,
41
- env: { CI: opts.json ? "1" : void 0, ...node_process.env }
42
- }
37
+ "oasdiff",
38
+ [subcommand, baseSpec, relativeSpecPath, ...formatArgs, ...failArgs],
39
+ { cwd: cliCommon.targetPaths.rootDir }
43
40
  );
44
41
  output = stdout.toString();
45
42
  } catch (err) {
46
- output = err.stdout;
43
+ output = err.stdout ?? "";
47
44
  failed = true;
48
45
  }
49
- if (opts.json) {
50
- const file = (await promises.readFile(path.resolve(cliCommon.targetPaths.rootDir, "ci-run-details.json"))).toString();
51
- const results = JSON.parse(file);
52
- console.log(file);
53
- if (!opts.ignore && results.failed) {
54
- throw new Error("Some checks failed");
55
- }
56
- await promises.rm(path.resolve(cliCommon.targetPaths.rootDir, "ci-run-details.json"));
57
- } else {
58
- console.log(reduceOpticOutput(output));
59
- if (!opts.ignore && failed) {
60
- throw new Error("Some checks failed");
61
- }
46
+ console.log(output);
47
+ if (failed && !opts.ignore) {
48
+ throw new Error("Breaking changes found");
62
49
  }
63
50
  }
64
51
  async function command(opts) {
@@ -1,15 +1,21 @@
1
1
  'use strict';
2
2
 
3
3
  var cliNode = require('@backstage/cli-node');
4
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
5
  var exec = require('../../../../lib/exec.cjs.js');
5
6
  var cliCommon = require('@backstage/cli-common');
6
- var helpers = require('../../../../lib/openapi/optic/helpers.cjs.js');
7
7
  var constants = require('../../../../lib/openapi/constants.cjs.js');
8
+ var util = require('../../../util.cjs.js');
9
+ var chalk = require('chalk');
10
+ var path = require('node:path');
11
+ var fs = require('fs-extra');
12
+
13
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
14
+
15
+ var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
8
16
 
9
- function cleanUpApiName(e) {
10
- e.apiName = e.apiName.replace(cliCommon.targetPaths.dir, "").replace(constants.YAML_SCHEMA_PATH, "");
11
- }
12
17
  async function command(opts) {
18
+ await util.ensureOasdiffInstalled();
13
19
  let packages = await cliNode.PackageGraph.listTargetPackages();
14
20
  let since = "";
15
21
  if (opts.since) {
@@ -26,53 +32,66 @@ async function command(opts) {
26
32
  (pkg) => changedOpenApiSpecs.some((e) => e.startsWith(`${pkg.dir}/`))
27
33
  );
28
34
  }
29
- const checkablePackages = packages.filter((e) => e.packageJson.scripts?.diff);
30
- try {
31
- const outputs = {
32
- completed: [],
33
- failed: [],
34
- noop: [],
35
- warning: [],
36
- severity: 0
37
- };
38
- for (const pkg of checkablePackages) {
39
- const sinceCommands = since ? ["--since", since] : [];
35
+ const packagesWithSpecs = [];
36
+ for (const pkg of packages) {
37
+ const specPath = path.join(pkg.dir, constants.YAML_SCHEMA_PATH);
38
+ if (await fs.pathExists(specPath)) {
39
+ packagesWithSpecs.push(pkg);
40
+ }
41
+ }
42
+ if (packagesWithSpecs.length === 0) {
43
+ console.log("No OpenAPI spec changes detected.");
44
+ return;
45
+ }
46
+ const { stdout: currentSha } = await exec.exec("git", ["rev-parse", "HEAD"]);
47
+ const sha = currentSha.toString().trim();
48
+ console.log(`### Summary for commit (${sha})
49
+ `);
50
+ const templatePath = backendPluginApi.resolvePackagePath(
51
+ "@backstage/repo-tools",
52
+ "templates/oasdiff-changelog.tmpl"
53
+ );
54
+ let hasFailures = false;
55
+ for (const pkg of packagesWithSpecs) {
56
+ const specPath = path.join(pkg.dir, constants.YAML_SCHEMA_PATH);
57
+ const relativeSpecPath = path.relative(cliCommon.targetPaths.rootDir, specPath).split(path.sep).join(path.posix.sep);
58
+ const pkgName = path.relative(cliCommon.targetPaths.rootDir, pkg.dir).split(path.sep).join(path.posix.sep);
59
+ const baseRef = since || constants.DEFAULT_BASE_REF;
60
+ const baseSpec = `${baseRef}:${relativeSpecPath}`;
61
+ try {
40
62
  const { stdout } = await exec.exec(
41
- "yarn",
42
- ["diff", "--ignore", "--json", ...sinceCommands],
43
- {
44
- cwd: pkg.dir
45
- }
63
+ "oasdiff",
64
+ [
65
+ "changelog",
66
+ baseSpec,
67
+ relativeSpecPath,
68
+ "--format",
69
+ "markdown",
70
+ "--template",
71
+ templatePath
72
+ ],
73
+ { cwd: cliCommon.targetPaths.rootDir }
74
+ );
75
+ const output = stdout.toString().trim();
76
+ if (output) {
77
+ console.log(`## ${pkgName}
78
+ `);
79
+ console.log(output);
80
+ console.log();
81
+ }
82
+ } catch (err) {
83
+ hasFailures = true;
84
+ console.log(`## ${pkgName}
85
+ `);
86
+ console.error(
87
+ chalk__default.default.red(
88
+ `Failed to diff OpenAPI spec: ${err instanceof Error ? err.message : "Unknown error"}
89
+ `
90
+ )
46
91
  );
47
- const result = JSON.parse(stdout.toString());
48
- outputs.completed.push(...result.completed ?? []);
49
- outputs.failed.push(...result.failed ?? []);
50
- outputs.noop.push(...result.noop ?? []);
51
- }
52
- for (const pkg of packages.filter((e) => !e.packageJson.scripts?.diff)) {
53
- outputs.warning?.push({
54
- apiName: `${pkg.dir}/`,
55
- warning: "No diff script found in package.json"
56
- });
57
- }
58
- outputs.completed.forEach(cleanUpApiName);
59
- outputs.failed.forEach(cleanUpApiName);
60
- outputs.noop.forEach(cleanUpApiName);
61
- outputs.warning?.forEach(cleanUpApiName);
62
- const { stdout: currentSha } = await exec.exec("git", ["rev-parse", "HEAD"]);
63
- console.log(
64
- helpers.generateCompareSummaryMarkdown(
65
- { sha: currentSha.toString().trim() },
66
- outputs,
67
- { verbose: true }
68
- )
69
- );
70
- const failed = outputs.failed.length > 0;
71
- if (failed) {
72
- throw new Error("Some checks failed");
73
92
  }
74
- } catch (err) {
75
- console.error(err);
93
+ }
94
+ if (hasFailures) {
76
95
  process.exit(1);
77
96
  }
78
97
  }
@@ -5,6 +5,11 @@ var crypto = require('node:crypto');
5
5
  var fs = require('node:fs');
6
6
  var os = require('node:os');
7
7
  var path = require('node:path');
8
+ var commandExists = require('command-exists');
9
+
10
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
11
+
12
+ var commandExists__default = /*#__PURE__*/_interopDefaultCompat(commandExists);
8
13
 
9
14
  const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
10
15
  function createBinRunner(cwd, path$1) {
@@ -55,6 +60,16 @@ ${stderr}`));
55
60
  });
56
61
  };
57
62
  }
63
+ async function ensureOasdiffInstalled() {
64
+ try {
65
+ await commandExists__default.default("oasdiff");
66
+ } catch {
67
+ throw new Error(
68
+ `oasdiff is not installed. Please install it to use this command: https://github.com/oasdiff/oasdiff#installation`
69
+ );
70
+ }
71
+ }
58
72
 
59
73
  exports.createBinRunner = createBinRunner;
74
+ exports.ensureOasdiffInstalled = ensureOasdiffInstalled;
60
75
  //# sourceMappingURL=util.cjs.js.map
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const YAML_SCHEMA_PATH = "src/schema/openapi.yaml";
4
+ const DEFAULT_BASE_REF = "origin/master";
4
5
  const OUTPUT_PATH = "src/schema/openapi/generated";
5
6
  const OLD_SCHEMA_PATH = `src/schema/openapi.generated.ts`;
6
7
  const TS_SCHEMA_PATH = `${OUTPUT_PATH}/router.ts`;
@@ -37,6 +38,7 @@ const OPENAPI_IGNORE_FILES = [
37
38
  "tsconfig.json"
38
39
  ];
39
40
 
41
+ exports.DEFAULT_BASE_REF = DEFAULT_BASE_REF;
40
42
  exports.OLD_SCHEMA_PATH = OLD_SCHEMA_PATH;
41
43
  exports.OPENAPI_IGNORE_FILES = OPENAPI_IGNORE_FILES;
42
44
  exports.OUTPUT_PATH = OUTPUT_PATH;
@@ -4,34 +4,21 @@ var cliCommon = require('@backstage/cli-common');
4
4
  var paths = require('./paths.cjs.js');
5
5
  var pLimit = require('p-limit');
6
6
  var path = require('node:path');
7
- var portFinder = require('portfinder');
8
7
 
9
8
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
10
9
 
11
10
  var pLimit__default = /*#__PURE__*/_interopDefaultCompat(pLimit);
12
- var portFinder__default = /*#__PURE__*/_interopDefaultCompat(portFinder);
13
11
 
14
- async function runner(paths$1, command, options) {
12
+ async function runner(paths$1, command) {
15
13
  const packages = await paths.resolvePackagePaths({ paths: paths$1 });
16
- const limit = pLimit__default.default(options?.concurrencyLimit ?? 5);
17
- let port = options?.startingPort && await portFinder__default.default.getPortPromise({
18
- // Prevent collisions with optic which runs 8000->8999
19
- port: options.startingPort,
20
- stopPort: options.startingPort + 1e3
21
- });
14
+ const limit = pLimit__default.default(5);
22
15
  const resultsList = await Promise.all(
23
16
  packages.map(
24
17
  (pkg) => limit(async () => {
25
18
  let resultText = "";
26
19
  try {
27
- if (port)
28
- port = options?.startingPort && await portFinder__default.default.getPortPromise({
29
- // Prevent collisions with optic which runs 8000->8999
30
- port: port + 1,
31
- stopPort: options.startingPort + 1e3
32
- });
33
20
  console.log(`## Processing ${pkg}`);
34
- await command(pkg, port ? { port } : void 0);
21
+ await command(pkg);
35
22
  } catch (err) {
36
23
  resultText = err.message;
37
24
  }
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var version = "0.17.4-next.0";
3
+ var version = "0.18.0";
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.4-next.0",
3
+ "version": "0.18.0",
4
4
  "description": "CLI for Backstage repo tooling ",
5
5
  "backstage": {
6
6
  "role": "cli"
@@ -43,12 +43,12 @@
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.9.3-next.0",
47
- "@backstage/catalog-model": "1.9.0",
48
- "@backstage/cli-common": "0.2.2",
49
- "@backstage/cli-node": "0.3.3",
50
- "@backstage/config-loader": "1.11.0-next.0",
51
- "@backstage/errors": "1.3.1",
46
+ "@backstage/backend-plugin-api": "^1.9.3",
47
+ "@backstage/catalog-model": "^1.9.0",
48
+ "@backstage/cli-common": "^0.3.0",
49
+ "@backstage/cli-node": "^0.3.4",
50
+ "@backstage/config-loader": "^1.11.0",
51
+ "@backstage/errors": "^1.3.1",
52
52
  "@electric-sql/pglite": "^0.3.0",
53
53
  "@manypkg/get-packages": "^1.1.3",
54
54
  "@microsoft/api-documenter": "^7.28.1",
@@ -62,7 +62,6 @@
62
62
  "@stoplight/spectral-rulesets": "^1.18.0",
63
63
  "@stoplight/spectral-runtime": "^1.1.2",
64
64
  "@stoplight/types": "^14.0.0",
65
- "@useoptic/openapi-utilities": "^0.55.0",
66
65
  "chalk": "^4.0.0",
67
66
  "chokidar": "^3.5.3",
68
67
  "codeowners-utils": "^1.0.2",
@@ -72,7 +71,7 @@
72
71
  "glob": "^13.0.0",
73
72
  "globby": "^11.0.0",
74
73
  "is-glob": "^4.0.3",
75
- "js-yaml": "^4.1.0",
74
+ "js-yaml": "^4.2.0",
76
75
  "just-diff": "^6.0.2",
77
76
  "knex": "^3.0.0",
78
77
  "knex-pglite": "^0.11.0",
@@ -80,16 +79,15 @@
80
79
  "lodash": "^4.17.21",
81
80
  "minimatch": "^10.2.1",
82
81
  "p-limit": "^3.0.2",
83
- "portfinder": "^1.0.32",
84
82
  "tar": "^7.5.6",
85
83
  "ts-morph": "^24.0.0",
86
84
  "yaml-diff-patch": "^2.0.0",
87
85
  "zod": "^3.25.76 || ^4.0.0"
88
86
  },
89
87
  "devDependencies": {
90
- "@backstage/backend-test-utils": "1.11.5-next.0",
91
- "@backstage/cli": "0.36.4-next.0",
92
- "@backstage/types": "1.2.2",
88
+ "@backstage/backend-test-utils": "^1.11.5",
89
+ "@backstage/cli": "^0.36.4",
90
+ "@backstage/types": "^1.2.2",
93
91
  "@types/is-glob": "^4.0.2",
94
92
  "@types/node": "^22.13.14",
95
93
  "@types/prettier": "^2.0.0",
@@ -99,7 +97,6 @@
99
97
  "@microsoft/api-extractor-model": "*",
100
98
  "@microsoft/tsdoc": "*",
101
99
  "@microsoft/tsdoc-config": "*",
102
- "@useoptic/optic": "^1.0.0",
103
100
  "prettier": "^2.8.1 || ^3.8.1",
104
101
  "typedoc": "^0.28.0",
105
102
  "typescript": "> 3.0.0"
@@ -0,0 +1,14 @@
1
+ {{ if .GroupedChanges }}
2
+ {{ range pathGroups .GroupedChanges }}
3
+ ### {{ .Group.Operation }} {{ .Group.Path }}
4
+ {{ range .Changes }}- {{ if .IsBreaking }}:warning:{{ end }} {{ .Text }}
5
+ {{ end }}
6
+ {{ end }}
7
+ {{ range sectionGroups .GroupedChanges }}
8
+ ### {{ capitalize .Group.Section }}
9
+ {{ range .Changes }}- {{ if .IsBreaking }}:warning:{{ end }} {{ .Text }}
10
+ {{ end }}
11
+ {{ end }}
12
+ {{ else }}
13
+ No changes
14
+ {{ end }}
@@ -1,68 +0,0 @@
1
- 'use strict';
2
-
3
- var fs = require('fs-extra');
4
- var cliCommon = require('@backstage/cli-common');
5
- var constants = require('../../../../lib/openapi/constants.cjs.js');
6
- var chalk = require('chalk');
7
- var exec = require('../../../../lib/exec.cjs.js');
8
- var helpers = require('../../../../lib/openapi/helpers.cjs.js');
9
-
10
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
11
-
12
- var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
13
- var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
14
-
15
- const ROUTER_TEST_PATHS = [
16
- "src/service/router.test.ts",
17
- "src/service/createRouter.test.ts"
18
- ];
19
- async function init() {
20
- try {
21
- await helpers.getPathToCurrentOpenApiSpec();
22
- } catch (err) {
23
- throw new Error(
24
- `OpenAPI.yaml not found in ${constants.YAML_SCHEMA_PATH}. Please create one and retry this command.`
25
- );
26
- }
27
- const opticConfigFilePath = await helpers.getRelativePathToFile("optic.yml");
28
- if (await fs__default.default.pathExists(opticConfigFilePath)) {
29
- throw new Error(`This directory already has an optic.yml file. Exiting.`);
30
- }
31
- await fs__default.default.writeFile(
32
- opticConfigFilePath,
33
- `ruleset:
34
- - breaking-changes
35
- capture:
36
- ${constants.YAML_SCHEMA_PATH}:
37
- # \u{1F527} Runnable example with simple get requests.
38
- # Run with "PORT=3000 optic capture ${constants.YAML_SCHEMA_PATH} --update interactive" in '${cliCommon.targetPaths.dir}'
39
- # You can change the server and the 'requests' section to experiment
40
- server:
41
- # This will not be used by 'backstage-repo-tools schema openapi test', but may be useful for interactive updates.
42
- url: http://localhost:3000
43
- requests:
44
- # \u2139\uFE0F Requests should be sent to the Optic proxy, the address of which is injected into 'run.command's env as OPTIC_PROXY (or the value of 'run.proxy_variable').
45
- run:
46
- # \u{1F527} Specify a command that will generate traffic
47
- command: yarn backstage-cli package test --no-watch ${ROUTER_TEST_PATHS.map(
48
- (e) => `"${e}"`
49
- ).join(" ")}
50
- `
51
- );
52
- if (await fs__default.default.pathExists(cliCommon.targetPaths.resolveRoot("node_modules/.bin/prettier"))) {
53
- await exec.exec(`yarn prettier`, ["--write", opticConfigFilePath]);
54
- }
55
- }
56
- async function singleCommand() {
57
- try {
58
- await init();
59
- console.log(chalk__default.default.green(`Successfully configured.`));
60
- } catch (err) {
61
- console.log(chalk__default.default.red(`OpenAPI tooling initialization failed.`));
62
- console.log(err.message);
63
- process.exit(1);
64
- }
65
- }
66
-
67
- exports.singleCommand = singleCommand;
68
- //# sourceMappingURL=init.cjs.js.map
@@ -1,97 +0,0 @@
1
- 'use strict';
2
-
3
- var fs = require('fs-extra');
4
- var path = require('node:path');
5
- var chalk = require('chalk');
6
- var cliCommon = require('@backstage/cli-common');
7
- var runner = require('../../../../lib/runner.cjs.js');
8
- var constants = require('../../../../lib/openapi/constants.cjs.js');
9
- var exec = require('../../../../lib/exec.cjs.js');
10
- var helpers = require('../../../../lib/openapi/helpers.cjs.js');
11
-
12
- function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
13
-
14
- var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
15
- var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
16
-
17
- async function test(directoryPath, { port }, options) {
18
- let openapiPath = path.join(directoryPath, constants.YAML_SCHEMA_PATH);
19
- try {
20
- openapiPath = await helpers.getPathToOpenApiSpec(directoryPath);
21
- } catch {
22
- return;
23
- }
24
- const opticConfigFilePath = path.join(directoryPath, "optic.yml");
25
- if (!await fs__default.default.pathExists(opticConfigFilePath)) {
26
- return;
27
- }
28
- let opticLocation = "";
29
- try {
30
- opticLocation = (await exec.exec(`yarn bin optic`, [], {
31
- /* eslint-disable-next-line no-restricted-syntax */
32
- cwd: cliCommon.findOwnPaths(__dirname).rootDir
33
- })).stdout;
34
- } catch (err) {
35
- throw new Error(
36
- `Failed to find an Optic CLI installation, ensure that you have @useoptic/optic installed in the root of your repo. If not, run yarn add @useoptic/optic from the root of your repo.`
37
- );
38
- }
39
- try {
40
- await exec.exec(
41
- `${opticLocation.trim()} capture`,
42
- [
43
- constants.YAML_SCHEMA_PATH,
44
- "--server-override",
45
- `http://localhost:${port}`,
46
- options?.update ? "--update" : ""
47
- ],
48
- {
49
- cwd: directoryPath,
50
- env: {
51
- ...process.env,
52
- PORT: `${port}`
53
- }
54
- }
55
- );
56
- } catch (err) {
57
- err.message = err.stderr + err.stdout;
58
- err.message = err.message.split("\n").map((e) => e.replace(/.{1} Sending requests to server/, "")).filter((e) => !e.includes("PASS")).filter((e) => e.trim()).join("\n");
59
- throw err;
60
- }
61
- if (await fs__default.default.pathExists(
62
- cliCommon.targetPaths.resolveRoot("node_modules/.bin/prettier")
63
- ) && options?.update) {
64
- await exec.exec(`yarn prettier`, ["--write", openapiPath]);
65
- }
66
- }
67
- async function bulkCommand(paths = [], options) {
68
- const resultsList = await runner.runner(
69
- paths,
70
- (dir, runnerOptions) => test(dir, runnerOptions, options),
71
- {
72
- concurrencyLimit: 1,
73
- startingPort: 9e3
74
- }
75
- );
76
- let failed = false;
77
- for (const { relativeDir, resultText } of resultsList) {
78
- if (resultText) {
79
- console.log();
80
- console.log(
81
- chalk__default.default.red(
82
- `OpenAPI runtime validation against tests failed in ${relativeDir}:`
83
- )
84
- );
85
- console.log(resultText.trimStart());
86
- failed = true;
87
- }
88
- }
89
- if (failed) {
90
- process.exit(1);
91
- } else {
92
- console.log(chalk__default.default.green("Verified all specifications against test data."));
93
- }
94
- }
95
-
96
- exports.bulkCommand = bulkCommand;
97
- //# sourceMappingURL=test.cjs.js.map
@@ -1,176 +0,0 @@
1
- 'use strict';
2
-
3
- var openapiUtilities = require('@useoptic/openapi-utilities');
4
-
5
- const getChecksLabel = (results, severity) => {
6
- const totalChecks = results.length;
7
- let failingChecks = 0;
8
- let exemptedFailingChecks = 0;
9
- for (const result of results) {
10
- if (result.passed) continue;
11
- if (result.severity < severity) continue;
12
- if (result.exempted) exemptedFailingChecks += 1;
13
- else failingChecks += 1;
14
- }
15
- const exemptedChunk = exemptedFailingChecks > 0 ? `, ${exemptedFailingChecks} exempted` : "";
16
- return failingChecks > 0 ? `\u26A0\uFE0F **${failingChecks}**/**${totalChecks}** failed${exemptedChunk}` : totalChecks > 0 ? `\u2705 **${totalChecks}** passed${exemptedChunk}` : `\u2139\uFE0F No automated checks have run`;
17
- };
18
- function getOperationsText(groupedDiffs, options) {
19
- const ops = openapiUtilities.getOperationsChanged(groupedDiffs);
20
- const operationsText = [
21
- ...[...ops.added].map((o) => `\`${o}\` (added)`),
22
- ...[...ops.changed].map((o) => `\`${o}\` (changed)`),
23
- ...[...ops.removed].map((o) => `\`${o}\` (removed)`)
24
- ].join("\n") ;
25
- return `${openapiUtilities.getOperationsChangedLabel(groupedDiffs)}
26
-
27
- ${operationsText}
28
- `;
29
- }
30
- const getCaptureIssuesLabel = ({
31
- unmatchedInteractions,
32
- mismatchedEndpoints
33
- }) => {
34
- return [
35
- ...unmatchedInteractions ? [
36
- `\u{1F195} ${unmatchedInteractions} undocumented path${unmatchedInteractions > 1 ? "s" : ""}`
37
- ] : [],
38
- ...mismatchedEndpoints ? [
39
- `\u26A0\uFE0F ${mismatchedEndpoints} mismatch${mismatchedEndpoints > 1 ? "es" : ""}`
40
- ] : []
41
- ].join("\n");
42
- };
43
- const getBreakagesRow = (breakage) => {
44
- return `
45
- - ${breakage.apiName}
46
- ${breakage.comparison.results.map(
47
- (s) => `
48
- - ${s.where}
49
- ${"```"}
50
- ${s.error}
51
- ${"```"}`
52
- )}`;
53
- };
54
- const addSummaryLine = (items, label) => {
55
- const length = Array.isArray(items) ? items.length : items;
56
- if (!length) return "";
57
- let text = length === 1 ? `1 API` : `${length} APIs`;
58
- text += ` had ${label}`;
59
- return text;
60
- };
61
- const generateCompareSummaryMarkdown = (commit, results, options) => {
62
- const anyCompletedHasWarning = results.completed.some(
63
- (s) => s.warnings.length > 0
64
- );
65
- const anyCompletedHasCapture = results.completed.some((s) => s.capture);
66
- if (results.completed.length === 0 && results.failed.length === 0 && results.failed.length === 0) {
67
- return `No API changes detected for commit (${commit.sha})`;
68
- }
69
- const breakages = results.completed.filter((s) => s.comparison.results.some((e) => !e.passed)).map((e) => ({
70
- ...e,
71
- comparison: {
72
- ...e.comparison,
73
- results: e.comparison.results.filter((f) => !f.passed)
74
- }
75
- }));
76
- const successfullyCompletedCount = results.completed.length - breakages.length;
77
- return `### Summary for commit (${commit.sha})
78
-
79
- ${addSummaryLine(results.noop, "no changes")}
80
-
81
- ${addSummaryLine(breakages.length, "breaking changes")}
82
-
83
- ${addSummaryLine(successfullyCompletedCount, "non-breaking changes")}
84
-
85
- ${addSummaryLine(results.warning, "warnings")}
86
-
87
- ${results.completed.length > 0 ? `### APIs with Changes
88
-
89
- <table>
90
- <thead>
91
- <tr>
92
- <th>API</th>
93
- <th>Changes</th>
94
- <th>Rules</th>
95
- ${anyCompletedHasWarning ? "<th>Warnings</th>" : ""}
96
- ${anyCompletedHasCapture ? "<th>Tests</th>" : ""}
97
- </tr>
98
- </thead>
99
- <tbody>
100
- ${results.completed.map(
101
- (s) => `<tr>
102
- <td>
103
- ${s.apiName}
104
- </td>
105
- <td>
106
- ${getOperationsText(s.comparison.groupedDiffs, {
107
- webUrl: s.opticWebUrl})}
108
- </td>
109
- <td>
110
- ${getChecksLabel(s.comparison.results, results.severity)}
111
- </td>
112
-
113
- ${anyCompletedHasWarning ? `<td>${s.warnings.join("\n")}</td>` : ""}
114
-
115
- ${anyCompletedHasCapture ? `
116
- <td>
117
- ${s.capture ? s.capture.success ? s.capture.mismatchedEndpoints || s.capture.unmatchedInteractions ? getCaptureIssuesLabel({
118
- unmatchedInteractions: s.capture.unmatchedInteractions,
119
- mismatchedEndpoints: s.capture.mismatchedEndpoints
120
- }) : `\u2705 ${s.capture.percentCovered}% coverage` : "\u274C\xA0Failed to run" : ""}
121
- </td>
122
- ` : ""}
123
- </tr>`
124
- ).join("\n")}
125
- </tbody>
126
- </table>` : ""}
127
-
128
- ${results.failed.length > 0 ? `### APIs with Errors
129
-
130
- <table>
131
- <thead>
132
- <tr>
133
- <th>API</th>
134
- <th>Error</th>
135
- </tr>
136
- </thead>
137
- <tbody>
138
- ${results.failed.map(
139
- (s) => `<tr>
140
- <td>${s.apiName}</td>
141
- <td>
142
-
143
- ${"```"}
144
- ${s.error}
145
- ${"```"}
146
-
147
- </td>
148
- </tr>`
149
- ).join("\n")}
150
- </tbody>
151
- </table>
152
- ` : ""}
153
-
154
- ${results.warning && results.warning.length ? `
155
- ### APIs with Warnings
156
- <table>
157
- <thead>
158
- <tr>
159
- <th>API</th>
160
- <th>Warning</th>
161
- </tr>
162
- </thead>
163
- <tbody>
164
- ${results.warning.map((e) => `<tr><td>${e.apiName}</td><td>${e.warning}</td></tr>`).join("\n")}
165
- </tbody>
166
- </table>` : ""}
167
- ${breakages.length > 0 ? `
168
- ### Routes with Breakages
169
-
170
- ${breakages.map(getBreakagesRow).join("\n")}
171
- ` : ""}
172
- `;
173
- };
174
-
175
- exports.generateCompareSummaryMarkdown = generateCompareSummaryMarkdown;
176
- //# sourceMappingURL=helpers.cjs.js.map