





















@@ -18,11 +18,35 @@ function formatSeconds(value) {
1818return value === null ? "" : `${value}s`;
1919}
202021+function percentile(values, percentileValue) {
22+if (values.length === 0) {
23+return null;
24+}
25+const sorted = [...values].toSorted((left, right) => left - right);
26+const index = Math.min(sorted.length - 1, Math.ceil(sorted.length * percentileValue) - 1);
27+return sorted[index];
28+}
29+2130function parseRunList(raw) {
2231const parsed = JSON.parse(raw);
2332return Array.isArray(parsed) ? parsed : [];
2433}
253435+function isPnpmStoreWarmupGatedJobName(name) {
36+return (
37+name === "build-artifacts" ||
38+name === "check-docs" ||
39+name === "check-guards" ||
40+name === "check-prod-types" ||
41+name === "check-lint" ||
42+name === "check-dependencies" ||
43+name === "check-test-types" ||
44+name.startsWith("check-additional-") ||
45+name.startsWith("checks-fast-") ||
46+(name.startsWith("checks-node-") && !name.startsWith("checks-node-compat-"))
47+);
48+}
49+2650function collectRunTimingContext(run) {
2751const created = parseTime(run.createdAt);
2852const updated = parseTime(run.updatedAt);
@@ -69,6 +93,46 @@ export function summarizeRunTimings(run, limit = 15) {
6993};
7094}
719596+export function summarizePnpmStoreWarmupBarrier(run, windowSeconds = 5) {
97+const { jobs } = collectRunTimingContext(run);
98+const preflight = jobs.find((job) => job.name === "preflight");
99+const warmup = jobs.find((job) => job.name === "pnpm-store-warmup");
100+if (!warmup?.started || !warmup?.completed) {
101+return null;
102+}
103+104+const postWarmupJobs = jobs.filter(
105+(job) =>
106+job.name !== "preflight" &&
107+job.name !== "security-fast" &&
108+job.name !== "pnpm-store-warmup" &&
109+isPnpmStoreWarmupGatedJobName(job.name) &&
110+job.status === "completed" &&
111+job.conclusion !== "skipped" &&
112+job.started !== null &&
113+job.started >= warmup.completed &&
114+(job.durationSeconds ?? 0) > 5,
115+);
116+const startDelays = postWarmupJobs
117+.map((job) => secondsBetween(warmup.completed, job.started))
118+.filter((delay) => delay !== null);
119+120+return {
121+activePostWarmupJobCount: postWarmupJobs.length,
122+firstPostWarmupStartDelaySeconds: startDelays.length === 0 ? null : Math.min(...startDelays),
123+postWarmupP95StartDelaySeconds: percentile(startDelays, 0.95),
124+postWarmupStartedWithinWindow: startDelays.filter((delay) => delay <= windowSeconds).length,
125+preflightToWarmupCompleteSeconds: secondsBetween(
126+preflight?.completed ?? null,
127+warmup.completed,
128+),
129+preflightToWarmupStartSeconds: secondsBetween(preflight?.completed ?? null, warmup.started),
130+warmupDurationSeconds: secondsBetween(warmup.started, warmup.completed),
131+warmupResult: `${warmup.status}/${warmup.conclusion}`,
132+ windowSeconds,
133+};
134+}
135+72136export function selectLatestMainPushCiRun(runs, headSha = null) {
73137const pushRuns = runs.filter((run) => run.event === "push");
74138if (headSha) {
@@ -193,15 +257,6 @@ function summarizeJobs(run) {
193257};
194258}
195259196-function percentile(values, percentileValue) {
197-if (values.length === 0) {
198-return null;
199-}
200-const sorted = [...values].toSorted((left, right) => left - right);
201-const index = Math.min(sorted.length - 1, Math.ceil(sorted.length * percentileValue) - 1);
202-return sorted[index];
203-}
204-205260function printSection(title, jobs, metric) {
206261console.log(title);
207262for (const job of jobs) {
@@ -265,11 +320,32 @@ async function main() {
265320return;
266321}
267322const runId = explicitRunId ?? (useLatestMain ? getLatestMainPushCiRunId() : getLatestCiRunId());
268-const summary = summarizeRunTimings(loadRun(runId), limit);
323+const run = loadRun(runId);
324+const summary = summarizeRunTimings(run, limit);
325+const warmupBarrier = summarizePnpmStoreWarmupBarrier(run);
269326270327console.log(
271328`CI run ${runId}: ${summary.status}/${summary.conclusion} wall=${formatSeconds(summary.wallSeconds)}`,
272329);
330+if (warmupBarrier) {
331+console.log("\npnpm-store-warmup barrier");
332+console.log(
333+[
334+`result=${warmupBarrier.warmupResult}`,
335+`preflight->start=${formatSeconds(warmupBarrier.preflightToWarmupStartSeconds)}`,
336+`duration=${formatSeconds(warmupBarrier.warmupDurationSeconds)}`,
337+`preflight->complete=${formatSeconds(warmupBarrier.preflightToWarmupCompleteSeconds)}`,
338+].join(" "),
339+);
340+console.log(
341+[
342+`active-post-warmup-jobs=${warmupBarrier.activePostWarmupJobCount}`,
343+`first-start-delay=${formatSeconds(warmupBarrier.firstPostWarmupStartDelaySeconds)}`,
344+`p95-start-delay=${formatSeconds(warmupBarrier.postWarmupP95StartDelaySeconds)}`,
345+`started-within-${warmupBarrier.windowSeconds}s=${warmupBarrier.postWarmupStartedWithinWindow}`,
346+].join(" "),
347+);
348+}
273349printSection("\nSlowest jobs", summary.byDuration, "durationSeconds");
274350printSection("\nLongest queues", summary.byQueue, "queueSeconds");
275351if (summary.badJobs.length > 0) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。