fix(flatpak): no OCI update source for prereleases; match --repo-url exactly

- A prerelease bundle (a version with `-`, as resolve-version marks it) gets no --repo-url: the
  OCI index serves only the latest stable release, so Flatpak would replace the prerelease
  with that older build. The generated script clears OCI_REPO for such a version and passes
  ${OCI_REPO:+--repo-url="$OCI_REPO"}.
- --verify passes only with the exact URL, the prerelease guard and the option on the active
  build-bundle command; a commented-out command never matches, nor is it edited.
- A script that already sets OCI_REPO or its own --repo-url fails the scaffold instead of being
  reported ready or overwritten.
- The OCI export assertion matches the whole line (CodeRabbit).
This commit is contained in:
Dylan M. Taylor committed 2026-11-02 19:46:48 -05:00
1 parent 7e8c2b6c7c
commit bdbd1e7f5e
2 files changed
+69 -19

No files matched your search

+26 -8
View File
@@ -96,9 +96,13 @@ scaffold_repo() {
has_oci_script=1
fi
# The .flatpak bundle's origin updates from the ArtCraft OCI repository, whose index points at GHCR.
# The .flatpak bundle's origin updates from the ArtCraft OCI repository, whose index points at GHCR:
# the exact URL, the prerelease guard, and the option on the active (uncommented) bundle command.
local has_repo_url=0
if grep -F -q -- '--repo-url=' "$bundle_sh"; then
# shellcheck disable=SC2016 # the patterns match the script's literal $VERSION, $FLATHUB and $OCI_REPO
if grep -qxF 'OCI_REPO=oci+https://getartcraft.com' "$bundle_sh" \
&& grep -qxF 'if [[ "$VERSION" == *-* ]]; then OCI_REPO=""; fi' "$bundle_sh" \
&& grep -qE '^[[:space:]]*flatpak build-bundle --runtime-repo="\$FLATHUB" \$\{OCI_REPO:\+--repo-url="\$OCI_REPO"\} ' "$bundle_sh"; then
has_repo_url=1
fi
@@ -238,6 +242,7 @@ EOF
if [[ "$has_repo_url" -eq 0 ]]; then
echo "[+] $(basename "$repo"): adding --repo-url to the bundle in flatpak-bundle.sh"
python3 - "$bundle_sh" << 'EOF'
import re
import sys
path = sys.argv[1]
@@ -245,16 +250,29 @@ with open(path, 'r', encoding='utf-8') as f:
content = f.read()
flathub = 'FLATHUB=https://dl.flathub.org/repo/flathub.flatpakrepo\n'
bundle = 'flatpak build-bundle --runtime-repo="$FLATHUB" '
if flathub not in content or bundle not in content:
# The active command only: a commented-out build-bundle line never matches.
bundle = re.compile(r'^([ \t]*)flatpak build-bundle --runtime-repo="\$FLATHUB" (.*)$', re.M)
found = bundle.search(content)
if flathub not in content or found is None:
sys.exit(f"error: {path} has no FLATHUB line or Flathub build-bundle command to extend")
content = content.replace(flathub, flathub + 'OCI_REPO=oci+https://getartcraft.com\n', 1)
if 'OCI_REPO=' in content or '--repo-url' in found.group(2):
sys.exit(f"error: {path} already sets OCI_REPO or --repo-url; make it match the scaffold by hand")
content = content.replace(
bundle,
'# --repo-url makes the installed origin update from the ArtCraft OCI repository.\n'
+ bundle + '--repo-url="$OCI_REPO" ',
flathub,
flathub
+ '# The OCI repository serves only the latest stable release, so a prerelease bundle gets no update\n'
+ '# source: Flatpak would otherwise replace it with that older stable build.\n'
+ 'OCI_REPO=oci+https://getartcraft.com\n'
+ 'if [[ "$VERSION" == *-* ]]; then OCI_REPO=""; fi\n',
1,
)
indent = found.group(1)
content = bundle.sub(
lambda m: indent + '# --repo-url makes the installed origin update from the ArtCraft OCI repository.\n'
+ indent + 'flatpak build-bundle --runtime-repo="$FLATHUB" ${OCI_REPO:+--repo-url="$OCI_REPO"} ' + m.group(2),
content,
count=1,
)
with open(path, 'w', encoding='utf-8') as f:
f.write(content)
+43 -11
View File
@@ -76,13 +76,15 @@ class ScaffoldFlatpakOciTests(unittest.TestCase):
self.assertIn("OCI=0", updated_bundle)
self.assertIn("--oci) OCI=1; shift ;;", updated_bundle)
self.assertIn("flatpak build-bundle --oci", updated_bundle)
self.assertIn("OCI_REPO=oci+https://getartcraft.com", updated_bundle)
self.assertIn("\nOCI_REPO=oci+https://getartcraft.com\n", updated_bundle)
# A prerelease bundle gets no update source (the repository serves only the latest stable).
self.assertIn('\nif [[ "$VERSION" == *-* ]]; then OCI_REPO=""; fi\n', updated_bundle)
self.assertIn(
'flatpak build-bundle --runtime-repo="$FLATHUB" --repo-url="$OCI_REPO" "$WORK/repo" "$OUT" "$APP_ID" stable',
'\nflatpak build-bundle --runtime-repo="$FLATHUB" ${OCI_REPO:+--repo-url="$OCI_REPO"} "$WORK/repo" "$OUT" "$APP_ID" stable\n',
updated_bundle,
)
# The OCI image export carries no repo URL.
self.assertIn('flatpak build-bundle --oci "$WORK/repo"', updated_bundle)
self.assertIn('\n flatpak build-bundle --oci "$WORK/repo" "$OUT_OCI" "$APP_ID" stable\n', updated_bundle)
# Check flatpak-oci.sh is created and executable
oci_script = self.packaging_dir / "flatpak-oci.sh"
@@ -106,16 +108,46 @@ class ScaffoldFlatpakOciTests(unittest.TestCase):
subprocess.run([str(SCRIPT), str(self.repo_dir)], capture_output=True, text=True, check=True)
self.assertEqual(self.bundle_file.read_text(encoding="utf-8"), updated_bundle)
def run_script(self, *args, check=True):
return subprocess.run([str(SCRIPT), *args, str(self.repo_dir)], capture_output=True, text=True, check=check)
def test_repo_url_is_added_to_an_already_scaffolded_bundle(self):
subprocess.run([str(SCRIPT), str(self.repo_dir)], capture_output=True, text=True, check=True)
self.run_script()
scaffolded = self.bundle_file.read_text(encoding="utf-8")
# Back to the OCI scaffold of before --repo-url: the three generated lines and the option gone.
content = scaffolded
for line in [
"# The OCI repository serves only the latest stable release, so a prerelease bundle gets no update\n",
"# source: Flatpak would otherwise replace it with that older stable build.\n",
"OCI_REPO=oci+https://getartcraft.com\n",
'if [[ "$VERSION" == *-* ]]; then OCI_REPO=""; fi\n',
"# --repo-url makes the installed origin update from the ArtCraft OCI repository.\n",
]:
content = content.replace(line, "")
self.bundle_file.write_text(content.replace(' ${OCI_REPO:+--repo-url="$OCI_REPO"}', ""), encoding="utf-8")
self.assertIn("missing (flag=1, script=1, repo_url=0)", self.run_script("--verify").stdout)
self.run_script()
self.assertEqual(self.bundle_file.read_text(encoding="utf-8"), scaffolded)
def test_a_commented_out_bundle_command_is_left_alone(self):
commented = '# flatpak build-bundle --runtime-repo="$FLATHUB" "$WORK/old" "$OUT" "$APP_ID" stable\n'
active = 'flatpak build-bundle --runtime-repo="$FLATHUB" "$WORK/repo"'
self.bundle_file.write_text(SAMPLE_BUNDLE_SH.replace(active, commented + active, 1), encoding="utf-8")
self.run_script()
content = self.bundle_file.read_text(encoding="utf-8")
content = content.replace('OCI_REPO=oci+https://getartcraft.com\n', '')
content = content.replace('# --repo-url makes the installed origin update from the ArtCraft OCI repository.\n', '')
self.bundle_file.write_text(content.replace(' --repo-url="$OCI_REPO"', ''), encoding="utf-8")
res = subprocess.run([str(SCRIPT), "--verify", str(self.repo_dir)], capture_output=True, text=True, check=True)
self.assertIn("missing (flag=1, script=1, repo_url=0)", res.stdout)
subprocess.run([str(SCRIPT), str(self.repo_dir)], capture_output=True, text=True, check=True)
self.assertEqual(self.bundle_file.read_text(encoding="utf-8").count('--repo-url="$OCI_REPO"'), 1)
self.assertIn(commented, content)
self.assertIn('flatpak build-bundle --runtime-repo="$FLATHUB" ${OCI_REPO:+--repo-url="$OCI_REPO"} "$WORK/repo"', content)
self.assertIn("fully scaffolded for Flatpak OCI", self.run_script("--verify").stdout)
def test_another_repo_url_is_reported_and_never_overwritten(self):
active = 'flatpak build-bundle --runtime-repo="$FLATHUB" '
original = SAMPLE_BUNDLE_SH.replace(active, active + '--repo-url=https://example.org/repo ', 1)
self.bundle_file.write_text(original, encoding="utf-8")
self.assertIn("repo_url=0", self.run_script("--verify").stdout)
res = self.run_script(check=False)
self.assertNotEqual(res.returncode, 0)
self.assertIn("already sets OCI_REPO or --repo-url", res.stderr)
self.assertIn('--repo-url=https://example.org/repo "$WORK/repo"', self.bundle_file.read_text(encoding="utf-8"))
if __name__ == "__main__":