Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 213 additions & 0 deletions .github/workflows/flatpak-spike.yml
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

Copy link
Copy Markdown
Contributor

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:

#!/usr/bin/env bash
set -euo pipefail

catalog="$(flatpak remote-ls flathub --user \
  --columns=application,branch,arch)"

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
  awk -v id="$id" \
    '$1 == id && $2 == "24.08" && $3 == "x86_64" { found = 1 }
     END { exit !found }' <<<"$catalog"
done

Repository: getopenscreen/openscreen

Length of output: 207


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file=".github/workflows/flatpak-spike.yml"
printf '%s\n' '--- relevant workflow lines ---'
sed -n '1,120p' "$file"

printf '%s\n' '--- related references ---'
rg -n -C 3 'remote-ls|flatpak install|24\.08|x86_64|branch|arch|FOUND|CATALOG' "$file"

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. Query application,branch,arch and require $2 == "24.08" and $3 == "x86_64" before marking the application as available.

🤖 Prompt for 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.

In @.github/workflows/flatpak-spike.yml at line 62, Update the Stage A catalog
query and filtering around CATALOG to include application, branch, and
architecture columns, and only mark entries available when the branch is 24.08
and architecture is x86_64. Preserve the existing application availability
handling for matching entries.

{
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"
125 changes: 125 additions & 0 deletions build/flatpak/com.getopenscreen.OpenScreen.yml
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
Comment thread
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
Comment thread
EtienneLescot marked this conversation as resolved.
sources:
- type: git
url: https://github.com/getopenscreen/openscreen.git
tag: v1.9.2
- generated-sources.json
Loading