feat: add the validate command
This commit is contained in:
parent
2e9d0d4b66
commit
5ba75a8e3d
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
113
src/changelog-schema.js
Normal 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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@ -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' ] }
|
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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 = "";
|
||||||
|
|
||||||
|
@ -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
16
src/command/validate.js
Normal 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,
|
||||||
|
};
|
16
src/command/validate.test.js
Normal file
16
src/command/validate.test.js
Normal 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();
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user