1 | 'use strict';
|
2 |
|
3 | var fs = require('fs-extra');
|
4 | var path = require('path');
|
5 | var index = require('./index-09611511.cjs.js');
|
6 | var chalk = require('chalk');
|
7 | var handlebars = require('handlebars');
|
8 | var ora = require('ora');
|
9 | var util = require('util');
|
10 | var recursive = require('recursive-readdir');
|
11 | var child_process = require('child_process');
|
12 | var errors = require('@backstage/errors');
|
13 |
|
14 | function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
15 |
|
16 | var fs__default = _interopDefaultLegacy(fs);
|
17 | var path__default = _interopDefaultLegacy(path);
|
18 | var chalk__default = _interopDefaultLegacy(chalk);
|
19 | var handlebars__default = _interopDefaultLegacy(handlebars);
|
20 | var ora__default = _interopDefaultLegacy(ora);
|
21 | var recursive__default = _interopDefaultLegacy(recursive);
|
22 |
|
23 | const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/;
|
24 | const USER_ID_RE = /^@[-\w]+$/;
|
25 | const EMAIL_RE = /^[^@]+@[-.\w]+\.[-\w]+$/i;
|
26 | const DEFAULT_OWNER = "@backstage/maintainers";
|
27 | async function getCodeownersFilePath(rootDir) {
|
28 | const possiblePaths = [
|
29 | path__default["default"].join(rootDir, ".github", "CODEOWNERS"),
|
30 | path__default["default"].join(rootDir, ".gitlab", "CODEOWNERS"),
|
31 | path__default["default"].join(rootDir, "docs", "CODEOWNERS"),
|
32 | path__default["default"].join(rootDir, "CODEOWNERS")
|
33 | ];
|
34 | for (const p of possiblePaths) {
|
35 | if (await fs__default["default"].pathExists(p)) {
|
36 | return p;
|
37 | }
|
38 | }
|
39 | return void 0;
|
40 | }
|
41 | function isValidSingleOwnerId(id) {
|
42 | if (!id || typeof id !== "string") {
|
43 | return false;
|
44 | }
|
45 | return TEAM_ID_RE.test(id) || USER_ID_RE.test(id) || EMAIL_RE.test(id);
|
46 | }
|
47 | function parseOwnerIds(spaceSeparatedOwnerIds) {
|
48 | if (!spaceSeparatedOwnerIds || typeof spaceSeparatedOwnerIds !== "string") {
|
49 | return void 0;
|
50 | }
|
51 | const ids = spaceSeparatedOwnerIds.split(" ").filter(Boolean);
|
52 | if (!ids.every(isValidSingleOwnerId)) {
|
53 | return void 0;
|
54 | }
|
55 | return ids;
|
56 | }
|
57 | async function addCodeownersEntry(ownedPath, ownerStr, codeownersFilePath) {
|
58 | const ownerIds = parseOwnerIds(ownerStr);
|
59 | if (!ownerIds || ownerIds.length === 0) {
|
60 | return false;
|
61 | }
|
62 | let filePath = codeownersFilePath;
|
63 | if (!filePath) {
|
64 | filePath = await getCodeownersFilePath(index.paths.targetRoot);
|
65 | if (!filePath) {
|
66 | return false;
|
67 | }
|
68 | }
|
69 | const allLines = (await fs__default["default"].readFile(filePath, "utf8")).split("\n");
|
70 | const commentLines = [];
|
71 | for (const line of allLines) {
|
72 | if (line[0] !== "#") {
|
73 | break;
|
74 | }
|
75 | commentLines.push(line);
|
76 | }
|
77 | const oldDeclarationEntries = allLines.filter((line) => line[0] !== "#").map((line) => line.split(/\s+/).filter(Boolean)).filter((tokens) => tokens.length >= 2).map((tokens) => ({
|
78 | ownedPath: tokens[0],
|
79 | ownerIds: tokens.slice(1)
|
80 | }));
|
81 | const newDeclarationEntries = oldDeclarationEntries.filter((entry) => entry.ownedPath !== "*").concat([{ ownedPath, ownerIds }]).sort((l1, l2) => l1.ownedPath.localeCompare(l2.ownedPath));
|
82 | newDeclarationEntries.unshift({
|
83 | ownedPath: "*",
|
84 | ownerIds: [DEFAULT_OWNER]
|
85 | });
|
86 | const longestOwnedPath = newDeclarationEntries.reduce((length, entry) => Math.max(length, entry.ownedPath.length), 0);
|
87 | const newDeclarationLines = newDeclarationEntries.map((entry) => {
|
88 | const entryPath = entry.ownedPath + " ".repeat(longestOwnedPath - entry.ownedPath.length);
|
89 | return [entryPath, ...entry.ownerIds].join(" ");
|
90 | });
|
91 | const newLines = [...commentLines, "", ...newDeclarationLines, ""];
|
92 | await fs__default["default"].writeFile(filePath, newLines.join("\n"), "utf8");
|
93 | return true;
|
94 | }
|
95 |
|
96 | const exec = util.promisify(child_process.exec);
|
97 | const TASK_NAME_MAX_LENGTH = 14;
|
98 | class Task {
|
99 | static log(name = "") {
|
100 | process.stderr.write(`${chalk__default["default"].green(name)}
|
101 | `);
|
102 | }
|
103 | static error(message = "") {
|
104 | process.stderr.write(`
|
105 | ${chalk__default["default"].red(message)}
|
106 |
|
107 | `);
|
108 | }
|
109 | static section(name) {
|
110 | const title = chalk__default["default"].green(`${name}:`);
|
111 | process.stderr.write(`
|
112 | ${title}
|
113 | `);
|
114 | }
|
115 | static exit(code = 0) {
|
116 | process.exit(code);
|
117 | }
|
118 | static async forItem(task, item, taskFunc) {
|
119 | const paddedTask = chalk__default["default"].green(task.padEnd(TASK_NAME_MAX_LENGTH));
|
120 | const spinner = ora__default["default"]({
|
121 | prefixText: chalk__default["default"].green(` ${paddedTask}${chalk__default["default"].cyan(item)}`),
|
122 | spinner: "arc",
|
123 | color: "green"
|
124 | }).start();
|
125 | try {
|
126 | const result = await taskFunc();
|
127 | spinner.succeed();
|
128 | return result;
|
129 | } catch (error) {
|
130 | spinner.fail();
|
131 | throw error;
|
132 | }
|
133 | }
|
134 | static async forCommand(command, options) {
|
135 | try {
|
136 | await Task.forItem("executing", command, async () => {
|
137 | await exec(command, { cwd: options == null ? void 0 : options.cwd });
|
138 | });
|
139 | } catch (error) {
|
140 | errors.assertError(error);
|
141 | if (error.stderr) {
|
142 | process.stderr.write(error.stderr);
|
143 | }
|
144 | if (error.stdout) {
|
145 | process.stdout.write(error.stdout);
|
146 | }
|
147 | if (options == null ? void 0 : options.optional) {
|
148 | Task.error(`Warning: Failed to execute command ${chalk__default["default"].cyan(command)}`);
|
149 | } else {
|
150 | throw new Error(`Failed to execute command '${chalk__default["default"].cyan(command)}', ${error}`);
|
151 | }
|
152 | }
|
153 | }
|
154 | }
|
155 | async function templatingTask(templateDir, destinationDir, context, versionProvider) {
|
156 | const files = await recursive__default["default"](templateDir).catch((error) => {
|
157 | throw new Error(`Failed to read template directory: ${error.message}`);
|
158 | });
|
159 | const isMonoRepo = await fs__default["default"].pathExists(index.paths.resolveTargetRoot("lerna.json"));
|
160 | for (const file of files) {
|
161 | const destinationFile = file.replace(templateDir, destinationDir);
|
162 | await fs__default["default"].ensureDir(path.dirname(destinationFile));
|
163 | if (file.endsWith(".hbs")) {
|
164 | await Task.forItem("templating", path.basename(file), async () => {
|
165 | const destination = destinationFile.replace(/\.hbs$/, "");
|
166 | const template = await fs__default["default"].readFile(file);
|
167 | const compiled = handlebars__default["default"].compile(template.toString(), {
|
168 | strict: true
|
169 | });
|
170 | const contents = compiled({ name: path.basename(destination), ...context }, {
|
171 | helpers: {
|
172 | versionQuery(name, versionHint) {
|
173 | return versionProvider(name, typeof versionHint === "string" ? versionHint : void 0);
|
174 | }
|
175 | }
|
176 | });
|
177 | await fs__default["default"].writeFile(destination, contents).catch((error) => {
|
178 | throw new Error(`Failed to create file: ${destination}: ${error.message}`);
|
179 | });
|
180 | });
|
181 | } else {
|
182 | if (isMonoRepo && file.match("tsconfig.json")) {
|
183 | continue;
|
184 | }
|
185 | await Task.forItem("copying", path.basename(file), async () => {
|
186 | await fs__default["default"].copyFile(file, destinationFile).catch((error) => {
|
187 | const destination = destinationFile;
|
188 | throw new Error(`Failed to copy file to ${destination} : ${error.message}`);
|
189 | });
|
190 | });
|
191 | }
|
192 | }
|
193 | }
|
194 | async function addPackageDependency(path, options) {
|
195 | try {
|
196 | const pkgJson = await fs__default["default"].readJson(path);
|
197 | const normalize = (obj) => {
|
198 | if (Object.keys(obj).length === 0) {
|
199 | return void 0;
|
200 | }
|
201 | return Object.fromEntries(Object.keys(obj).sort().map((key) => [key, obj[key]]));
|
202 | };
|
203 | pkgJson.dependencies = normalize({
|
204 | ...pkgJson.dependencies,
|
205 | ...options.dependencies
|
206 | });
|
207 | pkgJson.devDependencies = normalize({
|
208 | ...pkgJson.devDependencies,
|
209 | ...options.devDependencies
|
210 | });
|
211 | pkgJson.peerDependencies = normalize({
|
212 | ...pkgJson.peerDependencies,
|
213 | ...options.peerDependencies
|
214 | });
|
215 | await fs__default["default"].writeJson(path, pkgJson, { spaces: 2 });
|
216 | } catch (error) {
|
217 | throw new Error(`Failed to add package dependencies, ${error}`);
|
218 | }
|
219 | }
|
220 |
|
221 | exports.Task = Task;
|
222 | exports.addCodeownersEntry = addCodeownersEntry;
|
223 | exports.addPackageDependency = addPackageDependency;
|
224 | exports.getCodeownersFilePath = getCodeownersFilePath;
|
225 | exports.parseOwnerIds = parseOwnerIds;
|
226 | exports.templatingTask = templatingTask;
|
227 |
|