feat(CI): CHANGELOG.mdの追記個所をチェックするCIを追加 (#12963)
* feat(CI): CHANGELOG.mdの追記個所をチェックするCIを追加 * fix * remove strategy * fix * fix
This commit is contained in:
parent
bc8a741e14
commit
57017f2747
11 changed files with 3481 additions and 0 deletions
43
.github/workflows/changelog-check.yml
vendored
Normal file
43
.github/workflows/changelog-check.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
name: Check the description in CHANGELOG.md
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-changelog:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout head
|
||||||
|
uses: actions/checkout@v4.1.1
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4.0.1
|
||||||
|
with:
|
||||||
|
node-version-file: '.node-version'
|
||||||
|
|
||||||
|
- name: Checkout base
|
||||||
|
run: |
|
||||||
|
mkdir _base
|
||||||
|
cp -r .git _base/.git
|
||||||
|
cd _base
|
||||||
|
git fetch --depth 1 origin ${{ github.base_ref }}
|
||||||
|
git checkout origin/${{ github.base_ref }} CHANGELOG.md
|
||||||
|
|
||||||
|
- name: Copy to Checker directory for CHANGELOG-base.md
|
||||||
|
run: cp _base/CHANGELOG.md scripts/changelog-checker/CHANGELOG-base.md
|
||||||
|
- name: Copy to Checker directory for CHANGELOG-head.md
|
||||||
|
run: cp CHANGELOG.md scripts/changelog-checker/CHANGELOG-head.md
|
||||||
|
- name: diff
|
||||||
|
continue-on-error: true
|
||||||
|
run: diff -u CHANGELOG-base.md CHANGELOG-head.md
|
||||||
|
working-directory: scripts/changelog-checker
|
||||||
|
|
||||||
|
- name: Setup Checker
|
||||||
|
run: npm install
|
||||||
|
working-directory: scripts/changelog-checker
|
||||||
|
- name: Run Checker
|
||||||
|
run: npm run run
|
||||||
|
working-directory: scripts/changelog-checker
|
9
scripts/changelog-checker/.eslintrc.cjs
Normal file
9
scripts/changelog-checker/.eslintrc.cjs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
parserOptions: {
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'../../packages/shared/.eslintrc.js',
|
||||||
|
],
|
||||||
|
};
|
3
scripts/changelog-checker/.gitignore
vendored
Normal file
3
scripts/changelog-checker/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
coverage
|
||||||
|
.idea
|
2769
scripts/changelog-checker/package-lock.json
generated
Normal file
2769
scripts/changelog-checker/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
scripts/changelog-checker/package.json
Normal file
24
scripts/changelog-checker/package.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "changelog-checker",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"run": "vite-node src/index.ts",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:coverage": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mdast": "4.0.3",
|
||||||
|
"@types/node": "20.10.7",
|
||||||
|
"@vitest/coverage-v8": "1.1.3",
|
||||||
|
"mdast-util-to-string": "4.0.0",
|
||||||
|
"remark": "15.0.1",
|
||||||
|
"remark-parse": "11.0.0",
|
||||||
|
"typescript": "5.3.3",
|
||||||
|
"unified": "11.0.4",
|
||||||
|
"vite": "5.0.11",
|
||||||
|
"vite-node": "1.1.3",
|
||||||
|
"vitest": "1.1.3"
|
||||||
|
}
|
||||||
|
}
|
87
scripts/changelog-checker/src/checker.ts
Normal file
87
scripts/changelog-checker/src/checker.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import { Release } from './parser.js';
|
||||||
|
|
||||||
|
export class Result {
|
||||||
|
public readonly success: boolean;
|
||||||
|
public readonly message?: string;
|
||||||
|
|
||||||
|
private constructor(success: boolean, message?: string) {
|
||||||
|
this.success = success;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ofSuccess(): Result {
|
||||||
|
return new Result(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ofFailed(message?: string): Result {
|
||||||
|
return new Result(false, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* develop -> masterまたはrelease -> masterを想定したパターン。
|
||||||
|
* base側の先頭とhead側で追加された分のリリースより1つ前のバージョンが等価であるかチェックする。
|
||||||
|
*/
|
||||||
|
export function checkNewRelease(base: Release[], head: Release[]): Result {
|
||||||
|
const releaseCountDiff = head.length - base.length;
|
||||||
|
if (releaseCountDiff <= 0) {
|
||||||
|
return Result.ofFailed('Invalid release count.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseLatest = base[0];
|
||||||
|
const headPrevious = head[releaseCountDiff];
|
||||||
|
|
||||||
|
if (baseLatest.releaseName !== headPrevious.releaseName) {
|
||||||
|
return Result.ofFailed('Contains unexpected releases.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ofSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* topic -> developまたはtopic -> masterを想定したパターン。
|
||||||
|
* head側の最新リリース配下に書き加えられているかをチェックする。
|
||||||
|
*/
|
||||||
|
export function checkNewTopic(base: Release[], head: Release[]): Result {
|
||||||
|
if (head.length !== base.length) {
|
||||||
|
return Result.ofFailed('Invalid release count.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const headLatest = head[0];
|
||||||
|
for (let relIdx = 0; relIdx < base.length; relIdx++) {
|
||||||
|
const baseItem = base[relIdx];
|
||||||
|
const headItem = head[relIdx];
|
||||||
|
if (baseItem.releaseName !== headItem.releaseName) {
|
||||||
|
// リリースの順番が変わってると成立しないのでエラーにする
|
||||||
|
return Result.ofFailed(`Release is different. base:${baseItem.releaseName}, head:${headItem.releaseName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseItem.categories.length !== headItem.categories.length) {
|
||||||
|
// カテゴリごと書き加えられたパターン
|
||||||
|
if (headLatest.releaseName !== headItem.releaseName) {
|
||||||
|
// 最新リリース以外に追記されていた場合
|
||||||
|
return Result.ofFailed(`There is an error in the update history. expected additions:${headLatest.releaseName}, actual additions:${headItem.releaseName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// カテゴリ数の変動はないのでリスト項目の数をチェック
|
||||||
|
for (let catIdx = 0; catIdx < baseItem.categories.length; catIdx++) {
|
||||||
|
const baseCategory = baseItem.categories[catIdx];
|
||||||
|
const headCategory = headItem.categories[catIdx];
|
||||||
|
|
||||||
|
if (baseCategory.categoryName !== headCategory.categoryName) {
|
||||||
|
// カテゴリの順番が変わっていると成立しないのでエラーにする
|
||||||
|
return Result.ofFailed(`Category is different. base:${baseCategory.categoryName}, head:${headCategory.categoryName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseCategory.items.length !== headCategory.items.length) {
|
||||||
|
if (headLatest.releaseName !== headItem.releaseName) {
|
||||||
|
// 最新リリース以外に追記されていた場合
|
||||||
|
return Result.ofFailed(`There is an error in the update history. expected additions:${headLatest.releaseName}, actual additions:${headItem.releaseName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ofSuccess();
|
||||||
|
}
|
33
scripts/changelog-checker/src/index.ts
Normal file
33
scripts/changelog-checker/src/index.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import * as process from 'process';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { parseChangeLog } from './parser.js';
|
||||||
|
import { checkNewRelease, checkNewTopic } from './checker.js';
|
||||||
|
|
||||||
|
function abort(message?: string) {
|
||||||
|
if (message) {
|
||||||
|
console.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
if (!fs.existsSync('./CHANGELOG-base.md') || !fs.existsSync('./CHANGELOG-head.md')) {
|
||||||
|
console.error('CHANGELOG-base.md or CHANGELOG-head.md is missing.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = parseChangeLog('./CHANGELOG-base.md');
|
||||||
|
const head = parseChangeLog('./CHANGELOG-head.md');
|
||||||
|
|
||||||
|
const result = (base.length < head.length)
|
||||||
|
? checkNewRelease(base, head)
|
||||||
|
: checkNewTopic(base, head);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
abort(result.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
62
scripts/changelog-checker/src/parser.ts
Normal file
62
scripts/changelog-checker/src/parser.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import { unified } from 'unified';
|
||||||
|
import remarkParse from 'remark-parse';
|
||||||
|
import { Heading, List, Node } from 'mdast';
|
||||||
|
import { toString } from 'mdast-util-to-string';
|
||||||
|
|
||||||
|
export class Release {
|
||||||
|
public readonly releaseName: string;
|
||||||
|
public readonly categories: ReleaseCategory[];
|
||||||
|
|
||||||
|
constructor(releaseName: string, categories: ReleaseCategory[] = []) {
|
||||||
|
this.releaseName = releaseName;
|
||||||
|
this.categories = [...categories];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ReleaseCategory {
|
||||||
|
public readonly categoryName: string;
|
||||||
|
public readonly items: string[];
|
||||||
|
|
||||||
|
constructor(categoryName: string, items: string[] = []) {
|
||||||
|
this.categoryName = categoryName;
|
||||||
|
this.items = [...items];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHeading(node: Node): node is Heading {
|
||||||
|
return node.type === 'heading';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isList(node: Node): node is List {
|
||||||
|
return node.type === 'list';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseChangeLog(path: string): Release[] {
|
||||||
|
const input = fs.readFileSync(path, { encoding: 'utf8' });
|
||||||
|
const processor = unified().use(remarkParse);
|
||||||
|
|
||||||
|
const releases: Release[] = [];
|
||||||
|
const root = processor.parse(input);
|
||||||
|
|
||||||
|
let release: Release | null = null;
|
||||||
|
let category: ReleaseCategory | null = null;
|
||||||
|
for (const it of root.children) {
|
||||||
|
if (isHeading(it) && it.depth === 2) {
|
||||||
|
// リリース
|
||||||
|
release = new Release(toString(it));
|
||||||
|
releases.push(release);
|
||||||
|
} else if (isHeading(it) && it.depth === 3 && release) {
|
||||||
|
// リリース配下のカテゴリ
|
||||||
|
category = new ReleaseCategory(toString(it));
|
||||||
|
release.categories.push(category);
|
||||||
|
} else if (isList(it) && category) {
|
||||||
|
for (const listItem of it.children) {
|
||||||
|
// カテゴリ配下のリスト項目
|
||||||
|
category.items.push(toString(listItem));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return releases;
|
||||||
|
}
|
414
scripts/changelog-checker/test/checker.test.ts
Normal file
414
scripts/changelog-checker/test/checker.test.ts
Normal file
|
@ -0,0 +1,414 @@
|
||||||
|
import {expect, suite, test} from "vitest";
|
||||||
|
import {Release, ReleaseCategory} from "../src/parser";
|
||||||
|
import {checkNewRelease, checkNewTopic} from "../src/checker";
|
||||||
|
|
||||||
|
suite('checkNewRelease', () => {
|
||||||
|
test('headに新しいリリースがある1', () => {
|
||||||
|
const base = [new Release('2024.12.0')]
|
||||||
|
const head = [new Release('2024.12.1'), new Release('2024.12.0')]
|
||||||
|
|
||||||
|
const result = checkNewRelease(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('headに新しいリリースがある2', () => {
|
||||||
|
const base = [new Release('2024.12.0')]
|
||||||
|
const head = [new Release('2024.12.2'), new Release('2024.12.1'), new Release('2024.12.0')]
|
||||||
|
|
||||||
|
const result = checkNewRelease(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('リリースの数が同じ', () => {
|
||||||
|
const base = [new Release('2024.12.0')]
|
||||||
|
const head = [new Release('2024.12.0')]
|
||||||
|
|
||||||
|
const result = checkNewRelease(base, head)
|
||||||
|
|
||||||
|
console.log(result.message)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('baseにあるリリースがheadにない', () => {
|
||||||
|
const base = [new Release('2024.12.0')]
|
||||||
|
const head = [new Release('2024.12.2'), new Release('2024.12.1')]
|
||||||
|
|
||||||
|
const result = checkNewRelease(base, head)
|
||||||
|
|
||||||
|
console.log(result.message)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
suite('checkNewTopic', () => {
|
||||||
|
test('追記なし', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('最新バージョンにカテゴリを追加したときはエラーにならない', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('最新バージョンからカテゴリを削除したときはエラーにならない', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat3',
|
||||||
|
'feat4',
|
||||||
|
])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('最新バージョンに追記したときはエラーにならない', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
'feat3',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('最新バージョンから削除したときはエラーにならない', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('古いバージョンにカテゴリを追加したときはエラーになる', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
new ReleaseCategory('Client', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
console.log(result.message)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('古いバージョンからカテゴリを削除したときはエラーになる', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
console.log(result.message)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('古いバージョンに追記したときはエラーになる', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
'feat3',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
console.log(result.message)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('古いバージョンから削除したときはエラーになる', () => {
|
||||||
|
const base = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const head = [
|
||||||
|
new Release('2024.12.1', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
'feat2',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
new Release('2024.12.0', [
|
||||||
|
new ReleaseCategory('Server', [
|
||||||
|
'feat1',
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
const result = checkNewTopic(base, head)
|
||||||
|
|
||||||
|
console.log(result.message)
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
31
scripts/changelog-checker/tsconfig.json
Normal file
31
scripts/changelog-checker/tsconfig.json
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"strict": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types"
|
||||||
|
],
|
||||||
|
"lib": [
|
||||||
|
"esnext"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"test/**/*"
|
||||||
|
]
|
||||||
|
}
|
6
scripts/changelog-checker/vite.config.ts
Normal file
6
scripts/changelog-checker/vite.config.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import {defineConfig} from 'vite';
|
||||||
|
|
||||||
|
|
||||||
|
const config = defineConfig({});
|
||||||
|
|
||||||
|
export default config;
|
Loading…
Reference in a new issue