-
Notifications
You must be signed in to change notification settings - Fork 85
ci(flatpak): spike how far Flathub actually is #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| name: Flatpak spike | ||
|
|
||
| # THROWAWAY. This workflow exists to answer one question — how far from a Flathub | ||
| # submission is this repository — and should be deleted once it has answered it. | ||
| # It is `workflow_dispatch` only and runs on no event, so it costs nothing until | ||
| # someone asks. | ||
| # | ||
| # Every stage is `continue-on-error` on purpose. A spike that stops at the first | ||
| # failure tells you one thing; this one tells you all of them in a single run, and | ||
| # writes the verdict to the run summary. Read the summary, not the logs. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| ref: | ||
| description: "Ref to spike (defaults to the branch this is dispatched on)" | ||
| required: false | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| spike: | ||
| name: How far is Flathub | ||
| runs-on: ubuntu-latest | ||
| # The full attempt downloads a runtime, an SDK, two SDK extensions, a base app, | ||
| # every npm tarball in the lockfile and every crate in two Cargo.locks. If it | ||
| # has not concluded in an hour it has concluded something anyway. | ||
| timeout-minutes: 60 | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
| with: | ||
| ref: ${{ inputs.ref || github.ref }} | ||
| persist-credentials: false | ||
|
|
||
| - name: Install flatpak tooling | ||
| run: | | ||
| set -euo pipefail | ||
| sudo apt-get update -qq | ||
| sudo apt-get install -y -qq flatpak flatpak-builder python3-venv | ||
| flatpak remote-add --if-not-exists --user \ | ||
| flathub https://dl.flathub.org/repo/flathub.flatpakrepo | ||
| flatpak --version | ||
| flatpak-builder --version | ||
|
|
||
| # --------------------------------------------------------------------- | ||
| # Stage A is the cheapest and the most likely to invalidate the manifest: | ||
| # it asks Flathub what actually exists before anything tries to install it. | ||
| # The manifest guesses runtime 24.08, node22 and an Electron2 BaseApp at the | ||
| # same version. Any of the three can simply not be published. | ||
| - name: "Stage A — what versions actually exist" | ||
| id: versions | ||
| continue-on-error: true | ||
| run: | | ||
| set -euo pipefail | ||
| echo "## Stage A — availability" >> "$GITHUB_STEP_SUMMARY" | ||
| # Query ONCE and keep the exit status. Per-id queries behind `|| true` | ||
| # turn a broken remote into six "NOT PUBLISHED" lines, which is the one | ||
| # wrong answer this stage must never give: it would read as "Flathub does | ||
| # not ship the runtime" when it means "the question never got asked". | ||
| if ! CATALOG=$(flatpak remote-ls flathub --user --columns=application,branch 2>&1); then | ||
| { | ||
| echo | ||
| echo "**QUERY FAILED** — \`flatpak remote-ls\` errored, so nothing below is a" | ||
| echo "statement about what Flathub publishes. Stage A draws no conclusion." | ||
| echo | ||
| echo '```' | ||
| echo "$CATALOG" | tail -5 | ||
| echo '```' | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
| exit 1 | ||
| fi | ||
| for id in \ | ||
| org.freedesktop.Platform \ | ||
| org.freedesktop.Sdk \ | ||
| org.electronjs.Electron2.BaseApp \ | ||
| org.freedesktop.Sdk.Extension.node22 \ | ||
| org.freedesktop.Sdk.Extension.node20 \ | ||
| org.freedesktop.Sdk.Extension.rust-stable | ||
| do | ||
| FOUND=$(printf '%s\n' "$CATALOG" | awk -v id="$id" '$1 == id { printf "%s ", $2 }') | ||
| if [[ -n "$FOUND" ]]; then | ||
| echo "- \`$id\` → branches: $FOUND" >> "$GITHUB_STEP_SUMMARY" | ||
| else | ||
| echo "- \`$id\` → **NOT PUBLISHED**" >> "$GITHUB_STEP_SUMMARY" | ||
| fi | ||
| done | ||
|
|
||
| - name: "Stage B — install runtime, SDK, base and extensions" | ||
| id: install | ||
| continue-on-error: true | ||
| run: | | ||
| set -euo pipefail | ||
| flatpak install -y --user --noninteractive flathub \ | ||
| org.freedesktop.Platform/x86_64/24.08 \ | ||
| org.freedesktop.Sdk/x86_64/24.08 \ | ||
| org.electronjs.Electron2.BaseApp/x86_64/24.08 \ | ||
| org.freedesktop.Sdk.Extension.node22/x86_64/24.08 \ | ||
| org.freedesktop.Sdk.Extension.rust-stable/x86_64/24.08 | ||
|
|
||
| # --------------------------------------------------------------------- | ||
| # Stage C is the single biggest unknown for any Electron app on Flathub: | ||
| # the sandbox has no network, so every npm tarball must be declared as a | ||
| # pinned source. flatpak-node-generator derives that from package-lock.json. | ||
| # If it cannot handle this lockfile, nothing downstream matters. | ||
| - name: "Stage C — offline npm sources from the lockfile" | ||
| id: nodegen | ||
| continue-on-error: true | ||
| run: | | ||
| set -euo pipefail | ||
| # `node/` is a poetry project exposing the `flatpak-node-generator` console | ||
| # script — not a loose .py to invoke by path. Pinned: this tool decides | ||
| # whether the whole port is viable, so it should not change under us | ||
| # between two runs of the same spike. | ||
| git clone --filter=blob:none https://github.com/flatpak/flatpak-builder-tools.git /tmp/fbt | ||
| git -C /tmp/fbt checkout 737c0085912f9f7dabf9341d4608e2a77a51a73a | ||
| python3 -m venv /tmp/fbt-venv | ||
| /tmp/fbt-venv/bin/pip install --quiet /tmp/fbt/node | ||
| /tmp/fbt-venv/bin/flatpak-node-generator npm package-lock.json \ | ||
| -o build/flatpak/generated-sources.json | ||
| BYTES=$(stat -c%s build/flatpak/generated-sources.json) | ||
| COUNT=$(python3 -c "import json;print(len(json.load(open('build/flatpak/generated-sources.json'))))") | ||
| echo "bytes=$BYTES" >> "$GITHUB_OUTPUT" | ||
| echo "count=$COUNT" >> "$GITHUB_OUTPUT" | ||
| echo "generated $COUNT sources, $BYTES bytes" | ||
|
|
||
| # --------------------------------------------------------------------- | ||
| # Stage D: the same problem for Rust. Two lockfiles, and the compositor tree | ||
| # pulls bindgen, which wants libclang at build time as well as crates. | ||
| - name: "Stage D — vendor both Cargo lockfiles" | ||
| id: vendor | ||
| continue-on-error: true | ||
| run: | | ||
| set -euo pipefail | ||
| for dir in crates electron/native/pipewire-capture; do | ||
| echo "--- $dir ---" | ||
| ( cd "$dir" && cargo vendor --versioned-dirs /tmp/vendor-$(basename "$dir") \ | ||
| > /tmp/vendor-$(basename "$dir").toml ) | ||
| echo "$dir: $(find /tmp/vendor-$(basename "$dir") -maxdepth 1 -type d | wc -l) crates" | ||
| done | ||
|
|
||
| # --------------------------------------------------------------------- | ||
| # Stage E: the manifest ships a deliberately wrong ffmpeg sha256 so that a | ||
| # guessed digest can never reach a submission. Resolve the real one here. | ||
| - name: "Stage E — resolve the ffmpeg source digest" | ||
| id: ffmpeg | ||
| continue-on-error: true | ||
| run: | | ||
| set -euo pipefail | ||
| URL=https://ffmpeg.org/releases/ffmpeg-8.1.2.tar.xz | ||
| if curl -fsSL --retry 3 -o /tmp/ffmpeg.tar.xz "$URL"; then | ||
| SHA=$(sha256sum /tmp/ffmpeg.tar.xz | awk '{print $1}') | ||
| echo "sha=$SHA" >> "$GITHUB_OUTPUT" | ||
| echo "$URL → $SHA" | ||
| else | ||
| echo "::warning::$URL does not exist — the pinned build is BtbN's n8.1.2-34-g9b6c8969e0, which is a *snapshot*, not an upstream release. The manifest needs a real upstream version, and whichever one is chosen is not the tree the addon was tested against." | ||
| exit 1 | ||
| fi | ||
|
|
||
| # --------------------------------------------------------------------- | ||
| # Stage F: the actual build. Expected to fail — the interesting output is | ||
| # WHERE. Run it even when earlier stages failed, so the log exists. | ||
| - name: "Stage F — flatpak-builder" | ||
| id: build | ||
| continue-on-error: true | ||
| run: | | ||
| set -euo pipefail | ||
| flatpak-builder --user --install-deps-from=flathub --force-clean \ | ||
| --disable-rofiles-fuse \ | ||
| /tmp/flatpak-build build/flatpak/com.getopenscreen.OpenScreen.yml \ | ||
| 2>&1 | tee /tmp/flatpak-build.log | ||
|
|
||
| - name: Collect artifacts | ||
| if: always() | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: flatpak-spike | ||
| path: | | ||
| /tmp/flatpak-build.log | ||
| build/flatpak/generated-sources.json | ||
| if-no-files-found: warn | ||
| retention-days: 7 | ||
|
|
||
| - name: Verdict | ||
| if: always() | ||
| env: | ||
| A: ${{ steps.versions.outcome }} | ||
| B: ${{ steps.install.outcome }} | ||
| C: ${{ steps.nodegen.outcome }} | ||
| D: ${{ steps.vendor.outcome }} | ||
| E: ${{ steps.ffmpeg.outcome }} | ||
| F: ${{ steps.build.outcome }} | ||
| NPM_COUNT: ${{ steps.nodegen.outputs.count }} | ||
| FFMPEG_SHA: ${{ steps.ffmpeg.outputs.sha }} | ||
| run: | | ||
| { | ||
| echo "## Verdict" | ||
| echo | ||
| echo "| Stage | Outcome |" | ||
| echo "|---|---|" | ||
| echo "| A — versions exist | $A |" | ||
| echo "| B — runtime/SDK/base install | $B |" | ||
| echo "| C — offline npm sources (${NPM_COUNT:-n/a} entries) | $C |" | ||
| echo "| D — cargo vendor, both lockfiles | $D |" | ||
| echo "| E — ffmpeg source digest (${FFMPEG_SHA:-unresolved}) | $E |" | ||
| echo "| F — flatpak-builder | $F |" | ||
| echo | ||
| echo "Stage F failing is the expected result, not the finding. The finding is" | ||
| echo "which of A–E failed, because those are the ones that decide whether this" | ||
| echo "port is a week or a quarter. Read the tail of the build log in the" | ||
| echo "\`flatpak-spike\` artifact before drawing any conclusion from F." | ||
| } >> "$GITHUB_STEP_SUMMARY" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| # Flatpak manifest — SPIKE, not a submission. | ||
| # | ||
| # The point of this file is to be built by .github/workflows/flatpak-spike.yml and | ||
| # FAIL INFORMATIVELY. Flathub forbids the shortcut every other channel in this repo | ||
| # takes: "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." OpenScreen is MIT, | ||
| # so it is source-available, so no repackaged .deb and no extra-data escape hatch. | ||
| # | ||
| # Every value marked SPIKE below is a guess the CI run is supposed to confirm or | ||
| # refute. Do not treat this as a reviewed manifest. | ||
| app-id: com.getopenscreen.OpenScreen | ||
| runtime: org.freedesktop.Platform | ||
| # SPIKE: 24.08 is the runtime the Electron2 BaseApp is known to publish against. | ||
| # If the spike reports the base is missing for this version, bump both together — | ||
| # they must match or flatpak-builder refuses the base outright. | ||
| runtime-version: '24.08' | ||
| sdk: org.freedesktop.Sdk | ||
| base: org.electronjs.Electron2.BaseApp | ||
| base-version: '24.08' | ||
| sdk-extensions: | ||
| # SPIKE: package.json pins node 22.22.1. The freedesktop SDK ships node as a | ||
| # versioned extension and node22 may simply not exist for 24.08 — the spike | ||
| # enumerates what is actually available before this build is attempted. | ||
| - org.freedesktop.Sdk.Extension.node22 | ||
| # The two Rust crates (crates/, electron/native/pipewire-capture/) need cargo. | ||
| - org.freedesktop.Sdk.Extension.rust-stable | ||
| command: openscreen | ||
|
|
||
| finish-args: | ||
| - --share=ipc | ||
| - --socket=wayland | ||
| - --socket=fallback-x11 | ||
| - --socket=pulseaudio | ||
| # Screen capture goes through the portal, not through a raw socket. The helper in | ||
| # electron/native/pipewire-capture already speaks org.freedesktop.portal.ScreenCast, | ||
| # which is the sandboxed path, so this part of the port is unusually well placed. | ||
| - --talk-name=org.freedesktop.portal.ScreenCast | ||
| - --talk-name=org.freedesktop.portal.Desktop | ||
| # Webcam. | ||
| - --device=all | ||
| # Vulkan: the compositor addon is a Vulkan renderer. | ||
| - --device=dri | ||
| - --share=network | ||
| - --filesystem=xdg-videos | ||
| - --filesystem=xdg-documents | ||
|
|
||
| build-options: | ||
| append-path: /usr/lib/sdk/node22/bin:/usr/lib/sdk/rust-stable/bin | ||
| env: | ||
| # Keep npm and cargo inside the build dir; the sandbox has no HOME to speak of. | ||
| npm_config_cache: /run/build/openscreen/npm-cache | ||
| CARGO_HOME: /run/build/openscreen/cargo | ||
|
|
||
| modules: | ||
| # --------------------------------------------------------------------------- | ||
| # 1. ffmpeg FROM SOURCE. | ||
| # | ||
| # This is the module that decides whether the port is a week or a quarter, and it | ||
| # is the reason the spike exists. scripts/fetch-ffmpeg.mjs pins a BtbN *prebuilt* | ||
| # tree (ffmpeg-n8.1.2-…-linux64-lgpl-shared-8.1.tar.xz, sha256 c882a80f…) and | ||
| # Flathub will reject it: prebuilt is prebuilt whether or not it is sha-pinned. | ||
| # | ||
| # Worse, the prebuilt tree is not merely convenient. crates/compositor-view-napi | ||
| # RENAMES every ffmpeg dynamic symbol in the libraries it ships, because Electron | ||
| # links Chromium's own stripped libffmpeg.so into the same address space and the | ||
| # addon would otherwise bind to that. See scripts/build-linux-compositor-addon.mjs | ||
| # — it runs `nm -D --defined-only` over the vendored .so files and stages renamed | ||
| # copies. That mechanism needs headers AND libraries it owns, which rules out the | ||
| # org.freedesktop.Platform.ffmpeg-full extension (libraries, no dev headers). | ||
| # | ||
| # So: build ffmpeg from the upstream release tarball into /app, with the same LGPL | ||
| # shape as the pinned build, and let the existing script rename copies of it. | ||
| # SPIKE: the configure flags below are a first cut at matching what the addon and | ||
| # the pipewire helper actually link against. Expect this list to be wrong. | ||
| - name: ffmpeg | ||
| config-opts: | ||
| - --disable-static | ||
| - --enable-shared | ||
| - --disable-programs | ||
| - --disable-doc | ||
| - --enable-gpl | ||
| - --enable-version3 | ||
| - --enable-libvpx | ||
| - --enable-libopus | ||
| sources: | ||
| - type: archive | ||
| url: https://ffmpeg.org/releases/ffmpeg-8.1.2.tar.xz | ||
| # SPIKE: placeholder. The workflow resolves and prints the real digest; do | ||
| # not commit a guessed sha256, the build must fail loudly instead. | ||
| sha256: 0000000000000000000000000000000000000000000000000000000000000000 | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # 2. The application. | ||
| # | ||
| # generated-sources.json is NOT committed: flatpak-node-generator produces it from | ||
| # package-lock.json and it is large and entirely derived. The spike workflow writes | ||
| # it next to this file before building. If it is absent, that is the first thing to | ||
| # check — not a manifest bug. | ||
| - name: openscreen | ||
| buildsystem: simple | ||
| build-options: | ||
| env: | ||
| # Point the native build at the ffmpeg this manifest just built rather than | ||
| # at crates/thirdparty/, which nothing provisions inside the sandbox. | ||
| FFMPEG_DIR: /app | ||
| build-commands: | ||
| # SPIKE: build:linux is `fetch:ffmpeg:sdk && build:native:linux && | ||
| # build:native:compositor:linux && tsc && vite build && electron-builder …`. | ||
| # The fetch step is a network download and cannot run here, which is why the | ||
| # steps are spelled out instead of calling the npm script. | ||
| - npm ci --offline | ||
| - npm run build:native:linux | ||
| - npm run build:native:compositor:linux | ||
|
EtienneLescot marked this conversation as resolved.
|
||
| - npm run build-vite | ||
| # SPIKE: electron-builder's own linux targets are wrong here — Flathub packages | ||
| # the app tree directly. `--dir` is the closest thing; whether it cooperates | ||
| # with the BaseApp's electron is unknown and is a question for the run. | ||
| - npx electron-builder --linux dir --config.npmRebuild=false | ||
| - cp -r dist/linux-unpacked /app/openscreen | ||
|
EtienneLescot marked this conversation as resolved.
|
||
| sources: | ||
| - type: git | ||
| url: https://github.com/getopenscreen/openscreen.git | ||
| tag: v1.9.2 | ||
| - generated-sources.json | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: getopenscreen/openscreen
Length of output: 207
🏁 Script executed:
Repository: getopenscreen/openscreen
Length of output: 8156
🌐 Web query:
Flatpak command reference remote-ls --columns application branch arch output💡 Result:
The --columns option for the flatpak remote-ls command allows you to specify which information fields are displayed for each reference found in a remote repository [1][2]. Usage: --columns=FIELD,... You can provide a comma-separated list of fields or use the --columns option multiple times to add specific fields to the output [2]. Available fields include: name: Shows the application or runtime name [2]. description: Shows the description of the application [2]. application: Shows the application or runtime ID [2]. arch: Shows the architecture [2]. branch: Shows the branch [2]. version: Shows the version [2]. ref: Shows the full reference string [2]. origin: Shows the origin remote [2]. commit: Shows the active commit [2]. runtime: Shows the runtime used by the application [2]. installed-size: Shows the installed size [2]. download-size: Shows the download size [2]. options: Shows options associated with the ref [2]. all: Displays all available columns [1][2]. help: Lists all valid field names [2]. Additional notes: - You can abbreviate field names to their unique prefix (e.g., --columns=app for application) [1][2]. - You can change the ellipsization behavior for a column by appending:s (start),:m (middle),:e (end), or:f (full) to the field name (e.g., --columns=application:f) [2]. - For example, running flatpak remote-ls --columns=application,branch,arch will output only the application ID, branch, and architecture for the refs found [2].
Citations:
Restrict Stage A to the Stage B ref.
Stage B installs
x86_64/24.08, but Stage A accepts any branch and architecture. Queryapplication,branch,archand require$2 == "24.08"and$3 == "x86_64"before marking the application as available.🤖 Prompt for AI Agents