gh-changelogen
Version:
Changelog generator for GitHub Releases
105 lines (96 loc) • 3.2 kB
JavaScript
import { promises, constants } from 'node:fs';
import { resolve } from 'node:path';
import z from 'zod';
import { parse as parse$1 } from 'zodiarg';
import { $fetch } from 'ohmyfetch';
import 'node:child_process';
function isFunction(value) {
return typeof value === "function";
}
async function isExists(path) {
try {
await promises.access(path, constants.F_OK);
return true;
} catch {
return false;
}
}
function getHeaders(options) {
return {
accept: "application/vnd.github.v3+json",
authorization: `token ${options.token}`
};
}
async function fetcherDefault(tag, options = {}) {
if (!options.github) {
throw new Error(`'github' option is required`);
}
const url = `https://api.github.com/repos/${options.github}/releases/tags/${tag}`;
return $fetch(url, { headers: getHeaders(options), method: "GET" });
}
async function fetchGithubRelease(tag, options) {
const fetcher = options?.fetcher ?? fetcherDefault;
if (!isFunction(fetcher)) {
throw new Error("fetcher is not a function");
}
return fetcher(tag, options);
}
function generatorDefault(release) {
return Promise.resolve(`# ${release.name}${release.name !== release.tag_name ? ` ${release.tag_name}` : ""} (${release.published_at})
This changelog is generated by [GitHub Releases](${release.html_url})
${release.body}
`);
}
async function generateChangelog(release, generator = generatorDefault) {
if (!isFunction(generator)) {
throw new Error("generator is not a function");
}
return generator(release);
}
const GITHUB_TOKEN_KEY = "GITHUB_TOKEN";
const DEFAULT_CHANGELOG_FILE = "CHANGELOG.md";
function parse(args) {
return parse$1(
{
options: {
repo: z.string().describe("GitHub repository name, format `owner/repo` (e.g. `kazupon/gh-changelogen`)"),
tag: z.string().describe("GitHub release tag (e.g. `v0.0.1`)"),
output: z.string().default(DEFAULT_CHANGELOG_FILE).describe(
`Changelog file name to create or update. defaults to '${DEFAULT_CHANGELOG_FILE}' and resolved relative`
),
token: z.string().default(GITHUB_TOKEN_KEY).describe(`GitHub token, if you won\u2019t specify, respect '${GITHUB_TOKEN_KEY}' env`)
},
flags: {},
args: [],
alias: {}
},
args,
{ helpWithNoArgs: true, help: true }
);
}
async function writeChangelog(output, changelog) {
let existChangelog = "";
if (await isExists(output)) {
existChangelog = (await promises.readFile(output, "utf-8")).toString();
}
await promises.writeFile(output, [changelog, "\n", existChangelog].join(""), "utf-8");
}
async function main(args) {
const input = parse(args);
let token = input.options.token;
if (token === GITHUB_TOKEN_KEY) {
token = process.env.GITHUB_TOKEN || "";
if (!token) {
throw new Error(`Not found ${GITHUB_TOKEN_KEY} in env`);
}
}
const release = await fetchGithubRelease(input.options.tag, {
github: input.options.repo,
token
});
const changelog = await generateChangelog(release);
console.log(changelog);
const output = resolve(process.cwd(), input.options.output);
await writeChangelog(output, changelog);
}
export { main };