feat(timer): 碼錶移出領取,等工作樹建成之後才起
原本的順序是「放行 → 設 assignee 與標籤 → 起錶 → 處理分支」,而工作樹建立 失敗會中止整個領取——錶已經起了才失敗,使用者會被計一段什麼都沒做的時間, 而工時要準正是工時報表的立足點。 claim 只留領取鎖的兩件事(assignee 與標籤),起錶交給新的 timer.js,由流程 正本排在 branch-prep 之後。timer 已經跑在這顆議題上時什麼都不做:中斷後重跑 是它最常見的處境,重新起錶會把已經累積的時間切成兩段;跑在別顆上則照舊擋下, 不代勞停錶。 三支腳本讀碼錶的那段各留一份,趁這次收進 lib。 議題 #40 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 起錶。
|
||||
*
|
||||
* 錶與領取鎖是兩件事:鎖用 assignee 加標籤(見 claim.js),錶只管工時。分開的理由是
|
||||
* 時機不同——鎖要在開工之前就上好,錶則要等到**工作樹真的建好之後**才起。工作樹建立
|
||||
* 失敗會中止整個領取,錶要是先起了,使用者就被計了一段什麼都沒做的時間,
|
||||
* 而工時要準正是工時報表的立足點。
|
||||
*
|
||||
* 自己的錶跑在別顆議題上時擋下,不代勞停錶:那一段時間該記在哪顆議題上只有人知道,
|
||||
* 腳本自作主張會把工時記錯地方。錶已經跑在本議題上則什麼都不做——重新起錶會把已經
|
||||
* 累積的時間切成兩段,而中斷後重跑正是這支腳本最常見的處境。
|
||||
*
|
||||
* 用法:
|
||||
* node scripts/timer.js --repo owner/name --index 40 [--host <網址>] [--dry-run]
|
||||
*/
|
||||
import {
|
||||
ScriptError,
|
||||
expectOk,
|
||||
fetchIssue,
|
||||
giteaRequest,
|
||||
listStopwatches,
|
||||
main,
|
||||
parseFlags,
|
||||
parseIndex,
|
||||
parseRepo,
|
||||
preflight,
|
||||
resolveLogin,
|
||||
stopwatchOnIssue,
|
||||
} from './lib.js';
|
||||
|
||||
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}`;
|
||||
const dryRun = flags['dry-run'] === true;
|
||||
|
||||
const login = resolveLogin({ host: flags.host });
|
||||
// 試跑照樣讀現況:手寫一份固定的清單會跟實作走鐘,也說不出「這顆已經在計時了」
|
||||
if (!dryRun) await preflight(login, repo);
|
||||
const issue = await fetchIssue(login, repo, index);
|
||||
|
||||
const watches = await listStopwatches(login);
|
||||
const 已在計時 = stopwatchOnIssue(watches, repo, index) !== null;
|
||||
if (!已在計時 && watches.length > 0) {
|
||||
const elsewhere = watches[0];
|
||||
throw new ScriptError(
|
||||
'STOPWATCH_ON_OTHER_ISSUE',
|
||||
`你的碼錶正跑在 ${elsewhere.repo_owner_name}/${elsewhere.repo_name} 的議題 ` +
|
||||
`#${elsewhere.issue_index} 上,起錶前請先手動停錶,否則工時會記到那一顆去`,
|
||||
);
|
||||
}
|
||||
|
||||
// 已經在跑就不重起:重新起錶會把已經累積的時間切成兩段
|
||||
const planned = 已在計時 ? [] : [{ method: 'POST', path: `${issuePath}/stopwatch/start`, body: {} }];
|
||||
|
||||
if (dryRun) {
|
||||
return { dryRun: true, repo, index, title: issue.title, requests: planned, 已在計時 };
|
||||
}
|
||||
|
||||
for (const { method, path, body } of planned) {
|
||||
expectOk(await giteaRequest(login, method, path, { body }), `${method} ${path}`);
|
||||
}
|
||||
|
||||
return {
|
||||
repo,
|
||||
index: issue.number,
|
||||
title: issue.title,
|
||||
url: issue.html_url,
|
||||
碼錶中: true,
|
||||
已在計時,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user