Skip to content

Phase D: packaging, release gates and CI for the 2.0 catch-up - #84

Merged
kalopsia-space merged 12 commits into
linux-2.0from
port/linux-2.0-packaging
Oct 17, 2026
Merged

kalopsia-space merged 12 commits into
linux-2.0from
port/linux-2.0-packaging

Conversation

@kalopsia-space

Copy link
Copy Markdown
Collaborator

Phase D of docs/linux-2.0-catch-up.md.

What changes

  • Requirements: keyboard is Windows-only (FTHR_UI never imports it; only FTHR.spec names it), jeepney is back for the portal, and keyring's Linux backend (SecretStorage 3.5.0, jeepney 0.9.0) is pinned in requirements-uploader.txt with licence texts. Every uploader pin has a manylinux_2_34-or-older wheel for CPython 3.12 and 3.14.
  • Uploader builders: build_optional_uploaders.py is the one source of plugin version, terms/privacy versions, licence inventory and the Core binding format. Each builder regenerates uploader_bundle_manifest.py and keeps the other platform's hash, so a Windows build no longer erases the Linux hash. Both builders refuse to run when requirements-uploader.txt is not satisfied. The Linux builder moves to 1.4.0/v3, embeds the licences, includes sftp_provider and the Secret Service backend, drops FTHR_Hardware_ID, and unpacks its payload ELFs for the glibc gate.
  • FTHR_linux.spec: bundles Qt Quick/QML, ui/qml (with the .qsb), gatze.png and ui/capture_card_process.py. The filter mirrors FTHR.spec: it keeps the 5 QML import directories and only the Qt libraries in the manifest, and drops qmltooling.
  • Manifests: Qt runtime manifest gains 2.0's Windows Quick modules plus QmlCore/QuickWidgets/MultimediaQuick for Linux. The release asset manifest is 2.0's plus Linux artifact paths. It keeps the AppDir icon and drops the deleted social preview, and generate_release_assets.py --check no longer expects that preview.
  • Notices: the Linux and 2.0 notices are merged, with paramiko and the uploader dependencies, Qt Quick modules and the new assets added. pyproject gets back the Linux pythonpath, marker and excludes.
  • CI: every job that freezes or tests the uploader installs requirements-uploader.txt. The AppImage glibc check covers the uploader payload. From 2.0: the bash default shell and pwsh for MSBuild. All Linux jobs, the PyInstaller exit-code check and ubuntu release verification stay.

Verification (local, Arch)

  • verify_release_licenses.py --tree . passes (177 checks); --appdir on a fresh AppDir passes (190 checks).
  • verify_version_consistency.py, verify_engine_response_contract.py, verify_exception_handling.py and scan_repo_hygiene.py pass, and so does ruff check ..
  • verify_shared_memory_contract.py fails on the Linux layout because capture_bridge still has the 2.0 wchar layout. That is Phase B work. CI never reached this step before, because the licence step failed first.
  • bash build_linux.sh gets through the engine, the uploader (71 payload ELFs unpacked), PyInstaller, the licence gate and appimagetool. It stops at the glibc 2.35 gate, as expected for an Arch build. That gate now also reports the uploader payload.
  • With only the bundled QML import tree on the path, both Snapshot.qml and VideoPreview.qml load as Ready.
  • Full pytest run: no new failures, and three gate tests that failed on linux-2.0 now pass.

…g backend

2.0 replaced the Linux-only jeepney pin with an unconditional keyboard pin.
FTHR_UI never imports keyboard; only FTHR.spec names it as a hidden import,
so it gets a win32 marker and jeepney (GlobalShortcuts portal) comes back
with its non-Windows marker. Restore keyboard's licence text, which the
merge lost because the linux branch had dropped the package.

keyring resolves its Secret Service backend through SecretStorage and
jeepney on Linux. Pin both with linux markers in requirements-uploader.txt
so the frozen uploader is reproducible, and add their licence texts to the
optional-uploader inventory. Every pin has a manylinux_2_34-or-older wheel
for CPython 3.12 and 3.14, inside the glibc 2.35 baseline.
… builders

2.0's _write_core_manifest regenerates uploader_bundle_manifest.py
wholesale, which erased EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256 on every
Windows build, while build_linux_uploader.py still sealed the 1.1.0 plugin
against the 2026-08-24 terms. build_optional_uploaders.py is now the one
source of the plugin version, legal versions, licence inventory and binding
format: render_core_manifest() regenerates the file from those constants and
carries the other platform's hash over, and the Linux builder imports it.

Both builders now refuse to freeze when requirements-uploader.txt is not
satisfied. PyInstaller otherwise builds an executable without paramiko and
the sealed package only fails when it runs, which is why "Test built
optional upload packages" fails on Windows.

The Linux builder also embeds the optional-uploader licences like the
Windows package, names sftp_provider and keyring's Secret Service backend as
hidden imports, drops the FTHR_Hardware_ID path 2.0 removed, and unpacks the
ELF members of its one-file payload so the glibc gate can inspect them.
The 2.0 editor renders the clip preview and snapshots in QQuickWidget
scenes (ui/qml/*.qml importing QtQuick and QtMultimedia, with a precompiled
color.frag.qsb), so FTHR_linux.spec stops excluding QtQuick/QtQml, collects
ui/qml, gatze.png and ui/capture_card_process.py, and names the Quick
modules as hidden imports. Every other new 2.0 module is imported
statically; the build's PYZ table confirms it is collected.

Like FTHR.spec, the Qt hooks' output is filtered: only the QtQml,
QtQml/Models, QtQml/WorkerScript, QtQuick and QtMultimedia import
directories, only Qt libraries listed in qt_runtime_manifest.json, and no
qmltooling plugins (QML debugger; one links the GPL-only Quick 3D). The
filter is anchored to the wheel's qml/ destination so it cannot drop the
application's own ui/qml files.

The manifest gains 2.0's Windows Quick modules and licence sources, and
QmlCore, QuickWidgets and MultimediaQuick for Linux. A local build ships 25
Qt modules, every libQt6 NEEDED entry resolves inside the bundle, and both
scenes load with only the bundled QML import tree on the import path.
The licence gate failed on linux-2.0 because the kept linux asset manifest
did not know 2.0's new assets (Oswald Regular/SemiBold, gatze.png, 24 SVG
icons, the Ko-fi symbol, the polished installer artwork) and still listed
.github/social_preview.png, which upstream deleted.

Take 2.0's manifest, keep the linux entries for shared files and the AppDir
icon, and give every Windows-distributed new asset the same
_internal/assets path in the AppImage, where FTHR_linux.spec puts it.
generate_release_assets.py stops expecting the deleted social preview so
its --check passes again.
The merge took 2.0's THIRD_PARTY_NOTICES.md, which removed every Linux
entry (Linux FFmpeg, jeepney, Wayland protocols, PipeWire/D-Bus headers,
the AppImage runtime) although the AppImage still ships or uses them.
Start from the linux notices and add what 2.0 brought: the Windows-only
keyboard package, the Qt Quick/QML modules the editor bundles, the optional
uploader's dependencies (paramiko LGPL-2.1+, the Linux SecretStorage and
jeepney backend), and the new Oswald, SVG, user-provided and Ko-fi assets.
The merge took 2.0's pyproject.toml, which drops pythonpath, the
linux_only marker (--strict-markers turns its uses into errors) and the
FTHRcapture_linux/AppDir ruff excludes. Keep those and 2.0's new backup
directory excludes.
"Test built optional upload packages" failed on Windows with "No module
named 'paramiko'": no job installed requirements-uploader.txt, so the
sealed executable was frozen without its SFTP stack. The Windows engine,
AppImage and Python jobs now install it (tests/test_sftp_provider.py
imports paramiko too).

The AppImage job and build_linux.sh pass the unpacked uploader payload to
the glibc gate; the one-file executable inside the .fthrplugin was
invisible to it.

From 2.0: bash as the default shell (the MSBuild step moves to pwsh
explicitly) and the no-secrets comment. Kept from linux: every Linux job,
the PyInstaller exit-code check, ubuntu release verification and the
narrow "test_built_" run on Windows, since the python matrix already runs
the rest of the plugin boundary tests. The CI contract tests get back the
Linux engine job check and a check for the uploader lock installs.
FTHR.spec's QML filter matched any destination containing '/qml/'. The
application's own scenes are bundled at ui/qml/, so VideoPreview.qml,
Snapshot.qml and color.frag.qsb failed the "allowed QML module" test and
were dropped: the packaged editor loses the GPU preview (it silently falls
back to the CPU path) and snapshot scenes fail to load.

Anchor the filter to the PySide6 wheel's QML tree as PyInstaller's Qt hooks
lay it out (pyside6_library_info.qt_rel_dir: 'PySide6' on Windows, so
'PySide6/qml/'), as FTHR_linux.spec already does.

The licence gate now also requires the three files in --windows-dist,
--appdir and --all artifacts, byte-identical to FTHR_UI/ui/qml, so a filter
regression fails the release instead of shipping a degraded editor.
The Linux pytest row ignored test_linux_appimage_report.py,
test_linux_runtime.py, test_linux_tools.py and
test_capture_backend_detection.py, and the Windows row ignores them too, so
they ran nowhere. The commit that added the ignores gives no reason, and all
four pass headless: the report test only runs the shell script against a
fake image, and the backend test uses the engine "Prepare Linux test
artifacts" already builds with the pinned FFmpeg on LD_LIBRARY_PATH (the one
case that needs a real Wayland session skips itself). A CI contract test
keeps them in the Linux row.
The glibc gate saw the ELF members unpacked from the uploader's one-file
payload but not the executable that carries them: its PyInstaller bootloader
is what the host loader runs first. Pass it as a target in build_linux.sh
and in the AppImage CI job (it needs GLIBC_2.14 locally).
SecretStorage and jeepney are bundled into the Linux uploader so keyring's
Secret Service backend is available, but sftp_provider still saves
passwords only through Windows Credential Manager; the Linux wiring is the
next UI change. Say so in the notices, docs/linux.md and the uploader lock.
@kalopsia-space
kalopsia-space force-pushed the port/linux-2.0-packaging branch from a2be00b to 03fdc3c Compare October 17, 2026 14:03
test_frozen_extension_connects_to_real_sftp activates the sealed Windows
uploader package, which only the Windows engine job builds. In the python
matrix it failed with "bundle is missing" as soon as paramiko was installed
and the module could be collected. Skip it when the package is absent and
run it in "Test built optional upload packages" after the build.
@kalopsia-space
kalopsia-space merged commit 8c1903f into linux-2.0 Oct 17, 2026
5 of 9 checks passed
@kalopsia-space
kalopsia-space deleted the port/linux-2.0-packaging branch October 17, 2026 14:18
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.

1 participant