#!/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; }