Compare commits

...

3 Commits

Author SHA1 Message Date
1c7b6158f4 Merge branch 'lord8-validate'
fix #8
2023-05-18 14:06:45 -04:00
031f2cb825 add changelog 2023-05-18 14:06:19 -04:00
5ba75a8e3d feat: add the validate command 2023-05-18 14:04:51 -04:00
11 changed files with 177 additions and 71 deletions

View File

@ -2,7 +2,6 @@ project:
name: changelord name: changelord
homepage: https://git.babyl.ca/yanick/changelord.js homepage: https://git.babyl.ca/yanick/changelord.js
with_stats: true with_stats: true
ticket_url: null
releases: releases:
- version: NEXT - version: NEXT
changes: changes:
@ -15,6 +14,9 @@ releases:
commit: 167f631d1fe4eadba3ed5fdadbe378b8255d4ad2 commit: 167f631d1fe4eadba3ed5fdadbe378b8255d4ad2
- type: feat - type: feat
desc: git-gather also filters on descs desc: git-gather also filters on descs
- type: feat
desc: add the validate command
commit: 5ba75a8e3d42f633e38c3584898ef0085c48fb04
- version: 0.1.0 - version: 0.1.0
changes: changes:
- port the core of the Perl changelord to JavaScript. - port the core of the Perl changelord to JavaScript.

View File

@ -82,6 +82,10 @@ Outputs the latest non-NEXT release.
$ changelord latest-version $ changelord latest-version
3.2.0 3.2.0
### `changelord validate`
Validates the changelog source against its json schema.
### `changelord git-gather` ### `changelord git-gather`
Gathers change entries from git commits. If any are found, they are Gathers change entries from git commits. If any are found, they are

View File

@ -25,6 +25,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@yanick/updeep-remeda": "^2.2.0", "@yanick/updeep-remeda": "^2.2.0",
"ajv": "^8.12.0",
"consola": "^3.1.0", "consola": "^3.1.0",
"fs-extra": "^11.1.1", "fs-extra": "^11.1.1",
"markdown-utils": "^1.0.0", "markdown-utils": "^1.0.0",

113
src/changelog-schema.js Normal file
View File

@ -0,0 +1,113 @@
export default {
type: "object",
additionalProperties: false,
properties: {
change_types: {
items: {
additionalProperties: false,
properties: {
keywords: {
items: {
type: "string",
},
type: "array",
},
level: {
enum: ["major", "minor", "patch"],
},
title: {
type: "string",
},
},
type: "object",
},
type: "array",
},
project: {
additionalProperties: false,
properties: {
homepage: {
description: "url of the project's homepage",
examples: ["https://github.com/yanick/app-changelord"],
type: ["string", "null"],
},
name: {
description: "name of the project",
examples: ["App::Changelord"],
type: ["null", "string"],
},
ticket_url: {
description:
"perl code that takes a ticket string (e.g. 'GH123') via the `$_` variable and turns it into a link.",
examples: [
"s!GH(\\d+)!https://github.com/yanick/App-Changelord/issue/$1/",
{
'/^\\d+$/ ? "https://.../$_"': "undef",
},
],
type: "string",
},
with_stats: {
description: "if true, add git statistics when bumping the version.",
},
},
type: "object",
},
releases: {
items: {
oneOf: [
{
type: "string",
},
{
additionalProperties: false,
properties: {
changes: {
items: {
$ref: "#/$defs/change",
},
type: "array",
},
date: {
type: ["null", "string"],
},
version: {
type: ["null", "string"],
},
},
type: "object",
},
],
},
type: "array",
},
},
$defs: {
change: {
oneOf: [
{
type: "string",
},
{
additionalProperties: false,
properties: {
commit: {
type: ["string", "null"],
},
desc: {
type: "string",
},
ticket: {
type: ["string", "null"],
},
type: {
type: ["string", "null"],
},
},
required: ["desc"],
type: "object",
},
],
},
},
};

View File

@ -1,59 +0,0 @@
type: object
additionalProperties: false
properties:
project:
type: object
additionalProperties: false
properties:
homepage:
type: [ string, 'null' ]
description: url of the project's homepage
examples:
- https://github.com/yanick/app-changelord
name:
type: [ 'null', string ]
description: name of the project
examples:
- App::Changelord
ticket_url:
type: string
description: perl code that takes a ticket string (e.g. 'GH123') via the `$_` variable and turns it into a link.
examples:
- s!GH(\d+)!https://github.com/yanick/App-Changelord/issue/$1/
- /^\d+$/ ? "https://.../$_" : undef
with_stats:
description: if true, add git statistics when bumping the version.
change_types:
type: array
items:
type: object
additionalProperties: false
properties:
keywords:
type: array
items: { type: string }
level: { enum: [ major, minor, patch ] }
title: { type: string }
releases:
type: array
items:
oneOf:
- type: string
- type: object
additionalProperties: false
properties:
version: { type: [ 'null', string ] }
date: { type: ['null',string] }
changes: { type: 'array', items: { $ref: '#/$defs/change' } }
$defs:
change:
oneOf:
- type: string
- type: object
required: [ desc ]
additionalProperties: false
properties:
desc: { type: string }
ticket: { type: [ string, 'null' ] }
type: { type: [ string, 'null' ] }
commit: { type: [ string, 'null' ] }

View File

@ -9,6 +9,7 @@ import consola from "consola";
import u from "@yanick/updeep-remeda"; import u from "@yanick/updeep-remeda";
import { once } from "remeda"; import { once } from "remeda";
import simpleGit from "simple-git"; import simpleGit from "simple-git";
import Ajv from "ajv";
import print from "./command/print.js"; import print from "./command/print.js";
import init from "./command/init.js"; import init from "./command/init.js";
@ -17,7 +18,9 @@ import add from "./command/add.js";
import cut from "./command/cut.js"; import cut from "./command/cut.js";
import upcoming, { next_release } from "./command/upcoming.js"; import upcoming, { next_release } from "./command/upcoming.js";
import latest, { latest_version } from "./command/latest-version.js"; import latest, { latest_version } from "./command/latest-version.js";
import validate from "./command/validate.js";
import git_gather from "./command/git-gather.js"; import git_gather from "./command/git-gather.js";
import schemaV1 from "./changelog-schema.js";
consola.raw = (...args) => console.log(...args); consola.raw = (...args) => console.log(...args);
@ -26,7 +29,18 @@ yargs(hideBin(process.argv))
config.git = once(simpleGit); config.git = once(simpleGit);
config.consola = consola; config.consola = consola;
config.changelog = once(() => config.changelog = once(() =>
fs.readFile(config.source, "utf-8").then(yaml.parse) fs
.readFile(config.source, "utf-8")
.then(yaml.parse)
.then((doc) => {
const ajv = new Ajv();
const validate = ajv.compile(schemaV1);
const valid = validate(doc);
if (valid) return doc;
config.consola.error("invalid changelog: ", validate.errors);
throw "changelog is invalid";
})
); );
config.save_changelog = async (changelog) => { config.save_changelog = async (changelog) => {
if (!changelog) changelog = await config.changelog(); if (!changelog) changelog = await config.changelog();
@ -36,7 +50,9 @@ yargs(hideBin(process.argv))
config.latest_version = latest_version.bind(config); config.latest_version = latest_version.bind(config);
return config; return config;
}) })
.middleware((argv) => { .middleware((argv, yargs) => {
argv.yargs = yargs;
argv.add_to_next = async (entry) => { argv.add_to_next = async (entry) => {
const changelog = yaml.parse(await fs.readFile(argv.source, "utf-8")); const changelog = yaml.parse(await fs.readFile(argv.source, "utf-8"));
@ -70,6 +86,7 @@ yargs(hideBin(process.argv))
}) })
.command(latest) .command(latest)
.command(git_gather) .command(git_gather)
.command(validate)
.strictCommands() .strictCommands()
.help() .help()
.parse(); .parse();

View File

@ -19,7 +19,6 @@ export const base_changelog = {
name: null, name: null,
homepage: null, homepage: null,
with_stats: true, with_stats: true,
ticket_url: null,
}, },
releases: [{ version: "NEXT", changes: [] }], releases: [{ version: "NEXT", changes: [] }],
change_types, change_types,

View File

@ -103,7 +103,7 @@ export const render_release = (config, source) => (release) => {
}; };
const handler = async (config) => { const handler = async (config) => {
const source = await fs.readFile(config.source, "utf-8").then(yaml.parse); const source = await config.changelog();
let output = ""; let output = "";

View File

@ -1,12 +1,9 @@
import fs from "fs-extra"; import fs from "fs-extra";
import yaml from "yaml";
import schemaV1 from "../changelog-schema.js";
const handler = async ({ consola }) => { const handler = async ({ consola }) => {
consola.raw( consola.raw(yaml.stringify(schemaV1));
await fs.readFile(
new URL("../changelog-schema.yml", import.meta.url),
"utf-8"
)
);
}; };
export default { export default {

16
src/command/validate.js Normal file
View File

@ -0,0 +1,16 @@
const handler = async (config) => {
config.consola.start("validating changelog...");
try {
await config.changelog();
config.consola.success("valid!");
} catch (e) {
config.yargs.exit(1, "changelog is invalid :-(");
}
};
export default {
command: "validate",
desc: "validate the changelog against its json schema",
handler,
};

View File

@ -0,0 +1,16 @@
import { test, expect, vi } from "vitest";
import validate from "./validate.js";
test("basic", async () => {
const success = vi.fn();
const noop = () => true;
await validate.handler({
changelog: () => ({}),
consola: {
start: noop,
success,
},
});
expect(success).toHaveBeenCalled();
});