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