Files
jiantw83 a944141109 fix(留言分頁): 改用 timeline 分頁讀取留言
Gitea 議題留言端點無法可靠處理分頁;改由 timeline 逐頁篩選 comment 事件並去重,讓抽取、整併與 PR 留言流程取得完整集合。
2026-09-22 14:37:38 +08:00

108 lines
3.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* 把一顆工作包議題抽成實作階段要用的精簡 JSON。
*
* 與需求議題的抽取(issue-extract)同樣「只讀 body 不讀留言」,但多做三件事:
* 1. 待辦是巢狀的——每一項待辦底下掛它自己的驗收,並各自帶回未經修改的 `raw`,
* 下游靠 `raw` 做精確字串替換來勾選 checkbox,只改那一行,不重寫整份 body。
* 2. 介面契約是四欄表格,四欄都要留著。
* 3. body 說不出的兩個活狀態要現查:相依與領取人。
*
* 用法:
* node scripts/wp-extract.js --repo owner/name --index 9 [--host <網址>] [--dry-run]
*/
import {
UNMERGED_COMMENT_NOTE,
countUnmergedComments,
fetchIssue,
main,
pages,
parseFlags,
parseIndex,
parseRepo,
preflight,
resolveLogin,
} from './lib.js';
import {
checklistInSection,
listSection,
parseSections,
requirementIndex,
tableRows,
textSection,
} from './issue-body.js';
/** 介面契約表格由左到右的四欄 */
const CONTRACT_COLUMNS = ['介面', '產出者', '消費者', '形狀'];
main(async () => {
const flags = parseFlags(process.argv.slice(2), {
required: ['repo', 'index'],
optional: ['host'],
booleans: ['dry-run'],
});
const repo = parseRepo(flags.repo);
const index = parseIndex(flags.index);
const issuePath = `/repos/${repo}/issues/${index}`;
if (flags['dry-run']) {
return {
dryRun: true,
repo,
index,
requests: [
{ method: 'GET', path: issuePath },
{ method: 'GET', path: `${issuePath}/dependencies` },
{ method: 'GET', path: `${issuePath}/blocks` },
{ method: 'GET', path: `${issuePath}/timeline` },
],
note: UNMERGED_COMMENT_NOTE,
};
}
const login = resolveLogin({ host: flags.host });
const { user } = await preflight(login, repo);
const issue = await fetchIssue(login, repo, index);
const sections = parseSections(issue.body);
// 先讀完兩種相依再組輸出:欄位順序照契約寫成 {blocks, depends},
// 但請求順序維持「先問誰擋著我」,與 --dry-run 預告的一致。
const depends = await fetchLinked(login, `${issuePath}/dependencies`, '先決');
const blocks = await fetchLinked(login, `${issuePath}/blocks`, '阻擋');
return {
index: issue.number,
url: issue.html_url,
title: issue.title,
需求議題: requirementIndex(sections),
描述: textSection(sections, '描述'),
架構圖: textSection(sections, '架構圖'),
範圍邊界: listSection(sections, '範圍邊界'),
介面契約: tableRows(sections, '介面契約', CONTRACT_COLUMNS),
待辦: checklistInSection(issue.body, '待辦'),
整體驗收: listSection(sections, '整體驗收'),
repos: listSection(sections, 'repo 列表'),
相依: { blocks, depends },
未處理留言數: await countUnmergedComments(login, repo, index, user.login),
};
});
/**
* 讀一種相依關係上的議題編號。
* 逐頁讀完:半份清單會讓下游把實作順序排錯,那比直接報錯更難發現。
* @param {string} kind 出現在錯誤訊息裡的關係名稱
*/
async function fetchLinked(login, path, kind) {
const indexes = [];
for await (const issues of pages(login, path, {
limitCode: 'DEPENDENCY_LIMIT',
limitHint: `${path} 的${kind}關係太多,讀不完整份清單`,
})) {
for (const issue of issues) indexes.push(issue.number);
}
return indexes;
}