diff --git a/README.md b/README.md new file mode 100644 index 0000000..0773a06 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# Changelord, registrar of deeds extraordinaire + +Changelord is a changelog manager scratching my particular itches. +It's cli-based, and keep its data in a YAML file adhering +to a well-defined schema. + +The first iteration of `changelord` was written [in Perl][original]. You can +read its [introductory article][blog] on my blog. + +## Installation + + pnpm install changelord + +## CLI commands + +### Global options + +#### `--help` + +Outputs the list of commands and options. + +#### `--version` + +Outputs the `changelord` version. + +#### `--source` + +Specifies which source yaml file to use. Defaults to the `CHANGELOG.yml` file +in the current directory. + +### `changelord init` + +Initializes the changelog source file. The YAML file is made of three +sections. + +- `project` -- contains information and configuration about the project itself. +- `releases` -- the entries for the changelog proper. +- `change_types` -- defines all types of changes this project supports. + +### `changelord add` + +Adds an entry to the `NEXT` release. + + $ changelord add --type=maint added a changelog to the project. + +#### Options + +- `--type` -- type of change. +- `--ticket` -- associated ticket. + +### `changelord print` + +Renders the changelog as markdown. + +#### Options + +- `--no-next` -- don't show the `NEXT` section. + +### `changelord cut` + +Cuts the next release. That is, resolves the `NEXT` version number based on the +latest version and the changes in the `NEXT` section, and sets its date as +today. Modifies the source file with the result. + +#### Options + +- `--dry` -- Resolves the next version but only outputs the resulting section + without changing the source file. + +### `changelord schema` + +Outputs the JSON schema defining the structure of the source file. + +### `changelord upcoming` + +Outputs the changes listed in the `NEXT` release. + +### `changelord latest-version` + +Outputs the latest non-NEXT release. + + $ changelord latest-version + 3.2.0 + +[blog]: https://techblog.babyl.ca/entry/changelord/ +[original]: https://metacpan.org/dist/App-Changelord/view/bin/changelord diff --git a/Taskfile.yaml b/Taskfile.yaml index 0692409..65705da 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,6 +1,22 @@ # https://taskfile.dev -version: '3' +version: "3" + +vars: + PARENT_BRANCH: main tasks: - test: vitest run src + test: vitest run src test:dev: vitest src + + integrate: + deps: [test] + preconditions: + - sh: git is-clean + msg: checkout not clean + - sh: git diff-ls {{.PARENT_BRANCH}} | grep test + msg: no tests were added + - sh: git diff-ls {{.PARENT_BRANCH}} | grep CHANGELOG.yml + msg: no changelog entry detected + cmds: + - git checkout {{.PARENT_BRANCH}} + - git weld - diff --git a/package.json b/package.json index ab0dd1d..05b5638 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,18 @@ "version": "0.0.1", "description": "cli-based changelog manager", "type": "module", - "main": "src/index.js", + "main": "src/changelord.js", "bin": { "changelord": "./src/changelord.js" }, + "repository": { + "type": "git", + "url": "https://git.babyl.ca/yanick/changelord.js.git" + }, + "bugs": { + "url": "https://git.babyl.ca/yanick/changelord.js/issues" + }, + "homepage": "https://git.babyl.ca/yanick/changelord.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, diff --git a/src/changelord.js b/src/changelord.js index 52af277..d35da24 100755 --- a/src/changelord.js +++ b/src/changelord.js @@ -13,22 +13,23 @@ import schema from "./command/schema.js"; import add from "./command/add.js"; import cut from "./command/cut.js"; import upcoming from "./command/upcoming.js"; +import latest from "./command/latest-version.js"; consola.raw = (...args) => console.log(...args); yargs(hideBin(process.argv)) - .config({ - consola, - }) - .default("source", join(process.cwd(), "CHANGELOG.yml")) - .describe("source", "changelog source") - .command(print) - .command(init) - .command(add) - .command(schema) - .command(cut) - .command(print) - .command(upcoming) - .strictCommands() - .help() - .parse(); + .config({ + consola, + }) + .default("source", join(process.cwd(), "CHANGELOG.yml")) + .describe("source", "changelog source") + .command(init) + .command(add) + .command(print) + .command(cut) + .command(schema) + .command(upcoming) + .command(latest) + .strictCommands() + .help() + .parse(); diff --git a/src/command/add.js b/src/command/add.js index 4c48fe0..e04ae40 100644 --- a/src/command/add.js +++ b/src/command/add.js @@ -3,49 +3,51 @@ import yaml from "yaml"; import u from "@yanick/updeep-remeda"; const handler = async (config) => { - if (!config.change) - throw new Error("can't add a change without a description"); + if (!config.change) + throw new Error("can't add a change without a description"); - const entry = { - desc: config.change.join(" "), - }; + const entry = { + desc: config.change.join(" "), + }; - if (config.ticket) entry.ticket = config.ticket; - if (config.type) entry.type = config.type; + if (config.ticket) entry.ticket = config.ticket; + if (config.type) entry.type = config.type; - config.consola.start(`adding '${entry.desc}' to the changelog`); + config.consola.start(`adding '${entry.desc}' to the changelog`); - config.add_to_next(entry); + config.add_to_next(entry); - config.consola.success("done!"); + config.consola.success("done!"); }; export default { - command: "add [change...]", - desc: "add a change to the NEXT release", - builder: (yargs) => { - yargs - .string("ticket") - .string("type") - .middleware((argv) => { - argv.add_to_next = async (entry) => { - const changelog = yaml.parse(await fs.readFile(argv.source, "utf-8")); + command: "add [change...]", + desc: "add a change to the NEXT release", + builder: (yargs) => { + yargs + .string("ticket") + .describe("ticket", "ticket associated with the change") + .string("type") + .describe("type", "type of change") + .middleware((argv) => { + argv.add_to_next = async (entry) => { + const changelog = yaml.parse(await fs.readFile(argv.source, "utf-8")); - let next = changelog.releases.find(u.matches({ version: "NEXT" })); - if (!next) { - next = { version: "NEXT", changes: [] }; - changelog.releases.unshift(next); - } + let next = changelog.releases.find(u.matches({ version: "NEXT" })); + if (!next) { + next = { version: "NEXT", changes: [] }; + changelog.releases.unshift(next); + } - if (Object.keys(entry).length === 1) { - entry = entry.desc; - } + if (Object.keys(entry).length === 1) { + entry = entry.desc; + } - next.changes.push(entry); + next.changes.push(entry); - return fs.writeFile(argv.source, yaml.stringify(changelog)); - }; - }); - }, - handler, + return fs.writeFile(argv.source, yaml.stringify(changelog)); + }; + }); + }, + handler, }; diff --git a/src/command/latest-version.js b/src/command/latest-version.js new file mode 100644 index 0000000..4519ed2 --- /dev/null +++ b/src/command/latest-version.js @@ -0,0 +1,27 @@ +import yaml from "yaml"; +import fs from "fs-extra"; + +const handler = async (config) => { + const changelog = yaml.parse(await fs.readFile(config.source, "utf-8")); + + const latest_version = changelog.releases.find( + ({ version }) => version !== "NEXT" + ); + + if (config.json) { + config.consola.raw(latest_version); + } else { + config.consola.raw(latest_version.version); + } +}; + +export default { + command: "latest-version", + desc: "output the latest version present in the changelog", + builder: (yargs) => { + yargs + .boolean("json") + .describe("json", "output latest version entry as json"); + }, + handler, +};