yaclt
Version:
Yet Another Change Log Tool
128 lines (111 loc) • 4.01 kB
text/typescript
import fs from "fs";
import git from "isomorphic-git";
import path from "path";
import yargs from "yargs";
import { openInEditor, readLines, touchFile } from "../../utils/file-utils";
import { handleHooks, Hook } from "../../utils/hook-handler";
import { Logger } from "../../utils/logger";
import { formatToChangeTypeTemplate } from "../../utils/string-utils";
import { compileTemplate } from "../../utils/template-utils";
import { ActionOptions } from "../action-options";
import { ActionValidate } from "../validate";
import { WithChangeTypeStrategy } from "./strategies/with-change-type";
import { WithoutChangeTypeStrategy } from "./strategies/without-change-type";
export interface ActionPrepareReleaseOptions extends ActionOptions {
changelogFile: string;
releaseNumber: string;
template: string;
changeTypes: string[];
validationPattern: string;
releaseBranchPattern?: string;
edit: boolean;
preValidate?: Hook;
postValidate?: Hook;
prePrepare?: Hook;
postPrepare?: Hook;
}
const actionPrepareReleaseHandler = async (
options: ActionPrepareReleaseOptions
): Promise<void> => {
touchFile(options.changelogFile);
const valid = ActionValidate({
plumbing: options.plumbing,
logsDir: options.logsDir,
format: options.format,
changeTypes: options.changeTypes,
validationPattern: options.validationPattern,
preValidate: options.preValidate,
postValidate: options.postValidate,
});
if (!valid) {
yargs.exit(1, new Error("Invalid changelog entries found."));
process.exit(1);
}
if (!fs.existsSync(options.logsDir)) {
const message = `Cannot prepare a release because no changelogs were found in ${options.logsDir}`;
Logger.error(message);
yargs.exit(1, new Error(message));
return;
}
if (options.releaseBranchPattern) {
const branchTemplate = compileTemplate(options.releaseBranchPattern);
const branchName = branchTemplate({ releaseNumber: options.releaseNumber });
try {
const allBranches = await git.listBranches({ fs, dir: process.cwd() });
if (allBranches.includes(branchName)) {
Logger.error(
`Failed to create release branch: branch '${branchName}' already exists.`
);
return;
}
await git.branch({ fs, ref: branchName, dir: process.cwd() });
await git.checkout({ fs, ref: branchName, dir: process.cwd() });
} catch {
const message = `Failed to checkout release branch: ${branchName}`;
Logger.error(message);
yargs.exit(1, new Error(message));
return;
}
}
const fileNames = fs.readdirSync(options.logsDir);
const changeTypeCompiledTemplate = formatToChangeTypeTemplate(options.format);
const strategy = changeTypeCompiledTemplate
? new WithChangeTypeStrategy(
changeTypeCompiledTemplate,
options.changeTypes
)
: new WithoutChangeTypeStrategy();
for (const fileName of fileNames) {
const filePath = path.join(options.logsDir, fileName);
const lines = readLines(filePath);
for (const line of lines) {
strategy.processLine(line);
}
}
const changelogAddition = strategy.generate(
options.template,
options.releaseNumber
);
const existingContents = fs.readFileSync(options.changelogFile).toString();
const newContents = `${changelogAddition}\n${existingContents}`;
fs.writeFileSync(options.changelogFile, newContents);
// remove all markdown files in logsDir
fs.readdirSync(options.logsDir)
.filter((fileName: string) => fileName.endsWith(".md"))
.map((file: string) => fs.unlinkSync(path.join(options.logsDir, file)));
if (options.plumbing) {
return;
}
Logger.value(options.changelogFile);
Logger.success(
`${options.changelogFile} updated! Be sure to review the changes before committing.`
);
if (options.edit) {
openInEditor(options.changelogFile);
}
};
export const ActionPrepareRelease = handleHooks(
actionPrepareReleaseHandler,
"prePrepare",
"postPrepare"
);