@red-hat-developer-hub/extensions-cli 0.14.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 ADDED
@@ -0,0 +1,255 @@
1
+ # @red-hat-developer-hub/extensions-cli
2
+
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4fafdf3: Renamed plugins from marketplace to extensions
8
+
9
+ - Renamed all packages from `backstage-plugin-marketplace-*` to `backstage-plugin-extensions-*`
10
+ - Updated all internal references, exports, and API endpoints
11
+ - lazy load codeEditor to reduce the frontend plugin bundle size
12
+ - Made catalog entities directory path configurable via `extensions.directory` in app-config.yaml with fallback to `extensions` and `marketplace` directories
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [4fafdf3]
17
+ - @red-hat-developer-hub/backstage-plugin-extensions-common@0.14.0
18
+
19
+ ## 0.13.2
20
+
21
+ ### Patch Changes
22
+
23
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.13.2
24
+
25
+ ## 0.13.1
26
+
27
+ ### Patch Changes
28
+
29
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.13.1
30
+
31
+ ## 0.13.0
32
+
33
+ ### Minor Changes
34
+
35
+ - bee615a: Backstage version bump to v1.45.2
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [bee615a]
40
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.13.0
41
+
42
+ ## 0.12.2
43
+
44
+ ### Patch Changes
45
+
46
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.12.2
47
+
48
+ ## 0.12.1
49
+
50
+ ### Patch Changes
51
+
52
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.12.1
53
+
54
+ ## 0.12.0
55
+
56
+ ### Minor Changes
57
+
58
+ - f8e6f04: Backstage version bump to v1.44.1
59
+
60
+ ### Patch Changes
61
+
62
+ - Updated dependencies [f8e6f04]
63
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.12.0
64
+
65
+ ## 0.6.1
66
+
67
+ ### Patch Changes
68
+
69
+ - Updated dependencies [4d79286]
70
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.10.1
71
+
72
+ ## 0.6.0
73
+
74
+ ### Minor Changes
75
+
76
+ - b33db25: Backstage version bump to v1.42.5
77
+
78
+ ### Patch Changes
79
+
80
+ - Updated dependencies [b33db25]
81
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.10.0
82
+
83
+ ## 0.5.1
84
+
85
+ ### Patch Changes
86
+
87
+ - Updated dependencies [fdda9a1]
88
+ - Updated dependencies [cbe1174]
89
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.9.0
90
+
91
+ ## 0.5.0
92
+
93
+ ### Minor Changes
94
+
95
+ - 8887468: Backstage version bump to v1.41.1
96
+
97
+ ### Patch Changes
98
+
99
+ - Updated dependencies [8887468]
100
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.8.0
101
+
102
+ ## 0.4.10
103
+
104
+ ### Patch Changes
105
+
106
+ - Updated dependencies [171b7fd]
107
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.7.2
108
+
109
+ ## 0.4.9
110
+
111
+ ### Patch Changes
112
+
113
+ - Updated dependencies [fb23720]
114
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.7.1
115
+
116
+ ## 0.4.8
117
+
118
+ ### Patch Changes
119
+
120
+ - Updated dependencies [f70ccb1]
121
+ - Updated dependencies [7aac60c]
122
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.7.0
123
+
124
+ ## 0.4.7
125
+
126
+ ### Patch Changes
127
+
128
+ - ca2e330: Bump to backstage version 1.39.1
129
+ - 58ae1fe: Added an alert for production environment
130
+ Integrated the fetch configuration and install API calls
131
+ Fixed an import error
132
+ - Updated dependencies [ca2e330]
133
+ - Updated dependencies [3973839]
134
+ - Updated dependencies [58ae1fe]
135
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.6.0
136
+
137
+ ## 0.4.6
138
+
139
+ ### Patch Changes
140
+
141
+ - Updated dependencies [7ae0ca0]
142
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.5.0
143
+
144
+ ## 0.4.5
145
+
146
+ ### Patch Changes
147
+
148
+ - Updated dependencies [4413eef]
149
+ - Updated dependencies [7d138e5]
150
+ - Updated dependencies [f56908a]
151
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.4.0
152
+
153
+ ## 0.4.4
154
+
155
+ ### Patch Changes
156
+
157
+ - dd94b9d: use namespace parameter also for packages location
158
+
159
+ ## 0.4.3
160
+
161
+ ### Patch Changes
162
+
163
+ - e5e4655: use new Backstage `lazy` loading function
164
+
165
+ ## 0.4.2
166
+
167
+ ### Patch Changes
168
+
169
+ - 7517e3b: (export-csv) Always escape newlines in each CSV cell and add more columns
170
+
171
+ ## 0.4.1
172
+
173
+ ### Patch Changes
174
+
175
+ - 5a99fdd: use CJS imports instead of ESM to fix double-encapsulation import issue
176
+
177
+ ## 0.4.0
178
+
179
+ ### Minor Changes
180
+
181
+ - 6501387: Backstage version bump to v1.36.1
182
+
183
+ ### Patch Changes
184
+
185
+ - Updated dependencies [6501387]
186
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.3.0
187
+
188
+ ## 0.3.0
189
+
190
+ ### Minor Changes
191
+
192
+ - efc1592: add new marketplace-cli init command
193
+ - a4b60fd: Added a new command: `export-csv`, which generates a CSV file given a folder of plugin YAML files
194
+
195
+ ## 0.2.0
196
+
197
+ ### Minor Changes
198
+
199
+ - d3bcc76: rename apiVersion and annotations to extensions.backstage.io
200
+
201
+ ### Patch Changes
202
+
203
+ - Updated dependencies [d3bcc76]
204
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.2.0
205
+
206
+ ## 0.1.2
207
+
208
+ ### Patch Changes
209
+
210
+ - a7e38a9: remove core-plugin-api dependency from common
211
+ - Updated dependencies [a7e38a9]
212
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.1.2
213
+
214
+ ## 0.1.1
215
+
216
+ ### Patch Changes
217
+
218
+ - fa70a22: remove unused knex and zod dependency from common package
219
+ - Updated dependencies [fa70a22]
220
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.1.1
221
+
222
+ ## 0.1.0
223
+
224
+ ### Minor Changes
225
+
226
+ - d39d4e3: Initial tech-preview of the marketplace
227
+
228
+ ### Patch Changes
229
+
230
+ - f16dd69: Refactore and update API, UI, and catalog types
231
+ - Updated dependencies [d39d4e3]
232
+ - Updated dependencies [f16dd69]
233
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.1.0
234
+
235
+ ## 0.0.3
236
+
237
+ ### Patch Changes
238
+
239
+ - Updated dependencies [f310add]
240
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.0.9
241
+
242
+ ## 0.0.2
243
+
244
+ ### Patch Changes
245
+
246
+ - Updated dependencies [4e0ec06]
247
+ - @red-hat-developer-hub/backstage-plugin-marketplace-common@0.0.8
248
+
249
+ ## 0.0.1
250
+
251
+ ### Patch Changes
252
+
253
+ - 3c12103: Added new `marketplace-cli` and implement `generate` command.
254
+
255
+ This command generates Plugin entities based on the information from the [dynamic-plugins.default.yaml](https://github.com/redhat-developer/rhdh/blob/main/dynamic-plugins.default.yaml) and wrapper's `package.json`. It assumes that all `packages` in the config files are wrappers.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * Copyright The Backstage Authors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ const path = require('path');
19
+
20
+ // Figure out whether we're running inside the backstage repo or as an installed dependency
21
+ /* eslint-disable-next-line no-restricted-syntax */
22
+ const isLocal = require('fs').existsSync(path.resolve(__dirname, '../src'));
23
+
24
+ if (!isLocal) {
25
+ require('..');
26
+ } else {
27
+ require('@backstage/cli/config/nodeTransform.cjs');
28
+ require('../src');
29
+ }
@@ -0,0 +1,165 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var chalk = require('chalk');
6
+ var backstagePluginExtensionsCommon = require('@red-hat-developer-hub/backstage-plugin-extensions-common');
7
+ var fs = require('fs-extra');
8
+ var path = require('path');
9
+ var yaml = require('yaml');
10
+ var glob = require('glob');
11
+
12
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
13
+
14
+ var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
15
+ var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
16
+ var path__default = /*#__PURE__*/_interopDefaultCompat(path);
17
+ var yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml);
18
+ var glob__default = /*#__PURE__*/_interopDefaultCompat(glob);
19
+
20
+ class CSVGenerator {
21
+ /** The columns to be included in the CSV. */
22
+ columns;
23
+ /** The rows to be included in the CSV */
24
+ rows;
25
+ constructor(columns, rows = []) {
26
+ this.columns = columns;
27
+ this.rows = rows;
28
+ }
29
+ /** Escape problematic characters in a CSV cell */
30
+ static escapeCSVCell = (cell) => {
31
+ const removedNewLines = cell.replace(/\n/g, "\\n");
32
+ const regex = /[\s,"]/;
33
+ if (regex.exec(removedNewLines.replace(/ /g, ""))) {
34
+ return `"${removedNewLines.replace(/"/g, '""')}"`;
35
+ }
36
+ return removedNewLines;
37
+ };
38
+ /** Add a row to the CSV */
39
+ addRow(row) {
40
+ this.rows.push(row);
41
+ }
42
+ /**
43
+ * @param includeHeader Whether to include the header row in the CSV
44
+ * @returns A CSV generated from the rows and columns provided
45
+ */
46
+ generate(includeHeader = true) {
47
+ const header = Object.keys(this.columns).map((key) => CSVGenerator.escapeCSVCell(key)).join(",");
48
+ const rows = this.rows.map(
49
+ (row) => Object.values(this.columns).map((column) => CSVGenerator.escapeCSVCell(String(column(row) || ""))).join(",")
50
+ ).join("\n");
51
+ return includeHeader ? `${header}
52
+ ${rows}` : rows;
53
+ }
54
+ }
55
+ const isEntity = (entity) => {
56
+ return !!entity && typeof entity === "object" && "apiVersion" in entity && "kind" in entity;
57
+ };
58
+ const parseYamls = async (yamls, recurse = false) => {
59
+ return await Promise.all(
60
+ (yamls || []).flatMap((basePath) => {
61
+ return glob__default.default.sync(recurse ? "**/*.yaml" : "*.yaml", { cwd: basePath }).map(async (file) => {
62
+ const fileContent = await fs__default.default.readFile(
63
+ path__default.default.join(basePath, file),
64
+ "utf-8"
65
+ );
66
+ const plugins = yaml__default.default.parseAllDocuments(fileContent).map((doc) => doc.toJS()).filter(isEntity);
67
+ return plugins;
68
+ });
69
+ })
70
+ ).then((plugins) => plugins.flat());
71
+ };
72
+ const getPackagesOfType = (packages, allPackages, types) => {
73
+ return packages.map((name) => {
74
+ const pkg = allPackages[name];
75
+ if (!pkg) {
76
+ console.error(
77
+ chalk__default.default.red(`Package ${name} not found in the list of packages`)
78
+ );
79
+ return "";
80
+ }
81
+ const version = pkg.spec?.version ? `(${pkg.spec?.version})` : "(no version)";
82
+ if (types.includes(pkg?.spec?.backstage?.role || "")) {
83
+ return `${pkg.spec?.packageName} ${version}`;
84
+ }
85
+ return "";
86
+ }).filter(Boolean).join(", ");
87
+ };
88
+ var index = async ({
89
+ outputFile,
90
+ pluginsYamlPath,
91
+ recursive,
92
+ type
93
+ }) => {
94
+ if (!pluginsYamlPath) {
95
+ console.error(
96
+ "No plugins path provided! Provide one using --plugins-yaml-path"
97
+ );
98
+ process.exit(1);
99
+ }
100
+ const yamls = parseYamls(pluginsYamlPath.split(","), recursive);
101
+ const packages = {};
102
+ const packageCSV = new CSVGenerator({
103
+ name: (yaml) => yaml?.metadata?.name,
104
+ title: (yaml) => yaml?.metadata?.title,
105
+ version: (yaml) => yaml?.spec?.version,
106
+ author: (yaml) => yaml?.spec?.developer || yaml?.spec?.author || yaml?.spec?.owner || (yaml?.spec?.authors || []).join(","),
107
+ lifecycle: (yaml) => yaml?.spec?.lifecycle,
108
+ packages: (yaml) => (yaml?.spec?.partOf || []).join(", "),
109
+ role: (yaml) => yaml?.spec?.role || yaml?.spec?.backstage?.role
110
+ });
111
+ const pluginCSV = new CSVGenerator({
112
+ name: (p) => p?.metadata?.name,
113
+ title: (p) => p?.metadata?.title,
114
+ author: (p) => p?.spec?.developer || p?.spec?.author || p?.spec?.owner || (p?.spec?.authors || [])?.map((a) => a.name).join(", "),
115
+ categories: (p) => (p?.spec?.categories || []).join(", "),
116
+ lifecycle: (p) => p?.spec?.lifecycle,
117
+ metadataDescription: (p) => p?.metadata?.description,
118
+ specDescription: (p) => p?.spec?.description,
119
+ support: (p) => p?.spec?.support,
120
+ publisher: (p) => p?.spec?.publisher,
121
+ highlights: (p) => (p?.spec?.highlights || []).join(", "),
122
+ "certified-by": (p) => p?.metadata?.annotations?.["extensions.backstage.io/certified-by"],
123
+ "verified-by": (p) => p?.metadata?.annotations?.["extensions.backstage.io/verified-by"],
124
+ "pre-installed": (p) => p?.metadata?.annotations?.["extensions.backstage.io/pre-installed"],
125
+ packages: (p) => (p?.spec?.packages || []).join(", "),
126
+ "backend packages": (p) => getPackagesOfType(p?.spec?.packages || [], packages, [
127
+ "backend-plugin",
128
+ "backend-plugin-module"
129
+ ]),
130
+ "frontend packages": (p) => getPackagesOfType(p?.spec?.packages || [], packages, ["frontend-plugin"])
131
+ });
132
+ for (const yaml of await yamls) {
133
+ if (backstagePluginExtensionsCommon.isExtensionsPackage(yaml)) {
134
+ packages[yaml.metadata.name] = yaml;
135
+ packageCSV.addRow(yaml);
136
+ } else if (backstagePluginExtensionsCommon.isExtensionsPlugin(yaml)) {
137
+ pluginCSV.addRow(yaml);
138
+ }
139
+ }
140
+ const csvs = {};
141
+ switch (type) {
142
+ case "plugin":
143
+ csvs["-plugins.csv"] = pluginCSV.generate();
144
+ break;
145
+ case "package":
146
+ csvs["-packages.csv"] = packageCSV.generate();
147
+ break;
148
+ case "all":
149
+ default:
150
+ csvs["-plugins.csv"] = pluginCSV.generate();
151
+ csvs["-packages.csv"] = packageCSV.generate();
152
+ break;
153
+ }
154
+ for (const [suffix, csv] of Object.entries(csvs)) {
155
+ if (!outputFile) {
156
+ console.log(csv);
157
+ continue;
158
+ }
159
+ await fs__default.default.writeFile(`${outputFile}${suffix}`, csv);
160
+ }
161
+ process.exit(0);
162
+ };
163
+
164
+ exports.default = index;
165
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,212 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var fs = require('fs-extra');
6
+ var path = require('path');
7
+ var yaml = require('yaml');
8
+ var backstagePluginExtensionsCommon = require('@red-hat-developer-hub/backstage-plugin-extensions-common');
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 path__default = /*#__PURE__*/_interopDefaultCompat(path);
14
+ var yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml);
15
+
16
+ const DEFAULT_LIFECYCLE = "unknown";
17
+ var index = async (opts) => {
18
+ const { defaultDynamicPluginsConfig, outputDir, namespace, owner } = opts;
19
+ const entities = [];
20
+ if (defaultDynamicPluginsConfig) {
21
+ const defaultDynamicPluginsPath = path__default.default.resolve(defaultDynamicPluginsConfig);
22
+ const defaultDynamicPluginsContent = fs__default.default.readFileSync(
23
+ defaultDynamicPluginsPath,
24
+ "utf8"
25
+ );
26
+ const defaultPluginsConfig = yaml__default.default.parse(
27
+ defaultDynamicPluginsContent
28
+ );
29
+ for (const plugin of defaultPluginsConfig.plugins) {
30
+ const wrapperDir = plugin.package.replace("/dist/", "/wrappers/");
31
+ const wrapperPath = path__default.default.join(
32
+ path__default.default.dirname(defaultDynamicPluginsConfig),
33
+ wrapperDir
34
+ );
35
+ const packageJSONPath = path__default.default.join(wrapperPath, "package.json");
36
+ const packageJSON = fs__default.default.readJsonSync(packageJSONPath);
37
+ const packageName = guessPackageFromDependencies(
38
+ packageJSON.name,
39
+ packageJSON.dependencies
40
+ ) || packageJSON.name;
41
+ const links = [];
42
+ if (packageJSON.homepage) {
43
+ links.push({ url: packageJSON.homepage, title: "Homepage" });
44
+ }
45
+ if (packageJSON.bugs) {
46
+ const bugs = packageJSON.bugs;
47
+ if (bugs.url) {
48
+ links.push({ url: bugs.url, title: "Bugs" });
49
+ } else {
50
+ links.push({ url: packageJSON.bugs, title: "Bugs" });
51
+ }
52
+ }
53
+ let sourceLocation = "";
54
+ if (packageJSON.repository) {
55
+ const repo = packageJSON.repository;
56
+ const url = { title: "Source Code", url: "" };
57
+ if (repo.url) {
58
+ if (repo.url.startsWith("https://github.com")) {
59
+ url.url = `${repo.url}/tree/main/${repo.directory}`;
60
+ } else {
61
+ url.url = `${repo.url}/${repo.directory}`;
62
+ }
63
+ } else {
64
+ url.url = packageJSON.repository;
65
+ }
66
+ links.push(url);
67
+ sourceLocation = `url ${url.url}`;
68
+ }
69
+ let author = "";
70
+ if (packageJSON.author) {
71
+ const a = packageJSON.author;
72
+ if (a.name) {
73
+ author = a.name;
74
+ author += a.email ? ` <${a.email}>` : "";
75
+ author += a.url ? ` (${a.url})` : "";
76
+ } else {
77
+ author = packageJSON.author;
78
+ }
79
+ }
80
+ const partOf = [];
81
+ if (packageJSON.backstage?.pluginId) {
82
+ if (packageJSON.backstage?.role === "backend-plugin-module") {
83
+ const scope = getScope(packageName);
84
+ const module = getModuleName(packageName);
85
+ let pof = "";
86
+ if (scope) {
87
+ pof = `${scope}-`;
88
+ }
89
+ if (module) {
90
+ pof += `${module}-`;
91
+ }
92
+ pof += `${packageJSON.backstage.pluginId}-module`;
93
+ partOf.push(pof);
94
+ } else {
95
+ partOf.push(packageJSON.backstage.pluginId);
96
+ }
97
+ }
98
+ const appConfigExamples = [];
99
+ if (plugin.pluginConfig) {
100
+ appConfigExamples.push({
101
+ title: "Default configuration",
102
+ content: plugin.pluginConfig
103
+ });
104
+ }
105
+ const entity = {
106
+ apiVersion: backstagePluginExtensionsCommon.EXTENSIONS_API_VERSION,
107
+ kind: backstagePluginExtensionsCommon.ExtensionsKind.Package,
108
+ metadata: {
109
+ name: entityName(packageJSON.name),
110
+ namespace: namespace ?? undefined,
111
+ title: packageName,
112
+ links,
113
+ annotations: {
114
+ "backstage.io/source-location": sourceLocation
115
+ },
116
+ tags: packageJSON.keywords?.filter(
117
+ (k) => !k.startsWith("support:") && !k.startsWith("lifecycle:")
118
+ )
119
+ },
120
+ spec: {
121
+ owner,
122
+ packageName,
123
+ dynamicArtifact: plugin.package,
124
+ version: packageJSON.version,
125
+ backstage: {
126
+ role: packageJSON.backstage?.role,
127
+ supportedVersions: packageJSON.backstage?.["supported-versions"]
128
+ },
129
+ author,
130
+ lifecycle: packageJSON.keywords?.find((k) => k.startsWith("lifecycle:"))?.split(":")[1] ?? DEFAULT_LIFECYCLE,
131
+ partOf,
132
+ appConfigExamples
133
+ }
134
+ };
135
+ entities.push(entity);
136
+ }
137
+ }
138
+ if (outputDir) {
139
+ const outputDirPath = path__default.default.resolve(outputDir);
140
+ await fs__default.default.ensureDir(outputDirPath);
141
+ const location = {
142
+ apiVersion: "backstage.io/v1alpha1",
143
+ kind: "Location",
144
+ metadata: {
145
+ namespace,
146
+ name: "packages"
147
+ },
148
+ spec: {
149
+ targets: []
150
+ }
151
+ };
152
+ for (const entity of entities) {
153
+ const filename = `${entity.metadata.name}.yaml`;
154
+ const entityPath = path__default.default.join(outputDirPath, filename);
155
+ await fs__default.default.writeFile(entityPath, yaml__default.default.stringify(entity));
156
+ location.spec.targets?.push(`./${filename}`);
157
+ }
158
+ await fs__default.default.writeFile(
159
+ path__default.default.join(outputDirPath, "all.yaml"),
160
+ yaml__default.default.stringify(location)
161
+ );
162
+ } else {
163
+ for (const entity of entities) {
164
+ console.log(yaml__default.default.stringify(entity));
165
+ console.log("---");
166
+ }
167
+ }
168
+ };
169
+ function entityName(str) {
170
+ let name = str.toLowerCase().replace(/[^a-z0-9-_.]/g, "-").replace(/-+/g, "-");
171
+ if (name.startsWith("-")) {
172
+ name = name.substring(1);
173
+ }
174
+ if (name.endsWith("-")) {
175
+ name = name.slice(0, -1);
176
+ }
177
+ if (name.length > 63) {
178
+ name = name.substring(0, 63);
179
+ }
180
+ return name;
181
+ }
182
+ function guessPackageFromDependencies(pkgname, dependencies) {
183
+ if (!dependencies) {
184
+ return undefined;
185
+ }
186
+ if (Object.keys(dependencies).length === 1) {
187
+ return Object.keys(dependencies)[0];
188
+ }
189
+ for (const dep of Object.keys(dependencies)) {
190
+ const convertedName = dep.replaceAll("/", "-").replaceAll("@", "");
191
+ if (convertedName === pkgname) {
192
+ return dep;
193
+ }
194
+ }
195
+ return undefined;
196
+ }
197
+ function getScope(pkgname) {
198
+ if (pkgname.startsWith("@")) {
199
+ return pkgname.split("/")[0].substring(1);
200
+ }
201
+ return undefined;
202
+ }
203
+ function getModuleName(pkgname) {
204
+ const parts = pkgname.split("-module-");
205
+ if (parts.length > 1) {
206
+ return parts[1];
207
+ }
208
+ return undefined;
209
+ }
210
+
211
+ exports.default = index;
212
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ var lazy = require('../lib/lazy.cjs.js');
4
+
5
+ const registerCommands = (program) => {
6
+ program.command("init").description("init").action(lazy.lazy(() => import('./init/index.cjs.js'), "default"));
7
+ program.command("generate").description(
8
+ "Generate a Plugin entities for the extensions. By default, it will output entities to the standard output"
9
+ ).option(
10
+ "-p, --default-dynamic-plugins-config [path]",
11
+ "Path to the default dynamic plugins file (usually dynamic-plugins.default.yaml in rhdh source code)"
12
+ ).option(
13
+ "-o, --output-dir [path]",
14
+ "Path to the output directory. Each entity will be written to a separate file"
15
+ ).option(
16
+ "--namespace [namespace]",
17
+ "metadata.namespace for the generated Package entities"
18
+ ).option("--owner [owner]", "spec.owner for the generated Package entities").action(lazy.lazy(() => import('./generate/index.cjs.js'), "default"));
19
+ program.command("verify").description(
20
+ "Verify a set of extensions entities. By default, it will read entities from the standard input"
21
+ ).action(lazy.lazy(() => import('./verify/index.cjs.js'), "default"));
22
+ program.command("export-csv").description("Export a folder of extensions plugin YAMLs to a CSV file").option(
23
+ "-o, --output-file [path]",
24
+ 'Path to the output CSV file. By default, it will output to the standard output. When a file is specified, the "csv" file extension will be added automatically'
25
+ ).option(
26
+ "-p, --plugins-yaml-path [path]",
27
+ "Path to the default plugins folder, containing extensions plugin YAML files. Multiple paths can be provided, separated by commas"
28
+ ).option(
29
+ "-r, --recursive",
30
+ "Recursively search for YAML files in each directory provided in plugins-yaml-path"
31
+ ).option(
32
+ "-t, --type [type]",
33
+ 'The type of CSV to export. Can be one of: "plugin", "package", or "all". "all" will generate two files.',
34
+ "all"
35
+ ).action(lazy.lazy(() => import('./export-csv/index.cjs.js'), "default"));
36
+ };
37
+
38
+ exports.registerCommands = registerCommands;
39
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var fs = require('fs');
6
+ var inquirer = require('inquirer');
7
+ var glob = require('glob');
8
+ var yaml = require('yaml');
9
+ var backstagePluginExtensionsCommon = require('@red-hat-developer-hub/backstage-plugin-extensions-common');
10
+
11
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
12
+
13
+ var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
14
+ var inquirer__default = /*#__PURE__*/_interopDefaultCompat(inquirer);
15
+ var glob__default = /*#__PURE__*/_interopDefaultCompat(glob);
16
+ var yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml);
17
+
18
+ async function init() {
19
+ const pluginFolders = await glob__default.default.glob("plugins/*/package.json/../");
20
+ const autoGeneratePackages = pluginFolders.length === 0;
21
+ const cwd = process.cwd().split("/").pop() ?? "";
22
+ const defaultTitle = cwd.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
23
+ const questions = [
24
+ {
25
+ type: "input",
26
+ name: "title",
27
+ message: "Plugin name (display name)",
28
+ default: defaultTitle
29
+ }
30
+ ];
31
+ if (autoGeneratePackages) {
32
+ questions.push({
33
+ type: "input",
34
+ name: "packageName",
35
+ message: "NPM package name incl. org (e.g. @my-org/my-plugin)"
36
+ });
37
+ questions.push({
38
+ type: "checkbox",
39
+ name: "packages",
40
+ message: "Packages",
41
+ choices: [
42
+ { name: "Frontend", value: "frontend", checked: true },
43
+ { name: "Backend", value: "backend", checked: true }
44
+ ]
45
+ });
46
+ } else {
47
+ questions.push({
48
+ type: "checkbox",
49
+ name: "packages",
50
+ message: "Packages",
51
+ choices: pluginFolders.map((pluginFolder) => ({
52
+ name: pluginFolder,
53
+ value: pluginFolder,
54
+ checked: true
55
+ }))
56
+ });
57
+ }
58
+ const answers = await inquirer__default.default.prompt(questions);
59
+ console.log("answers", answers);
60
+ const { title, packageName, namespace, author } = answers;
61
+ let name = (packageName || title).toLowerCase().replace(/^@/, "");
62
+ if (name.includes("/")) {
63
+ name = name.substring(name.lastIndexOf("/") + 1);
64
+ }
65
+ const plugin = {
66
+ apiVersion: backstagePluginExtensionsCommon.EXTENSIONS_API_VERSION,
67
+ kind: backstagePluginExtensionsCommon.ExtensionsKind.Plugin,
68
+ metadata: {
69
+ namespace,
70
+ name,
71
+ title,
72
+ description: "Plugin summary"
73
+ },
74
+ spec: {
75
+ author,
76
+ description: "# Plugin name\n\nFull plugin description..."
77
+ }
78
+ };
79
+ console.log(yaml__default.default.stringify(plugin).trim());
80
+ if (autoGeneratePackages) {
81
+ if (answers.packages.includes("frontend")) {
82
+ const frontendPackage = {
83
+ apiVersion: backstagePluginExtensionsCommon.EXTENSIONS_API_VERSION,
84
+ kind: backstagePluginExtensionsCommon.ExtensionsKind.Package,
85
+ metadata: {
86
+ namespace,
87
+ name: packageName.toLowerCase().replace(/^@/, "").replaceAll(/[^a-z0-9]/g, "-"),
88
+ title: packageName
89
+ },
90
+ spec: {
91
+ packageName,
92
+ version: "0.1.0",
93
+ partOf: [plugin.metadata.name]
94
+ }
95
+ };
96
+ console.log("---");
97
+ console.log(yaml__default.default.stringify(frontendPackage).trim());
98
+ }
99
+ if (answers.packages.includes("backend")) {
100
+ const backendPackage = {
101
+ apiVersion: backstagePluginExtensionsCommon.EXTENSIONS_API_VERSION,
102
+ kind: backstagePluginExtensionsCommon.ExtensionsKind.Package,
103
+ metadata: {
104
+ namespace,
105
+ name: `${packageName.toLowerCase().replace(/^@/, "").replaceAll(/[^a-z0-9]/g, "-")}-backend`,
106
+ title: `${packageName}-backend`
107
+ },
108
+ spec: {
109
+ packageName: `${packageName}-backend`,
110
+ version: "0.1.0",
111
+ partOf: [plugin.metadata.name]
112
+ }
113
+ };
114
+ console.log("---");
115
+ console.log(yaml__default.default.stringify(backendPackage).trim());
116
+ }
117
+ } else {
118
+ answers.packages.forEach((pluginFolder) => {
119
+ const packageJsonPath = `${pluginFolder}/package.json`;
120
+ const packageJson = JSON.parse(
121
+ fs__default.default.readFileSync(packageJsonPath, { encoding: "utf8" })
122
+ );
123
+ const pkg = {
124
+ apiVersion: backstagePluginExtensionsCommon.EXTENSIONS_API_VERSION,
125
+ kind: backstagePluginExtensionsCommon.ExtensionsKind.Package,
126
+ metadata: {
127
+ namespace,
128
+ name: packageJson.name.toLowerCase().replace(/^@/, "").replaceAll(/[^a-z0-9]/g, "-")
129
+ },
130
+ spec: {
131
+ packageName: packageJson.name,
132
+ version: packageJson.version,
133
+ partOf: [plugin.metadata.name]
134
+ }
135
+ };
136
+ console.log("---");
137
+ console.log(yaml__default.default.stringify(pkg).trim());
138
+ });
139
+ }
140
+ }
141
+
142
+ exports.default = init;
143
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,99 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var chalk = require('chalk');
6
+ var fs = require('fs-extra');
7
+ var glob = require('glob');
8
+ var yaml = require('yaml');
9
+ var backstagePluginExtensionsCommon = require('@red-hat-developer-hub/backstage-plugin-extensions-common');
10
+
11
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
12
+
13
+ var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
14
+ var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
15
+ var yaml__default = /*#__PURE__*/_interopDefaultCompat(yaml);
16
+
17
+ const packages = {};
18
+ const plugins = {};
19
+ var index = async () => {
20
+ const filenames = await glob.glob("**/*.yaml", { ignore: "node_modules/**" });
21
+ filenames.sort();
22
+ for (const filename of filenames) {
23
+ const content = await fs__default.default.readFile(filename, "utf8");
24
+ const parsed = await yaml__default.default.parse(content);
25
+ if (Array.isArray(content)) {
26
+ for (const chunk of content) {
27
+ await load(filename, chunk);
28
+ }
29
+ } else {
30
+ await load(filename, parsed);
31
+ }
32
+ }
33
+ verifyPackages();
34
+ verifyPlugins();
35
+ };
36
+ function stringifyEntityRef(entity) {
37
+ return `${entity.metadata.namespace ?? "default"}/${entity.metadata.name}`;
38
+ }
39
+ function isEntity(maybe) {
40
+ return "apiVersion" in maybe && "kind" in maybe;
41
+ }
42
+ async function load(filename, content) {
43
+ if (typeof content !== "object") {
44
+ throw new Error(`Expected object, got ${typeof content}`);
45
+ }
46
+ if (backstagePluginExtensionsCommon.isExtensionsPackage(content)) {
47
+ packages[stringifyEntityRef(content)] = content;
48
+ } else if (backstagePluginExtensionsCommon.isExtensionsPlugin(content)) {
49
+ plugins[stringifyEntityRef(content)] = content;
50
+ } else if (isEntity(content)) {
51
+ console.log(
52
+ `Ignore file ${chalk__default.default.blue(filename)} with apiVersion ${content.apiVersion} and kind ${content.kind}`
53
+ );
54
+ } else {
55
+ console.log(
56
+ `Ignore file ${chalk__default.default.blue(filename)} without apiVersion or kind`
57
+ );
58
+ }
59
+ }
60
+ async function verifyPackages() {
61
+ for (const pkg of Object.values(packages)) {
62
+ const displayName = pkg.metadata.title ?? pkg.metadata.name ?? "unknown";
63
+ console.log(`Verifying package ${chalk__default.default.blue(displayName)}...`);
64
+ if (!pkg.metadata.title) {
65
+ console.log(` - missing title`);
66
+ }
67
+ if (pkg.spec?.partOf?.length) {
68
+ for (const partOf of pkg.spec.partOf) {
69
+ if (!plugins[partOf]) {
70
+ console.log(` - missing plugin ${partOf}`);
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ async function verifyPlugins() {
77
+ for (const plugin of Object.values(plugins)) {
78
+ const displayName = plugin.metadata.title ?? plugin.metadata.name ?? "unknown";
79
+ console.log(`Verifying plugin ${chalk__default.default.blue(displayName)}`);
80
+ if (!plugin.metadata.title) {
81
+ console.log(` - missing title`);
82
+ }
83
+ if (plugin.spec?.packages?.length) {
84
+ for (const pkg of plugin.spec.packages) {
85
+ if (typeof pkg === "string") {
86
+ if (!packages[pkg]) {
87
+ console.log(` - missing package ${pkg}`);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ if (!plugin.spec?.documentation?.length) {
93
+ console.log(` - missing documentation`);
94
+ }
95
+ }
96
+ }
97
+
98
+ exports.default = index;
99
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ var commander = require('commander');
4
+ var chalk = require('chalk');
5
+ var errors = require('./lib/errors.cjs.js');
6
+ var index = require('./commands/index.cjs.js');
7
+
8
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
9
+
10
+ var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
11
+
12
+ const main = (argv) => {
13
+ commander.program.name("extensions-cli");
14
+ index.registerCommands(commander.program);
15
+ commander.program.on("command:*", () => {
16
+ console.log();
17
+ console.log(chalk__default.default.red(`Invalid command: ${commander.program.args.join(" ")}`));
18
+ console.log();
19
+ commander.program.outputHelp();
20
+ process.exit(1);
21
+ });
22
+ commander.program.parse(argv);
23
+ };
24
+ process.on("unhandledRejection", (rejection) => {
25
+ if (rejection instanceof Error) {
26
+ errors.exitWithError(rejection);
27
+ } else {
28
+ errors.exitWithError(new Error(`Unknown rejection: '${rejection}'`));
29
+ }
30
+ });
31
+ main(process.argv);
32
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ var chalk = require('chalk');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
6
+
7
+ var chalk__default = /*#__PURE__*/_interopDefaultCompat(chalk);
8
+
9
+ class CustomError extends Error {
10
+ get name() {
11
+ return this.constructor.name;
12
+ }
13
+ }
14
+ class ExitCodeError extends CustomError {
15
+ code;
16
+ constructor(code, command) {
17
+ super(
18
+ command ? `Command '${command}' exited with code ${code}` : `Child exited with code ${code}`
19
+ );
20
+ this.code = code;
21
+ }
22
+ }
23
+ function exitWithError(error) {
24
+ if (error instanceof ExitCodeError) {
25
+ process.stderr.write(`
26
+ ${chalk__default.default.red(error.message)}
27
+
28
+ `);
29
+ process.exit(error.code);
30
+ } else {
31
+ process.stderr.write(`
32
+ ${chalk__default.default.red(`${error.stack}`)}
33
+
34
+ `);
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ exports.CustomError = CustomError;
40
+ exports.ExitCodeError = ExitCodeError;
41
+ exports.exitWithError = exitWithError;
42
+ //# sourceMappingURL=errors.cjs.js.map
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@backstage/errors');
4
+ var errors$1 = require('./errors.cjs.js');
5
+
6
+ function lazy(moduleLoader, exportName) {
7
+ return async (...args) => {
8
+ try {
9
+ const mod = await moduleLoader();
10
+ const actualModule = mod.default;
11
+ const actionFunc = actualModule[exportName];
12
+ await actionFunc(...args);
13
+ process.exit(0);
14
+ } catch (error) {
15
+ errors.assertError(error);
16
+ errors$1.exitWithError(error);
17
+ }
18
+ };
19
+ }
20
+
21
+ exports.lazy = lazy;
22
+ //# sourceMappingURL=lazy.cjs.js.map
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@red-hat-developer-hub/extensions-cli",
3
+ "description": "CLI for the Extensions",
4
+ "version": "0.14.0",
5
+ "backstage": {
6
+ "role": "cli"
7
+ },
8
+ "homepage": "https://red.ht/rhdh",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/redhat-developer/rhdh-plugins",
12
+ "directory": "workspaces/extensions/packages/cli"
13
+ },
14
+ "keywords": [
15
+ "backstage"
16
+ ],
17
+ "license": "Apache-2.0",
18
+ "main": "dist/index.cjs.js",
19
+ "scripts": {
20
+ "build": "backstage-cli package build",
21
+ "lint": "backstage-cli package lint",
22
+ "test": "backstage-cli package test",
23
+ "clean": "backstage-cli package clean",
24
+ "start": "nodemon --"
25
+ },
26
+ "bin": "bin/extensions-cli",
27
+ "files": [
28
+ "bin",
29
+ "dist/**/*.js"
30
+ ],
31
+ "dependencies": {
32
+ "@backstage/catalog-model": "^1.7.6",
33
+ "@backstage/cli": "^0.34.5",
34
+ "@backstage/errors": "^1.2.7",
35
+ "@backstage/types": "^1.2.2",
36
+ "@red-hat-developer-hub/backstage-plugin-extensions-common": "^0.14.0",
37
+ "chalk": "^4.1.2",
38
+ "commander": "^12.0.0",
39
+ "fs-extra": "^11.2.0",
40
+ "glob": "^9.3.5",
41
+ "inquirer": "^12.4.3",
42
+ "yaml": "^2.7.0"
43
+ },
44
+ "nodemonConfig": {
45
+ "watch": "./src",
46
+ "exec": "bin/extensions-cli",
47
+ "ext": "ts"
48
+ },
49
+ "devDependencies": {
50
+ "@types/fs-extra": "^11.0.4"
51
+ }
52
+ }