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…
	
	Add table
		Add a link
		
	
		Reference in a new issue