Skip to content

ci(linux): guard the native payload, declare patchelf, drop the dead zsync glob - #298

Merged
EtienneLescot merged 2 commits into
mainfrom
chore/linux-ci-hygiene
Aug 7, 2026
Merged

ci(linux): guard the native payload, declare patchelf, drop the dead zsync glob#298
EtienneLescot merged 2 commits into
mainfrom
chore/linux-ci-hygiene

Conversation

@EtienneLescot

@EtienneLescotEtienneLescot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Three findings from an audit of the Linux build chain. None affects a published artifact today — CI rebuilds everything on a fresh checkout, so these are the missing nets plus one stale doc claim.

1. before-pack.cjs asserted nothing on Linux

The hook branched on win32 and darwin, then ended on:

// Linux ships no native addon of its own; nothing to assert.

That was true when it was written and stopped being true when the wgpu compositor addon (d645a8ba) and the PipeWire capture helper (ef71c970) landed. macOS got MAC_REQUIRED precisely because "returned early on any non-win32 platform" had already let a broken bundle through once; Linux now gets the symmetric guard.

LINUX_REQUIRED covers compositor_view.node, the five symbol-renamed ffmpeg .so files, openscreen-pipewire-helper, whisper-stt-server and its ggml sidecars — everything linux.extraResources ships via filter: ["linux-*/**"].

The helper's ffmpeg/ subdirectory is checked separately, because a name match is not the property that matters: it has to be a directory holding the unrenamed libraries. The .so files one level up have every symbol renamed to osff_* so the addon cannot bind to Chromium's bundled ffmpeg — the helper needs the originals.

That separate check also caught a real collision worth naming: npm run fetch:ffmpeg vendors the static ffmpeg binary to exactly electron/native/bin/linux-x64/ffmpeg, the path build:native:linux wants as a directory. Whoever runs second loses. CI never sees it — build:linux only runs fetch:ffmpeg:sdk, which does not write that file — but it bites anyone who runs the full fetch by hand, and the resulting helper cannot start. The guard now says so in one line instead of failing at runtime.

Also: node scripts/before-pack.cjs on Linux fell through to the Windows branch and reported a missing D3D11 addon at a win32 path.

2. patchelf is an undeclared hard dependency

build-linux-compositor-addon.mjs:191 calls resolvePatchelf() unconditionally, which throws when it is absent — and its own docstring says "it is not installed by default anywhere". The apt line installed only libarchive-tools. It works today because the ubuntu-24.04 runner image preinstalls patchelf 0.18.0; declaring it stops the build from depending on the image's contents.

3. release/**/*.zsync has matched nothing for a while

zsync is electron-updater's delta format. This repo has no updater (grep -rn 'electron-updater\|autoUpdater\|latest-linux' → empty), and app-builder-lib 26.x contains no occurrence of "zsync" at all — appImageUtil.js calls appendBlockmap, embedding the block map instead. The last green Linux build logs there will be 3 files uploaded.

It stayed invisible because if-no-files-found: error evaluates the union of the patterns, so one dead glob among live ones never fails. ci-workflows.md:104 still advertised zsync as an output; corrected.

Related issue

No issue — found while auditing the Linux build/packaging chain.

Type of change

  • Refactor / maintenance

Release impact

  • No release note needed

Desktop impact

  • Linux
  • Installer / packaging

Screenshots / video

n/a

Testing

scripts/before-pack.cjs exercised on Ubuntu 24.04 x86_64 against the real electron/native/bin/linux-x64/ payload, four cases:

CaseResult
Complete payloadLinux native payload complete in electron/native/bin/linux-x64, addon up to date. — exit 0
ffmpeg is a file, not a directorynames the fetch:ffmpeg collision and the fix — exit 1
openscreen-pipewire-helper removednames the helper and npm run build:native:linux — exit 1
ffmpeg/ present but emptynames the helper's own ffmpeg .so files — exit 1

patchelf requirement reproduced: env -u PATCHELF PATH=/usr/bin:/bin node scripts/build-linux-compositor-addon.mjsError: patchelf not found.

build.yml re-parsed with yaml.safe_load; biome check clean on before-pack.cjs.

The macOS path is refactored, not changed: checkMacNativePayload now delegates to a shared checkNativePayload with the same list, thresholds and message text. label became a full noun ("Metal compositor addon" rather than "Metal") so the freshness check can also guard the capture helper, which is not a compositor addon.

Not exercised here: a real electron-builder --linux pack, and the macOS branch.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved Linux packaging validation to detect missing or outdated native components before release builds.
    • Added clearer diagnostics for invalid Linux helper assets and standalone Linux checks.
  • Documentation

    • Clarified that Linux releases provide AppImage, Debian, and Pacman packages without obsolete zsync files.
  • Chores

    • Updated Linux build preparation to include required tooling for native compositor builds.

…zsync glob
Three findings from an audit of the Linux build chain. None of them affects a
published artifact today — they are the missing nets and one stale doc claim.
before-pack.cjs asserted nothing on Linux. Its comment said "Linux ships no
native addon of its own", written before the wgpu compositor addon and the
PipeWire capture helper landed, and never revisited. macOS got a payload
check for exactly this reason; Linux now gets the symmetric one, covering
compositor_view.node, the five symbol-renamed ffmpeg .so files, the helper,
whisper-stt-server and its ggml sidecars.
The helper's ffmpeg/ subdirectory is checked separately, because a name match
is not the property that matters: it has to be a directory holding the
*unrenamed* libraries. That also catches a real collision — `fetch:ffmpeg`
vendors the static ffmpeg binary to that exact path, so running it by hand
replaces the directory with a file and produces a helper that cannot start.
CI never sees it, `build:linux` only runs fetch:ffmpeg:sdk.
Running `node scripts/before-pack.cjs` on Linux also fell through to the
Windows branch and reported a missing D3D11 addon at a win32 path.
patchelf is an unconditional dependency of build-linux-compositor-addon.mjs
(resolvePatchelf throws without it) and was not in the apt line. It works
because the ubuntu-24.04 image preinstalls it; declaring it stops the build
depending on the runner image's contents.
The release/**/*.zsync upload glob has matched nothing since the
app-builder-lib 26.x bump — that version dropped zsync for an embedded block
map, and there is no updater in this repo to consume one anyway. It stayed
invisible because if-no-files-found: error evaluates the union of the
patterns, so a dead glob alongside live ones never fails.
@coderabbitai

coderabbitaiBot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c58d9526-5ae1-40e7-a30c-d644e0d97db0

📥 Commits

Reviewing files that changed from the base of the PR and between 1ba8e42 and 3f88b38.

📒 Files selected for processing (1)
  • scripts/before-pack.cjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/before-pack.cjs

📝 Walkthrough

Walkthrough

Linux CI now installs patchelf, removes .zsync uploads, and documents package outputs. Packaging checks validate Linux compositor and PipeWire payloads, FFmpeg assets, and artifact freshness. Linux diagnostics now use the Linux validation path.

Changes

Linux packaging validation

Layer / File(s)Summary
Linux build dependencies and artifact outputs
.github/workflows/build.yml, technical-documentation/engineering/ci-workflows.md
Linux CI installs libarchive-tools and patchelf. Artifact uploads include AppImage, Debian, and Pacman packages without .zsync files.
Shared native payload and freshness checks
scripts/before-pack.cjs
Packaging checks validate Linux and macOS native payloads, FFmpeg asset shapes, and artifact freshness using custom source paths and labels.
Linux packaging and diagnostics integration
scripts/before-pack.cjs
Linux packaging validates compositor and PipeWire helper artifacts. Standalone Linux diagnostics use the Linux-specific checks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant beforePack
participant NativePayloadChecker
participant LinuxArtifacts
beforePack->>NativePayloadChecker: validate Linux payload and artifact freshness
NativePayloadChecker->>LinuxArtifacts: inspect compositor and PipeWire helper files
LinuxArtifacts-->>NativePayloadChecker: return payload presence, shape, and freshness
NativePayloadChecker-->>beforePack: allow packaging or raise validation error
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main Linux CI, native payload validation, dependency, and obsolete zsync glob changes.
Description check✅ PassedThe description covers the required sections and provides clear scope, impact, rationale, testing details, and known testing limitations.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/linux-ci-hygiene

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/before-pack.cjs`:
- Around line 121-154: Update LINUX_REQUIRED by replacing the single combined
FFmpeg matcher with five separate requirements for libavcodec, libavformat,
libavutil, libswresample, and libswscale. Each requirement must match only its
library family, retain the existing compositor-linking description and Linux fix
guidance, and require at least one matching file so stale copies of another
family cannot satisfy validation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 227b36d3-9d4a-443f-a73b-e7feb28d070f

📥 Commits

Reviewing files that changed from the base of the PR and between e3dbb4c and 1ba8e42.

📒 Files selected for processing (3)
  • .github/workflows/build.yml
  • scripts/before-pack.cjs
  • technical-documentation/engineering/ci-workflows.md

Comment threadscripts/before-pack.cjs
`atLeast: 5` portait sur le total de correspondances d'une regex combinée, pas
sur la présence de chaque famille. Cinq copies versionnées d'une même
bibliothèque — libavcodec.so.58 à .62 laissées par un build précédent —
satisfaisaient le compte pendant qu'une autre manquait : le paquet passait la
garde et le compositeur ne chargeait pas, exactement le mode de panne que cette
garde existe pour attraper.
Une exigence par famille, ce qui nomme aussi précisément celle qui manque dans
le message d'erreur au lieu d'un « au moins 5 » opaque.
Exercé contre des payloads fabriqués : le cas dégradé passait avec l'ancienne
regex et est refusé avec la nouvelle, chacune des cinq familles est requise
séparément, et le payload complet reste accepté.
@EtienneLescot
EtienneLescot merged commit a51ab19 into mainAug 7, 2026
16 checks passed
@EtienneLescot
EtienneLescot deleted the chore/linux-ci-hygiene branch August 7, 2026 10:05
Sign up for freeto 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

@EtienneLescot