Files

42 lines
2.5 KiB
JavaScript
Executable File

#!/usr/bin/env node
import { ScriptError, expectOk, giteaRequest, main, parseFlags, parseIndex, parseRepo, preflight, readTextFile, resolveLogin } from './lib.js';
const SECTIONS = ['摘要', '需求議題', '工作包議題', '變更內容', '設計重點', '解決的問題', '影響的功能', '測試結果'];
const EMPTY_TALK = new Set(['無', '沒有', 'N/A', 'n/a', '已測試', '已測試通過', '測試通過', '正常', 'ok', 'OK']);
main(async () => {
const flags = parseFlags(process.argv.slice(2), {
required: ['repo', 'head', 'base', 'body-file', 'index'],
optional: ['issue-repo', 'host'],
booleans: ['dry-run'],
});
const repo = parseRepo(flags.repo);
const issueRepo = parseRepo(flags['issue-repo'] ?? flags.repo);
const index = parseIndex(flags.index);
const body = readTextFile(flags['body-file'], '--body-file');
checkSections(body);
checkTestResult(body);
const pullsPath = `/repos/${repo}/pulls`;
const payload = { title: flags.head, head: flags.head, base: flags.base, body };
const login = resolveLogin({ host: flags.host });
if (flags['dry-run']) return { dryRun: true, repo, issueRepo, index, head: flags.head, base: flags.base, title: flags.head, requests: [{ method: 'POST', path: pullsPath, body: payload }] };
await preflight(login, repo);
const existing = await findOpenPull(login, repo, flags.head);
const pull = existing ?? expectOk(await giteaRequest(login, 'POST', pullsPath, { body: payload }), `POST ${pullsPath}`);
return { repo, issueRepo, index, created: existing === null, title: pull.title, url: pull.html_url, number: pull.number, head: flags.head, base: flags.base };
});
function checkSections(body) {
const headings = [...body.matchAll(/^## (.+)$/gm)].map((match) => match[1].trim());
if (headings.length !== SECTIONS.length || headings.some((heading, i) => heading !== SECTIONS[i])) throw new ScriptError('BAD_PR_BODY', `PR 描述必須依序包含:${SECTIONS.join('、')}`);
}
function checkTestResult(body) {
const match = body.match(/^## 測試結果\s*\n([\s\S]*?)(?=^## |$)/m);
if (!match || EMPTY_TALK.has(match[1].trim())) throw new ScriptError('BAD_PR_TEST_RESULT', '測試結果必須填入實際執行的命令與輸出');
}
async function findOpenPull(login, repo, head) {
const path = `/repos/${repo}/pulls`;
const pulls = expectOk(await giteaRequest(login, 'GET', path, { query: { state: 'open' } }), `GET ${path}`) ?? [];
return pulls.find((pull) => pull.head?.label === head || pull.head?.ref === head) ?? null;
}