Skip to content

feat(release): five-minute container quickstart via GHCR image - #61

Merged
tang-vu merged 5 commits into
mainfrom
devin/1789924613-five-minute-try
Oct 7, 2026
Merged

tang-vu merged 5 commits into
mainfrom
devin/1789924613-five-minute-try

Conversation

@tang-vu

@tang-vu tang-vu commented Sep 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

A maintainer can now evaluate ContribAI end-to-end in ~5 minutes with only a container runtime — no Rust toolchain, no config file, no GitHub token, no LLM key, and zero writes:

docker run --rm ghcr.io/tang-vu/contribai:latest demo
docker run --rm ghcr.io/tang-vu/contribai:latest demo --json

The demo command exercises the real production pipeline offline (consent → permit → admission → evidence → protected-path fail-closed probe → human-review gate → no submission), so the 5-minute path demos the safety model, not a stripped-down toy.

What changed

  • .github/workflows/release.yml — new image job after release: builds the existing locked Dockerfile, smoke-tests the exact pushed image with the same --version and demo --json assertions the release binaries get (external_writes_enabled==false, submission=="not_attempted", candidate.evidence_valid, protected_path_probe.admission_allowed==false), pushes :<tag> + :latest to ghcr.io/tang-vu/contribai, and attests the pushed digest via the already-pinned actions/attest. Least-privilege job permissions (contents: read, packages: write, id-token, attestations); plain docker CLI so no new third-party actions.
  • README.md / docs/QUICKSTART.md / docs/RELEASING.md / docs/deployment-guide.md — the quickstart now leads with the docker run path; RELEASING documents the new job and the one-time owner step (see below).
  • .dockerignore (new) — build context trimmed to the locked workspace inputs the Dockerfile actually COPYs.
  • _typos.toml (new) — allowlists ratatui; the existing typos hook otherwise rewrites the crate name to "Ratatouille" in any commit touching the README.
  • crates/contribai-rs/src/cli/mod.rs — three tests pinning the read-only CLI defaults: contribute has submit:false, patrol has respond:false, --dry-run parses without --submit.
  • crates/contribai-rs/src/exec/runner.rs — spawned workspace commands run with PYTHONDONTWRITEBYTECODE=1; the env allowlist now matches case-insensitively.
  • crates/contribai-rs/src/core/admission.rs — repository_path_error now rejects drive-letter paths (C:/..., d:...).
  • .github/workflows/ci.yml — the Test job installs pytest into the runner's python before cargo test (--user, with --break-system-packages as the PEP 668 fallback for the ubuntu image).
  • Cargo.lock — rustls 0.23.43→0.23.45, rustls-webpki 0.103.13→0.103.15 (RUSTSEC-2026-0285).

Pre-existing issues fixed along the way (main CI was red)

Every CI run on main has been failing: all three Test jobs plus RustSec audit. Root causes found while verifying this branch — all fixed here, and this PR's CI is fully green:

  1. Missing pytest on GH runners — the v7 e2e fixtures drive a real python -m pytest red-to-green leg, but runner images don't ship the module, so the run ended failed on all three OSes. The new ci.yml step installs it.
  2. Stale-bytecode validation flake — a solver rewrite landing in the same mtime second with the same byte length left a __pycache__ that CPython's timestamp+size invalidation treats as valid, so a later check could observe pre-fix code. It failed closed (spurious repair — not a safety bypass). BoundedRunner now sets PYTHONDONTWRITEBYTECODE=1 for every spawned command; a regression test asserts pytest runs leave no __pycache__.
  3. Windows env scrub dropped PATH — scrub_environment matched the allowlist case-sensitively, but Windows reports Path, so spawned commands got no PATH on Windows. The allowlist match is now case-insensitive and inserts the canonical spelling.
  4. Drive-letter paths accepted on Unix — repository_path_error let C:/evil.rs through because Path::components sees normal components on non-Windows hosts — silent acceptance in the shared validator used by admission scope, evidence v3, solver paths, and RunWorkspace::resolve. Now rejected; the existing traversal test that asserted this passes on Linux.
  5. RUSTSEC-2026-0285 — rustls 0.23.43 in the lockfile; bumped to 0.23.45 so cargo audit / the RustSec job pass.

What I did NOT change

  • No safety gate was removed or weakened: manifest consent, capability flags (--submit/--respond), permit binding, protected paths, fail-closed checks, human review, draft-PR-only, MCP read-only — all untouched.
  • No new release machinery: the image job extends the existing release workflow; the Dockerfile itself is unchanged.
  • No new network surface for the CLI path — demo stays offline.

Owner action required (one-time)

After the next tagged release, the first GHCR push creates a private package. Set the contribai package visibility to public once (Package settings under the owner's GitHub profile → ghcr.io/tang-vu/contribai → Package settings → Change visibility), or external users can't pull. The Dockerfile's org.opencontainers.image.source label already links the package to this repo.

Verification

  • cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings: clean
  • cargo test --workspace --locked: 839 passed, 0 failed
  • cargo build --workspace --release: clean
  • node scripts/check-installers.mjs, node scripts/check-site.mjs, node --check site/app.js: pass
  • docker build + docker run ... --version + demo --json assertions + full demo output: PASS locally
  • PR CI: all 14 checks green (was: 3× Test + RustSec red on main)

AI-assisted (Devin); the reviewer should confirm the workflow permissions and the GHCR visibility step.

Link to Devin session: https://app.devin.ai/sessions/868648d1623d4933b5da68d5ee560e02
Open in Devin Desktop: https://app.devin.ai/desktop/session/868648d1623d4933b5da68d5ee560e02?variant=devin
Requested by: @tang-vu

…er paths

Two pre-existing bugs surfaced while verifying the suite in this
environment:

- repository_path_error silently accepted drive-letter paths like
  'C:/evil.rs': Path::components treats them as normal components on
  Unix even though they are absolute on Windows. Reject them in the
  shared validator (admission scope, evidence v3, solver paths,
  RunWorkspace::resolve).
- Spawned workspace commands could leave a __pycache__ behind; a solver
  rewrite landing in the same mtime second with the same byte length
  made timestamp+size invalidation treat stale bytecode as valid, so a
  later check observed pre-fix code (the v7 e2e python fixture flake).
  Commands now run with PYTHONDONTWRITEBYTECODE=1 so no bytecode cache
  can be written into the workspace between legs.

Also bumps rustls 0.23.43 -> 0.23.45 (RUSTSEC-2026-0285) so the
cargo-audit pre-commit hook passes.
A maintainer can now evaluate ContribAI end to end with only a container
runtime — no Rust toolchain, no config, no token, no writes:

    docker run --rm ghcr.io/tang-vu/contribai:latest demo

- release.yml gains an image job after release: builds the locked
  Dockerfile, smoke-tests the exact image with the same --version and
  demo --json assertions the binaries get, pushes :<tag> and :latest to
  GHCR, and attests the pushed digest. Job permissions are
  least-privilege and actions stay SHA-pinned.
- .dockerignore keeps the build context to the locked workspace inputs.
- README leads Quick start with the zero-install path; QUICKSTART,
  RELEASING, and the deployment guide document it and the one-time
  owner step (make the GHCR package public).
- New CLI tests pin the read-only defaults: contribute needs --submit,
  patrol needs --respond, and --dry-run parses independently.
- _typos.toml allowlists 'ratatui' so the existing typos hook stops
  rewriting the crate name in docs.
@tang-vu tang-vu self-assigned this Sep 20, 2026
@vercel

vercel Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
contribai Ready Ready Preview Oct 3, 2026 4:43pm UTC

The v7 e2e tests drive a real 'python -m pytest' red-to-green leg in the
run workspace, but runner images do not ship the pytest module, so both
legs exit non-zero and the run ends failed on all three OSes. Install it
into the python on PATH (user site, with --break-system-packages only as
the PEP 668 fallback for the ubuntu image). This is the pre-existing
main CI failure.
Windows env names are case-insensitive and reported as e.g. 'Path', so
an exact-match allowlist dropped PATH (and any mixed-case var) from
spawned command environments there. Match case-insensitively and insert
under the canonical allowlist spelling so the child always sees PATH.
Caught by the windows CI leg's secrets_are_scrubbed_from_environment.
Match the CI fixture prerequisite in every release build job so the
Python lifecycle tests can finish before the GHCR image job starts.
Guard bootstrap ordering in the existing installer contract and document
why pytest is required.

Verified: missing-pytest lifecycle fails, installed-pytest lifecycle
passes; 838 workspace tests pass (1 ignored doc test), release build,
conformance, demo, formatting, installer contracts and negative mutations.
Rust 1.88 Clippy reproduces the 290 unchanged baseline diagnostics.

AI-assisted by OpenAI Codex. No release dispatch or package publication.
tang-vu added a commit that referenced this pull request Oct 5, 2026
Keep tree-sitter 0.26.11 and tree-sitter-language 0.1.7 because the proposed 0.27.0 and 0.1.8 releases require Rust 1.90. Preserve the other 13 grouped dependency updates.

Verified Rust 1.88 locked all-target check, Rust 1.99 formatting and strict Clippy, release build, conformance, and 833 tests excluding the pre-existing path validation failure addressed in #61.

AI-assisted implementation and verification; maintainer review remains required.
@tang-vu
tang-vu merged commit cea4725 into main Oct 7, 2026
14 checks passed
tang-vu added a commit that referenced this pull request Oct 7, 2026
Integrate main cea4725 while retaining the 13 dependency updates and
Rust 1.88-compatible tree-sitter 0.26.11. Preserve main's PATH, path
validation, bytecode, pytest prerequisite, and rustls security fixes.

The resulting tree matches the independently reviewed local #61+#64
integration. Stable fmt, all-targets clippy, release build, and offline
conformance passed locally; two Python e2e fixtures remain blocked by
missing local pytest. Hosted CI must validate this exact branch head.

AI-assisted by OpenAI Codex.

This branch was successfully deployed

1 active deployment
Preview — 2e082fc5 Deployed Oct 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.

2 participants