@toon-protocol/rig 4.3.0 → 4.5.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-UZZ2JKEC.js";
10
10
  import {
11
11
  CI_MANUAL_TRIGGER_KIND,
12
12
  CI_SECRET_UPDATE_KIND,
@@ -19,6 +19,7 @@ import {
19
19
  buildCiWorkflowProgress,
20
20
  buildCiWorkflowResult,
21
21
  collectRepoObjects,
22
+ containsSecretValue,
22
23
  decryptSecretUpdate,
23
24
  discoverWorkflows,
24
25
  effectiveSecrets,
@@ -34,19 +35,21 @@ import {
34
35
  parseCiServiceControl,
35
36
  parseRepoAddress,
36
37
  parseWorkflow,
38
+ redactSecretValues,
37
39
  repoAddress,
38
40
  runGit,
39
41
  selectServiceRequests,
40
42
  validateSecretUpdate,
41
43
  writeGitObjects
42
- } from "./chunk-3RWM7PBM.js";
44
+ } from "./chunk-S2BMLXAD.js";
43
45
  import {
46
+ ARWEAVE_GATEWAYS,
44
47
  REPOSITORY_STATE_KIND,
45
48
  authorizedStatusAuthors,
46
49
  defaultWebSocketFactory,
47
50
  fetchRemoteState,
48
51
  queryRelay
49
- } from "./chunk-GWA3CSUR.js";
52
+ } from "./chunk-HMED76GU.js";
50
53
  import {
51
54
  ChannelMapStore,
52
55
  channelStatus,
@@ -881,6 +884,7 @@ async function saveSecrets(stateDir, secrets) {
881
884
  }
882
885
 
883
886
  // src/ci/coordinator.ts
887
+ var DEFAULT_CONCURRENCY = 1;
884
888
  var DEFAULT_RUN_TIMEOUT_MS = 30 * 60 * 1e3;
885
889
  var DEFAULT_ADVERTISEMENT_TTL = 30 * 60;
886
890
  var RENEW_FRACTION = 5 / 6;
@@ -935,7 +939,7 @@ var Serial = class {
935
939
  async function startCoordinator(opts) {
936
940
  const me = opts.coordinatorPubkey.toLowerCase();
937
941
  const { publisher, relayUrl, runner } = opts;
938
- const concurrency = Math.max(1, opts.concurrency ?? 1);
942
+ const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);
939
943
  const timeoutMs = opts.timeoutMs ?? DEFAULT_RUN_TIMEOUT_MS;
940
944
  const clock = opts.clock ?? nowSeconds;
941
945
  const scheduler = opts.scheduler ?? realScheduler;
@@ -1108,6 +1112,7 @@ async function startCoordinator(opts) {
1108
1112
  await advertise();
1109
1113
  const queue = [];
1110
1114
  const activeAborts = /* @__PURE__ */ new Set();
1115
+ const activeRuns = /* @__PURE__ */ new Set();
1111
1116
  let active = 0;
1112
1117
  let inflight = 0;
1113
1118
  const idleWaiters = [];
@@ -1135,13 +1140,22 @@ async function startCoordinator(opts) {
1135
1140
  jobs: []
1136
1141
  };
1137
1142
  const now = clock();
1138
- await publish(buildCiWorkflowResult({ ...base, conclusion }, now));
1143
+ const resultEventId = await publish(
1144
+ buildCiWorkflowResult({ ...base, conclusion }, now)
1145
+ );
1139
1146
  await publish(
1140
1147
  buildCiWorkflowProgress(
1141
1148
  { ...base, status: "concluded", conclusion, expiresAt: now + adTtl },
1142
1149
  now
1143
1150
  )
1144
1151
  );
1152
+ opts.onRunConcluded?.({
1153
+ runId: run.runId,
1154
+ repoAddr: run.repo.addr,
1155
+ trigger: run.trigger,
1156
+ conclusion,
1157
+ resultEventId
1158
+ });
1145
1159
  };
1146
1160
  const executeRun = async (run) => {
1147
1161
  const { repo, trigger, workflow } = run;
@@ -1210,6 +1224,7 @@ async function startCoordinator(opts) {
1210
1224
  scheduleRenew();
1211
1225
  const abort = new AbortController();
1212
1226
  activeAborts.add(abort);
1227
+ run.abort = abort;
1213
1228
  let timedOut = false;
1214
1229
  const timeoutHandle = scheduler.setTimeout(() => {
1215
1230
  timedOut = true;
@@ -1246,7 +1261,7 @@ async function startCoordinator(opts) {
1246
1261
  ]);
1247
1262
  } catch (err) {
1248
1263
  log(
1249
- `[ci] ${label}: runner failed: ${err instanceof Error ? err.message : String(err)}`
1264
+ `[ci] ${label}: runner failed: ${redactSecretValues(err instanceof Error ? err.message : String(err), Object.values(run.secrets))}`
1250
1265
  );
1251
1266
  result = {
1252
1267
  conclusion: "startup_failure",
@@ -1261,12 +1276,15 @@ async function startCoordinator(opts) {
1261
1276
  }
1262
1277
  let conclusion = result.conclusion;
1263
1278
  if (timedOut) conclusion = "timed_out";
1264
- else if (stopping && abort.signal.aborted) conclusion = "cancelled";
1279
+ else if (run.superseded || stopping && abort.signal.aborted)
1280
+ conclusion = "cancelled";
1281
+ const secretValues = Object.values(run.secrets);
1265
1282
  for (const job of result.jobs) {
1266
- const { tail, omitted } = logTail(job.log);
1283
+ const jobLog = redactSecretValues(job.log, secretValues);
1284
+ const { tail, omitted } = logTail(jobLog);
1267
1285
  const logReceipt = await serial.run(
1268
1286
  () => uploadBlob({
1269
- body: Buffer.from(job.log, "utf-8"),
1287
+ body: Buffer.from(jobLog, "utf-8"),
1270
1288
  contentType: "text/plain; charset=utf-8",
1271
1289
  repoId: repo.repoId
1272
1290
  })
@@ -1275,6 +1293,12 @@ async function startCoordinator(opts) {
1275
1293
  for (const artifact of job.artifacts) {
1276
1294
  try {
1277
1295
  const body = await readFile2(artifact.path);
1296
+ if (containsSecretValue(body, secretValues)) {
1297
+ log(
1298
+ `[ci] ${label}: artifact ${redactSecretValues(artifact.filename, secretValues)} not uploaded \u2014 it contains an injected secret`
1299
+ );
1300
+ continue;
1301
+ }
1278
1302
  const receipt = await serial.run(
1279
1303
  () => uploadBlob({
1280
1304
  body,
@@ -1289,7 +1313,7 @@ async function startCoordinator(opts) {
1289
1313
  });
1290
1314
  } catch (err) {
1291
1315
  log(
1292
- `[ci] ${label}: artifact ${artifact.filename} skipped: ${err instanceof Error ? err.message : String(err)}`
1316
+ `[ci] ${label}: artifact ${redactSecretValues(`${artifact.filename} skipped: ${err instanceof Error ? err.message : String(err)}`, secretValues)}`
1293
1317
  );
1294
1318
  }
1295
1319
  }
@@ -1325,7 +1349,7 @@ async function startCoordinator(opts) {
1325
1349
  await publish(progress("in_progress"));
1326
1350
  }
1327
1351
  const now = clock();
1328
- await publish(
1352
+ const resultEventId = await publish(
1329
1353
  buildCiWorkflowResult(
1330
1354
  {
1331
1355
  trigger,
@@ -1341,17 +1365,26 @@ async function startCoordinator(opts) {
1341
1365
  );
1342
1366
  await publish(progress("concluded", conclusion));
1343
1367
  log(`[ci] ${label}: ${conclusion}`);
1368
+ opts.onRunConcluded?.({
1369
+ runId: run.runId,
1370
+ repoAddr: repo.addr,
1371
+ trigger,
1372
+ conclusion,
1373
+ resultEventId
1374
+ });
1344
1375
  };
1345
1376
  const pump = () => {
1346
1377
  while (active < concurrency && queue.length > 0 && !stopping) {
1347
1378
  const run = queue.shift();
1348
1379
  active += 1;
1349
1380
  inflight += 1;
1381
+ activeRuns.add(run);
1350
1382
  void executeRun(run).catch((err) => {
1351
1383
  log(
1352
1384
  `[ci] run ${run.runId.slice(0, 8)} failed: ${err instanceof Error ? err.message : String(err)}`
1353
1385
  );
1354
1386
  }).finally(async () => {
1387
+ activeRuns.delete(run);
1355
1388
  active -= 1;
1356
1389
  inflight -= 1;
1357
1390
  await releaseCheckout(run.checkout);
@@ -1411,6 +1444,27 @@ async function startCoordinator(opts) {
1411
1444
  );
1412
1445
  pump();
1413
1446
  };
1447
+ const supersedePrRuns = async (repo, prEventId, updateEventId) => {
1448
+ const samePr = (run) => run.repo === repo && run.trigger.pr?.prEventId === prEventId;
1449
+ const why = `superseded by PR update ${updateEventId.slice(0, 8)} \u2014 cancelled`;
1450
+ for (const run of queue.filter(samePr)) {
1451
+ queue.splice(queue.indexOf(run), 1);
1452
+ run.superseded = true;
1453
+ log(
1454
+ `[ci] ${repo.repoId} ${run.workflow.path}@${run.trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]: queued run ${why}`
1455
+ );
1456
+ await concludeWithoutJobs(run, "cancelled", void 0);
1457
+ await releaseCheckout(run.checkout);
1458
+ }
1459
+ for (const run of activeRuns) {
1460
+ if (!samePr(run) || run.superseded) continue;
1461
+ run.superseded = true;
1462
+ log(
1463
+ `[ci] ${repo.repoId} ${run.workflow.path}@${run.trigger.commit.slice(0, 7)} [${run.runId.slice(0, 8)}]: ${why}`
1464
+ );
1465
+ run.abort?.abort();
1466
+ }
1467
+ };
1414
1468
  let checkoutCounter = 0;
1415
1469
  const handleTrigger = async (t) => {
1416
1470
  const { repo } = t;
@@ -1672,6 +1726,7 @@ async function startCoordinator(opts) {
1672
1726
  );
1673
1727
  return;
1674
1728
  }
1729
+ await supersedePrRuns(repo, pr.id, ev.id);
1675
1730
  await handleTrigger({
1676
1731
  repo,
1677
1732
  reason: "pull_request",
@@ -1966,15 +2021,22 @@ Options:
1966
2021
  or hex; NIP-C1 operator policy); repeatable \u2014 for
1967
2022
  running your own coordinator against a repo you do
1968
2023
  not maintain; such runs show as lower trust
1969
- --concurrency <n> runs executed at once (default 1); more queue
1970
- --timeout <seconds> wall-clock budget per run \u2192 timed_out (default 1800)
1971
- --gateway <url> gateway that serves the store's raw bytes, for log +
1972
- artifact links (default: ${PREFERRED_GATEWAY})
2024
+ --concurrency <n> runs executed at once (default ${DEFAULT_CONCURRENCY}); more queue
2025
+ --timeout <seconds> wall-clock budget per run \u2192 timed_out
2026
+ (default ${DEFAULT_RUN_TIMEOUT_MS / 1e3})
2027
+ --gateway <url> gateway that serves the store's raw bytes: where log +
2028
+ artifact links point, and the first place the
2029
+ coordinator reads objects from (<url>/raw/<txId>)
2030
+ before the shared gateway list (default:
2031
+ RIG_ARWEAVE_GATEWAY, else ${PREFERRED_GATEWAY})
1973
2032
  --act-bin <path> the act executable (default: RIG_ACT_BIN, else PATH)
1974
2033
  --platform <label=img> runs-on label \u2192 Docker image; repeatable
1975
2034
  (default: ubuntu-latest=${DEFAULT_ACT_PLATFORMS["ubuntu-latest"]})
1976
2035
  --workdir <dir> where commits are checked out for runs (default:
1977
2036
  <state-dir>/work)
2037
+ --once stop after the first run concludes (its Workflow
2038
+ Result and final progress marker are on the relay);
2039
+ ignored or refused triggers do not count
1978
2040
  --json emit ONE JSON document describing the coordinator
1979
2041
  at start; everything else goes to stderr
1980
2042
  --standalone accepted for symmetry; the coordinator always runs
@@ -2008,6 +2070,7 @@ function parseServeArgs(args) {
2008
2070
  "act-bin": { type: "string" },
2009
2071
  platform: { type: "string", multiple: true },
2010
2072
  workdir: { type: "string" },
2073
+ once: { type: "boolean", default: false },
2011
2074
  json: { type: "boolean", default: false },
2012
2075
  standalone: { type: "boolean", default: false },
2013
2076
  "no-daemon": { type: "boolean", default: false },
@@ -2062,7 +2125,11 @@ function parseServeArgs(args) {
2062
2125
  throw new ServeUsageError(`${flag} expects a positive integer`);
2063
2126
  return n;
2064
2127
  };
2065
- const concurrency = positiveInt("--concurrency", values.concurrency, 1);
2128
+ const concurrency = positiveInt(
2129
+ "--concurrency",
2130
+ values.concurrency,
2131
+ DEFAULT_CONCURRENCY
2132
+ );
2066
2133
  const timeoutMs = positiveInt("--timeout", values.timeout, DEFAULT_RUN_TIMEOUT_MS / 1e3) * 1e3;
2067
2134
  let platforms;
2068
2135
  if (values.platform && values.platform.length > 0) {
@@ -2083,10 +2150,11 @@ function parseServeArgs(args) {
2083
2150
  requesters,
2084
2151
  concurrency,
2085
2152
  timeoutMs,
2086
- gateway: values.gateway ?? PREFERRED_GATEWAY,
2153
+ ...values.gateway !== void 0 ? { gateway: values.gateway } : {},
2087
2154
  ...values["act-bin"] !== void 0 ? { actBin: values["act-bin"] } : {},
2088
2155
  ...platforms ? { platforms } : {},
2089
2156
  ...values.workdir !== void 0 ? { workdir: values.workdir } : {},
2157
+ once: values.once,
2090
2158
  json: values.json
2091
2159
  };
2092
2160
  }
@@ -2147,7 +2215,7 @@ function makeAffordabilityCheck(args) {
2147
2215
  return false;
2148
2216
  };
2149
2217
  }
2150
- function untilStopRequested(signal) {
2218
+ function untilStopRequested(signal, firstRun) {
2151
2219
  return new Promise((resolve2) => {
2152
2220
  let settled = false;
2153
2221
  const finish = (reason) => {
@@ -2163,6 +2231,7 @@ function untilStopRequested(signal) {
2163
2231
  signal?.addEventListener("abort", () => finish("abort"), { once: true });
2164
2232
  process.once("SIGINT", onSigint);
2165
2233
  process.once("SIGTERM", onSigterm);
2234
+ void firstRun?.then(finish);
2166
2235
  });
2167
2236
  }
2168
2237
  async function runCiServe(args, deps) {
@@ -2181,6 +2250,8 @@ async function runCiServe(args, deps) {
2181
2250
  io.err(CI_SERVE_USAGE);
2182
2251
  return 2;
2183
2252
  }
2253
+ const gateway = (flags.gateway ?? forced.env["RIG_ARWEAVE_GATEWAY"] ?? PREFERRED_GATEWAY).replace(/\/+$/, "");
2254
+ const readGateways = [`${gateway}/raw`, ...ARWEAVE_GATEWAYS];
2184
2255
  let runner;
2185
2256
  if (forced.runner) {
2186
2257
  runner = forced.runner;
@@ -2210,11 +2281,16 @@ async function runCiServe(args, deps) {
2210
2281
  const stateDir = forced.stateDir ?? defaultCoordinatorStateDir(forced.env, coordinator);
2211
2282
  const workdir = flags.workdir ?? join4(stateDir, "work");
2212
2283
  const version = rigVersion();
2284
+ let concludeFirstRun = () => void 0;
2285
+ const firstRun = flags.once ? new Promise((resolve2) => {
2286
+ concludeFirstRun = resolve2;
2287
+ }) : void 0;
2213
2288
  handle = await startCoordinator({
2214
2289
  coordinatorPubkey: coordinator,
2215
2290
  publisher: ctx.publisher,
2216
2291
  relayUrl: flags.relay,
2217
- gatewayUrl: flags.gateway,
2292
+ gatewayUrl: gateway,
2293
+ gateways: readGateways,
2218
2294
  repos: flags.repos,
2219
2295
  runner,
2220
2296
  stateDir,
@@ -2232,6 +2308,7 @@ async function runCiServe(args, deps) {
2232
2308
  ...forced.fetchFn ? { fetchFn: forced.fetchFn } : {},
2233
2309
  ...forced.resolveSha ? { resolveSha: forced.resolveSha } : {},
2234
2310
  ...forced.clock ? { clock: forced.clock } : {},
2311
+ ...firstRun ? { onRunConcluded: (run) => concludeFirstRun(run) } : {},
2235
2312
  log: (line) => io.err(line)
2236
2313
  });
2237
2314
  const npub = hexToNpub(coordinator);
@@ -2246,12 +2323,13 @@ async function runCiServe(args, deps) {
2246
2323
  coordinator,
2247
2324
  coordinatorNpub: npub,
2248
2325
  relay: flags.relay,
2249
- gateway: flags.gateway,
2326
+ gateway,
2250
2327
  repos: flags.repos,
2251
2328
  requesters: flags.requesters,
2252
2329
  runner: { family: runner.family, selectors: runner.selectors },
2253
2330
  concurrency: flags.concurrency,
2254
2331
  timeoutSeconds: flags.timeoutMs / 1e3,
2332
+ once: flags.once,
2255
2333
  stateDir,
2256
2334
  workdir,
2257
2335
  secretsKey: handle.secretsKeyPubkey,
@@ -2273,11 +2351,13 @@ async function runCiServe(args, deps) {
2273
2351
  io.out(`State: ${stateDir}`);
2274
2352
  io.out("Maintainers authorize this coordinator with:");
2275
2353
  io.out(` rig ci request ${npub}`);
2276
- io.out("Listening \u2014 press Ctrl-C to stop.");
2354
+ io.out(
2355
+ flags.once ? "Listening for one run \u2014 stops after it concludes (or Ctrl-C)." : "Listening \u2014 press Ctrl-C to stop."
2356
+ );
2277
2357
  }
2278
- const reason = await untilStopRequested(forced.signal);
2358
+ const reason = await untilStopRequested(forced.signal, firstRun);
2279
2359
  io.err(
2280
- `rig ci serve: ${reason} \u2014 stopping (active runs conclude cancelled)`
2360
+ 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
2361
  );
2282
2362
  await handle.stop();
2283
2363
  handle = void 0;
@@ -2300,4 +2380,4 @@ export {
2300
2380
  makeAffordabilityCheck,
2301
2381
  runCiServe
2302
2382
  };
2303
- //# sourceMappingURL=ci-serve-ZVJBZ3W3.js.map
2383
+ //# sourceMappingURL=ci-serve-PYEHIBN7.js.map