Skip to content

Packaging: the Homebrew cask workflow is dormant, and there is no Flathub package #335

Description

@EtienneLescot

Context

Most of the distribution plumbing already exists. State checked on 2026-08-10:

Channel Workflow Status
winget publish-winget.yml Configured. WINGET_IDENTIFIER=OpenScreen.OpenScreen is set; microsoft/winget-pkgs still only carries manifests/s/SiddharthVaddem/OpenScreen, so the new identifier lands on the next release.
AUR aur-publish.yml Configured (AUR_PACKAGE_NAME, AUR_SSH_PRIVATE_KEY).
Nix bump-nix-package.yml Configured.
Microsoft Store MSSTORE_PRODUCT_ID set.
Homebrew cask update-homebrew-cask.yml Dormant — see below. (Update 2026-08-11: fixed and publishing; the tap carries 1.9.2.)
Flathub Absent. (Update 2026-08-11: bigger than it looks — see the status note at the bottom.)

Two gaps remain.

1. The Homebrew cask job is skipped on every release

update-homebrew-cask.yml is gated on:

if: ... && vars.HOMEBREW_TAP_OWNER != '' && vars.HOMEBREW_TAP_REPO != ''

Neither variable exists on the repo, so the job is skipped every time — and a skipped job is green. This is the exact failure mode that publish-winget.yml documents in the comment above its own if:, after eight releases "succeeded" without publishing anything (#148). The same guard shape is still in place here.

  • Create the tap repo (getopenscreen/homebrew-openscreen)
  • Set HOMEBREW_TAP_OWNER and HOMEBREW_TAP_REPO
  • Move the config check out of if: into a step that reports its absence, mirroring what publish-winget.yml now does
  • Set the HOMEBREW_TAP_TOKEN secret
  • Publish a cask — Casks/openscreen.rb at 1.9.2 landed in the tap on 2026-08-11
  • Verify brew install --cask getopenscreen/openscreen/openscreen on a Mac

If the tap isn't wanted, delete the workflow instead — dormant-and-green is the worst of the three options.

2. No Flathub package

A Flathub search for "openscreen" returns zero results. It is the main Linux discovery surface (GNOME Software and KDE Discover both pull from it) and the only channel here with no automation at all. The AppImage/deb/rpm/pacman targets don't help — Flathub needs its own manifest submitted to flathub/flathub.

  • Submit a manifest to flathub/flathub
  • Wire release automation once the app ID is accepted

Deliberately not proposed

Chocolatey and Scoop. Windows is already covered by winget plus the Store; add them only if someone asks.

Why this is worth finishing

Beyond installation convenience, these are the highest-authority pages that link the project by name, and they intercept "install openscreen" before it reaches the stale third-party download mirrors currently outranking getopenscreen.com.

Status, 2026-08-11

Homebrew publishes. The chain is closed: the tap repository exists, HOMEBREW_TAP_OWNER and HOMEBREW_TAP_REPO are set, #340 moved the configuration test into a reporting step, and HOMEBREW_TAP_TOKEN was created as a fine-grained PAT with contents write on the tap alone. GITHUB_TOKEN could never have stood in for it — it is scoped to this repository and cannot push to a second one.

Proven rather than assumed: update-homebrew-cask.yml was dispatched at v1.9.2 (run 31535851764), every step green through Commit and push to tap, and Casks/openscreen.rb now carries version 1.9.2 with both architecture digests. For contrast, the previous run of the same workflow — the release event earlier the same day, before #340 merged — concluded skipped, which is the failure this issue was opened about, on the record one last time.

What is still unverified is the consumer end: brew install --cask getopenscreen/openscreen/openscreen needs a Mac, and neither the tap contents nor CI can answer for it. The DMGs it points at are notarized, since the Apple credentials are present, so it should not need the quarantine dance.

Flathub is a port, not a manifest. Flathub's requirements forbid the shortcut every other channel here uses:

All source available submissions must be built entirely from source code. This requirement applies to the main application component defined in the manifest, as well as any runtime dependencies included in the manifest.

Prepackaged binaries — .deb, AppImage or similar — are prohibited, and extra-data is not an escape hatch: it exists for non-redistributable sources, and upstream authorship implies redistribution rights, so a submission from this repository has no grounds to use it. OpenScreen is MIT, therefore source-available, therefore it must build inside the sandbox with no network. Concretely that means offline npm sources generated from the lockfile, vendored crates for the Rust compositor and the pipewire-capture helper, Electron from org.electronjs.Electron2.BaseApp — and ffmpeg built from source, which is the expensive part.

ffmpeg deserves its own paragraph, because it is what makes this a port rather than a manifest. scripts/fetch-ffmpeg.mjs pins a BtbN prebuilt shared tree, which the rule above rejects. Substituting the org.freedesktop.Platform.ffmpeg-full extension does not work either, and the reason is specific: scripts/build-linux-compositor-addon.mjs renames every ffmpeg dynamic symbol in the libraries the addon ships, because Electron links Chromium's own stripped libffmpeg.so into the same address space and the addon would otherwise bind to that. Doing so needs headers and libraries the build owns; the extension ships libraries without dev headers. So ffmpeg has to be compiled inside the manifest, and whether that reproduces the tree the addon was actually tested against is the open question. None of it can be built or verified on a Windows machine — flatpak-builder is Linux-only — which is what #347 exists to settle in CI.

One defect found in the metadata that just landed. build/com.getopenscreen.OpenScreen.metainfo.xml declares <launchable type="desktop-id">openscreen.desktop</launchable>, but Flatpak only exports desktop files prefixed with the app ID, so the Flatpak will ship com.getopenscreen.OpenScreen.desktop and this launchable will not resolve. The CI gate added in #340 cannot catch it: appstreamcli validate checks AppStream correctness, not Flathub's naming rules, which live in flatpak-builder-lint.

To be explicit about a non-problem, since it looks like one: electron-builder.json5 sets appId: com.etiennelescot.openscreen while the AppStream ID is com.getopenscreen.OpenScreen. These live in different namespaces and Flathub constrains only its own — the metainfo <id> must equal the Flatpak ID, which is the manifest filename, and those two already agree. The Electron appId does not need to change, and should not: macOS keys TCC grants to it, the cask's zap trash paths are built from it, and the Store identity is separate again.

Verdict du spike, 2026-08-12

Deux runs de flatpak-spike.yml (31537831529, 31575509206) : c'est faisable, et la partie chère est prouvée.

Question Réponse
runtime 24.08, BaseApp Electron2 24.08, node22, rust-stable existent ? oui, tout s'installe
flatpak-node-generator digère package-lock.json ? oui — 770 paquets → 1572 sources, 903 Ko
Les deux Cargo.lock vendorisent ? oui
Tarball ffmpeg upstream ? oui, 8.1.2 (464beb5e…) ; le pin BtbN est ce release + 34 commits
ffmpeg compile depuis les sources dans le sandbox ? oui — ~7 min, libav* construits, le build atteint le module app

Le paragraphe ci-dessus sur ffmpeg comme obstacle principal reste vrai sur le pourquoi (Flathub refuse le binaire BtbN, et l'extension ffmpeg-full n'a pas les headers dont le renommage de symboles a besoin), mais l'obstacle est franchi : compiler ffmpeg dans le manifeste marche.

Ce qui reste est du câblage ordinaire, plus la revue humaine de Flathub :

  1. Chemin du cache npm. npm ci --offline meurt en ENOTCACHED sur zustand-5.0.14.tgz : les sources générées atterrissent dans flatpak-node/npm-cache, alors que le manifeste pointe npm_config_cache ailleurs. Détail de configuration.
  2. Sources cargo dans le manifeste — un cargo-sources.json par lockfile.
  3. Empaqueter Electron : command: openscreen ne résout rien, et le <launchable> du metainfo doit devenir com.getopenscreen.OpenScreen.desktop.

Note sur l'outillage : le Stage E du run 2 a signalé « does not exist » sur un curl: (35) Connection reset by peer — un faux négatif, exactement la confusion « requête cassée » / « absent » que la revue de #347 avait corrigée sur le Stage A. Le digest étant désormais épinglé dans le manifeste, le Stage E n'a plus d'objet et devrait disparaître.

Progression du portage, 2026-08-12

Cinq runs. Chaque mur tombe et découvre le suivant, plus petit — sauf le dernier, qui est une décision produit.

Mur Cause État
ffmpeg prébuilt refusé Règle Flathub « tout depuis les sources » ✅ module ffmpeg 8.1.2 compilé (~7 min)
npm ciENOTCACHED Chemins de cache inventés. Ils sont positionnels : flatpak-node/ sous le répertoire du module, et XDG_CACHE_HOME est là où le binaire Electron est mis en cache ✅ passe
Crates hors-ligne CARGO_HOME doit être le chemin que flatpak-cargo-generator code en dur ✅ les deux lockfiles fusionnent dans un seul vendor/ sans collision
bindgen : Unable to find libclang Le SDK ne fournit pas libclang ✅ extension llvm18 + LIBCLANG_PATH
patchelf not found C'est lui qui réécrit les symboles ffmpeg pour ne pas se lier au libffmpeg.so de Chromium ✅ module 0.18.0, cleanup: ['*']
Garde before-pack : payload Linux incomplet Il manque le helper whisper.cpp et ses .so ggml décision à prendre

Vérifié au passage : le helper pipewire compile et s'exécute — son « no ScreenCast portal » est sa propre sonde constatant qu'un runner CI n'a pas de session graphique, pas un échec de build. Et FFMPEG_DIR=/app résout : avcodec, avformat, avutil, swscale, swresample sont liés depuis le module compilé juste avant.

Le blocage restant n'est pas technique. electron/native/whisper-stt/CMakeLists.txt récupère whisper.cpp, cpp-httplib et nlohmann/json par FetchContent, donc par le réseau, qu'un sandbox Flatpak n'a pas. Deux issues, et elles ne donnent pas le même produit :

  1. Parité complète — déclarer les trois dépôts comme sources git épinglées et rediriger FetchContent avec -DFETCHCONTENT_SOURCE_DIR_<NAME>. Faisable : scripts/build-whisper-stt.sh transmet déjà ses arguments à cmake (extra_cmake_flags=("$@")). Coût : trois pins de plus à maintenir, et probablement un ou deux murs supplémentaires puisque whisper.cpp tire ggml.
  2. Flatpak sans STT — désactiver la transcription dans cette variante. Le garde before-pack existe précisément pour empêcher ça : sans le helper, « transcription and captions fail with a developer error shown to end users ». Il faudrait donc un chemin de désactivation propre, pas seulement contourner le garde.

Tant que ce n'est pas tranché, le manifeste ne peut pas produire d'app complète.

Le manifeste Flatpak construit et passe le linter, 2026-08-13

Quatorze runs de flatpak-spike.yml. Le manifeste vit dans build/flatpak/ et se construit hors réseau, entièrement depuis les sources : Success! en ~20 min, quatre modules (patchelfspirv-headersffmpegopenscreen), export nommé d'après l'app ID (fichier desktop, huit tailles d'icône, metainfo).

Verdict de flatpak-builder-lint, l'outil que Flathub applique à la soumission :

Mode Erreurs
manifest aucune (un avertissement : le runtime 25.08 existe)
repo appstream-screenshots-not-mirrored-in-ostree, appstream-external-screenshot-url

Les deux erreurs repo ne sont pas dans nos fichiers : le contrôle exige une ref OSTree screenshots/<arch> que seule l'étape de mise en miroir de Flathub crée, et il se désactive en présence d'une « test ref » — ce que fait leur CI. Un repo construit localement n'a ni l'une ni l'autre, donc c'est la seule classe de finding que ce spike ne peut pas trancher.

Les murs franchis, dans l'ordre, avec ce que chacun a coûté : ffmpeg depuis les sources (Flathub refuse le binaire BtbN) ; les chemins de cache npm, qui sont positionnels et où XDG_CACHE_HOME est ce qui met Electron en cache ; CARGO_HOME au chemin que le générateur code en dur ; llvm18 pour le libclang de bindgen ; patchelf, qui réécrit les symboles ffmpeg ; les trois dépôts FetchContent du helper STT, épinglés tag + commit, redirigés par une nouvelle couture WHISPER_EXTRA_CMAKE_FLAGS dans build-whisper-stt.sh ; SPIRV-Headers accordé au Vulkan du runtime ; le staging de ffmpeg là où le helper pipewire le cherche vraiment ; --config.electronDist pour supprimer tout téléchargement ; et directories.output, qui est release/${version} et pas dist/.

Deux gardes du repo ont fait exactement leur travail et méritent d'être cités : before-pack a refusé un payload sans le helper STT, puis sans les .so du helper — deux trous que le build avait franchis en silence.

Ce qui reste avant une soumission :

  1. Personne n'a lancé l'app. Un build vert n'est pas une app qui marche. Il faut un bureau Linux avec un portail — ni la CI ni le linter ne peuvent répondre.
  2. La source est un type: dir le temps du spike. Flathub veut type: git tag + commit, impossible tant qu'aucune release ne contient le metainfo et le fichier desktop : v1.9.2 est antérieur aux deux.
  3. mirror-urls sur le module ffmpeg : deux échecs réseau de ffmpeg.org sur quatorze runs.
  4. Écart de 34 commits entre ffmpeg 8.1.2 upstream et le snapshot BtbN épinglé — ça compile, ce n'est pas exercé à l'exécution.
  5. Éventuellement passer au runtime 25.08, ce qui rouvre les questions de versions des Stages A et B (node22, llvm18, BaseApp Electron).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions