ci: publish versioned packages with verified tarballs and OIDC - #206
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
3 issues found across 6 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="scripts/pack-release.mjs">
<violation number="1" location="scripts/pack-release.mjs:13">
P1: The `--ignore-scripts` option does not suppress npm's `prepare` hook during `npm pack`. This runs surface's Bun build and SDK's TypeScript build; the release-tooling fixture and publish job lack that build environment, so the workflow fails before validation or publication. Pack the already-built artifacts without lifecycle preparation, or remove the redundant repack.</violation>
</file>
<file name="scripts/version-packages.mjs">
<violation number="1" location="scripts/version-packages.mjs:6">
P2: Custom versions accepted by this regex can be silently normalized by `npm version`, so the workflow publishes and tags a version different from the requested `custom_version`. Reject noncanonical inputs or verify that npm's resolved version exactly matches the requested value before propagating it.</violation>
</file>
<file name=".github/workflows/publish.yml">
<violation number="1" location=".github/workflows/publish.yml:217">
P1: When the selected branch advances after dispatch, this push rejects because the job publishes artifacts built from stale `github.sha`. That failure occurs after npm publish, leaving immutable package versions without the commit, tag, or GitHub Release. Reserve or verify the branch ref before publishing and use a release-specific ref to prevent this partial release.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| const destination = resolve(output); | ||
| mkdirSync(destination, { recursive: true }); | ||
| const [packed] = JSON.parse(execFileSync('npm', [ | ||
| 'pack', '--ignore-scripts', '--json', '--pack-destination', destination, |
There was a problem hiding this comment.
P1: The --ignore-scripts option does not suppress npm's prepare hook during npm pack. This runs surface's Bun build and SDK's TypeScript build; the release-tooling fixture and publish job lack that build environment, so the workflow fails before validation or publication. Pack the already-built artifacts without lifecycle preparation, or remove the redundant repack.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/pack-release.mjs, line 13:
<comment>The `--ignore-scripts` option does not suppress npm's `prepare` hook during `npm pack`. This runs surface's Bun build and SDK's TypeScript build; the release-tooling fixture and publish job lack that build environment, so the workflow fails before validation or publication. Pack the already-built artifacts without lifecycle preparation, or remove the redundant repack.</comment>
<file context>
@@ -0,0 +1,60 @@
+const destination = resolve(output);
+mkdirSync(destination, { recursive: true });
+const [packed] = JSON.parse(execFileSync('npm', [
+ 'pack', '--ignore-scripts', '--json', '--pack-destination', destination,
+], { cwd: directory, encoding: 'utf8' }));
+const archive = join(destination, packed.filename);
</file context>
| git commit -m "chore(release): v${NEW_VERSION}" | ||
| fi | ||
| git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}" | ||
| git push --atomic origin "HEAD:refs/heads/${RELEASE_BRANCH}" "refs/tags/v${NEW_VERSION}" |
There was a problem hiding this comment.
P1: When the selected branch advances after dispatch, this push rejects because the job publishes artifacts built from stale github.sha. That failure occurs after npm publish, leaving immutable package versions without the commit, tag, or GitHub Release. Reserve or verify the branch ref before publishing and use a release-specific ref to prevent this partial release.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish.yml, line 217:
<comment>When the selected branch advances after dispatch, this push rejects because the job publishes artifacts built from stale `github.sha`. That failure occurs after npm publish, leaving immutable package versions without the commit, tag, or GitHub Release. Reserve or verify the branch ref before publishing and use a release-specific ref to prevent this partial release.</comment>
<file context>
@@ -0,0 +1,242 @@
+ git commit -m "chore(release): v${NEW_VERSION}"
+ fi
+ git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
+ git push --atomic origin "HEAD:refs/heads/${RELEASE_BRANCH}" "refs/tags/v${NEW_VERSION}"
+ - name: Create GitHub Release
+ if: ${{ !inputs.dry_run }}
</file context>
|
|
||
| // The SDK is the version anchor; no package independently computes a bump. | ||
| const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`); | ||
| if (process.env.CUSTOM_VERSION && !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(process.env.CUSTOM_VERSION)) { |
There was a problem hiding this comment.
P2: Custom versions accepted by this regex can be silently normalized by npm version, so the workflow publishes and tags a version different from the requested custom_version. Reject noncanonical inputs or verify that npm's resolved version exactly matches the requested value before propagating it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/version-packages.mjs, line 6:
<comment>Custom versions accepted by this regex can be silently normalized by `npm version`, so the workflow publishes and tags a version different from the requested `custom_version`. Reject noncanonical inputs or verify that npm's resolved version exactly matches the requested value before propagating it.</comment>
<file context>
@@ -0,0 +1,32 @@
+
+// The SDK is the version anchor; no package independently computes a bump.
+const paths = ['surface', 'sdk', 'runtime-linux-x64'].map((name) => `packages/${name}/package.json`);
+if (process.env.CUSTOM_VERSION && !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(process.env.CUSTOM_VERSION)) {
+ throw new Error('custom_version must be a semantic version');
+}
</file context>
CI on #206 failed with: Build authoring surface error: working directory '/home/runner/work/flows/flows/surface': No such file or directory Same blind spot as `cd sdk`: my rewrite pattern required a trailing slash, so every bare directory VALUE was invisible to it. Five remained: .github/workflows/cloud-runtime-artifact.yml working-directory: surface npm ci --prefix sdk working-directory: sdk testdata/backlog-picker.flow.yaml cd sdk && npm ci ... workflows/bootstrap-gate1.yaml cd sdk && npm install ... backlog-picker.flow.yaml is safe to edit: unlike tick-heartbeat and hello-ladder it has no pinned sha256, so changing it breaks no spec-parity assertion. Three layers of verification missed these and CI did not: the vitest suite never runs these files, the structure lens read the diff rather than executing it, and surface-package-gate.sh does not touch cloud-runtime-artifact.yml. The first thing to actually run this workflow found it immediately, which is the argument for landing a branch's CI before trusting a local green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
… misses Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
This reverts d380008 from the layout branch. Not a judgement on the work — it is a duplicate, and it landed in the wrong place. I spawned two codex agents on the same task after misreading the first as dead. Both delivered a release pipeline: the finn-mini run opened #206 as a proper stacked PR, and the sf-mini run committed a second, independent implementation directly onto this branch. That left #205 carrying both a layout move and a release pipeline, which is two changes in one PR and makes the layout impossible to review on its own. #205 goes back to being only the move. #206 carries the pipeline and is stacked on it, which is the shape its own description already claims. The reverted work remains in this branch's history at d380008 if it turns out to be the better of the two implementations — that comparison is worth making on the merits, not settling by which agent pushed last. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
Reviewed. This is good work, and it solves the problem I had hacked around.Disclosure: I wrote the brief this was built from, and I wrote the interim workflow it replaces. So I have reviewed it against the requirements rather than against my own earlier attempt, which was worse. The thing I most wanted to see, and it is hereMy discarded version built the surface and symlinked it so the SDK could compile against a sibling whose manifest pointed at the registry. That is a workaround. This does it properly:
Requirements check
Beyond the brief: I was wrong in one of my own notes about this PR. I recorded it as "a single job where relayfile uses ordered ones" from a What is actually blocking itIts three red checks are inherited from my bug, not from this work. #206 is stacked on #205, and #205 had stale paths that failed This needs a rebase onto current #205 for its CI to say anything meaningful. I have not rebased it myself — it is your branch and I have already caused one collision today by having two agents on this task. One question, not a blocker
|
…y version Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
076727a to
c97c8b4
Compare
…eeded on the API-key path Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
* refactor(layout): move sdk/ and surface/ under packages/
Consolidates the layout onto the convention relay and relayfile use, so all
three publishable packages sit together:
packages/sdk
packages/surface
packages/runtime-linux-x64
Scope note: the headline is 138 files and ~1557 references, but only 19 needed
changing. The rest live in ops/reviews transcripts, DRIVE-LOG, briefs and past
run reports — records of what was true when written, deliberately left alone
rather than rewritten to match a layout that did not exist yet.
The subtle half is path arithmetic inside the moved trees. Tests reached the
repo root with two levels of `..`, which now lands at packages/. Every
repo-root climb is now three levels; package-relative single-`..` uses
(RELAYFLOWS_SDK_DIST, dist/cli.js) are unchanged and must stay that way.
testdata/tick-heartbeat.* and hello-ladder.* are reverted rather than updated:
their content is pinned by a sha256 shared across the SDK/kernel spec-parity
boundary, so rewriting a comment inside them breaks the hash. Their prose now
names the old paths; correcting that means regenerating the pinned hash on both
sides, which is a separate change.
Verified in the moved layout:
kernel: cargo test --workspace 165 passed, 0 failed
sdk: vitest run 662 passed, 3 skipped, 0 failed
(including the real-analyzer gate-2 case and the daemon-kill
resume case, both of which exercise repo-root paths)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
* fix(layout): migrate the path references the first sweep could not see
The structure lens failed this branch and was right. My rewrite used a negative
lookbehind excluding `/` and `.`, which skipped every PATH-PREFIXED reference —
`./sdk/dist`, `$repo_root/sdk`, `../surface/src`, `${REPO}/sdk/dist` — and
`cd sdk` has no trailing slash to match at all. Those are exactly the executable
ones.
Five live paths were left pointing at directories that no longer exist:
- workflows/drive.yaml and drive-cloud.yaml: the guards were migrated
(`[ -f packages/sdk/package.json ]`) but the bodies were not, so `cd sdk`
short-circuited npm ci and npm test, and `require("./sdk/dist/index.js")`
threw. The drive verify gate was broken by its own migration.
- scripts/surface-package-gate.sh: `$repo_root/surface` and `$repo_root/sdk`,
the body of the surface-package gate whose paths filter had already moved.
- regressions/tsconfig.json and examples/research/tsconfig.json.
- ops/probes/pr134-repair-0903/*.mjs, where one file had its println migrated
and its imports left behind.
`@relayflows/surface` specifiers are package names, not paths, and are untouched.
Worth recording why the suites did not catch this: 662 SDK and 165 kernel tests
pass either way, because none of them runs drive.yaml, the gate script, the
regressions tsconfig, or the probes. A green suite was never going to see it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
* fix(layout): the two depth-dependent paths inside the moved manifests
Running scripts/surface-package-gate.sh found what neither suite could:
$ tsc -p ../regressions/tsconfig.json
error TS5058: The specified path does not exist: '../regressions/tsconfig.json'
From packages/surface, `../regressions` is now packages/regressions. Same class
as the test climbs, in a package.json script rather than a test file.
- packages/surface `typecheck:regressions`: ../regressions -> ../../regressions
- packages/sdk `test:prep`: ../kernel -> ../../kernel, ../testdata -> ../../testdata.
`sh ../ops/cargo.sh` inside it is NOT changed: it runs after `cd` into kernel/,
which did not move, so it is already repo-root relative.
Why the 662-test run missed it: I invoked vitest directly, so `npm test` never
ran, so `test:prep` never ran. Bypassing the package's own entry point to run its
tests skips the part of the package that knows where things are.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
* fix(layout): the no-trailing-slash path references CI caught
CI on #206 failed with:
Build authoring surface
error: working directory '/home/runner/work/flows/flows/surface': No such file or directory
Same blind spot as `cd sdk`: my rewrite pattern required a trailing slash, so
every bare directory VALUE was invisible to it. Five remained:
.github/workflows/cloud-runtime-artifact.yml working-directory: surface
npm ci --prefix sdk
working-directory: sdk
testdata/backlog-picker.flow.yaml cd sdk && npm ci ...
workflows/bootstrap-gate1.yaml cd sdk && npm install ...
backlog-picker.flow.yaml is safe to edit: unlike tick-heartbeat and hello-ladder
it has no pinned sha256, so changing it breaks no spec-parity assertion.
Three layers of verification missed these and CI did not: the vitest suite never
runs these files, the structure lens read the diff rather than executing it, and
surface-package-gate.sh does not touch cloud-runtime-artifact.yml. The first
thing to actually run this workflow found it immediately, which is the argument
for landing a branch's CI before trusting a local green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
* feat(release): build and verify ordered OIDC npm releases
Session-Id: 01a0766a-1a37-73d1-9f4f-f3f5e435c735
* Revert "feat(release): build and verify ordered OIDC npm releases"
This reverts d380008 from the layout branch. Not a judgement on the work — it is
a duplicate, and it landed in the wrong place.
I spawned two codex agents on the same task after misreading the first as dead.
Both delivered a release pipeline: the finn-mini run opened #206 as a proper
stacked PR, and the sf-mini run committed a second, independent implementation
directly onto this branch. That left #205 carrying both a layout move and a
release pipeline, which is two changes in one PR and makes the layout
impossible to review on its own.
#205 goes back to being only the move. #206 carries the pipeline and is stacked
on it, which is the shape its own description already claims. The reverted work
remains in this branch's history at d380008 if it turns out to be the better of
the two implementations — that comparison is worth making on the merits, not
settling by which agent pushed last.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
* fix(layout): update backlog-picker's canonical spec alongside its flow
CI caught this on #205:
AssertionError: step "build-sdk" diverges from the canonical spec:
expected 'cd sdk && npm ci --silent …' to be 'cd packages/sdk && npm ci --silent …'
testdata/backlog-picker.flow.yaml and backlog-picker.spec.canonical.json are a
compiled pair, and spec-parity compares them. I changed the flow's `cd sdk` and
left the canonical spec behind, so the two disagreed.
Unlike tick-heartbeat and hello-ladder, this pair carries no pinned sha256 — so
editing both is the correct fix rather than reverting, and nothing downstream
depends on the old bytes.
vitest tests/spec-parity tests/backlog-picker* -> 45 passed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
---------
Co-authored-by: kjgbot <kjgbot@agentrelay.dev>
Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1 Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
c97c8b4 to
1b981b1
Compare
… credential Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR Session-Id: c228933d-4f94-4d83-9a9a-daf3c83b94f1
Adds a dispatch-only npm release pipeline for the three packages moved by #205. The workflow follows relayfile's publish workflow: build/version once, pass artifacts to publishing, then commit release versions and create a GitHub Release after successful publication.
Stacked on #205 (
refactor/packages-layout); this PR contains only the release workflow, its tooling/tests, and package repository-directory corrections.dry_rundefaults to true. The six dispatch inputs arepackage,version,custom_version,preid,dry_run, andtag. As in the reference, real releases requirepackage=all; individual selections support dry runs because every package's version advances together.--no-save --package-lock=false; the build asserts that the installed surface is not a symlink. Published manifests contain registry versions.npm pack, unpack the tarballs, and assert entrypoints, declarations, versions, internal dependencies, and executable bits. Runtime checks require both nonempty binaries and execute the unpacked daemon plus the compiled CLI against the deterministic fixture, including dry runs. All checks precede the first publish.cargo build --locked --release -p relayflowdandbun build packages/sdk/src/cli-executable.ts --compile --target=bun-linux-x64. Publication is sequential: surface, SDK, runtime.contents: write,id-token: write, andnpm publish --access public --provenance --ignore-scripts; no npm token. Configure each package's npm trusted publisher forAgentWorkforce/flows, workflowpublish.ymlbefore a real release. Release lockfile generation explicitly resolves the newly published surface and rejects a remaining local link.Validation was run on macOS arm64. The Linux build/smoke steps and hosted OIDC authentication have not been executed here; Docker did not respond. No package was published, release created, or PR merged. The hash-pinned fixtures, historical records, SDK path-depth code, review-swarm workflow, and swarm scripts are unchanged.
Validation commands and captured output excerpts (full suites, no changes to their tests):
The initial default-worker run overlapped the kernel workload and failed two timing bounds. It is not being reported as green:
PATH=/Users/khaliqgant/.bun/bin:$PATH RELAYFLOWS_ALLOW_ANALYZER_SKIP=1 ./node_modules/.bin/vitest runThe rerun changed only worker concurrency. The real Claude analyzer also ran successfully; the three skips are the existing real-CLI-adapter cases.
RELAYFLOWS_ALLOW_ANALYZER_SKIP=1permits unavailable-analyzer diagnostics and does not force that case to skip.actionlint .github/workflows/publish.ymlexited 0 with no diagnostics;git diff --checkexited 0 with no output.Final kernel rerun at committed head
076727a06b3d700d0b85bf6473d0a8fbc014bd17: 165 passed, 0 failed across the workspace.