Skip to content

ci: actually run the Soroban contract checks, and ship the workflow as an example - #19

Merged
Aladeenb merged 4 commits into
mainfrom
soroban/ci-template
Sep 3, 2026
Merged

Aladeenb merged 4 commits into
mainfrom
soroban/ci-template

Conversation

@Aladeenb

@Aladeenb Aladeenb commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

The bug

The rust job in ci.yml has never run. It discovered crates with:

find examples -mindepth 2 -maxdepth 2 -name Cargo.toml

Every Rust example keeps its crate in a contract/ subdirectory, so its Cargo.toml sits at depth 3. The scan returned [], the job was guarded by if: needs.discover.outputs.rust != '[]', and a skipped job is not a failure. Both Soroban contracts have gone unbuilt and untested in CI since the job was written, with the badge green throughout.

The node half is unaffected: package.json really does sit at depth 2.

The fix

A new soroban-contract.yml running four steps per contract, cheapest and most specific first:

Step Catches
cargo fmt --check diff noise
cargo clippy --all-targets --locked -- -D warnings lints, test code included
cargo test --locked behaviour, native target
cargo build --target wasm32v1-none --release --locked that it still builds for the target it deploys to

Three decisions worth reviewing:

  • The matrix lists its contracts rather than discovering them. Discovery's failure mode is silence: a pattern matching nothing and one matching everything look identical from outside. The list costs one line per new contract and turns a wrong path into a red job instead of an absent one. The example README argues the other side too, and gives the assert-non-empty version for anyone who prefers discovery.
  • wasm32v1-none, not wasm32-unknown-unknown. It is the target the pinned image in soroban-reproducible-build/build.conf builds, and CI should compile the artifact that actually ships.
  • --locked throughout, since Cargo.lock is committed and dependency drift should be a red build.

Also adds least-privilege permissions, a concurrency group, and a Rust cache.

The dead rust job is removed rather than repaired, since fixing the depth would only duplicate the new workflow. Your npm test --if-present step is preserved untouched.

The example

examples/soroban-ci documents the workflow, using the depth-3 bug as its worked case.

GitHub only runs workflows from .github/workflows, so the file necessarily exists twice. Rather than trust nobody forgets the second copy, the discover job diffs them and fails on any difference. The duplication stays; what goes is the chance of it passing unnoticed.

Verified locally

Both contracts pass all four steps. The drift check was tested in both directions: identical passes, a stray line fails.

Root README row is Building, not Working, because the workflow has never actually run on GitHub. This PR is the first time it will.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AcvxDnqPzixzsnf4dj1Fvd

Aladeenb and others added 3 commits September 3, 2026 20:57
Adds the workflow before removing the broken one it replaces, so no commit in
this series leaves Rust unchecked.

Four steps per contract, cheapest and most specific first so the failure you
read is the failure you have: cargo fmt --check, clippy with -D warnings over
--all-targets, cargo test, then a release build. Only the last needs a Wasm
target; Soroban unit tests compile natively, which is what lets test.rs link
ed25519-dalek and use std against a #![no_std] contract.

The matrix lists its contracts rather than discovering them. Discovery fails
silently: a pattern matching nothing and a pattern matching everything look the
same from outside, and only one of them tells you. The list costs one line per
new contract and turns a wrong path into a red job instead of an absent one.

Builds wasm32v1-none, not wasm32-unknown-unknown. It is the target the pinned
image in soroban-reproducible-build/build.conf builds, and CI should compile the
artifact that actually ships.

--locked throughout, since Cargo.lock is committed and dependency drift should
be a red build rather than a quiet resolution to newer versions than the ones
the contract was tested and deployed against.

Also adds the hardening the repo's other workflows have and this kind of file
usually lacks: least-privilege permissions, a concurrency group that supersedes
in-flight runs, and a Rust cache. Toolchain setup sits after checkout so a
failed checkout costs nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcvxDnqPzixzsnf4dj1Fvd
The discover step scanned for manifests with:

  find examples -mindepth 2 -maxdepth 2 -name Cargo.toml

Every Rust example keeps its crate in a contract/ subdirectory, so its
Cargo.toml sits at depth 3, not 2. The scan returned [], the job was guarded by
`if: needs.discover.outputs.rust != '[]'`, and a skipped job is not a failure.
Both Soroban contracts have gone unbuilt and untested in CI since the day the
job was written, with the badge green throughout.

Fixing the depth would only duplicate the previous commit, which checks the same
two contracts more thoroughly. So the job goes rather than gets repaired, and
soroban-contract.yml owns Rust from here.

The node half of discover is untouched: package.json does sit at depth 2, so
that scan works and its matrix is populated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcvxDnqPzixzsnf4dj1Fvd
The example's subject is the bug two commits back: a Soroban contract's CI
should fail loudly when it is misconfigured rather than go green by checking
nothing. The depth-3 find that skipped both contracts is the worked case, and
the argument for listing contracts instead of discovering them is made from it.

GitHub only runs workflows from .github/workflows at the repository root, so an
example directory cannot hold a live one and the file necessarily exists twice.
Rather than rely on nobody forgetting the second copy, the discover job now
diffs them and fails on any difference. The duplication stays; what goes is the
chance of it passing unnoticed.

The README names the alternative that lost. Fixing the find depth and keeping
discovery is genuinely nicer, since a new example needs no edit, and the list
costs a line every time one is added. It also gives the version for anyone who
wants both: keep discovery and assert the result is non-empty. What the example
argues against is discovery with no floor under it.

Root README row goes in at Building rather than Working. The workflow passes
locally against both contracts, all four steps, but it has never run on GitHub,
and that is what Working means here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcvxDnqPzixzsnf4dj1Fvd
@vercel

vercel Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
privy-stellar-onboarding Ready Ready Preview Sep 3, 2026 6:16pm UTC
stellar-trace Ready Ready Preview Sep 3, 2026 6:16pm UTC
stellar-wallets-kit-onboarding Ready Ready Preview Sep 3, 2026 6:16pm UTC

Request Review

This branch was successfully deployed

3 active deployments
Preview – privy-stellar-onboarding — ceeb7558 Deployed Sep 3, 2026 by vercel[bot]
Preview – stellar-wallets-kit-onboarding — ceeb7558 Deployed Sep 3, 2026 by vercel[bot]
Preview – stellar-trace — ceeb7558 Deployed Sep 3, 2026 by vercel[bot]
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.

1 participant