Skip to content

ci(release): one cached Linux build and non-blocking reproducibility - #73

Open
leoisadev1 wants to merge 4 commits into
mainfrom
cursor/cnv-47-release-cache-0ddf
Open

leoisadev1 wants to merge 4 commits into
mainfrom
cursor/cnv-47-release-cache-0ddf

Conversation

@leoisadev1

@leoisadev1 leoisadev1 commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Fixes CNV-47

Summary

Linux release CI was spending ~2.5h on two cold full builds, then compare.py failed the job and skipped linux-release-review. That blocking double-build is gone from the publish path. Runners are unchanged from main (Tenki for CI / umbrella Release / version-packages; GitHub-hosted for the platform release jobs).

  • Release path is one cached build: Swatinem/rust-cache, bun install cache, pinned packaging/.cache inputs, and the container Cargo target at $RUNNER_TEMP/convt-linux-compile (outside the checkout so it cannot enter the source tarball).
  • Reproducibility (Release reproducibility) is workflow_dispatch only — no schedule. It rebuilds twice from cold, runs compare.py on Linux, and compares unsigned Mac/Windows artifacts. It fails only itself and still uploads both trees when the compare fails. It uses the same runners as the platform release jobs (ubuntu-24.04, macos-15, windows-latest).
  • Concurrency: platform release workflows cancel in-progress duplicates (group = workflow + ref + version). The umbrella Release queues (cancel-in-progress: false) so a same-version desktop/Cargo.lock push cannot abort a live publish. No auto-retry.
  • Outputs unchanged: AppImage/deb/rpm/linux tar.gz, source tarball, source-audit.json, release-manifest.json, update-manifest.json, artifact name linux-release-review, SOURCE_DATE_EPOCH, signing, package contents.

Mac and Windows never had a double-build gate on the release path. They now get rust-cache / bun cache, and Mac/Windows unsigned pairs are in the dispatch-only repro workflow.

Do not dispatch, cancel, or rerun Build release linux / Release on main. This PR does not merge.

Evidence

python3 -m unittest discover -s scripts/release -p 'test_*.py' -v — 27 tests, OK (test_workflows.py: one Linux build, dispatch-only repro, artifact names preserved; no assertion that runners must be free).

python3 -m unittest discover -s packaging/release -p 'test_*.py' — OK. AppImage runtime skip lives in appimage.sh so the hash-pinned appimage-source-build.py recipe is unchanged.

PR CI 37686580423 is green on the restored Tenki labels (Release scripts 6s, Web 3m, Linux rust ~1m, Windows rust ~4m, macOS rust ~5m).

Review follow-up kept from 4ee2c0f:

  • Linux repro rms the mktemp placeholder before update:keygen (wx refuses an existing file).
  • Umbrella Release queues instead of cancelling.
  • Bundle caches use exact lock keys only (no prefix restore-keys that can restore a stale pdfium.tgz).
  • Windows repro deletes target/ before each build.
  • Mac repro shares the pinned FFmpeg cache with the release path (compares two app/DMG rebuilds, not two FFmpeg builds).
  • rust-cache is not used in publish (contents: write).

Cold vs warm timings (estimated; dry-run dispatch on this branch was not started):

Path Before After (est.)
Linux release, cold ~1h45m–2h30m (two full builds; 37607929480 / 37621664130 / 37641703105) ~50–80 min (one build; native/FFmpeg + empty-cache source proofs still run)
Linux release, warm cache n/a (never cached) ~20–45 min. Cargo target + pinned downloads + AppImage runtime hash-skip help; rebuild-linux-source.sh / rebuild-cli.sh still compile CLI/app from empty caches inside release-linux.sh
15–20 min target — Payload compile/package can approach this on a warm cache. The two empty-cache source proofs are the remaining floor unless those move to the repro workflow too.
Mac / Windows release already one build same, plus rust-cache / bun cache (Mac already cached source FFmpeg)
Repro workflow was the Linux release gate dispatch only; ~2× a single cold platform build; does not block publish

Merge Danger

Door: two-way for workflows. After merge, the next Linux release on main is one cached build and will not run compare.py. Byte drift will not fail publish; catch it by dispatching Release reproducibility.

Blast radius: GitHub Actions only, plus appimage.sh cache-hit skip (runtime still verified against runtime_sha256) and docs. No signing, no package payload, no compare.py logic, no published v0.2.0 assets. Compile cache is $RUNNER_TEMP/convt-linux-compile — not in the frozen tree. source.py already drops target/ and .cache (#64/#65). Runners match main.

Runners

Unchanged from main.

Optional: faster Tenki sizes

Not switched in this PR. From Tenki runner sizes:

Current Next size up Why it might help
CI Rust Linux tenki-standard-large-8c-16g (8c/16G) tenki-standard-large-plus-16c-32g (16c/32G) Clippy + cargo test of the workspace. Tenki’s own guide puts Rust compiles on large or large-plus. Roughly 20–40% wall-clock if the suite is CPU-bound.
CI Rust macOS tenki-macos-26-medium (6c/16G) tenki-macos-26-large (8c/32G) Desktop suite is the slow macOS job. Extra RAM helps GPUI link; extra cores help rustc. Maybe a few minutes, not a 2×.
Release publish tenki-standard-large-8c-16g tenki-standard-large-plus-16c-32g Only cargo fetch + manifest work; little to gain.
Release preflight / CI Web / Version packages tenki-standard-medium-4c-8g stay Script / bun jobs. Medium is enough.

Linux release itself stays on GitHub-hosted ubuntu-24.04 (Tenki’s crates.io proxy breaks the offline build container). A bigger Tenki box would not apply there unless that proxy issue is solved. The remaining Linux floor after cache is the empty-cache source proofs, which more cores help only a little.

Open in Web Open in Cursor 

Devin Review

Stop the Linux release path from rebuilding twice from cold. Publish
from a single cached build, move compare.py to a dispatch-only
reproducibility workflow, and switch Tenki jobs to GitHub-hosted
runners. Fixes CNV-47.

Co-authored-by: Leo <leoisadev1@users.noreply.github.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

Devin Review

Comment on lines +52 to +54
key=$(mktemp)
chmod 600 "$key"
bun run update:keygen "$key"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Linux reproducibility fails before building

Every Linux reproducibility dispatch fails when update:keygen receives the existing file created by mktemp. Its exclusive file creation rejects that path, so neither rebuild runs.

Learn more

The Linux reproducibility job needs one temporary Ed25519 seed shared by both builds. mktemp creates a file, while keygen writes its seed with flag: "wx", which rejects any existing file. The first key generation exits before either build starts. The release Linux workflow removes its placeholder before invoking the same command.

Example: Dispatch with platform=linux. mktemp creates /tmp/tmp.example; update:keygen /tmp/tmp.example fails with EEXIST. No artifacts reach the comparison.

Recommended fix: Remove the placeholder before calling update:keygen, as in Prepare external update seed, while keeping the generated seed outside the checkout.

Suggested change
key=$(mktemp)
chmod 600 "$key"
bun run update:keygen "$key"
key=$(mktemp)
chmod 600 "$key"
rm "$key"
bun run update:keygen "$key"

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +214 to +222
function Invoke-UnsignedWindows($Destination) {
if (Test-Path packaging/out/windows) { Remove-Item -Recurse -Force packaging/out/windows }
./packaging/windows/build.ps1 -VerificationOnly
./packaging/windows/installer.ps1
New-Item -ItemType Directory -Force $Destination | Out-Null
Copy-Item packaging/out/windows/*.msi $Destination
}
Invoke-UnsignedWindows packaging/out/windows-a
Invoke-UnsignedWindows packaging/out/windows-b

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Windows comparison reuses compiled Rust artifacts

The second Windows build clears its output but retains Cargo's target directory. Its MSI comparison therefore does not test two cold Rust compilations.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@greptile-apps

greptile-apps Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[High risk] Restructures CI/CD release workflows and build caching.

The PR appears safe to merge; no blocking finding remains.

What we checked:

  • Same-version pushes stop publishing: Release keeps cancel-in-progress: false, so a new push does not cancel the running publish.
  • Windows compares reused builds: Invoke-UnsignedWindows deletes target before each build, preventing reuse of the first build's executables.

Summary

Linux releases now use one cached build. A separate, manually started workflow rebuilds and compares Linux, macOS, and Windows artifacts.

  • The latest commit intentionally restores Tenki for CI, release preflight/publish, and version-packages. Its stated reason is sponsorship; leoisadev1 is credited as co-author. The PR description still describes the earlier GitHub-hosted plan.
  • Earlier fixes remain: temporary key creation, queued publishing, exact download-cache keys, and fresh Windows Rust builds.
  • The macOS check explicitly compares app/DMG rebuilds with shared FFmpeg binaries.
  • No new actionable issues were found. Release workflows were not started.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Release[Release] --> Preflight[Check version]
  Preflight --> Linux[One cached Linux build]
  Preflight --> Mac[One macOS build]
  Preflight --> Windows[Optional Windows build]
  Linux --> Publish[Assemble and publish]
  Mac --> Publish
  Windows --> Publish
  Manual[Manual reproducibility run] --> Pair[Build twice and compare]
Loading

Reviews (4) · Last reviewed commit: "ci: restore Tenki runners and keep the c..." · Reviewed by Greptile

Comment thread .github/workflows/release-reproducibility.yml
Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release-linux.yml Outdated
Comment thread .github/workflows/release-reproducibility.yml
Comment thread .github/workflows/release-reproducibility.yml
Comment thread .github/workflows/release.yml Outdated
cursoragent and others added 3 commits October 7, 2026 20:32
The first PR run sat on apt-get install ffmpeg for 40+ minutes on
ubuntu-24.04. Bound each attempt and retry so a stuck mirror cannot
hold the job.

Co-authored-by: Leo <leoisadev1@users.noreply.github.com>
update:keygen refuses an existing mktemp file, so Linux compare never
ran. Also queue the umbrella Release instead of cancelling a live
publish on a same-version push, drop prefix cache restores that can
leave stale pinned downloads, and wipe target/ between Windows
compare builds.

Co-authored-by: Leo <leoisadev1@users.noreply.github.com>
Leo's Tenki sponsorship means runner cost is not the problem. Put CI,
Release preflight/publish and version-packages back on the same Tenki
labels as main, restore actionlint.yaml, and drop the GitHub-hosted
apt retry. The single cached Linux build and dispatch-only
reproducibility workflow stay.

Co-authored-by: Leo <leoisadev1@users.noreply.github.com>
@cursor cursor Bot changed the title ci(release): one cached build and GitHub-hosted runners ci(release): one cached Linux build and non-blocking reproducibility Oct 7, 2026

This branch has not been deployed

No deployments
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