Skip to content

ci: build from refs - #11

Merged
josephjclark merged 6 commits into
mainfrom
ci-build-from-refs
Sep 8, 2026
Merged

ci: build from refs#11
josephjclark merged 6 commits into
mainfrom
ci-build-from-refs

Conversation

@doc-han

@doc-han doc-han commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

What this does

The CI workflow that's supposed to test any Lightning branch didn't actually
work. It had specific software versions typed into it by hand, and Lightning
has since moved on to newer ones. Instead of updating those by hand again
(which just breaks the next time Lightning upgrades), the workflow now reads
the required versions directly from whatever Lightning branch it's testing.
It also installs a system package Lightning needs that was missing before.

Separately, if someone runs this harness on a machine with the wrong software
versions installed, it now says so plainly and tells them how to fix it,
instead of failing with a cryptic error partway through.

How to use and test it

bun run typecheck
LIGHTNING=../lightning WORKER=../kit bun run test

Expect ✓ tests/happy-path.spec.ts (1 test), ~35s on a warm checkout.

Preflight:

# checkout's .tool-versions matches your installed toolchain: silent pass
# mismatch: throws, e.g.
#   erlang 28.5 pinned, but `erl` here is 27.3.3
#   Fix: `asdf install` inside <dir>

In CI:

# test on main branch
gh workflow run test-lightning-branch.yml -f lightning_ref=main -f worker=latest

# test on this branch
gh workflow run test-lightning-branch.yml --ref ci-build-from-refs -f lightning_ref=main -f worker=latest

Run #11
(ubuntu-latest): setup-beam resolved Erlang/OTP 28 + Elixir 1.18.4 from
Lightning's .tool-versions, libsodium-dev installed, suite passed —
1 passed (1), 211s cold (no caching yet).

Where to look, in order

  1. .github/workflows/test-lightning-branch.yml
    — the spec for this PR. Lightning is checked out into .cache/lightning
    with actions/checkout (not cloned by the harness), and erlef/setup-beam
    / actions/setup-node read its .tool-versions directly
    (version-type: strict, required when reading from a file).
  2. src/toolchain.ts — the preflight. Erlang and Elixir
    are hard failures; node is advisory only (a major-version skew — v22 vs a
    v24 pin — works fine in practice, so it only warns).
  3. src/ci.ts — one function, stepSummary. A no-op outside
    Actions (checks $GITHUB_STEP_SUMMARY), so it's safe to call from anywhere
    without an environment check at each call site.
  4. src/stack.ts + tests/matchers.ts
    — the two places that now call stepSummary: a boot failure (Lightning or
    worker dying/timing out) and a failed run assertion. Both already had a
    formatted message; this just mirrors it to the summary page too.
  5. README.md — new "Running in CI" section, and drops the
    now-stale note about needing bootstrap-from-config (Kickstart is on
    Lightning main as of this PR).

… step summaries

The dispatch workflow had never run, and couldn't have: it pinned OTP 27.3.3
while Lightning pins 28.5, and lacked libsodium-dev for enacl. Instead of
re-pinning, read Erlang/Elixir/node from the checkout's own .tool-versions via
setup-beam/setup-node version-file, so a Lightning bump can't silently break it.

Lightning is now cloned with actions/checkout into .cache/lightning and passed
to the harness as a local path — the checkout exists before any later step, so
cache keys can hashFiles() against it when caching lands.

Harness side: `up` now preflights the host toolchain against .tool-versions
and fails with an install hint, rather than three steps later with mix exiting
126. Boot failures and failed-run assertions also mirror their explanation to
$GITHUB_STEP_SUMMARY, so a red CI run explains itself on the summary page.

Nothing is cached yet — this run establishes the cold-path baseline.
@doc-han

doc-han commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

The flow, end to end

The workflow checks out this repo, then checks out Lightning (any ref/repo,
same grammar src/source.ts already used) into .cache/lightning. It reads
that checkout's .tool-versions to install Erlang/Elixir/node, installs
libsodium-dev, then runs bun run test with LIGHTNING pointed at the
checkout path — from there it's the exact same code path as a local
LIGHTNING=../lightning bun run test. Inside that path, up now calls
checkToolchain() before deps.get, and any boot failure or failed-run
assertion additionally writes to $GITHUB_STEP_SUMMARY.

Stops

src/toolchain.ts

readToolVersions parses .tool-versions (asdf's format: <tool> <version>
per line, # comments). checkToolchain then runs erl/elixir from
inside the checkout directory
(cwd: dir in probe()) — that's the only
way asdf's shims resolve the checkout's own pins rather than whatever's
active in the harness's own directory. This matters: if this ran from the
harness root instead, it would silently check the wrong checkout's toolchain
against no pins at all in .tool-versions-less setups, or the harness's own
pins if it had any.

Elixir's version string embeds the OTP it was compiled for
(Elixir 1.18.4 (compiled with Erlang/OTP 28)), so the check parses that out
and compares it against the -otp-N suffix in the pin (1.18.4-otp-28) — an
Elixir binary compiled for the wrong OTP major is a real failure mode asdf
lets happen silently otherwise.

Diagnosis story: this file exists because asdf install erlang 28.5 exited
0 while the build had actually failed
(a configure error deep in the
build log). The only tell was asdf list erlang not showing 28.5. That's a
fact about asdf/kerl, not about Lightning, but it's why the preflight
checks the result (erl/elixir actually running and reporting the pinned
version) rather than trusting any installer's exit code.

src/ci.ts

Nine lines. The only reason it's its own file rather than inlined at the two
call sites: both stack.ts and tests/matchers.ts need it, and neither
should import from the other.

.github/workflows/test-lightning-branch.yml

Diff-read this one, don't just read the final version — the previous version
looked plausible (postgres service, setup-beam, bun install, run test) but had
never actually completed a run; the three visible past runs in the Actions
tab failed in the "Run contract tests" step, and are from a prior
docker-compose-based version of this workflow (predating boot-from-source),
not this native-process one.

Two things worth checking closely:

  • version-type: strict is required by erlef/setup-beam whenever
    version-file is set — omitting it makes the action error out rather than
    silently falling back to a default. Confirmed against the action's own
    README rather than assumed.
  • KEEP_STACK=1 is kept from the old workflow so tmp/*.log survive to the
    upload-artifact step; the explicit bun run down at the end replaces
    vitest's own teardown. Same shape as before, just re-verified it's still
    necessary now that the checkout path changed.

@doc-han
doc-han requested a review from josephjclark September 7, 2026 13:05
@josephjclark

Copy link
Copy Markdown
Contributor

There's deprecation warning on the action saying that node 20 is deprecated. Can we make sure the node actions are all on latest?

@josephjclark

josephjclark commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

There is so much noise from lightning and setup logging in the contract tests bit. The only test stuff runs on line 1487. Is there anything we can do about this? Disable logging? but that might be a problem for debug

@@ -1,10 +1,16 @@
name: Test against a Lightning branch

# Boots a real Lightning (built from the chosen ref) and a real ws-worker on

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it time to update this workflow name? Or are we still treating this just as a test canary?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Workflow name updated. Now looks something like below
OpenFn/lightning@main vs worker latest

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very cool! Dynamic action names

We still need to update the static/generic name though because it looks like thisin the UI right now

image

Comment thread .github/workflows/test-lightning-branch.yml Outdated
Comment thread .github/workflows/test-lightning-branch.yml
Comment thread src/toolchain.ts Outdated
Comment thread src/toolchain.ts Outdated
Comment thread src/toolchain.ts Outdated
Comment thread src/toolchain.ts
deps.get, npm/pnpm install, and mix lightning.kickstart all ran with
stdio: inherit, so a successful CI run's console was mostly dependency
resolution noise and (for kickstart) every Ecto query at Lightning's dev
debug level — the actual test result was buried under it.

These commands now write to tmp/prep.log instead. Silent when they
succeed; the accumulated log is dumped to the console and
$GITHUB_STEP_SUMMARY the moment one fails, so the failure signal doesn't
lose anything, only the noise around a pass does. Also uploaded as a CI
artifact alongside lightning.log/worker.log.

Verified: a full local run's console output dropped from several hundred
lines to 25; a forced mix failure (bogus task name) still prints the
real error and reaches the step summary.
Four comments referenced our own build process rather than what a reader
needs (a specific error code we hit, a caching decision not yet made, a
'this run measures' aside about the PR's own rollout). Reworded to state
what the code does and why, independent of how this PR came together.
Every workflow_dispatch run showed up identically in the Actions tab
('Test against a Lightning branch #N') until opened. run-name (the one
GitHub key documented to support the inputs context here) surfaces
lightning_repo@lightning_ref and the worker spec right in the list.
@doc-han
doc-han requested a review from josephjclark September 7, 2026 16:48
@josephjclark
josephjclark merged commit 2e00d1d into main Sep 8, 2026
1 of 2 checks passed
@josephjclark
josephjclark deleted the ci-build-from-refs branch September 8, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants