GitHub Actions CI for the polymarket_fetch Rust workspace. It had been red on every push since 2026-05-19; as of 2026-05-22 it is fully green. This note records what was broken and the fixes — most are reusable gotchas.

The headline lesson

Pin the Rust toolchain in rust-toolchain.toml. A floating channel = "stable" means the CI runner picks up whatever the newest stable is, while the local dev machine sits on an older release. Newer clippy ships new lints, so code passes cargo clippy locally and fails the same check in CI. That skew is exactly why CI “kept failing” for no obvious reason. rust-toolchain.toml is now pinned to 1.95.0 — bumping it is a deliberate, reviewed edit, not a drift.

What was failing

fmt

Unformatted code. Fixed with cargo fmt --all.

clippy

CI ran clippy from the latest stable (1.95.0); the local machine was on 1.91.0. Lints that only exist in newer clippy — use_self, while_let_loop, unnecessary_sort_by — passed locally and failed in CI. Fixed the lints, then pinned rust-toolchain.toml to 1.95.0 so local and CI use an identical compiler and lint set.

cargo-deny

Three separate problems in the deny job:

  1. Docker action couldn’t honor the toolchain file. EmbarkStudios/cargo-deny-action@v2 runs in an Alpine/musl container. It read rust-toolchain.toml and tried to use an uninstalled stable-x86_64-unknown-linux-musl target. Fix: dropped the Docker action. The deny job now runs on a plain ubuntu-latest runner with taiki-e/install-action@cargo-deny followed by cargo deny check.

  2. Wildcard path dependency flagged. cargo-deny rejected the internal path dependency as a wildcard. Fix: set allow-wildcard-paths = true in deny.toml and set publish = false on the two binary crates — cargo-deny only permits wildcard path deps when the crate is not published.

  3. Two RUSTSEC advisories. rustls-pemfile (unmaintained) and tokio-tar (vuln) were both flagged. Both entered the graph only through testcontainers dev-dependencies. Rather than suppress them with an ignore list, bumped testcontainers 0.23 → 0.27 and testcontainers-modules 0.11 → 0.15, which dropped both crates from the dependency graph entirely.

Docker build

Added a multi-stage Dockerfile that builds the lag-probe binary on top of a slim Debian runtime, plus .github/workflows/docker.yml which builds the image on every push.

Image push is intentionally disabled

docker.yml runs with push: false. A registry hasn’t been chosen yet — GHCR vs Google Artifact Registry. Both enablement paths are written into the workflow as comments, ready to uncomment once the call is made.