chore(flatpak): pin every build source to an immutable reference (#443) - #655
Conversation
The audit first: of the 251 remote sources the committed manifest fetches, 250 were already pinned by URL + sha256 (the native chain, every pub.dev archive, every Flutter engine artifact, both plugin downloads) and one was not. The template's Flutter source carried only `tag: '3.44.7'`, and a tag can be repointed upstream. So the pin itself is one line. What was actually missing is everything that keeps the pins from drifting apart later, because nothing held: * the hand-authored template to any rule at all. Every existing check reads the *generated* manifest, so a floating source added where a human edits would be caught one regeneration later, if ever; * the template and the generated manifest to each other. Bumping mpv in the template (or hand-editing the generated manifest) builds something other than what the source of truth says; * `.flutter-version` to the Flatpak's Flutter. An SDK bump could leave the package building the old toolchain, silently, since the generated module's tag, commit and filename were tied to nothing; * the generated per-plugin native pins to the lockfile. flatpak-flutter picks its fixes by "highest entry not newer than the locked version", so a `sqlite3_flutter_libs` bump reuses the previous version's patch and pre-fetched SQLite amalgamation instead of failing. What this adds * `flatpak/flatpak-flutter.yml`: the Flutter source now carries `tag` and `commit`. The tag is what the generator reads, the commit is what makes it a pin. Verified against `git ls-remote --tags`, and it is the same commit the generated SDK module already recorded, so the generated manifest is byte-identical (nothing was regenerated in this PR). * `scripts/check_flatpak_sources.py`: reads both manifests, every generated module and source list they include, `pubspec.lock` and `.flutter-version`, and fails on a source with no sha256, a git source without a full commit or tracking a branch, a URL that resolves at download time (`HEAD`, `refs/heads/`, `latest`, `?branch=`, a default-branch snapshot), plain HTTP, a type that carries no content pin (`svn`, `bzr`, `extra-data`), a local path that is absolute or escapes the checkout, the two manifests disagreeing, a Flutter pin that only moved in one place, engine artifacts from two engines, a Dart package that drifted from the lockfile, a plugin patch applied to a version it was not written for, a `FetchContent` URL with no `URL_HASH`, and a `TOOL_COMMIT` that is not a full commit. It fails on a source type it does not recognise rather than skipping it. * `test/tooling/check_flatpak_sources_test.py`: 48 tests. A miniature checkout that passes, then one broken copy per rule, plus the real repository as the last case. * `docs/flatpak-source-pinning.md`: which four files are the sources of truth, what pins what, how to refresh a pin (and what to read in the diff), and the four inputs that genuinely cannot be content-addressed, with the reason for each: the runtime and SDK OSTree refs (freezing them would keep us on an unpatched platform, and Flathub updates them in place), the SDK extension, and the app's own checkout, which git already names. That table is compared against the manifest in both directions, so an unpinnable input needs a written reason and a reason cannot outlive its input. * `scripts/regenerate_flatpak_sources.sh` now checks its own output before it exits, refuses a flatpak-flutter whose `requirements.txt` stopped pinning every dependency, and rebuilds its venv when `TOOL_COMMIT` moves. * CI runs the check and its tests on every PR (offline, no Flutter or Flatpak), and `scripts/verify_linux.sh` is the local twin. Not in scope, deliberately: no dependency version changed, no auto-merge, no sandbox permission touched, and Android/F-Droid tooling is untouched. The existing `flatpak_offline_build_test.dart` still owns the other half (that a declared source lands where the offline build reads it); the two overlap only on "a remote source carries its own digest", so this one can run without a toolchain at regeneration time. Closes #443
The row explaining why the runtime cannot be content-addressed quoted `runtime-version: '50'`, which turns into a stale number the moment the runtime is bumped. The rule is what matters (an exact released version, never a channel), and scripts/check_flatpak_sources.py is what enforces it, so the literal adds nothing but a thing to forget.
|
"Build and launch Flatpak" is failing here, and it is not this PR. Flagging it rather than fixing it, because the fix changes what ships. The job dies in the pre-build Flathub lint step: Why it is not this PR's:
No fix exists to port, and neither candidate belongs in this PR:
I have not re-run the job. The failure is a linter reading a committed file, so it is deterministic rather than flaky, and the base-branch run above is stronger evidence than a re-run would be. One small thing this PR does make easier: Everything else on the head commit is green or still running; the rest of this PR's own checks, including the new source-pin check and its 48 tests, pass. Generated by Claude Code |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Repository integrity reviewCLEAN Previously reported repository-integrity findings are resolved. |
The GNOME 51 migration (#660) moved the compiler extension from llvm20 to llvm22 in both manifests. The unpinnable-inputs table still named llvm20, which scripts/check_flatpak_sources.py reports from both sides: an input with no written reason, and a reason for an input that is gone. That is the check doing its job on its first real bump, so the fix is the row, not the rule.
|
The GNOME 51 blocker is gone: #660 landed the runtime migration on The merge did surface one thing, which is the new check earning its keep on its first real bump. #660 moved the compiler extension from Fixed in 3601a17 by following the row to Re-validated on the merged head: the pin check passes over all 251 remote sources, its 48 tests pass, and Generated by Claude Code |
Closes #443
The audit first
Of the 251 remote sources the committed manifest fetches, 250 were already pinned by URL + sha256 and one was not.
tag+committag: '3.44.7'only, and a tag can be repointed upstreamtag+commit, held to the generated module and to.flutter-versionpubspec.lockstorage.googleapis.comURL + sha256URL_HASHin the committed patchflatpak-flutter(the generator)TOOL_COMMIT, a full commitdirsourceSo the pin itself is one line. What was missing is everything that keeps the pins from drifting apart afterwards, because nothing held:
.flutter-versionto the Flatpak's Flutter. An SDK bump could leave the package building the old toolchain, silently, since the generated module's tag, commit and filename were tied to nothing;sqlite3_flutter_libsbump quietly reuses the previous version's patch and pre-fetched amalgamation instead of failing.What this adds
flatpak/flatpak-flutter.yml: the Flutter source carriestagandcommit. The tag is what the generator reads; the commit is what makes it a pin. Verified withgit ls-remote --tags, and it is the same commit the generated SDK module already recorded, so the generated manifest is byte-identical. Nothing was regenerated in this PR.scripts/check_flatpak_sources.py: reads both manifests, every generated module and source list they include,pubspec.lockand.flutter-version, and fails on a source with no sha256 (or only MD5/SHA-1), agitsource without a full commit or tracking a branch, a URL that resolves at download time (HEAD,refs/heads/,latest,?branch=, a default-branch snapshot), plain HTTP, a type with no content pin (svn,bzr,extra-data), a local path that is absolute or escapes the checkout, the two manifests disagreeing, a Flutter pin that only moved in one place, engine artifacts from two engines, a Dart package that drifted from the lockfile, a plugin patch applied to a version it was not written for, aFetchContentURL with noURL_HASH, and aTOOL_COMMITthat is not a full commit. A source type it does not recognise is a failure, not a skip.test/tooling/check_flatpak_sources_test.py: 48 tests. A miniature checkout that passes, one broken copy per rule, and the real repository as the last case.docs/flatpak-source-pinning.md: which four files are the sources of truth, what pins what, how to refresh a pin and what to read in the resulting diff, and the four inputs that genuinely cannot be immutable with the reason for each (the runtime and SDK OSTree refs, where freezing a commit would keep Linthra on an unpatched platform and is not how Flathub builds; the SDK extension, which has no per-commit form; and the app's own checkout, which git already names). That table is compared against the manifest both ways, so an unpinnable input needs a written reason and a reason cannot outlive its input.scripts/regenerate_flatpak_sources.shnow checks its own output before exiting, refuses a flatpak-flutter whoserequirements.txtstopped pinning every dependency, and rebuilds its venv whenTOOL_COMMITmoves.scripts/verify_linux.shis the local twin.Deliberately not in scope
No dependency version changed, no auto-merge, no sandbox permission touched, and Android/F-Droid tooling is untouched. Refreshing sources stays a normal reviewable diff.
test/tooling/flatpak_offline_build_test.dart(#442) still owns the other half, that a declared source lands where the offline build reads it. The two overlap only on "a remote source carries its own digest", which is what lets this one run with no toolchain at regeneration time.One open question for review
An automated
deps/dart-packagesPR bumpspubspec.lockonly, andcheck_dependency_update_files.shkeeps the derived Flatpak pins off that branch, so the regeneration has to be a follow-up PR. That tension predates this PR (flatpak_offline_build_test.dartalready compares the lockfile to the generated sources), so this documents the flow indocs/dependency-updates.mdrather than widening the guard. Happy to addflatpak/generated/sources/pubspec.jsonto thedart-packagesallowlist instead if riding along on the bot branch is preferred.Verified locally
Plus the neighbouring tooling tests (
check_flatpak_permissions_test,check_linux_runner_test,flathub_builder_lint_test,flatpak_bundle_test,make_flatpak_smoke_manifest_test,flatpak_smoke_cleanup_test). The Dart tests need a Flutter SDK, which this environment has no toolchain for, so CI runs those.