@telefonica/opensource-scaffold
Version:
Scaffolding for open source projects. A CLI tool to create open source repositories with standard tools and resources
192 lines (191 loc) • 6.67 kB
JavaScript
// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital
// SPDX-License-Identifier: Apache-2.0
import { select, checkbox, input } from "@inquirer/prompts";
import { Command, Option } from "commander";
import { Checker } from "./Checker.js";
import { Creator } from "./Creator.js";
import { SUPPORTED_LICENSES } from "./License.js";
import { LOG_LEVELS } from "./Logger.js";
const SUPPORTED_LICENSES_OPTIONS = SUPPORTED_LICENSES.map((supportedLicense) => ({
name: supportedLicense,
value: supportedLicense,
})).sort((a, b) => a.name.localeCompare(b.name));
/**
* Validates a repository URL
* @param repoUrl The repository URL
* @returns True if the URL is valid, an error message otherwise
*/
export function validateRepositoryUrl(repoUrl) {
if (!Creator.validateRepositoryUrl(repoUrl)) {
return "Invalid repository URL. It should include the protocol and don't have a trailing slash";
}
return true;
}
/**
* Validates an email
* @param str The email
* @returns True if the email is valid or empty, an error message otherwise
*/
export function validateEmail(str) {
if (str.length && !Creator.validateEmail(str)) {
return "Invalid email";
}
return true;
}
/**
* Check scaffold resources
* @param options Options from commander
*/
export async function check(options) {
const checker = new Checker({
log: options.log,
});
const result = await checker.check();
if (!result.valid) {
if (options.exitOverride) {
throw new Error(result.report.message);
}
else {
console.error(result.report.message);
process.exit(1);
}
}
}
/**
* Create scaffold resources
* @param options Options from commander
*/
export async function create(options) {
const overwrite = options.overwrite;
let license = options.license;
let resourcesToOverwrite = [];
let projectName = options.name;
let projectDescription = options.description;
let copyrightHolder = options.copyright;
let repo = options.repo;
let email = options.email;
function validationError(message) {
if (options.exitOverride) {
throw new Error(message);
}
else {
console.error(message);
process.exit(1);
}
}
if (!projectName && !options.prompts) {
validationError("Project name is required. Please provide it using the --name option or enable prompts");
}
if (!license && !options.prompts) {
validationError("License is required. Please provide it using the --license option or enable prompts");
}
if (!repo && !options.prompts) {
validationError("Repo URL is required. Please provide it using the --repo option or enable prompts");
}
if (!copyrightHolder && !options.prompts) {
validationError("Copyright holder is required. Please provide it using the --copyright option or enable prompts");
}
if (!license && options.prompts) {
license = (await select({
message: "Select a license",
choices: SUPPORTED_LICENSES_OPTIONS,
}));
}
if (!projectName && options.prompts) {
projectName = await input({
message: "Enter the project name",
required: true,
});
}
if (!copyrightHolder && options.prompts) {
copyrightHolder = await input({
message: "Enter the project copyright holder",
required: true,
});
}
if (!repo && options.prompts) {
repo = await input({
message: "Enter the repository URL, including the protocol and without the trailing slash (https://github.com/owner/repo)",
required: false,
validate: validateRepositoryUrl,
});
}
if (!projectDescription && options.prompts) {
projectDescription = await input({
message: "Enter the project description (optional)",
required: false,
});
}
if (!email && options.prompts) {
email = await input({
message: "Enter the community email (optional)",
required: false,
validate: validateEmail,
});
}
const creator = new Creator({
log: options.log,
license: license,
projectName: projectName,
projectDescription,
copyrightHolder,
overwrite,
repositoryUrl: repo,
communityEmail: email,
});
if (!overwrite && options.prompts) {
const conflicts = creator.getConflicts(true);
if (conflicts.length > 0) {
resourcesToOverwrite = (await checkbox({
message: "Next files already exist. Select those you want to overwrite",
choices: conflicts.map((resource) => ({
name: resource.name,
value: resource,
})),
}));
creator.setResourcesToOverwrite(resourcesToOverwrite);
}
}
await creator.create();
}
/**
* Run the CLI
* @param exitOverride Enable exit override, for testing purposes
*/
export async function run(exitOverride) {
const program = new Command();
program
.name("Open Source Scaffold")
.description("CLI to create or check open source project scaffolding");
program
.command("create")
.description("Create scaffold resources")
.addOption(new Option("--log <log>", "Log level").choices(LOG_LEVELS).default("info"))
.addOption(new Option("-l, --license <license>", "License type").choices(SUPPORTED_LICENSES))
.addOption(new Option("-n, --name <name>", "Project name"))
.addOption(new Option("-d, --description <description>", "Project description"))
.addOption(new Option("-c, --copyright <copyright>", "Copyright"))
.addOption(new Option("-r, --repo <repo>", "Repository URL"))
.addOption(new Option("-e, --email <email>", "Community email"))
.addOption(new Option("--overwrite", "Overwrite existing files"))
.addOption(new Option("--no-prompts", "Disable prompts"))
.showHelpAfterError()
.action(async (options) => {
await create({
...options,
exitOverride,
});
});
program
.command("check")
.description("Check scaffold resources")
.addOption(new Option("--log <log>", "Log level").choices(LOG_LEVELS).default("info"))
.showHelpAfterError()
.action(async (options) => {
await check({
...options,
exitOverride,
});
});
program.parse();
}