wp-extract 與 wp-list 都在問「這顆工作包掛在哪顆需求底下」。規則寫兩份, 某天只會有一邊被改到,而分岔的樣子是「清單裡看得到、抽取卻說不是」。 順手把測試裡兩種取段落的寫法統一,並刪掉沒有人傳過的參數。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
88 lines
3.1 KiB
JavaScript
88 lines
3.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* 列出一顆需求議題底下的工作包。
|
|
*
|
|
* 使用者手上常常只有需求議題的編號——留言發在那裡、討論也在那裡——但真正要動手的
|
|
* 單位是工作包。這一支把「哪幾顆工作包掛在這顆需求底下」答出來,讓他從清單裡挑一顆,
|
|
* 而不是自己去 Gitea 網頁上翻。
|
|
*
|
|
* **判準沿用工作包抽取那一套**:關聯段落裡的 `需求議題:#<編號>`。判準與 wp-extract 的
|
|
* `需求議題` 欄位共用 issue-body 的 requirementIndex,不是各寫一份長得像的解析——
|
|
* 標籤、標題前綴、相依關係都當得了歸屬判準,但各發明一套就會與抽取契約分岔。
|
|
*
|
|
* **PR 不算工作包。** 每個 PR 都是議題,而 pr-create 產出的 PR 描述本來就有
|
|
* 「需求議題:#N」那一行,只看 body 會把 PR 混進清單裡。
|
|
*
|
|
* 清單逐頁讀完,讀不完寧可報錯:半份清單會讓使用者從缺了幾顆的清單裡挑,
|
|
* 而且他看不出來缺的是哪幾顆。
|
|
*
|
|
* 用法:
|
|
* node scripts/wp-list.js --repo owner/name --requirement 7 [--host <網址>] [--dry-run]
|
|
*/
|
|
import {
|
|
main,
|
|
pages,
|
|
parseFlags,
|
|
parseIndex,
|
|
parseRepo,
|
|
preflight,
|
|
resolveLogin,
|
|
} from './lib.js';
|
|
import { parseSections, requirementIndex } from './issue-body.js';
|
|
|
|
main(async () => {
|
|
const flags = parseFlags(process.argv.slice(2), {
|
|
required: ['repo', 'requirement'],
|
|
optional: ['host'],
|
|
booleans: ['dry-run'],
|
|
});
|
|
const repo = parseRepo(flags.repo);
|
|
const requirement = parseIndex(flags.requirement, '--requirement');
|
|
const issuesPath = `/repos/${repo}/issues`;
|
|
|
|
if (flags['dry-run']) {
|
|
return {
|
|
dryRun: true,
|
|
repo,
|
|
需求議題: requirement,
|
|
requests: [{ method: 'GET', path: issuesPath }],
|
|
note: '議題清單逐頁讀完,頁數取決於 repo 的議題總數,事前無法列舉。',
|
|
};
|
|
}
|
|
|
|
const login = resolveLogin({ host: flags.host });
|
|
await preflight(login, repo);
|
|
|
|
const 工作包 = [];
|
|
for await (const issues of pages(login, issuesPath, {
|
|
query: { state: 'all' },
|
|
limitCode: 'WORK_PACKAGE_LIMIT',
|
|
limitHint: `翻不完 ${repo} 的議題,列不出 #${requirement} 底下的完整工作包清單;請直接在 Gitea 上確認`,
|
|
})) {
|
|
for (const issue of issues) {
|
|
if (!belongsTo(issue, requirement)) continue;
|
|
工作包.push({
|
|
index: issue.number,
|
|
title: issue.title,
|
|
url: issue.html_url,
|
|
state: issue.state,
|
|
assignee: issue.assignee?.login ?? null,
|
|
});
|
|
}
|
|
}
|
|
// 依編號排序:Gitea 的回傳順序會隨排序設定而變,清單的順序卻是使用者挑選的依據
|
|
工作包.sort((a, b) => a.index - b.index);
|
|
|
|
return { repo, 需求議題: requirement, 工作包, 數量: 工作包.length };
|
|
});
|
|
|
|
|
|
/**
|
|
* 這顆議題是不是掛在指定需求底下的工作包。
|
|
* PR 先擋掉——它的描述也有「需求議題:#N」那一行,但它不是工作包。
|
|
*/
|
|
function belongsTo(issue, requirement) {
|
|
if (issue.pull_request != null) return false;
|
|
return requirementIndex(parseSections(issue.body)) === requirement;
|
|
}
|