@toon-protocol/rig 4.3.0 → 4.4.0

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.
@@ -6,7 +6,7 @@ import {
6
6
  renderIdentityLine,
7
7
  rigVersion,
8
8
  withForcedStandalone
9
- } from "./chunk-5HFOPKAB.js";
9
+ } from "./chunk-CTRP2UAQ.js";
10
10
  import {
11
11
  CI_MANUAL_TRIGGER_KIND,
12
12
  CI_SECRET_UPDATE_KIND,
@@ -39,7 +39,7 @@ import {
39
39
  selectServiceRequests,
40
40
  validateSecretUpdate,
41
41
  writeGitObjects
42
- } from "./chunk-3RWM7PBM.js";
42
+ } from "./chunk-RL5XUWR4.js";
43
43
  import {
44
44
  REPOSITORY_STATE_KIND,
45
45
  authorizedStatusAuthors,
@@ -1108,6 +1108,7 @@ async function startCoordinator(opts) {
1108
1108
  await advertise();
1109
1109
  const queue = [];
1110
1110
  const activeAborts = /* @__PURE__ */ new Set();
1111
+ const activeRuns = /* @__PURE__ */ new Set();
1111
1112
  let active = 0;
1112
1113
  let inflight = 0;
1113
1114
  const idleWaiters = [];
@@ -1135,13 +1136,22 @@ async function startCoordinator(opts) {
1135
1136
  jobs: []
1136
1137
  };
1137
1138
  const now = clock();
1138
- await publish(buildCiWorkflowResult({ ...base, conclusion }, now));
1139
+ const resultEventId = await publish(
1140
+ buildCiWorkflowResult({ ...base, conclusion }, now)
1141
+ );
1139
1142
  await publish(
1140
1143
  buildCiWorkflowProgress(
1141
1144
  { ...base, status: "concluded", conclusion, expiresAt: now + adTtl },
1142
1145
  now
1143
1146
  )
1144
1147
  );
1148
+ opts.onRunConcluded?.({
1149
+ runId: run.runId,
1150
+ repoAddr: run.repo.addr,
1151
+ trigger: run.trigger,
1152
+ conclusion,
1153
+ resultEventId
1154
+ });
1145
1155
  };
1146
1156
  const executeRun = async (run) => {
1147
1157
  const { repo, trigger, workflow } = run;
@@ -1210,6 +1220,7 @@ async function startCoordinator(opts) {
1210
1220
  scheduleRenew();
1211
1221
  const abort = new AbortController();
1212
1222
  activeAborts.add(abort);
1223
+ run.abort = abort;
1213
1224
  let timedOut = false;
1214
1225
  const timeoutHandle = scheduler.setTimeout(() => {
1215
1226
  timedOut = true;
@@ -1261,7 +1272,8 @@ async function startCoordinator(opts) {
1261
1272
  }
1262
1273
  let conclusion = result.conclusion;
1263
1274
  if (timedOut) conclusion = "timed_out";
1264
- else if (stopping && abort.signal.aborted) conclusion = "cancelled";
1275
+ else if (run.superseded || stopping && abort.signal.aborted)
1276
+ conclusion = "cancelled";
1265
1277
  for (const job of result.jobs) {
1266
1278
  const { tail, omitted } = logTail(job.log);
1267
1279
  const logReceipt = await serial.run(
@@ -1325,7 +1337,7 @@ async function startCoordinator(opts) {
1325
1337
  await publish(progress("in_progress"));
1326
1338
  }
1327
1339
  const now = clock();
1328
- await publish(
1340
+ const resultEventId = await publish(
1329
1341
  buildCiWorkflowResult(
1330
1342
  {
1331
1343
  trigger,
@@ -1341,17 +1353,26 @@ async function startCoordinator(opts) {
1341
1353
  );
1342
1354
  await publish(progress("concluded", conclusion));
1343
1355
  log(`[ci] ${label}: ${conclusion}`);
1356
+ opts.onRunConcluded?.({
1357
+ runId: run.runId,
1358
+ repoAddr: repo.addr,
1359
+ trigger,
1360
+ conclusion,
1361
+ resultEventId
1362
+ });
1344
1363
  };
1345
1364
  const pump = () => {
1346
1365
  while (active < concurrency && queue.length > 0 && !stopping) {
1347
1366
  const run = queue.shift();
1348
1367
  active += 1;
1349
1368
  inflight += 1;
1369
+ activeRuns.add(run);
1350
1370
  void executeRun(run).catch((err) => {
1351
1371
  log(
1352
1372
  `[ci] run ${run.runId.slice(0, 8)} failed: ${err instanceof Error ? err.message : String(err)}`
1353
1373
  );
1354
1374
  }).finally(async () => {
1375
+ activeRuns.delete(run);
1355
1376
  active -= 1;
1356
1377
  inflight -= 1;
1357
1378
  await releaseCheckout(run.checkout);
@@ -1411,6 +1432,27 @@ async function startCoordinator(opts) {
1411
1432
  );
1412
1433
  pump();
1413
1434
  };
1435
+ const supersedePrRuns = async (repo, prEventId, updateEventId) => {
1436
+ const samePr = (run) => run.repo === repo && run.trigger.pr?.prEventId === prEventId;
1437
+ const why = `superseded by PR update ${updateEventId.slice(0, 8)} \u2014 cancelled`;
1438
+ for (const run of queue.filter(samePr)) {
1439
+ queue.splice(queue.indexOf(run), 1);
1440
+ run.superseded = true;
1441
+ log(
1442
+ `[ci] ${repo.repoId} ${run.workflow.path}@${run.trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]: queued run ${why}`
1443
+ );
1444
+ await concludeWithoutJobs(run, "cancelled", void 0);
1445
+ await releaseCheckout(run.checkout);
1446
+ }
1447
+ for (const run of activeRuns) {
1448
+ if (!samePr(run) || run.superseded) continue;
1449
+ run.superseded = true;
1450
+ log(
1451
+ `[ci] ${repo.repoId} ${run.workflow.path}@${run.trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]: ${why}`
1452
+ );
1453
+ run.abort?.abort();
1454
+ }
1455
+ };
1414
1456
  let checkoutCounter = 0;
1415
1457
  const handleTrigger = async (t) => {
1416
1458
  const { repo } = t;
@@ -1672,6 +1714,7 @@ async function startCoordinator(opts) {
1672
1714
  );
1673
1715
  return;
1674
1716
  }
1717
+ await supersedePrRuns(repo, pr.id, ev.id);
1675
1718
  await handleTrigger({
1676
1719
  repo,
1677
1720
  reason: "pull_request",
@@ -1975,6 +2018,9 @@ Options:
1975
2018
  (default: ubuntu-latest=${DEFAULT_ACT_PLATFORMS["ubuntu-latest"]})
1976
2019
  --workdir <dir> where commits are checked out for runs (default:
1977
2020
  <state-dir>/work)
2021
+ --once stop after the first run concludes (its Workflow
2022
+ Result and final progress marker are on the relay);
2023
+ ignored or refused triggers do not count
1978
2024
  --json emit ONE JSON document describing the coordinator
1979
2025
  at start; everything else goes to stderr
1980
2026
  --standalone accepted for symmetry; the coordinator always runs
@@ -2008,6 +2054,7 @@ function parseServeArgs(args) {
2008
2054
  "act-bin": { type: "string" },
2009
2055
  platform: { type: "string", multiple: true },
2010
2056
  workdir: { type: "string" },
2057
+ once: { type: "boolean", default: false },
2011
2058
  json: { type: "boolean", default: false },
2012
2059
  standalone: { type: "boolean", default: false },
2013
2060
  "no-daemon": { type: "boolean", default: false },
@@ -2087,6 +2134,7 @@ function parseServeArgs(args) {
2087
2134
  ...values["act-bin"] !== void 0 ? { actBin: values["act-bin"] } : {},
2088
2135
  ...platforms ? { platforms } : {},
2089
2136
  ...values.workdir !== void 0 ? { workdir: values.workdir } : {},
2137
+ once: values.once,
2090
2138
  json: values.json
2091
2139
  };
2092
2140
  }
@@ -2147,7 +2195,7 @@ function makeAffordabilityCheck(args) {
2147
2195
  return false;
2148
2196
  };
2149
2197
  }
2150
- function untilStopRequested(signal) {
2198
+ function untilStopRequested(signal, firstRun) {
2151
2199
  return new Promise((resolve2) => {
2152
2200
  let settled = false;
2153
2201
  const finish = (reason) => {
@@ -2163,6 +2211,7 @@ function untilStopRequested(signal) {
2163
2211
  signal?.addEventListener("abort", () => finish("abort"), { once: true });
2164
2212
  process.once("SIGINT", onSigint);
2165
2213
  process.once("SIGTERM", onSigterm);
2214
+ void firstRun?.then(finish);
2166
2215
  });
2167
2216
  }
2168
2217
  async function runCiServe(args, deps) {
@@ -2210,6 +2259,10 @@ async function runCiServe(args, deps) {
2210
2259
  const stateDir = forced.stateDir ?? defaultCoordinatorStateDir(forced.env, coordinator);
2211
2260
  const workdir = flags.workdir ?? join4(stateDir, "work");
2212
2261
  const version = rigVersion();
2262
+ let concludeFirstRun = () => void 0;
2263
+ const firstRun = flags.once ? new Promise((resolve2) => {
2264
+ concludeFirstRun = resolve2;
2265
+ }) : void 0;
2213
2266
  handle = await startCoordinator({
2214
2267
  coordinatorPubkey: coordinator,
2215
2268
  publisher: ctx.publisher,
@@ -2232,6 +2285,7 @@ async function runCiServe(args, deps) {
2232
2285
  ...forced.fetchFn ? { fetchFn: forced.fetchFn } : {},
2233
2286
  ...forced.resolveSha ? { resolveSha: forced.resolveSha } : {},
2234
2287
  ...forced.clock ? { clock: forced.clock } : {},
2288
+ ...firstRun ? { onRunConcluded: (run) => concludeFirstRun(run) } : {},
2235
2289
  log: (line) => io.err(line)
2236
2290
  });
2237
2291
  const npub = hexToNpub(coordinator);
@@ -2252,6 +2306,7 @@ async function runCiServe(args, deps) {
2252
2306
  runner: { family: runner.family, selectors: runner.selectors },
2253
2307
  concurrency: flags.concurrency,
2254
2308
  timeoutSeconds: flags.timeoutMs / 1e3,
2309
+ once: flags.once,
2255
2310
  stateDir,
2256
2311
  workdir,
2257
2312
  secretsKey: handle.secretsKeyPubkey,
@@ -2273,11 +2328,13 @@ async function runCiServe(args, deps) {
2273
2328
  io.out(`State: ${stateDir}`);
2274
2329
  io.out("Maintainers authorize this coordinator with:");
2275
2330
  io.out(` rig ci request ${npub}`);
2276
- io.out("Listening \u2014 press Ctrl-C to stop.");
2331
+ io.out(
2332
+ flags.once ? "Listening for one run \u2014 stops after it concludes (or Ctrl-C)." : "Listening \u2014 press Ctrl-C to stop."
2333
+ );
2277
2334
  }
2278
- const reason = await untilStopRequested(forced.signal);
2335
+ const reason = await untilStopRequested(forced.signal, firstRun);
2279
2336
  io.err(
2280
- `rig ci serve: ${reason} \u2014 stopping (active runs conclude cancelled)`
2337
+ typeof reason === "string" ? `rig ci serve: ${reason} \u2014 stopping (active runs conclude cancelled)` : `rig ci serve: run ${reason.runId.slice(0, 8)} concluded ${reason.conclusion} (result ${reason.resultEventId.slice(0, 8)}) \u2014 --once, stopping`
2281
2338
  );
2282
2339
  await handle.stop();
2283
2340
  handle = void 0;
@@ -2300,4 +2357,4 @@ export {
2300
2357
  makeAffordabilityCheck,
2301
2358
  runCiServe
2302
2359
  };
2303
- //# sourceMappingURL=ci-serve-ZVJBZ3W3.js.map
2360
+ //# sourceMappingURL=ci-serve-P4HOLAMP.js.map