@backstage/cli
Version:
CLI for developing Backstage plugins and apps
244 lines (236 loc) • 8.41 kB
JavaScript
var fs = require('fs-extra');
var path = require('path');
var index = require('./index-d2845aa8.cjs.js');
var chalk = require('chalk');
var handlebars = require('handlebars');
var ora = require('ora');
var util = require('util');
var recursive = require('recursive-readdir');
var child_process = require('child_process');
var errors = require('@backstage/errors');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
var handlebars__default = /*#__PURE__*/_interopDefaultLegacy(handlebars);
var ora__default = /*#__PURE__*/_interopDefaultLegacy(ora);
var recursive__default = /*#__PURE__*/_interopDefaultLegacy(recursive);
const TEAM_ID_RE = /^@[-\w]+\/[-\w]+$/;
const USER_ID_RE = /^@[-\w]+$/;
const EMAIL_RE = /^[^@]+@[-.\w]+\.[-\w]+$/i;
const DEFAULT_OWNER = "@backstage/maintainers";
async function getCodeownersFilePath(rootDir) {
const possiblePaths = [
path__default["default"].join(rootDir, ".github", "CODEOWNERS"),
path__default["default"].join(rootDir, ".gitlab", "CODEOWNERS"),
path__default["default"].join(rootDir, "docs", "CODEOWNERS"),
path__default["default"].join(rootDir, "CODEOWNERS")
];
for (const p of possiblePaths) {
if (await fs__default["default"].pathExists(p)) {
return p;
}
}
return void 0;
}
function isValidSingleOwnerId(id) {
if (!id || typeof id !== "string") {
return false;
}
return TEAM_ID_RE.test(id) || USER_ID_RE.test(id) || EMAIL_RE.test(id);
}
function parseOwnerIds(spaceSeparatedOwnerIds) {
if (!spaceSeparatedOwnerIds || typeof spaceSeparatedOwnerIds !== "string") {
return void 0;
}
const ids = spaceSeparatedOwnerIds.split(" ").filter(Boolean);
if (!ids.every(isValidSingleOwnerId)) {
return void 0;
}
return ids;
}
async function addCodeownersEntry(ownedPath, ownerStr, codeownersFilePath) {
const ownerIds = parseOwnerIds(ownerStr);
if (!ownerIds || ownerIds.length === 0) {
return false;
}
let filePath = codeownersFilePath;
if (!filePath) {
filePath = await getCodeownersFilePath(index.paths.targetRoot);
if (!filePath) {
return false;
}
}
const allLines = (await fs__default["default"].readFile(filePath, "utf8")).split("\n");
const commentLines = [];
for (const line of allLines) {
if (line[0] !== "#") {
break;
}
commentLines.push(line);
}
const oldDeclarationEntries = allLines.filter((line) => line[0] !== "#").map((line) => line.split(/\s+/).filter(Boolean)).filter((tokens) => tokens.length >= 2).map((tokens) => ({
ownedPath: tokens[0],
ownerIds: tokens.slice(1)
}));
const newDeclarationEntries = oldDeclarationEntries.filter((entry) => entry.ownedPath !== "*").concat([{ ownedPath, ownerIds }]).sort((l1, l2) => l1.ownedPath.localeCompare(l2.ownedPath));
newDeclarationEntries.unshift({
ownedPath: "*",
ownerIds: [DEFAULT_OWNER]
});
const longestOwnedPath = newDeclarationEntries.reduce(
(length, entry) => Math.max(length, entry.ownedPath.length),
0
);
const newDeclarationLines = newDeclarationEntries.map((entry) => {
const entryPath = entry.ownedPath + " ".repeat(longestOwnedPath - entry.ownedPath.length);
return [entryPath, ...entry.ownerIds].join(" ");
});
const newLines = [...commentLines, "", ...newDeclarationLines, ""];
await fs__default["default"].writeFile(filePath, newLines.join("\n"), "utf8");
return true;
}
const exec = util.promisify(child_process.exec);
const TASK_NAME_MAX_LENGTH = 14;
class Task {
static log(name = "") {
process.stderr.write(`${chalk__default["default"].green(name)}
`);
}
static error(message = "") {
process.stderr.write(`
${chalk__default["default"].red(message)}
`);
}
static section(name) {
const title = chalk__default["default"].green(`${name}:`);
process.stderr.write(`
${title}
`);
}
static exit(code = 0) {
process.exit(code);
}
static async forItem(task, item, taskFunc) {
const paddedTask = chalk__default["default"].green(task.padEnd(TASK_NAME_MAX_LENGTH));
const spinner = ora__default["default"]({
prefixText: chalk__default["default"].green(` ${paddedTask}${chalk__default["default"].cyan(item)}`),
spinner: "arc",
color: "green"
}).start();
try {
const result = await taskFunc();
spinner.succeed();
return result;
} catch (error) {
spinner.fail();
throw error;
}
}
static async forCommand(command, options) {
try {
await Task.forItem("executing", command, async () => {
await exec(command, { cwd: options == null ? void 0 : options.cwd });
});
} catch (error) {
errors.assertError(error);
if (error.stderr) {
process.stderr.write(error.stderr);
}
if (error.stdout) {
process.stdout.write(error.stdout);
}
if (options == null ? void 0 : options.optional) {
Task.error(`Warning: Failed to execute command ${chalk__default["default"].cyan(command)}`);
} else {
throw new Error(
`Failed to execute command '${chalk__default["default"].cyan(command)}', ${error}`
);
}
}
}
}
async function templatingTask(templateDir, destinationDir, context, versionProvider, isMonoRepo) {
const files = await recursive__default["default"](templateDir).catch((error) => {
throw new Error(`Failed to read template directory: ${error.message}`);
});
for (const file of files) {
const destinationFile = file.replace(templateDir, destinationDir);
await fs__default["default"].ensureDir(path.dirname(destinationFile));
if (file.endsWith(".hbs")) {
await Task.forItem("templating", path.basename(file), async () => {
const destination = destinationFile.replace(/\.hbs$/, "");
const template = await fs__default["default"].readFile(file);
const compiled = handlebars__default["default"].compile(template.toString(), {
strict: true
});
const contents = compiled(
{ name: path.basename(destination), ...context },
{
helpers: {
versionQuery(name, versionHint) {
return versionProvider(
name,
typeof versionHint === "string" ? versionHint : void 0
);
}
}
}
);
await fs__default["default"].writeFile(destination, contents).catch((error) => {
throw new Error(
`Failed to create file: ${destination}: ${error.message}`
);
});
});
} else {
if (isMonoRepo && file.match("tsconfig.json")) {
continue;
}
await Task.forItem("copying", path.basename(file), async () => {
await fs__default["default"].copyFile(file, destinationFile).catch((error) => {
const destination = destinationFile;
throw new Error(
`Failed to copy file to ${destination} : ${error.message}`
);
});
});
}
}
}
async function addPackageDependency(path, options) {
try {
const pkgJson = await fs__default["default"].readJson(path);
const normalize = (obj) => {
if (Object.keys(obj).length === 0) {
return void 0;
}
return Object.fromEntries(
Object.keys(obj).sort().map((key) => [key, obj[key]])
);
};
pkgJson.dependencies = normalize({
...pkgJson.dependencies,
...options.dependencies
});
pkgJson.devDependencies = normalize({
...pkgJson.devDependencies,
...options.devDependencies
});
pkgJson.peerDependencies = normalize({
...pkgJson.peerDependencies,
...options.peerDependencies
});
await fs__default["default"].writeJson(path, pkgJson, { spaces: 2 });
} catch (error) {
throw new Error(`Failed to add package dependencies, ${error}`);
}
}
exports.Task = Task;
exports.addCodeownersEntry = addCodeownersEntry;
exports.addPackageDependency = addPackageDependency;
exports.getCodeownersFilePath = getCodeownersFilePath;
exports.parseOwnerIds = parseOwnerIds;
exports.templatingTask = templatingTask;
//# sourceMappingURL=tasks-6771fb2c.cjs.js.map
;