Skip to content

fix(0141): build and verify the Lambdas before every deploy - #325

Merged
adamkoot merged 4 commits into
developfrom
fix/0141_deploy-ships-stale-lambda-assets
Sep 21, 2026
Merged

adamkoot merged 4 commits into
developfrom
fix/0141_deploy-ships-stale-lambda-assets

Conversation

@adamkoot

Copy link
Copy Markdown
Collaborator

Summary

  • make deploy-production, -compute, -eventbridge and diff-production now depend on make build-lambdas: one cargo lambda build over every asset the CDK app references (cargo decides freshness — ~1 s when current), followed by a check that each target/lambda/<name>/bootstrap is an executable aarch64 ELF distinct from every other asset's. Before this, the deploy zipped whatever was on disk: a stale prices-api reached production on 2026-08-03, and on 2026-08-12 the directory held eleven 10-byte #!/bin/sh stubs.
  • The build refuses to vouch for files nobody ships: it stops if any *_ASSET_DIR override the CDK reads is set, or if cargo's target dir is not the one CDK packages.
  • Every per-stack deploy-production-* target passes --exclusively, so deploying ApiGateway no longer deploys Compute as a side effect. A change spanning both stacks needs Compute first — noted at the target and in the runbook.
  • CI runs the same script instead of its own inline build/verify steps, and the 12 new node:test guards run as the infra project's Nx test target (CI + verify:staged/verify:push).
  • Runbooks (0072 step 6/7, deploy-ledger-processor.md), infra/README.md and the CI wiki page updated; GET /health is documented as a gateway mock that proves nothing about a deploy.

Verified locally by staling and stubbing real artifacts (results table in the task file). Not verified: a real cdk deploy --exclusively against the account — the first operator deploy is that check, and it may show a new S3Key for every Lambda in the stack, since the build is now the group build CI uses. The task stays active until then.

`make deploy-production-compute` compiled the CDK TypeScript only and
zipped whatever sat in target/lambda/. On 2026-08-03 that shipped a stale
prices-api behind a clean diff and a green deploy; on 2026-08-12 the same
directory held eleven 10-byte `#!/bin/sh` stubs.

Every target that can ship a Lambda (deploy-production, -compute,
-eventbridge) now depends on `build-lambdas`. Cargo decides freshness — a
1 s no-op when the artifacts are current — and each bootstrap is then
refused unless it is an executable aarch64 ELF distinct from every other
asset's. CI runs the same script instead of its own inline copy.

Every per-stack deploy target passes --exclusively, so a deploy of
ApiGateway can no longer deploy Compute as a side effect.
The 0072 runbook's step 6 records the Rust-build prerequisite and warns
that "heals the 0132 drift" is only true from a current tree; step 7 and
the infra README say why GET /health — a gateway mock — proves nothing
about a deploy. The ledger-processor runbook builds through
`make build-lambdas` rather than a hand-typed single-crate cargo command.

The task records the four live runs behind its acceptance criteria, what
was not verified (a real `cdk deploy --exclusively`, the changed CI
steps), and that the first Compute deploy may re-hash every Lambda in the
stack because the build is now the group build CI uses.
`node --test tools/scripts/` only works on newer Node: on the 22.22.0 CI
uses, a directory argument is loaded as a module and the step fails. The
tests now run from a glob, as the infra project's Nx `test` target, so
CI's `nx run-many` and the verify:staged / verify:push hooks run them on
the pinned Node.

The guard could still vouch for files nobody ships. With an `*_ASSET_DIR`
override exported, CDK packages that directory instead; with
CARGO_TARGET_DIR or build.target-dir set, cargo writes the fresh
bootstraps elsewhere and the old ones under target/lambda/ verify. Both
are now refused, the second before anything is built.

`diff-production` builds the Lambdas first, so the diff an operator
approves is of the artifacts the deploy ships. The branch and dirty state
are printed again after verification, directly above `cdk deploy`.

The Makefile tests no longer go blind if the asset-dir literals move out
of the stack files, and no longer run `make` at import.
The ledger-processor runbook no longer recommends
LEDGER_PROCESSOR_ASSET_DIR, and says that a change spanning Compute and
ApiGateway needs Compute deployed first now that neither target deploys
the other. The CI wiki page footnotes the two steps that became one.

The task carries all ten findings with their outcomes, including the one
kept as is: --exclusively on deploy-production-apigateway.
@karczuRF

Copy link
Copy Markdown
Collaborator

Review

No correctness bugs found. One low-severity risk. The new tests (node --test "tools/scripts/**/*.test.mjs") pass on the PR head, 12 of 12.

Finding

tools/scripts/build-lambda-assets.sh:326 (also infra/Makefile:61,91,105,199) (low): deploys now depend on an unpinned local rustc

Every Lambda deploy target and make diff-production now compile the Rust on the operator's machine. The compiler version is pinned only inside ci.yml; the repo has no rust-toolchain.toml. The script's own header says rustc 1.98 or newer fails every aarch64 link under zig.

So after a routine rustup update to stable, these all fail at the link step, including when deploying an urgent fix:

  • deploy-production
  • deploy-production-compute
  • deploy-production-eventbridge
  • diff-production, even though it is read-only

It fails loudly and ships nothing, but no Lambda can be deployed until someone rolls the compiler back. A rust-toolchain.toml pinned to 1.97.1 removes the risk. The operator machine is on 1.97.1 today, so this is latent.

Checked and found correct

  • Bootstrap verification: the ELF magic and aarch64 byte checks in verify-lambda-bootstraps.sh are correct.
  • Asset-dir override refusal: refusing any *_ASSET_DIR override matches exactly how the CDK reads those variables, and nothing in .github sets one.
  • Target-dir check: it catches both a CARGO_TARGET_DIR setting and a build.target-dir config.
  • Tests are safe: make -n can't run real aws commands. The nested $(MAKE) flush-production-cache is also a dry run.
  • --exclusively: it only changes the ApiGateway deploy, the only stack that depends on Compute. The PR documents that a change spanning both stacks now needs Compute deployed first.
  • Coverage: the only stacks that use target/lambda are Compute and EventBridge, and both deploy targets now build the Lambdas first.
  • CI step: dropping shell: bash is fine. The script is committed as executable and has a bash shebang.

@adamkoot
adamkoot merged commit a8806bd into develop Sep 21, 2026
4 checks passed
adamkoot added a commit that referenced this pull request Sep 21, 2026
0141's target globs tools/scripts/**/*.test.mjs, so since it merged it
already runs these too: 30 cases in one step, 12 + 18, on the pinned
Node 22.22.0. The separate step was doing the second half twice. This is
decision 13's follow-up, which #325 landing unblocked.
@adamkoot
adamkoot deleted the fix/0141_deploy-ships-stale-lambda-assets branch September 21, 2026 07:13
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