ci(flatpak): restore previous builder cache and skip src-only PRs (#825)

The builder action restore-keys is `flatpak-builder-${arch}`. Our key
was `flatpak-builder-${hash}`, which the action stores as
`flatpak-builder-${hash}-x86_64`. That does not start with
`flatpak-builder-x86_64`, so a packaging hash change (a lock refresh,
or merging one) discarded the whole .flatpak-builder cache and
recompiled numpy and pywhispercpp from source, ~18 min instead of ~4.

Put ${{ matrix.arch }} in the key first so the fallback matches. Then
flatpak-builder skips unchanged modules and only rebuilds what the
hash change actually touched.

PRs no longer watch src/**. python-test already covers those, and the
expensive compile does not. push to main still does, so a sandbox
install break fails the default branch.
This commit is contained in:
Jatin K Malik
2026-09-12 13:42:42 -07:00
committed by GitHub
parent d5656b8060
commit 79df154292
4 changed files with 55 additions and 6 deletions
+9 -4
View File
@@ -16,7 +16,10 @@ on:
branches: [ main, master ]
paths:
- 'packaging/flatpak/**'
- 'src/**'
# src/** is push-only. A text-injection PR already runs python-test;
# the Flatpak job's cost is compiling numpy and pywhispercpp from
# source, which those PRs do not change. main still builds src-only
# pushes so a sandbox install break fails the default branch.
- 'pyproject.toml'
# The dependency manifest is generated from the hash-pinned export, so a
# lock refresh moves what this build ships. Without this, `just lock`
@@ -67,10 +70,12 @@ jobs:
with:
bundle: vocalinux-${{ matrix.arch }}.flatpak
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml
# Stable key so .flatpak-builder cache restores across commits.
# The action appends -${arch} and restore-keys is
# `flatpak-builder-${arch}`, so the key has to start with that
# prefix. Putting the packaging hash first made every lock
# refresh a full rebuild (numpy + pywhispercpp from source).
# github.sha in the key forced a full rebuild every run.
# Arch is appended automatically by the action.
cache-key: flatpak-builder-${{ hashFiles('packaging/flatpak/**') }}
cache-key: flatpak-builder-${{ matrix.arch }}-${{ hashFiles('packaging/flatpak/**') }}
arch: ${{ matrix.arch }}
artifact-name: vocalinux-flatpak-${{ matrix.arch }}
# Flathub mirrors screenshots at submission time; mirror locally so the
+4 -2
View File
@@ -275,7 +275,9 @@ jobs:
with:
bundle: Vocalinux-${{ steps.get_version.outputs.VERSION }}-x86_64.flatpak
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml
cache-key: flatpak-builder-${{ hashFiles('packaging/flatpak/**') }}
# Same prefix rule as flatpak.yml: restore-keys is
# `flatpak-builder-${arch}`. The action appends -${arch} again.
cache-key: flatpak-builder-x86_64-${{ hashFiles('packaging/flatpak/**') }}
arch: x86_64
# Pinned actions/upload-artifact below, same digest as AppImage.
upload-artifact: false
@@ -310,7 +312,7 @@ jobs:
with:
bundle: Vocalinux-${{ steps.get_version.outputs.VERSION }}-aarch64.flatpak
manifest-path: packaging/flatpak/com.vocalinux.Vocalinux.yml
cache-key: flatpak-builder-${{ hashFiles('packaging/flatpak/**') }}
cache-key: flatpak-builder-aarch64-${{ hashFiles('packaging/flatpak/**') }}
arch: aarch64
upload-artifact: false
mirror-screenshots-url: https://dl.flathub.org/media
+35
View File
@@ -242,6 +242,17 @@ def test_no_source_is_pinned_to_one_python_abi(source: dict[str, str]) -> None:
)
def _on_event_block(event: str) -> str:
"""The body of one `on:` event, up to the next same-indent key."""
match = re.search(
rf"^ {re.escape(event)}:\n(.*?)(?=^ [a-z_]+:|\Z)",
WORKFLOW.read_text(encoding="utf-8"),
re.M | re.S,
)
assert match, f"flatpak.yml has no on.{event} block"
return match.group(1)
def test_a_lock_refresh_reaches_the_flatpak_build() -> None:
"""The manifest is generated from requirements/, so a change there changes
what the Flatpak ships. Without the path filter, `just lock` could move
@@ -251,3 +262,27 @@ def test_a_lock_refresh_reaches_the_flatpak_build() -> None:
"flatpak.yml does not watch requirements/**, so a lock refresh changes"
" what the Flatpak builds and runs no build"
)
def test_prs_skip_the_flatpak_build_when_only_src_changed() -> None:
"""numpy and pywhispercpp compile from source. A text-injection PR does
not need that. push to main still watches src/** so a sandbox install
break still fails the default branch."""
assert "'src/**'" not in _on_event_block("pull_request")
assert "'src/**'" in _on_event_block("push")
def test_builder_cache_key_is_a_prefix_the_action_can_restore() -> None:
"""The action restore-keys is `flatpak-builder-${arch}`.
cache-key is passed through as `${cache-key}-${arch}`. A key of
`flatpak-builder-${hash}` becomes `flatpak-builder-${hash}-x86_64`,
which does not start with `flatpak-builder-x86_64`, so a packaging
hash change discarded the previous .flatpak-builder cache and
recompiled numpy and pywhispercpp from scratch (~18 min). Putting
`${{ matrix.arch }}` in the key first makes the fallback match.
"""
assert (
"cache-key: flatpak-builder-${{ matrix.arch }}-"
"${{ hashFiles('packaging/flatpak/**') }}" in WORKFLOW.read_text(encoding="utf-8")
)
+7
View File
@@ -202,6 +202,13 @@ def test_flatpak_release_jobs_reuse_ci_builder_pins():
assert "options: --privileged" in block, f"{name} is not a privileged container"
assert _FLATPAK_BUILDER in block, f"{name} does not use the pinned builder action"
assert f"arch: {arch}" in block
assert (
f"cache-key: flatpak-builder-{arch}-${{{{ hashFiles('packaging/flatpak/**') }}}}"
in block
), (
f"{name} cache-key must start with flatpak-builder-{arch} so the "
"action's restore-keys prefix matches after it appends -${arch}"
)
assert f"runs-on: {runner}" in block
assert f"Vocalinux-${{{{ steps.get_version.outputs.VERSION }}}}-{arch}.flatpak" in block
assert "actions/upload-artifact" in block, f"{name} does not upload a workflow artifact"