@morehao/dsh-github-trending 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/client.js +200 -193
- package/lib/{chunk-VF6DOIQQ.js → chunk-R3EDPRRO.js} +45 -10
- package/lib/index.js +1 -1
- package/lib/internal.d.ts +15 -1
- package/lib/internal.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.7.1] - 2026-11-01
|
|
4
|
+
|
|
5
|
+
> 依赖升级:`dsh.core.version` 0.7.1 → **0.8.0**,`@morehao/trendradar-contract` 0.4.0 → **0.5.0**。
|
|
6
|
+
> 两者均已发布(核心 `v0.8.0` / 契约 `0.5.0`)。wire 仍 `ipcSchema=2` / `digestSchema=2`。
|
|
7
|
+
|
|
8
|
+
### 新增 — 发版单仓分析
|
|
9
|
+
|
|
10
|
+
- **订阅页重做**:一个列表 + 上方工具条(搜索 / +添加仓库 / 选择 / **生成变更分析**);
|
|
11
|
+
去掉 ⌘K 命令栏、底部快捷键行与冗长说明。
|
|
12
|
+
- **行操作**:「触发分析」(只分析该仓)· 「查看变更」(原「变更」改名)· 「移除」。
|
|
13
|
+
- **生成变更分析**:`POST /releases/run`——不传 repo 跑全部,传 repo 只跑该仓;单仓按行显示
|
|
14
|
+
「分析中…」,全局给结果横幅;完成后刷新列表。
|
|
15
|
+
- 单仓触发**不推进 `lastReleaseWeek`**(避免把本周误标已检查、导致周调度跳过),全量仍推进;
|
|
16
|
+
旧核心(< 0.8.0)单仓触发降级为全量、不传 `--repo`。
|
|
17
|
+
- 发版运行记录新增 `scopeRepo`;并发触发由后端拒绝(409)。
|
|
18
|
+
- 文档抽屉(发版记录 markdown)保持不变。
|
|
19
|
+
|
|
3
20
|
## [v0.6.1] - 2026-11-01
|
|
4
21
|
|
|
5
22
|
### 变更
|
package/client.js
CHANGED
|
@@ -454,7 +454,7 @@ var DICT = {
|
|
|
454
454
|
"releases.budget.auto": "自动",
|
|
455
455
|
"releases.budget.fixed": "指定",
|
|
456
456
|
"subs.title": "订阅",
|
|
457
|
-
"subs.desc": "
|
|
457
|
+
"subs.desc": "管理发版订阅仓库,查看每周变更记录。",
|
|
458
458
|
"subs.select": "选择",
|
|
459
459
|
"subs.select.done": "完成",
|
|
460
460
|
"subs.cmd.placeholder": "搜索 owner/repo,或输入完整仓库名直接添加",
|
|
@@ -481,8 +481,18 @@ var DICT = {
|
|
|
481
481
|
"subs.removed.many": "已移除 {count} 个仓库,下次运行随产物提交。",
|
|
482
482
|
"subs.remove": "移除",
|
|
483
483
|
"subs.removing": "移除中…",
|
|
484
|
-
"subs.row.changes": "
|
|
484
|
+
"subs.row.changes": "查看变更",
|
|
485
485
|
"subs.row.changesRepo": "查看 {repo} 的变更",
|
|
486
|
+
"subs.row.trigger": "触发分析",
|
|
487
|
+
"subs.row.triggerRepo": "只对 {repo} 生成变更分析",
|
|
488
|
+
"subs.search.placeholder": "搜索仓库",
|
|
489
|
+
"subs.search.label": "搜索仓库",
|
|
490
|
+
"subs.toolbar.add": "+ 添加仓库",
|
|
491
|
+
"subs.toolbar.analyze": "生成变更分析",
|
|
492
|
+
"subs.analyzing": "分析中…",
|
|
493
|
+
"subs.analyze.done": "变更分析已生成 · {count} 个仓库有更新",
|
|
494
|
+
"subs.analyze.doneOne": "{repo} 已更新",
|
|
495
|
+
"subs.analyze.failed": "分析失败:{message}",
|
|
486
496
|
"subs.row.removeRepo": "移除 {repo}",
|
|
487
497
|
"subs.col.repo": "仓库",
|
|
488
498
|
"subs.col.tag": "最近版本",
|
|
@@ -966,7 +976,7 @@ var DICT = {
|
|
|
966
976
|
"releases.budget.auto": "Auto",
|
|
967
977
|
"releases.budget.fixed": "Fixed",
|
|
968
978
|
"subs.title": "Subscriptions",
|
|
969
|
-
"subs.desc": "
|
|
979
|
+
"subs.desc": "Manage release-subscription repos and review weekly change records.",
|
|
970
980
|
"subs.select": "Select",
|
|
971
981
|
"subs.select.done": "Done",
|
|
972
982
|
"subs.cmd.placeholder": "Search owner/repo, or type a full repo name to add it",
|
|
@@ -993,8 +1003,18 @@ var DICT = {
|
|
|
993
1003
|
"subs.removed.many": "Removed {count} repos; committed with artifacts on the next run.",
|
|
994
1004
|
"subs.remove": "Remove",
|
|
995
1005
|
"subs.removing": "Removing…",
|
|
996
|
-
"subs.row.changes": "
|
|
1006
|
+
"subs.row.changes": "View changes",
|
|
997
1007
|
"subs.row.changesRepo": "Show changes for {repo}",
|
|
1008
|
+
"subs.row.trigger": "Analyze",
|
|
1009
|
+
"subs.row.triggerRepo": "Generate change analysis for {repo} only",
|
|
1010
|
+
"subs.search.placeholder": "Search repositories",
|
|
1011
|
+
"subs.search.label": "Search repositories",
|
|
1012
|
+
"subs.toolbar.add": "+ Add repository",
|
|
1013
|
+
"subs.toolbar.analyze": "Generate change analysis",
|
|
1014
|
+
"subs.analyzing": "Analyzing…",
|
|
1015
|
+
"subs.analyze.done": "Change analysis ready · {count} repos updated",
|
|
1016
|
+
"subs.analyze.doneOne": "{repo} updated",
|
|
1017
|
+
"subs.analyze.failed": "Analysis failed: {message}",
|
|
998
1018
|
"subs.row.removeRepo": "Remove {repo}",
|
|
999
1019
|
"subs.col.repo": "Repo",
|
|
1000
1020
|
"subs.col.tag": "Latest version",
|
|
@@ -1434,6 +1454,24 @@ var CSS = `
|
|
|
1434
1454
|
|
|
1435
1455
|
/* ——— 订阅:命令栏 + 四列表格 + 选择条 + 发版记录抽屉 ——— */
|
|
1436
1456
|
.dshgt-subs{gap:12px;padding-bottom:10px;}
|
|
1457
|
+
/* 工具条:搜索 + 添加 + 选择 + 生成变更分析;列表在上方操作区之下 */
|
|
1458
|
+
.dshgt-subs-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-bottom:12px;border-bottom:1px solid color-mix(in srgb,var(--gt-border) 70%,transparent);}
|
|
1459
|
+
.dshgt-subs-search{position:relative;flex:1;min-width:180px;max-width:340px;display:flex;align-items:center;}
|
|
1460
|
+
.dshgt-subs-search svg{position:absolute;left:9px;color:var(--gt-muted);pointer-events:none;}
|
|
1461
|
+
.dshgt-subs-search input{width:100%;height:32px;box-sizing:border-box;padding:0 10px 0 30px;border:1px solid var(--gt-border);border-radius:8px;background:var(--gt-bg);color:var(--gt-fg);font:inherit;font-size:13px;}
|
|
1462
|
+
.dshgt-subs-search input:focus{outline:2px solid color-mix(in srgb,var(--gt-accent) 30%,transparent);outline-offset:1px;border-color:color-mix(in srgb,var(--gt-accent) 45%,var(--gt-border));}
|
|
1463
|
+
.dshgt-subs-search input::placeholder{color:var(--gt-muted);}
|
|
1464
|
+
.dshgt-subs-toolbar-spacer{flex:1 1 auto;min-width:0;}
|
|
1465
|
+
.dshgt-subs-addform{display:flex;align-items:center;gap:6px;}
|
|
1466
|
+
.dshgt-subs-addform input{height:32px;box-sizing:border-box;width:200px;padding:0 9px;border:1px solid var(--gt-border);border-radius:8px;background:var(--gt-bg);color:var(--gt-fg);font:inherit;font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}
|
|
1467
|
+
.dshgt-subs-addform input:focus{outline:2px solid color-mix(in srgb,var(--gt-accent) 30%,transparent);outline-offset:1px;}
|
|
1468
|
+
.dshgt-subs-act.gen{color:var(--gt-muted);}
|
|
1469
|
+
.dshgt-subs-act.gen:hover{color:var(--gt-accent);background:color-mix(in srgb,var(--gt-accent) 10%,transparent);}
|
|
1470
|
+
.dshgt-subs-act.gen:disabled{opacity:.5;cursor:not-allowed;}
|
|
1471
|
+
.dshgt-subs-rowbusy{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--gt-accent);padding:3px 4px;white-space:nowrap;}
|
|
1472
|
+
.dshgt-spin{width:12px;height:12px;border-radius:50%;border:2px solid color-mix(in srgb,var(--gt-accent) 30%,transparent);border-top-color:var(--gt-accent);animation:dshgt-spin .7s linear infinite;display:inline-block;flex:none;}
|
|
1473
|
+
@keyframes dshgt-spin{to{transform:rotate(360deg);}}
|
|
1474
|
+
.dshgt-subs-row.is-analyzing{background:color-mix(in srgb,var(--gt-accent) 6%,var(--gt-bg));}
|
|
1437
1475
|
.dshgt-subs-head{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap;}
|
|
1438
1476
|
.dshgt-subs-head-main{min-width:0;}
|
|
1439
1477
|
.dshgt-subs-head h2{margin:0;font-size:17px;font-weight:650;letter-spacing:-.01em;}
|
|
@@ -1781,11 +1819,6 @@ function releaseRunsOf(state) {
|
|
|
1781
1819
|
const nested = Array.isArray(state.status?.releaseRuns) ? state.status.releaseRuns : void 0;
|
|
1782
1820
|
return root ?? nested ?? [];
|
|
1783
1821
|
}
|
|
1784
|
-
function lastReleaseAtOf(state) {
|
|
1785
|
-
if (state === null || state === void 0) return void 0;
|
|
1786
|
-
const value = state.lastReleaseAt ?? state.status?.lastReleaseAt;
|
|
1787
|
-
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1788
|
-
}
|
|
1789
1822
|
function getPreview(runId, signal) {
|
|
1790
1823
|
return apiCall("GET", `/preview?id=${encodeURIComponent(runId)}`, void 0, signal === void 0 ? {} : { signal });
|
|
1791
1824
|
}
|
|
@@ -1833,6 +1866,45 @@ async function postSubscriptions(action, repos) {
|
|
|
1833
1866
|
const payload = await apiCall("POST", "/subscriptions", { action, repos });
|
|
1834
1867
|
return normalizeSubscriptionList(payload);
|
|
1835
1868
|
}
|
|
1869
|
+
async function postReleasesRun(repo) {
|
|
1870
|
+
const payload = await apiCall("POST", "/releases/run", repo === void 0 ? {} : { repo });
|
|
1871
|
+
return normalizeReleaseRun(payload.release);
|
|
1872
|
+
}
|
|
1873
|
+
function normalizeReleaseRun(raw) {
|
|
1874
|
+
const r = typeof raw === "object" && raw !== null ? raw : {};
|
|
1875
|
+
const str = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
1876
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
1877
|
+
const opt = (v) => typeof v === "string" && v !== "" ? v : void 0;
|
|
1878
|
+
const status = r.status === "ok" || r.status === "failed" || r.status === "skipped" || r.status === "cancelled" ? r.status : "failed";
|
|
1879
|
+
const record = {
|
|
1880
|
+
id: str(r.id),
|
|
1881
|
+
startedAt: str(r.startedAt),
|
|
1882
|
+
finishedAt: str(r.finishedAt),
|
|
1883
|
+
status,
|
|
1884
|
+
checked: num(r.checked),
|
|
1885
|
+
changed: num(r.changed),
|
|
1886
|
+
newTags: num(r.newTags),
|
|
1887
|
+
summarized: num(r.summarized),
|
|
1888
|
+
degraded: num(r.degraded),
|
|
1889
|
+
failed: num(r.failed),
|
|
1890
|
+
rateLimited: num(r.rateLimited),
|
|
1891
|
+
durationMs: num(r.durationMs)
|
|
1892
|
+
};
|
|
1893
|
+
const maybe = {
|
|
1894
|
+
tokens: typeof r.tokens === "number" ? r.tokens : void 0,
|
|
1895
|
+
commit: opt(r.commit),
|
|
1896
|
+
prUrl: opt(r.prUrl),
|
|
1897
|
+
branch: opt(r.branch),
|
|
1898
|
+
errorCode: opt(r.errorCode),
|
|
1899
|
+
errorMessage: opt(r.errorMessage),
|
|
1900
|
+
coreVersion: opt(r.coreVersion),
|
|
1901
|
+
scopeRepo: opt(r.scopeRepo)
|
|
1902
|
+
};
|
|
1903
|
+
for (const [k, v] of Object.entries(maybe)) {
|
|
1904
|
+
if (v !== void 0) record[k] = v;
|
|
1905
|
+
}
|
|
1906
|
+
return record;
|
|
1907
|
+
}
|
|
1836
1908
|
async function getSubscriptionDoc(repo, volume, signal) {
|
|
1837
1909
|
const query = `?repo=${encodeURIComponent(repo)}${volume === void 0 ? "" : `&volume=${encodeURIComponent(String(volume))}`}`;
|
|
1838
1910
|
const payload = await apiCall(
|
|
@@ -4403,12 +4475,6 @@ function monthDay(value) {
|
|
|
4403
4475
|
if (value === void 0 || value === "") return "—";
|
|
4404
4476
|
return DATE_RE2.test(value) ? value.slice(5) : value;
|
|
4405
4477
|
}
|
|
4406
|
-
function clock(value) {
|
|
4407
|
-
return /^\d{2}:\d{2}/.test(value) ? value.slice(0, 5) : value;
|
|
4408
|
-
}
|
|
4409
|
-
function weekdayLabel(t, weekday) {
|
|
4410
|
-
return weekday >= 1 && weekday <= 7 ? t(`weekday.${weekday}`) : String(weekday);
|
|
4411
|
-
}
|
|
4412
4478
|
function toneOf(status) {
|
|
4413
4479
|
if (status === "ok") return "ok";
|
|
4414
4480
|
if (status === "rate_limited") return "warn";
|
|
@@ -4466,47 +4532,6 @@ function highlight(text, needle) {
|
|
|
4466
4532
|
if (index < text.length) parts.push(text.slice(index));
|
|
4467
4533
|
return parts;
|
|
4468
4534
|
}
|
|
4469
|
-
function weekStart(date) {
|
|
4470
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
|
4471
|
-
if (match === null) return date;
|
|
4472
|
-
const value = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3])));
|
|
4473
|
-
const dayIndex = (value.getUTCDay() + 6) % 7;
|
|
4474
|
-
value.setUTCDate(value.getUTCDate() - dayIndex);
|
|
4475
|
-
return value.toISOString().slice(0, 10);
|
|
4476
|
-
}
|
|
4477
|
-
function metaOf(t, store) {
|
|
4478
|
-
const state = store?.state ?? null;
|
|
4479
|
-
const schedule = state?.schedule;
|
|
4480
|
-
const releaseSchedule = state?.settings?.releases?.schedule;
|
|
4481
|
-
const weekday = typeof schedule?.releaseWeekday === "number" ? schedule.releaseWeekday : releaseSchedule?.weekday;
|
|
4482
|
-
const time = typeof schedule?.releaseTime === "string" && schedule.releaseTime !== "" ? schedule.releaseTime : releaseSchedule?.time;
|
|
4483
|
-
const timezone = (typeof schedule?.releaseTimezone === "string" && schedule.releaseTimezone !== "" ? schedule.releaseTimezone : releaseSchedule?.timezone) ?? schedule?.timezone ?? "Asia/Shanghai";
|
|
4484
|
-
const cadence = weekday === void 0 || time === void 0 || time === "" ? t("subs.meta.cadence") : t("subs.meta.cadence.full", { weekday: weekdayLabel(t, weekday), time: clock(time) });
|
|
4485
|
-
const nextAt = typeof schedule?.nextReleaseAt === "string" && schedule.nextReleaseAt !== "" ? schedule.nextReleaseAt : void 0;
|
|
4486
|
-
const next = nextAt === void 0 ? void 0 : shortTime(nextAt);
|
|
4487
|
-
const runs = releaseRunsOf(state);
|
|
4488
|
-
const newest = runs.length === 0 ? void 0 : runs.reduce((a, b) => Date.parse(b.finishedAt || b.startedAt) > Date.parse(a.finishedAt || a.startedAt) ? b : a);
|
|
4489
|
-
const lastCheckAt = newest?.finishedAt ?? lastReleaseAtOf(state);
|
|
4490
|
-
const lastCheck = typeof lastCheckAt === "string" && lastCheckAt !== "" ? shortTime(lastCheckAt) : void 0;
|
|
4491
|
-
let lastWeek;
|
|
4492
|
-
if (newest !== void 0) {
|
|
4493
|
-
const today = zonedDateString(/* @__PURE__ */ new Date(), timezone);
|
|
4494
|
-
const currentFrom = weekStart(today);
|
|
4495
|
-
const lastFrom = addDays(currentFrom, -7);
|
|
4496
|
-
const at = newest.finishedAt !== "" ? newest.finishedAt : newest.startedAt;
|
|
4497
|
-
const runDate = zonedDateString(new Date(at), timezone);
|
|
4498
|
-
if (runDate >= lastFrom && runDate <= addDays(lastFrom, 6)) {
|
|
4499
|
-
lastWeek = t("subs.meta.lastWeek", { from: monthDay(lastFrom), to: monthDay(addDays(lastFrom, 6)), changed: newest.changed });
|
|
4500
|
-
} else if (runDate >= currentFrom && runDate <= addDays(currentFrom, 6)) {
|
|
4501
|
-
lastWeek = t("subs.meta.thisWeek", { from: monthDay(currentFrom), to: monthDay(addDays(currentFrom, 6)), changed: newest.changed });
|
|
4502
|
-
}
|
|
4503
|
-
}
|
|
4504
|
-
const info = { cadence };
|
|
4505
|
-
if (next !== void 0) info.next = next;
|
|
4506
|
-
if (lastWeek !== void 0) info.lastWeek = lastWeek;
|
|
4507
|
-
if (lastCheck !== void 0) info.lastCheck = lastCheck;
|
|
4508
|
-
return info;
|
|
4509
|
-
}
|
|
4510
4535
|
function isMissingDoc(code, message) {
|
|
4511
4536
|
if (code !== void 0 && /NOT_?FOUND|MISSING|NO_?DOC/i.test(code)) return true;
|
|
4512
4537
|
return /not\s*found|no such file|enoent|404|不存在|no release document|has no release|无发版记录/i.test(message);
|
|
@@ -4661,7 +4686,10 @@ function SubscriptionsView(props) {
|
|
|
4661
4686
|
const [selected, setSelected] = import_react.default.useState([]);
|
|
4662
4687
|
const [confirming, setConfirming] = import_react.default.useState(null);
|
|
4663
4688
|
const [openRepo, setOpenRepo] = import_react.default.useState(null);
|
|
4664
|
-
const [
|
|
4689
|
+
const [addOpen, setAddOpen] = import_react.default.useState(false);
|
|
4690
|
+
const [addValue, setAddValue] = import_react.default.useState("");
|
|
4691
|
+
const [analyzing, setAnalyzing] = import_react.default.useState(null);
|
|
4692
|
+
const [result, setResult] = import_react.default.useState(null);
|
|
4665
4693
|
const load = import_react.default.useCallback((signal) => {
|
|
4666
4694
|
getSubscriptions(signal).then((next) => {
|
|
4667
4695
|
if (signal?.aborted === true) return;
|
|
@@ -4678,23 +4706,10 @@ function SubscriptionsView(props) {
|
|
|
4678
4706
|
load(controller.signal);
|
|
4679
4707
|
return () => controller.abort();
|
|
4680
4708
|
}, [load]);
|
|
4681
|
-
const inputRef = import_react.default.useRef(null);
|
|
4682
|
-
import_react.default.useEffect(() => {
|
|
4683
|
-
const onKey = (event) => {
|
|
4684
|
-
if (event.key.toLowerCase() !== "k" || !(event.metaKey || event.ctrlKey)) return;
|
|
4685
|
-
event.preventDefault();
|
|
4686
|
-
inputRef.current?.focus?.();
|
|
4687
|
-
};
|
|
4688
|
-
document.addEventListener("keydown", onKey);
|
|
4689
|
-
return () => document.removeEventListener("keydown", onKey);
|
|
4690
|
-
}, []);
|
|
4691
4709
|
const rows = mergeRows(list);
|
|
4692
4710
|
const needle = query.trim().toLowerCase();
|
|
4693
4711
|
const matched = needle === "" ? rows : rows.filter((row) => row.name.toLowerCase().includes(needle));
|
|
4694
4712
|
const sorted = sortRows(matched, sort);
|
|
4695
|
-
const candidate = needle !== "" && REPO_PATTERN.test(query.trim()) ? query.trim() : void 0;
|
|
4696
|
-
const subscribed = candidate !== void 0 && rows.some((row) => row.name.toLowerCase() === candidate.toLowerCase());
|
|
4697
|
-
const activeFocus = focus >= 0 && focus < sorted.length ? focus : -1;
|
|
4698
4713
|
const toggleSelect = (repo) => {
|
|
4699
4714
|
setSelected((current) => current.includes(repo) ? current.filter((item) => item !== repo) : [...current, repo]);
|
|
4700
4715
|
};
|
|
@@ -4704,8 +4719,8 @@ function SubscriptionsView(props) {
|
|
|
4704
4719
|
setNote(null);
|
|
4705
4720
|
postSubscriptions("add", [repo]).then((next) => {
|
|
4706
4721
|
setList(next);
|
|
4707
|
-
|
|
4708
|
-
|
|
4722
|
+
setAddValue("");
|
|
4723
|
+
setAddOpen(false);
|
|
4709
4724
|
setNote(t("subs.add.queued", { repo }));
|
|
4710
4725
|
}).catch((failure) => setError(errorText(failure))).finally(() => setBusy(null));
|
|
4711
4726
|
};
|
|
@@ -4734,6 +4749,22 @@ function SubscriptionsView(props) {
|
|
|
4734
4749
|
setConfirming(null);
|
|
4735
4750
|
}).finally(() => setBusy(null));
|
|
4736
4751
|
};
|
|
4752
|
+
const runAnalyze = (repo) => {
|
|
4753
|
+
if (analyzing !== null) return;
|
|
4754
|
+
setAnalyzing(repo === void 0 ? { scope: "all" } : { scope: "one", repo });
|
|
4755
|
+
setError(null);
|
|
4756
|
+
setResult(null);
|
|
4757
|
+
postReleasesRun(repo).then((record) => {
|
|
4758
|
+
setResult({ scope: repo === void 0 ? "all" : "one", ...repo === void 0 ? {} : { repo }, ok: record.status !== "failed", changed: record.changed });
|
|
4759
|
+
if (record.status === "failed" && record.errorMessage !== void 0) {
|
|
4760
|
+
setError(t("subs.analyze.failed", { message: record.errorMessage }));
|
|
4761
|
+
}
|
|
4762
|
+
load();
|
|
4763
|
+
}).catch((failure) => {
|
|
4764
|
+
setError(errorText(failure));
|
|
4765
|
+
setResult({ scope: repo === void 0 ? "all" : "one", ...repo === void 0 ? {} : { repo }, ok: false, changed: 0 });
|
|
4766
|
+
}).finally(() => setAnalyzing(null));
|
|
4767
|
+
};
|
|
4737
4768
|
const onSort = (key) => {
|
|
4738
4769
|
setSort((current) => current.key === key ? { key, dir: current.dir === 1 ? -1 : 1 } : { key, dir: 1 });
|
|
4739
4770
|
};
|
|
@@ -4754,48 +4785,19 @@ function SubscriptionsView(props) {
|
|
|
4754
4785
|
active ? h("span", { className: "dshgt-subs-ord" }, sort.dir === 1 ? "▲" : "▼") : null
|
|
4755
4786
|
);
|
|
4756
4787
|
};
|
|
4757
|
-
const
|
|
4758
|
-
|
|
4759
|
-
if (event.key === "Escape") {
|
|
4760
|
-
setQuery("");
|
|
4761
|
-
setFocus(-1);
|
|
4762
|
-
if (selecting) {
|
|
4763
|
-
setSelecting(false);
|
|
4764
|
-
setSelected([]);
|
|
4765
|
-
}
|
|
4766
|
-
return;
|
|
4767
|
-
}
|
|
4768
|
-
if (event.key === "Enter") {
|
|
4769
|
-
if (candidate !== void 0 && !subscribed) {
|
|
4770
|
-
event.preventDefault();
|
|
4771
|
-
addRepo(candidate);
|
|
4772
|
-
return;
|
|
4773
|
-
}
|
|
4774
|
-
if (activeFocus >= 0) {
|
|
4775
|
-
event.preventDefault();
|
|
4776
|
-
setOpenRepo(sorted[activeFocus]?.name ?? null);
|
|
4777
|
-
}
|
|
4778
|
-
return;
|
|
4779
|
-
}
|
|
4780
|
-
if (event.key === "ArrowDown") {
|
|
4781
|
-
event.preventDefault();
|
|
4782
|
-
setFocus(Math.min(sorted.length - 1, focus + 1));
|
|
4783
|
-
return;
|
|
4784
|
-
}
|
|
4785
|
-
if (event.key === "ArrowUp") {
|
|
4786
|
-
event.preventDefault();
|
|
4787
|
-
setFocus(Math.max(-1, focus - 1));
|
|
4788
|
-
}
|
|
4789
|
-
};
|
|
4790
|
-
const meta = metaOf(t, props.store);
|
|
4791
|
-
const metaRight = needle !== "" ? [t("subs.meta.match", { matched: sorted.length, total: rows.length })] : [
|
|
4792
|
-
meta.lastCheck === void 0 ? void 0 : t("subs.meta.lastCheck", { time: meta.lastCheck }),
|
|
4793
|
-
meta.lastWeek
|
|
4794
|
-
].filter((item) => item !== void 0);
|
|
4788
|
+
const canAdd = REPO_PATTERN.test(addValue.trim());
|
|
4789
|
+
const alreadySubscribed = canAdd && rows.some((row) => row.name.toLowerCase() === addValue.trim().toLowerCase());
|
|
4795
4790
|
const drawerRow = openRepo === null ? void 0 : rows.find((row) => row.name === openRepo);
|
|
4796
|
-
const
|
|
4791
|
+
const globalBusy = analyzing !== null;
|
|
4792
|
+
const banner = result === null ? null : result.ok ? h(
|
|
4793
|
+
Alert,
|
|
4794
|
+
{ kind: "ok" },
|
|
4795
|
+
result.scope === "all" ? t("subs.analyze.done", { count: result.changed }) : t("subs.analyze.doneOne", { repo: result.repo ?? "" })
|
|
4796
|
+
) : null;
|
|
4797
|
+
const rowNodes = sorted.map((row) => {
|
|
4797
4798
|
const item = row.item;
|
|
4798
4799
|
const tone = toneOf(item?.status);
|
|
4800
|
+
const rowAnalyzing = analyzing !== null && analyzing.scope === "one" && analyzing.repo === row.name;
|
|
4799
4801
|
const checkbox = selecting ? h(
|
|
4800
4802
|
"span",
|
|
4801
4803
|
{ key: "cb", role: "cell", className: "dshgt-subs-cbwrap" },
|
|
@@ -4813,7 +4815,7 @@ function SubscriptionsView(props) {
|
|
|
4813
4815
|
{
|
|
4814
4816
|
key: row.name,
|
|
4815
4817
|
role: "row",
|
|
4816
|
-
className: `dshgt-subs-row${
|
|
4818
|
+
className: `dshgt-subs-row${rowAnalyzing ? " is-analyzing" : ""}`,
|
|
4817
4819
|
onClick: () => {
|
|
4818
4820
|
if (selecting) toggleSelect(row.name);
|
|
4819
4821
|
else setOpenRepo(row.name);
|
|
@@ -4836,6 +4838,21 @@ function SubscriptionsView(props) {
|
|
|
4836
4838
|
h(
|
|
4837
4839
|
"span",
|
|
4838
4840
|
{ key: "act", role: "cell", className: "dshgt-subs-actions" },
|
|
4841
|
+
rowAnalyzing ? h("span", { className: "dshgt-subs-rowbusy" }, h("span", { className: "dshgt-spin" }), t("subs.analyzing")) : h(
|
|
4842
|
+
"button",
|
|
4843
|
+
{
|
|
4844
|
+
type: "button",
|
|
4845
|
+
className: "dshgt-subs-act gen",
|
|
4846
|
+
"aria-label": t("subs.row.triggerRepo", { repo: row.name }),
|
|
4847
|
+
title: t("subs.row.triggerRepo", { repo: row.name }),
|
|
4848
|
+
disabled: globalBusy || busy !== null,
|
|
4849
|
+
onClick: (event) => {
|
|
4850
|
+
event.stopPropagation();
|
|
4851
|
+
runAnalyze(row.name);
|
|
4852
|
+
}
|
|
4853
|
+
},
|
|
4854
|
+
t("subs.row.trigger")
|
|
4855
|
+
),
|
|
4839
4856
|
h(
|
|
4840
4857
|
"button",
|
|
4841
4858
|
{
|
|
@@ -4871,10 +4888,7 @@ function SubscriptionsView(props) {
|
|
|
4871
4888
|
{ className: "dshgt-subs-empty" },
|
|
4872
4889
|
h("b", null, t("subs.empty")),
|
|
4873
4890
|
h("span", null, t("subs.empty.hint"))
|
|
4874
|
-
) : sorted.length === 0 ?
|
|
4875
|
-
// 输入完整 owner/repo 时上方已给出「+ 添加」建议,这里不再叠一句「没有匹配」。
|
|
4876
|
-
null
|
|
4877
|
-
) : h(
|
|
4891
|
+
) : sorted.length === 0 ? h(
|
|
4878
4892
|
"div",
|
|
4879
4893
|
{ className: "dshgt-subs-empty" },
|
|
4880
4894
|
h("b", null, t("subs.empty.match")),
|
|
@@ -4898,101 +4912,94 @@ function SubscriptionsView(props) {
|
|
|
4898
4912
|
return h(
|
|
4899
4913
|
"div",
|
|
4900
4914
|
{ className: "dshgt-view dshgt-subs" },
|
|
4901
|
-
|
|
4902
|
-
"section",
|
|
4903
|
-
{ className: "dshgt-subs-head" },
|
|
4904
|
-
h(
|
|
4905
|
-
"div",
|
|
4906
|
-
{ className: "dshgt-subs-head-main" },
|
|
4907
|
-
h("h2", null, t("subs.title")),
|
|
4908
|
-
h("p", { className: "dshgt-subs-dek" }, t("subs.desc"))
|
|
4909
|
-
),
|
|
4910
|
-
h(
|
|
4911
|
-
"div",
|
|
4912
|
-
{ className: "dshgt-subs-head-actions" },
|
|
4913
|
-
h(
|
|
4914
|
-
Btn,
|
|
4915
|
-
{
|
|
4916
|
-
small: true,
|
|
4917
|
-
onClick: () => {
|
|
4918
|
-
if (selecting) setSelected([]);
|
|
4919
|
-
setSelecting(!selecting);
|
|
4920
|
-
}
|
|
4921
|
-
},
|
|
4922
|
-
selecting ? t("subs.select.done") : t("subs.select")
|
|
4923
|
-
)
|
|
4924
|
-
)
|
|
4925
|
-
),
|
|
4915
|
+
// 工具条:搜索 + 添加 + 选择 + 生成变更分析(主操作)
|
|
4926
4916
|
h(
|
|
4927
4917
|
"div",
|
|
4928
|
-
{ className: "dshgt-subs-
|
|
4918
|
+
{ className: "dshgt-subs-toolbar" },
|
|
4929
4919
|
h(
|
|
4930
4920
|
"div",
|
|
4931
|
-
{ className: "dshgt-subs-
|
|
4921
|
+
{ className: "dshgt-subs-search" },
|
|
4922
|
+
h(
|
|
4923
|
+
"svg",
|
|
4924
|
+
{ width: 15, height: 15, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": 2, "stroke-linecap": "round", "aria-hidden": "true" },
|
|
4925
|
+
h("circle", { cx: 11, cy: 11, r: 7 }),
|
|
4926
|
+
h("path", { d: "m20 20-3.5-3.5" })
|
|
4927
|
+
),
|
|
4932
4928
|
h("input", {
|
|
4933
4929
|
type: "text",
|
|
4934
|
-
ref: inputRef,
|
|
4935
4930
|
value: query,
|
|
4936
|
-
placeholder: t("subs.
|
|
4937
|
-
"aria-label": t("subs.
|
|
4938
|
-
"aria-keyshortcuts": "Meta+K Control+K",
|
|
4931
|
+
placeholder: t("subs.search.placeholder"),
|
|
4932
|
+
"aria-label": t("subs.search.label"),
|
|
4939
4933
|
autoComplete: "off",
|
|
4940
4934
|
spellCheck: false,
|
|
4941
|
-
onInput: (event) =>
|
|
4942
|
-
|
|
4943
|
-
setFocus(-1);
|
|
4944
|
-
},
|
|
4945
|
-
onKeyDown: onCommandKey
|
|
4946
|
-
}),
|
|
4947
|
-
h("kbd", { className: "dshgt-subs-kbd" }, "⌘K")
|
|
4935
|
+
onInput: (event) => setQuery(event.target.value)
|
|
4936
|
+
})
|
|
4948
4937
|
),
|
|
4949
|
-
h(
|
|
4938
|
+
h("div", { className: "dshgt-subs-toolbar-spacer" }),
|
|
4939
|
+
addOpen ? h(
|
|
4950
4940
|
"div",
|
|
4951
|
-
{ className: "dshgt-subs-
|
|
4941
|
+
{ className: "dshgt-subs-addform" },
|
|
4942
|
+
h("input", {
|
|
4943
|
+
type: "text",
|
|
4944
|
+
value: addValue,
|
|
4945
|
+
placeholder: t("subs.add.placeholder"),
|
|
4946
|
+
"aria-label": t("subs.add.placeholder"),
|
|
4947
|
+
autoComplete: "off",
|
|
4948
|
+
spellCheck: false,
|
|
4949
|
+
autoFocus: true,
|
|
4950
|
+
onInput: (event) => setAddValue(event.target.value),
|
|
4951
|
+
onKeyDown: (event) => {
|
|
4952
|
+
if (event.isComposing === true) return;
|
|
4953
|
+
if (event.key === "Enter" && canAdd && !alreadySubscribed) addRepo(addValue.trim());
|
|
4954
|
+
if (event.key === "Escape") {
|
|
4955
|
+
setAddOpen(false);
|
|
4956
|
+
setAddValue("");
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
}),
|
|
4952
4960
|
h(
|
|
4953
|
-
|
|
4954
|
-
null,
|
|
4955
|
-
t("subs.
|
|
4956
|
-
" · ",
|
|
4957
|
-
h(
|
|
4958
|
-
"button",
|
|
4959
|
-
{ type: "button", className: "dshgt-subs-linkbtn", title: t("subs.param.link"), onClick: () => props.onOpenSettings() },
|
|
4960
|
-
meta.cadence
|
|
4961
|
-
),
|
|
4962
|
-
meta.next === void 0 ? null : ` · ${t("subs.meta.next", { time: meta.next })}`
|
|
4963
|
-
),
|
|
4964
|
-
metaRight.length === 0 ? null : h(
|
|
4965
|
-
"span",
|
|
4966
|
-
{ className: "dshgt-subs-meta-right" },
|
|
4967
|
-
metaRight.flatMap((part, index) => index === 0 ? [part] : [h("span", { key: `sep${index}`, className: "dshgt-subs-meta-sep" }, "·"), part])
|
|
4961
|
+
Btn,
|
|
4962
|
+
{ small: true, variant: "primary", disabled: busy !== null || !canAdd || alreadySubscribed, onClick: () => canAdd && !alreadySubscribed && addRepo(addValue.trim()) },
|
|
4963
|
+
t("subs.add.confirm")
|
|
4968
4964
|
)
|
|
4965
|
+
) : h(
|
|
4966
|
+
Btn,
|
|
4967
|
+
{
|
|
4968
|
+
small: true,
|
|
4969
|
+
disabled: busy !== null,
|
|
4970
|
+
onClick: () => {
|
|
4971
|
+
setAddOpen(true);
|
|
4972
|
+
setAddValue("");
|
|
4973
|
+
}
|
|
4974
|
+
},
|
|
4975
|
+
t("subs.toolbar.add")
|
|
4976
|
+
),
|
|
4977
|
+
h(
|
|
4978
|
+
Btn,
|
|
4979
|
+
{
|
|
4980
|
+
small: true,
|
|
4981
|
+
onClick: () => {
|
|
4982
|
+
if (selecting) setSelected([]);
|
|
4983
|
+
setSelecting(!selecting);
|
|
4984
|
+
}
|
|
4985
|
+
},
|
|
4986
|
+
selecting ? t("subs.select.done") : t("subs.select")
|
|
4987
|
+
),
|
|
4988
|
+
h(
|
|
4989
|
+
Btn,
|
|
4990
|
+
{
|
|
4991
|
+
small: true,
|
|
4992
|
+
variant: "primary",
|
|
4993
|
+
disabled: globalBusy || busy !== null,
|
|
4994
|
+
onClick: () => runAnalyze()
|
|
4995
|
+
},
|
|
4996
|
+
analyzing !== null && analyzing.scope === "all" ? t("subs.analyzing") : t("subs.toolbar.analyze")
|
|
4969
4997
|
)
|
|
4970
4998
|
),
|
|
4971
|
-
|
|
4972
|
-
"div",
|
|
4973
|
-
{ className: "dshgt-subs-suggest" },
|
|
4974
|
-
subscribed ? [
|
|
4975
|
-
h("span", { key: "pill", className: "dshgt-tag" }, t("subs.cmd.subscribed")),
|
|
4976
|
-
h("span", { key: "repo", className: "repo" }, candidate),
|
|
4977
|
-
h("span", { key: "hint", className: "hint" }, t("subs.cmd.subscribed.hint"))
|
|
4978
|
-
] : [
|
|
4979
|
-
h("span", { key: "plus", className: "plus" }, "+"),
|
|
4980
|
-
h("span", { key: "add" }, t("subs.cmd.add")),
|
|
4981
|
-
h("span", { key: "repo", className: "repo" }, candidate),
|
|
4982
|
-
h("span", { key: "hint", className: "hint" }, t("subs.cmd.add.hint"))
|
|
4983
|
-
]
|
|
4984
|
-
),
|
|
4999
|
+
banner,
|
|
4985
5000
|
error === null ? null : h(Alert, { kind: "err" }, error),
|
|
4986
5001
|
note === null ? null : h(Alert, { kind: "ok" }, note),
|
|
4987
5002
|
tableBody,
|
|
4988
|
-
h(
|
|
4989
|
-
"div",
|
|
4990
|
-
{ className: "dshgt-subs-keys" },
|
|
4991
|
-
h("span", null, t("subs.keys.move")),
|
|
4992
|
-
h("span", null, t("subs.keys.enter")),
|
|
4993
|
-
h("span", null, t("subs.keys.esc")),
|
|
4994
|
-
h("span", null, t("subs.keys.sort"))
|
|
4995
|
-
),
|
|
4996
5003
|
selecting && rows.length > 0 ? h(
|
|
4997
5004
|
"div",
|
|
4998
5005
|
{ className: "dshgt-subs-selbar" },
|
|
@@ -1612,6 +1612,7 @@ function buildCoreArgs(channelPath, options) {
|
|
|
1612
1612
|
if (options.period !== void 0) args.push("--period", options.period);
|
|
1613
1613
|
if (options.stages !== void 0 && options.stages.length > 0) args.push("--stages", options.stages.join(","));
|
|
1614
1614
|
if (options.force === true) args.push("--force");
|
|
1615
|
+
if (options.repo !== void 0 && options.repo !== "") args.push("--repo", options.repo);
|
|
1615
1616
|
return args;
|
|
1616
1617
|
}
|
|
1617
1618
|
async function runCore(options) {
|
|
@@ -1627,7 +1628,8 @@ async function runCore(options) {
|
|
|
1627
1628
|
date: options.date,
|
|
1628
1629
|
period: options.period,
|
|
1629
1630
|
stages: options.stages,
|
|
1630
|
-
force: options.force
|
|
1631
|
+
force: options.force,
|
|
1632
|
+
...options.repo === void 0 ? {} : { repo: options.repo }
|
|
1631
1633
|
});
|
|
1632
1634
|
const child = spawn2(options.core.path, args, {
|
|
1633
1635
|
env: sanitizedEnv(process.env, {
|
|
@@ -2751,6 +2753,7 @@ function versionAtLeast(a, b) {
|
|
|
2751
2753
|
var runner_exports = {};
|
|
2752
2754
|
__export(runner_exports, {
|
|
2753
2755
|
FORCE_MIN_CORE_VERSION: () => FORCE_MIN_CORE_VERSION,
|
|
2756
|
+
REPO_MIN_CORE_VERSION: () => REPO_MIN_CORE_VERSION,
|
|
2754
2757
|
Runner: () => Runner,
|
|
2755
2758
|
isoWeekName: () => isoWeekName,
|
|
2756
2759
|
periodFileBase: () => periodFileBase,
|
|
@@ -2760,6 +2763,7 @@ import { createHash as createHash2 } from "crypto";
|
|
|
2760
2763
|
import { existsSync as existsSync4, readFileSync as readFileSync3, realpathSync, statSync as statSync5 } from "fs";
|
|
2761
2764
|
import path9 from "path";
|
|
2762
2765
|
var FORCE_MIN_CORE_VERSION = "0.7.1";
|
|
2766
|
+
var REPO_MIN_CORE_VERSION = "0.8.0";
|
|
2763
2767
|
function repoProblem(repoPath) {
|
|
2764
2768
|
if (repoPath.trim() === "") return { code: "CONFIG_INVALID", message: "\u8FD8\u6CA1\u6709\u9009\u62E9\u5F52\u6863\u4ED3\u5E93" };
|
|
2765
2769
|
if (!existsSync4(repoPath)) return { code: "REPO_NOT_GIT", message: `\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${repoPath}` };
|
|
@@ -3010,6 +3014,7 @@ var Runner = class {
|
|
|
3010
3014
|
this.active = { id, controller, startedAt };
|
|
3011
3015
|
const logFile = store.newLogPath(id);
|
|
3012
3016
|
let proxy;
|
|
3017
|
+
const requestedRepo = options.repo !== void 0 && options.repo !== "" ? options.repo : void 0;
|
|
3013
3018
|
try {
|
|
3014
3019
|
const core = await locateCore({ settings, env, allowDownload: settings.coreAutoDownload });
|
|
3015
3020
|
if (core.downloaded === true) {
|
|
@@ -3022,6 +3027,14 @@ var Runner = class {
|
|
|
3022
3027
|
`\u6838\u5FC3\u4E0D\u652F\u6301 provider\u300C${settings.model.provider}\u300D\uFF1B\u53EF\u7528\uFF1A${handshakeResult.supportedProviders.join(" / ")}`
|
|
3023
3028
|
);
|
|
3024
3029
|
}
|
|
3030
|
+
let effectiveRepo = requestedRepo;
|
|
3031
|
+
if (requestedRepo !== void 0 && !versionAtLeast(handshakeResult.version.version, REPO_MIN_CORE_VERSION)) {
|
|
3032
|
+
store.appendLog(
|
|
3033
|
+
logFile,
|
|
3034
|
+
`[release] \u6838\u5FC3 ${handshakeResult.version.version} \u4F4E\u4E8E ${REPO_MIN_CORE_VERSION}\uFF0C\u5355\u4ED3\u5206\u6790\u964D\u7EA7\u4E3A\u5168\u91CF`
|
|
3035
|
+
);
|
|
3036
|
+
effectiveRepo = void 0;
|
|
3037
|
+
}
|
|
3025
3038
|
const model = await this.openModelChannel({ settings, credentials, env, logFile });
|
|
3026
3039
|
proxy = model.proxy;
|
|
3027
3040
|
const github = await resolveGithubToken(credentials, settings.github.authMode, settings.github.tokenRef, { env });
|
|
@@ -3041,6 +3054,7 @@ var Runner = class {
|
|
|
3041
3054
|
dryRun: false,
|
|
3042
3055
|
trigger: options.trigger === "schedule" ? "schedule" : "manual",
|
|
3043
3056
|
stages: ["releases"],
|
|
3057
|
+
...effectiveRepo === void 0 ? {} : { repo: effectiveRepo },
|
|
3044
3058
|
repoCount,
|
|
3045
3059
|
...model.baseURLOverride === void 0 ? {} : { baseURLOverride: model.baseURLOverride },
|
|
3046
3060
|
signal: controller.signal,
|
|
@@ -3053,11 +3067,12 @@ var Runner = class {
|
|
|
3053
3067
|
settings,
|
|
3054
3068
|
startedAt,
|
|
3055
3069
|
logFile,
|
|
3056
|
-
cancelled: controller.signal.aborted
|
|
3070
|
+
cancelled: controller.signal.aborted,
|
|
3071
|
+
...requestedRepo === void 0 ? {} : { scopeRepo: requestedRepo }
|
|
3057
3072
|
});
|
|
3058
3073
|
} catch (error) {
|
|
3059
3074
|
const aborted = controller.signal.aborted || error instanceof IpcError && error.code === "CORE_CANCELLED";
|
|
3060
|
-
return this.recordReleaseFailure({ id, error, aborted, settings, startedAt, startedMs, logFile });
|
|
3075
|
+
return this.recordReleaseFailure({ id, error, aborted, settings, startedAt, startedMs, logFile, ...requestedRepo === void 0 ? {} : { scopeRepo: requestedRepo } });
|
|
3061
3076
|
} finally {
|
|
3062
3077
|
if (proxy !== void 0) {
|
|
3063
3078
|
const port = proxy.port;
|
|
@@ -3135,9 +3150,10 @@ var Runner = class {
|
|
|
3135
3150
|
...outcome.prUrl === void 0 ? {} : { prUrl: outcome.prUrl },
|
|
3136
3151
|
...outcome.branch === void 0 ? {} : { branch: outcome.branch },
|
|
3137
3152
|
...outcome.error === void 0 ? input.cancelled ? { errorCode: "CORE_CANCELLED", errorMessage: "\u8FD0\u884C\u5DF2\u53D6\u6D88\uFF1A\u6838\u5FC3\u8FDB\u7A0B\u5DF2\u7EC8\u6B62\uFF0C\u672A\u63D0\u4EA4\u4EFB\u4F55\u72B6\u6001" } : {} : { errorCode: outcome.error.code, errorMessage: redact(outcome.error.message) },
|
|
3138
|
-
...outcome.coreVersion === "" ? {} : { coreVersion: outcome.coreVersion }
|
|
3153
|
+
...outcome.coreVersion === "" ? {} : { coreVersion: outcome.coreVersion },
|
|
3154
|
+
...input.scopeRepo === void 0 ? {} : { scopeRepo: input.scopeRepo }
|
|
3139
3155
|
};
|
|
3140
|
-
this.persistReleaseRun(record, settings, input.logFile);
|
|
3156
|
+
this.persistReleaseRun(record, settings, input.logFile, { advanceWeek: input.scopeRepo === void 0 });
|
|
3141
3157
|
return record;
|
|
3142
3158
|
}
|
|
3143
3159
|
/** 运行在拿到信封之前就失败(定位/握手/模型/代理/超时/取消):也要留一条可复盘记录。 */
|
|
@@ -3158,20 +3174,24 @@ var Runner = class {
|
|
|
3158
3174
|
rateLimited: 0,
|
|
3159
3175
|
durationMs: Date.now() - input.startedMs,
|
|
3160
3176
|
errorCode: code,
|
|
3161
|
-
errorMessage: message
|
|
3177
|
+
errorMessage: message,
|
|
3178
|
+
...input.scopeRepo === void 0 ? {} : { scopeRepo: input.scopeRepo }
|
|
3162
3179
|
};
|
|
3163
|
-
this.persistReleaseRun(record, input.settings, input.logFile);
|
|
3180
|
+
this.persistReleaseRun(record, input.settings, input.logFile, { advanceWeek: input.scopeRepo === void 0 });
|
|
3164
3181
|
return record;
|
|
3165
3182
|
}
|
|
3166
3183
|
/**
|
|
3167
3184
|
* 落盘发版运行:只写 state(history.json 是榜单的),顺带推进 lastReleaseAt;
|
|
3168
3185
|
* 只有 ok/skipped 才锁 `lastReleaseWeek`——失败不锁,调度下一 tick 才有机会重试。
|
|
3186
|
+
* `advanceWeek=false`(单仓手动触发)时:仍写 releaseRuns 与 lastReleaseAt,但**不**锁
|
|
3187
|
+
* lastReleaseWeek——否则会把本周标记为已检查,导致周调度跳过本周真正的检查。
|
|
3169
3188
|
*/
|
|
3170
|
-
persistReleaseRun(record, settings, logFile) {
|
|
3189
|
+
persistReleaseRun(record, settings, logFile, opts = {}) {
|
|
3171
3190
|
const store = this.deps.store;
|
|
3191
|
+
const advanceWeek = opts.advanceWeek !== false;
|
|
3172
3192
|
const succeeded = record.status === "ok" || record.status === "skipped";
|
|
3173
3193
|
let week;
|
|
3174
|
-
if (succeeded) {
|
|
3194
|
+
if (succeeded && advanceWeek) {
|
|
3175
3195
|
try {
|
|
3176
3196
|
week = isoWeekKey(new Date(record.finishedAt), settings.releases.schedule.timezone);
|
|
3177
3197
|
} catch {
|
|
@@ -4038,7 +4058,10 @@ function createRoutes(deps) {
|
|
|
4038
4058
|
const code = error instanceof IpcError ? error.code : "INTERNAL";
|
|
4039
4059
|
const message = redact(error instanceof Error ? error.message : String(error));
|
|
4040
4060
|
deps.logger?.("route.error", { pathname, method, code, message });
|
|
4041
|
-
const status = code === "CONFIG_INVALID" ?
|
|
4061
|
+
const status = code === "CONFIG_INVALID" ? (
|
|
4062
|
+
// 「已有一次运行在进行中」是并发冲突而非参数错误:用 409,前端据此禁用按钮并提示。
|
|
4063
|
+
message.includes("\u8FD0\u884C\u5728\u8FDB\u884C\u4E2D") ? 409 : 400
|
|
4064
|
+
) : code === "NOT_FOUND" ? 404 : 500;
|
|
4042
4065
|
sendJson2(response, status, { ok: false, code, message });
|
|
4043
4066
|
}
|
|
4044
4067
|
};
|
|
@@ -4117,6 +4140,18 @@ async function dispatch(method, pathname, url, request, deps) {
|
|
|
4117
4140
|
if (method === "POST" && pathname === "/run/cancel") {
|
|
4118
4141
|
return { cancelled: deps.runner.cancel() };
|
|
4119
4142
|
}
|
|
4143
|
+
if (method === "POST" && pathname === "/releases/run") {
|
|
4144
|
+
const body = await readBody2(request);
|
|
4145
|
+
let repo;
|
|
4146
|
+
if (body.repo !== void 0) {
|
|
4147
|
+
if (typeof body.repo !== "string" || !isValidRepo(body.repo.trim())) {
|
|
4148
|
+
throw new IpcError("CONFIG_INVALID", `repo \u5FC5\u987B\u662F owner/repo \u5F62\u6001\uFF1A${String(body.repo)}`);
|
|
4149
|
+
}
|
|
4150
|
+
repo = body.repo.trim();
|
|
4151
|
+
}
|
|
4152
|
+
const release = await deps.runner.runReleases({ trigger: "manual", ...repo === void 0 ? {} : { repo } });
|
|
4153
|
+
return { release };
|
|
4154
|
+
}
|
|
4120
4155
|
if (method === "GET" && pathname === "/repo/branch") {
|
|
4121
4156
|
const repoPath = url.searchParams.get("path") ?? settings.repo.path;
|
|
4122
4157
|
return { repo: await readDefaultBranch(repoPath, { env }) };
|
package/lib/index.js
CHANGED
package/lib/internal.d.ts
CHANGED
|
@@ -631,6 +631,8 @@ interface ReleaseRunRecord {
|
|
|
631
631
|
errorCode?: string;
|
|
632
632
|
errorMessage?: string;
|
|
633
633
|
coreVersion?: string;
|
|
634
|
+
/** 单仓手动触发时记录该仓库(owner/repo);全量运行缺省。仅展示用。 */
|
|
635
|
+
scopeRepo?: string;
|
|
634
636
|
}
|
|
635
637
|
/** 发版运行记录上限:一周一次的话正好留半年,够复盘又不会让 state.json 膨胀。 */
|
|
636
638
|
declare const RELEASE_RUN_LIMIT = 30;
|
|
@@ -984,6 +986,8 @@ interface RunCoreOptions {
|
|
|
984
986
|
* `['releases']` 就是「只跑发版订阅」——不抓榜单、不渲染 digest、也不合并订阅 pending。
|
|
985
987
|
*/
|
|
986
988
|
stages?: string[];
|
|
989
|
+
/** 单仓发版分析(核心 `run --stages releases --repo owner/repo`):只处理该仓库;须与 stages=['releases'] 搭配。 */
|
|
990
|
+
repo?: string;
|
|
987
991
|
/** 订阅仓库数:用于把配置里的 `auto` 并发/预算解析成数字(发版运行传入)。 */
|
|
988
992
|
repoCount?: number;
|
|
989
993
|
signal?: AbortSignal;
|
|
@@ -1004,6 +1008,7 @@ declare function buildCoreArgs(channelPath: string, options: {
|
|
|
1004
1008
|
period?: Period;
|
|
1005
1009
|
stages?: string[];
|
|
1006
1010
|
force?: boolean;
|
|
1011
|
+
repo?: string;
|
|
1007
1012
|
}): string[];
|
|
1008
1013
|
declare function runCore(options: RunCoreOptions): Promise<RunCoreResult>;
|
|
1009
1014
|
|
|
@@ -1446,6 +1451,11 @@ declare namespace pendingPr {
|
|
|
1446
1451
|
* 与 core patch 发版 `0.7.1` 对齐;`dsh.core.version` 应 >= 它,dev override 指向更旧核心时靠闸门降级。
|
|
1447
1452
|
*/
|
|
1448
1453
|
declare const FORCE_MIN_CORE_VERSION = "0.7.1";
|
|
1454
|
+
/**
|
|
1455
|
+
* 支持 `run --stages releases --repo`(单仓发版分析)的最低核心版本。
|
|
1456
|
+
* 与 core v0.8.0 对齐;核心更旧时单仓触发降级为全量(不传 --repo)。
|
|
1457
|
+
*/
|
|
1458
|
+
declare const REPO_MIN_CORE_VERSION = "0.8.0";
|
|
1449
1459
|
interface RunSummary {
|
|
1450
1460
|
id: string;
|
|
1451
1461
|
ok: boolean;
|
|
@@ -1553,6 +1563,7 @@ declare class Runner {
|
|
|
1553
1563
|
*/
|
|
1554
1564
|
runReleases(options: {
|
|
1555
1565
|
trigger: 'manual' | 'schedule';
|
|
1566
|
+
repo?: string;
|
|
1556
1567
|
}): Promise<ReleaseRunRecord>;
|
|
1557
1568
|
/** 发版运行用哪一天:发版调度时区下的「今天」(不是榜单调度时区,两者可独立配置)。 */
|
|
1558
1569
|
private releaseDate;
|
|
@@ -1572,6 +1583,8 @@ declare class Runner {
|
|
|
1572
1583
|
/**
|
|
1573
1584
|
* 落盘发版运行:只写 state(history.json 是榜单的),顺带推进 lastReleaseAt;
|
|
1574
1585
|
* 只有 ok/skipped 才锁 `lastReleaseWeek`——失败不锁,调度下一 tick 才有机会重试。
|
|
1586
|
+
* `advanceWeek=false`(单仓手动触发)时:仍写 releaseRuns 与 lastReleaseAt,但**不**锁
|
|
1587
|
+
* lastReleaseWeek——否则会把本周标记为已检查,导致周调度跳过本周真正的检查。
|
|
1575
1588
|
*/
|
|
1576
1589
|
private persistReleaseRun;
|
|
1577
1590
|
private summarize;
|
|
@@ -1592,6 +1605,7 @@ declare class Runner {
|
|
|
1592
1605
|
}
|
|
1593
1606
|
|
|
1594
1607
|
declare const runner_FORCE_MIN_CORE_VERSION: typeof FORCE_MIN_CORE_VERSION;
|
|
1608
|
+
declare const runner_REPO_MIN_CORE_VERSION: typeof REPO_MIN_CORE_VERSION;
|
|
1595
1609
|
type runner_RunRequestOptions = RunRequestOptions;
|
|
1596
1610
|
type runner_RunSummary = RunSummary;
|
|
1597
1611
|
type runner_Runner = Runner;
|
|
@@ -1601,7 +1615,7 @@ declare const runner_isoWeekName: typeof isoWeekName;
|
|
|
1601
1615
|
declare const runner_periodFileBase: typeof periodFileBase;
|
|
1602
1616
|
declare const runner_projectUsage: typeof projectUsage;
|
|
1603
1617
|
declare namespace runner {
|
|
1604
|
-
export { runner_FORCE_MIN_CORE_VERSION as FORCE_MIN_CORE_VERSION, type runner_RunRequestOptions as RunRequestOptions, type runner_RunSummary as RunSummary, runner_Runner as Runner, type runner_RunnerDeps as RunnerDeps, runner_isoWeekName as isoWeekName, runner_periodFileBase as periodFileBase, runner_projectUsage as projectUsage };
|
|
1618
|
+
export { runner_FORCE_MIN_CORE_VERSION as FORCE_MIN_CORE_VERSION, runner_REPO_MIN_CORE_VERSION as REPO_MIN_CORE_VERSION, type runner_RunRequestOptions as RunRequestOptions, type runner_RunSummary as RunSummary, runner_Runner as Runner, type runner_RunnerDeps as RunnerDeps, runner_isoWeekName as isoWeekName, runner_periodFileBase as periodFileBase, runner_projectUsage as projectUsage };
|
|
1605
1619
|
}
|
|
1606
1620
|
|
|
1607
1621
|
/** 订阅命令超时:核心只做一次 GitHub 请求/本地文件读写,8 s 足够。 */
|
package/lib/internal.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morehao/dsh-github-trending",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "GitHub 趋势日报插件:每天定时 spawn 本地 trendradar 核心,抓取 Trending、生成中文简介与总结,提交到你的仓库并自动合并 PR。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@morehao/dsh-plugin-kit": "0.1.0",
|
|
54
|
-
"@morehao/trendradar-contract": "0.
|
|
54
|
+
"@morehao/trendradar-contract": "0.5.0"
|
|
55
55
|
},
|
|
56
56
|
"dsh": {
|
|
57
57
|
"bundle": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"core": {
|
|
61
61
|
"repo": "morehao/trendradar",
|
|
62
|
-
"version": "0.
|
|
62
|
+
"version": "0.8.0"
|
|
63
63
|
},
|
|
64
64
|
"client": {
|
|
65
65
|
"platform": "web",
|