@backstage/repo-tools 0.3.3-next.0 → 0.3.3

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,27 @@
1
1
  # @backstage/repo-tools
2
2
 
3
+ ## 0.3.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 75702e85862a: Bumped `@microsoft/api-extractor` dependency to `^7.36.4`, and `@microsoft/api-documenter` to `^7.22.33`.
8
+ - 1f3337ebc707: Introducing a new, experimental command `backstage-repo-tools generate-catalog-info`, which can be used to create standardized `catalog-info.yaml` files for each Backstage package in a Backstage monorepo. It can also be used to automatically fix existing `catalog-info.yaml` files with the correct metadata (including `metadata.name`, `metadata.title`, and `metadata.description` introspected from the package's `package.json`, as well as `spec.owner` introspected from `CODEOWNERS`), e.g. in a post-commit hook.
9
+ - ebeb77586975: Update `schema openapi generate` command to now create a default router that can be imported and used directly.
10
+ - Updated dependencies
11
+ - @backstage/cli-node@0.1.3
12
+ - @backstage/catalog-model@1.4.1
13
+ - @backstage/cli-common@0.1.12
14
+ - @backstage/errors@1.2.1
15
+
16
+ ## 0.3.3-next.1
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+ - @backstage/cli-node@0.1.3-next.0
22
+ - @backstage/cli-common@0.1.12
23
+ - @backstage/errors@1.2.1
24
+
3
25
  ## 0.3.3-next.0
4
26
 
5
27
  ### Patch Changes
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ var YAML = require('js-yaml');
4
+ var pLimit = require('p-limit');
5
+ var path = require('path');
6
+ var yamlDiffPatch = require('yaml-diff-patch');
7
+ var chalk = require('chalk');
8
+ var cliNode = require('@backstage/cli-node');
9
+ var codeownersUtils = require('codeowners-utils');
10
+ var fs = require('fs');
11
+ var util = require('util');
12
+
13
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
+
15
+ var YAML__default = /*#__PURE__*/_interopDefaultLegacy(YAML);
16
+ var pLimit__default = /*#__PURE__*/_interopDefaultLegacy(pLimit);
17
+ var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
18
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
19
+
20
+ const readFile = util.promisify(fs__default["default"].readFile);
21
+ const writeFile = util.promisify(fs__default["default"].writeFile);
22
+ function isBackstagePackage(packageJson) {
23
+ var _a;
24
+ return packageJson && packageJson.hasOwnProperty("backstage") && ((_a = packageJson == null ? void 0 : packageJson.backstage) == null ? void 0 : _a.role) !== "undefined";
25
+ }
26
+
27
+ async function loadCodeowners() {
28
+ const maybeFiles = await Promise.allSettled(
29
+ codeownersUtils.CODEOWNERS_PATHS.map(
30
+ async (path$1) => readFile(path.resolve(".", path$1), { encoding: "utf-8" })
31
+ )
32
+ );
33
+ const file = maybeFiles.find(
34
+ (maybeFile) => maybeFile.status === "fulfilled"
35
+ );
36
+ if (!file) {
37
+ throw new Error(
38
+ "This utility expects a CODEOWNERS file, but no such file was found."
39
+ );
40
+ }
41
+ return codeownersUtils.parse(file.value);
42
+ }
43
+ function getPossibleCodeowners(codeowners, relPath) {
44
+ const codeownerMaybe = codeownersUtils.matchFile(relPath, codeowners);
45
+ return codeownerMaybe ? codeownerMaybe.owners.map(
46
+ (owner) => (owner.match(/(?:\@[^\/]+\/)?([^\@\/]*)$/) || [])[1]
47
+ ) : [];
48
+ }
49
+ function getOwnerFromCodeowners(codeowners, absPath) {
50
+ const relPath = path.relative(".", absPath);
51
+ const possibleOwners = getPossibleCodeowners(codeowners, relPath);
52
+ const owner = possibleOwners.slice(-1)[0];
53
+ if (!owner) {
54
+ throw new Error(`${relPath} isn't owned by anyone in CODEOWNERS`);
55
+ }
56
+ return owner;
57
+ }
58
+
59
+ var generateCatalogInfo = async (opts) => {
60
+ const { dryRun = false } = opts;
61
+ const packages = await cliNode.PackageGraph.listTargetPackages();
62
+ const codeowners = await loadCodeowners();
63
+ const limit = pLimit__default["default"](10);
64
+ const results = await Promise.allSettled(
65
+ packages.map(
66
+ ({ packageJson, dir }) => limit(async () => {
67
+ if (!isBackstagePackage(packageJson)) {
68
+ return;
69
+ }
70
+ const infoYamlPath = path.resolve(dir, "catalog-info.yaml");
71
+ let yamlString = "";
72
+ try {
73
+ yamlString = await readFile(infoYamlPath, { encoding: "utf-8" });
74
+ } catch (e) {
75
+ if (e.code === "ENOENT") {
76
+ await createCatalogInfoYaml({
77
+ yamlPath: infoYamlPath,
78
+ packageJson,
79
+ codeowners,
80
+ dryRun
81
+ });
82
+ return;
83
+ }
84
+ throw e;
85
+ }
86
+ await fixCatalogInfoYaml({
87
+ yamlPath: infoYamlPath,
88
+ packageJson,
89
+ codeowners,
90
+ yamlString,
91
+ dryRun
92
+ });
93
+ })
94
+ )
95
+ );
96
+ const rejects = results.filter(
97
+ (r) => r.status === "rejected"
98
+ );
99
+ if (rejects.length > 0) {
100
+ console.error(
101
+ chalk__default["default"].red("Unable to create or fix catalog-info.yaml files\n")
102
+ );
103
+ rejects.forEach((reject) => console.error(` ${reject.reason}`));
104
+ console.error();
105
+ process.exit(1);
106
+ }
107
+ };
108
+ function createCatalogInfoYaml(options) {
109
+ const { codeowners, dryRun, packageJson, yamlPath } = options;
110
+ const owner = getOwnerFromCodeowners(codeowners, yamlPath);
111
+ const entity = createOrMergeEntity(packageJson, owner);
112
+ return dryRun ? Promise.resolve(console.error(`Create ${path.relative(".", yamlPath)}`)) : writeFile(yamlPath, YAML__default["default"].dump(entity));
113
+ }
114
+ function fixCatalogInfoYaml(options) {
115
+ var _a, _b, _c;
116
+ const { codeowners, dryRun, packageJson, yamlPath, yamlString } = options;
117
+ const possibleOwners = getPossibleCodeowners(
118
+ codeowners,
119
+ path.relative(".", yamlPath)
120
+ );
121
+ const safeName = packageJson.name.replace(/[^a-z0-9_\-\.]+/g, "-").replace(/^[^a-z0-9]|[^a-z0-9]$/g, "");
122
+ let yamlJson;
123
+ try {
124
+ yamlJson = YAML__default["default"].load(yamlString);
125
+ } catch (e) {
126
+ throw new Error(`Unable to parse ${path.relative(".", yamlPath)}: ${e}`);
127
+ }
128
+ const badOwner = !possibleOwners.includes((_a = yamlJson.spec) == null ? void 0 : _a.owner);
129
+ const badTitle = yamlJson.metadata.title !== packageJson.name;
130
+ const badName = yamlJson.metadata.name !== safeName;
131
+ const badType = ((_b = yamlJson.spec) == null ? void 0 : _b.type) !== `backstage-${packageJson.backstage.role}`;
132
+ const badDesc = yamlJson.metadata.description !== packageJson.description;
133
+ if (badOwner || badTitle || badName || badType || badDesc) {
134
+ const owner = badOwner ? getOwnerFromCodeowners(codeowners, yamlPath) : (_c = yamlJson.spec) == null ? void 0 : _c.owner;
135
+ const newJson = createOrMergeEntity(packageJson, owner, yamlJson);
136
+ return dryRun ? Promise.resolve(console.error(`Update ${path.relative(".", yamlPath)}`)) : writeFile(yamlPath, yamlDiffPatch.yamlOverwrite(yamlString, newJson));
137
+ }
138
+ return Promise.resolve();
139
+ }
140
+ function createOrMergeEntity(packageJson, owner, existingEntity = {}) {
141
+ const safeEntityName = packageJson.name.replace(/[^a-z0-9_\-\.]+/g, "-").replace(/^[^a-z0-9]|[^a-z0-9]$/g, "");
142
+ return {
143
+ ...existingEntity,
144
+ apiVersion: "backstage.io/v1alpha1",
145
+ kind: "Component",
146
+ metadata: {
147
+ ...existingEntity.metadata,
148
+ // Provide default name/title/description values.
149
+ name: safeEntityName,
150
+ title: packageJson.name,
151
+ ...packageJson.description ? { description: packageJson.description } : void 0
152
+ },
153
+ spec: {
154
+ lifecycle: "experimental",
155
+ ...existingEntity.spec,
156
+ type: `backstage-${packageJson.backstage.role}`,
157
+ owner
158
+ }
159
+ };
160
+ }
161
+
162
+ exports["default"] = generateCatalogInfo;
163
+ //# sourceMappingURL=generate-catalog-info-84fe20dd.cjs.js.map
package/dist/index.cjs.js CHANGED
@@ -86,6 +86,16 @@ function registerCommands(program) {
86
86
  )
87
87
  );
88
88
  program.command("type-deps").description("Find inconsistencies in types of all packages and plugins").action(lazy(() => Promise.resolve().then(function () { return require('./cjs/type-deps-5eacd931.cjs.js'); }).then((m) => m.default)));
89
+ program.command("generate-catalog-info").option(
90
+ "--dry-run",
91
+ "Shows what would happen without actually writing any yaml."
92
+ ).description("Create or fix info yaml files for all backstage packages").action(
93
+ lazy(
94
+ () => Promise.resolve().then(function () { return require('./cjs/generate-catalog-info-84fe20dd.cjs.js'); }).then(
95
+ (m) => m.default
96
+ )
97
+ )
98
+ );
89
99
  registerSchemaCommand(program);
90
100
  }
91
101
  function lazy(getActionFunc) {
@@ -101,7 +111,7 @@ function lazy(getActionFunc) {
101
111
  };
102
112
  }
103
113
 
104
- var version = "0.3.3-next.0";
114
+ var version = "0.3.3";
105
115
 
106
116
  const main = (argv) => {
107
117
  commander.program.name("backstage-repo-tools").version(version);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/repo-tools",
3
3
  "description": "CLI for Backstage repo tooling ",
4
- "version": "0.3.3-next.0",
4
+ "version": "0.3.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,12 +32,13 @@
32
32
  "dependencies": {
33
33
  "@apidevtools/swagger-parser": "^10.1.0",
34
34
  "@apisyouwonthate/style-guide": "^1.4.0",
35
+ "@backstage/catalog-model": "^1.4.1",
35
36
  "@backstage/cli-common": "^0.1.12",
36
- "@backstage/cli-node": "^0.1.2",
37
+ "@backstage/cli-node": "^0.1.3",
37
38
  "@backstage/errors": "^1.2.1",
38
39
  "@manypkg/get-packages": "^1.1.3",
39
- "@microsoft/api-documenter": "^7.19.27",
40
- "@microsoft/api-extractor": "^7.33.7",
40
+ "@microsoft/api-documenter": "^7.22.33",
41
+ "@microsoft/api-extractor": "^7.36.4",
41
42
  "@stoplight/spectral-core": "^1.18.0",
42
43
  "@stoplight/spectral-formatters": "^1.1.0",
43
44
  "@stoplight/spectral-functions": "^1.7.2",
@@ -46,6 +47,7 @@
46
47
  "@stoplight/spectral-runtime": "^1.1.2",
47
48
  "@stoplight/types": "^13.14.0",
48
49
  "chalk": "^4.0.0",
50
+ "codeowners-utils": "^1.0.2",
49
51
  "commander": "^9.1.0",
50
52
  "fs-extra": "10.1.0",
51
53
  "glob": "^8.0.3",
@@ -54,10 +56,11 @@
54
56
  "lodash": "^4.17.21",
55
57
  "minimatch": "^5.1.1",
56
58
  "p-limit": "^3.0.2",
57
- "ts-node": "^10.0.0"
59
+ "ts-node": "^10.0.0",
60
+ "yaml-diff-patch": "^2.0.0"
58
61
  },
59
62
  "devDependencies": {
60
- "@backstage/cli": "^0.22.10-next.0",
63
+ "@backstage/cli": "^0.22.10",
61
64
  "@backstage/types": "^1.1.0",
62
65
  "@types/is-glob": "^4.0.2",
63
66
  "@types/mock-fs": "^4.13.0",