From 84f4d0a24772a2bf19ffd887e4a4b486bd9039db Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 12:14:05 -0400 Subject: [PATCH 1/7] ci: automate FSDK source updates Assisted-by: GitHub Copilot GPT-5.6 via pi --- .github/workflows/auto-update.yml | 46 ++---- .github/workflows/update-fsdk-sources.yml | 76 +++++++++ .../2026-09-16-fsdk-dependency-updates.md | 33 ++++ elements/printer-app/brlaser.bst | 2 +- elements/printer-app/c2050.bst | 2 +- elements/printer-app/c2esp.bst | 2 +- elements/printer-app/cjet.bst | 2 +- elements/printer-app/dymo-cups-drivers.bst | 2 +- elements/printer-app/foo2zjs.bst | 2 +- elements/printer-app/fxlinuxprint.bst | 2 +- elements/printer-app/hpijs.bst | 2 +- elements/printer-app/m2300w.bst | 2 +- elements/printer-app/min12xxw.bst | 2 +- elements/printer-app/pappl.bst | 2 +- elements/printer-app/pnm2ppa.bst | 2 +- elements/printer-app/printer-driver-oki.bst | 2 +- elements/printer-app/psutils.bst | 2 +- elements/printer-app/ptouch-driver.bst | 2 +- elements/printer-app/pxljr.bst | 2 +- elements/printer-app/pyppd.bst | 2 +- elements/printer-app/rastertosag-gdi.bst | 2 +- elements/printer-app/splix.bst | 2 +- scripts/update-fsdk-sources.py | 154 ++++++++++++++++++ 23 files changed, 298 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/update-fsdk-sources.yml create mode 100644 docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md create mode 100755 scripts/update-fsdk-sources.py diff --git a/.github/workflows/auto-update.yml b/.github/workflows/auto-update.yml index b546d78..6862f35 100644 --- a/.github/workflows/auto-update.yml +++ b/.github/workflows/auto-update.yml @@ -1,41 +1,27 @@ -name: Push new tag update to stable branch +name: Update Snap dependencies on: schedule: - - cron: '9 7 * * *' + - cron: "9 7 * * *" workflow_dispatch: - inputs: - workflow_choice: - description: "Choose YAML to update" - required: true - default: "both" - type: choice - options: - - snapcraft - - rockcraft - - both + +concurrency: + group: update-snap-dependencies + cancel-in-progress: true + +permissions: + contents: write + pull-requests: write jobs: - update-yamls: + update-snap: runs-on: ubuntu-latest steps: - - name: Checkout this repo - uses: actions/checkout@v4 - - - name: Run desktop-snaps action (Snapcraft) - if: ${{ github.event_name == 'schedule' || github.event.inputs.workflow_choice == 'snapcraft' || github.event.inputs.workflow_choice == 'both' }} - uses: ubuntu/desktop-snaps@stable + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Update Snapcraft dependencies + uses: ubuntu/desktop-snaps@f1886f483e2251bc0a1d099951946966677ffbd4 # stable with: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ github.token }} repo: ${{ github.repository }} version-schema: '^ghostpdl-(\d+\.\d+\.\d+)' - - - name: Run desktop-snaps action (Rockcraft) - if: ${{ github.event_name == 'schedule' || github.event.inputs.workflow_choice == 'rockcraft' || github.event.inputs.workflow_choice == 'both' }} - uses: ubuntu/desktop-snaps@stable - with: - token: ${{ secrets.GITHUB_TOKEN }} - repo: ${{ github.repository }} - rock-version-schema: '^ghostpdl-(\d+\.\d+\.\d+)' - yaml-path: 'rockcraft.yaml' - readme-path: 'README.md' diff --git a/.github/workflows/update-fsdk-sources.yml b/.github/workflows/update-fsdk-sources.yml new file mode 100644 index 0000000..41805a9 --- /dev/null +++ b/.github/workflows/update-fsdk-sources.yml @@ -0,0 +1,76 @@ +name: Update FSDK sources + +on: + schedule: + - cron: "23 7 * * *" + workflow_dispatch: + +concurrency: + group: update-fsdk-sources + cancel-in-progress: true + +permissions: + contents: read + +jobs: + update: + runs-on: ubuntu-24.04 + timeout-minutes: 180 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: taiki-e/install-action@d438492cf8a250514fa2d34b30bc3c0dc37c65ff # v2 + with: + tool: just + - name: Install host dependencies + run: sudo apt-get update && sudo apt-get install --yes fuse3 podman + - name: Track sources and synchronize metadata + run: scripts/update-fsdk-sources.py --update + - name: Check for updates + id: changes + run: | + if git diff --quiet -- VERSION elements; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + - name: Verify CUPS patch compatibility + if: steps.changes.outputs.changed == 'true' + run: just verify-cups-patch-chain + - name: Verify updated appliance + if: steps.changes.outputs.changed == 'true' + run: just verify + - name: Mint Mergeraptor token + if: steps.changes.outputs.changed == 'true' + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.MERGERAPTOR_APP_ID }} + private-key: ${{ secrets.MERGERAPTOR_PRIVATE_KEY }} + - name: Commit and propose atomic update + if: steps.changes.outputs.changed == 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + branch=auto/update-fsdk-sources + git config user.name 'mergeraptor[bot]' + git config user.email '${{ secrets.MERGERAPTOR_APP_ID }}+mergeraptor[bot]@users.noreply.github.com' + git switch --create "$branch" + git add VERSION elements + git commit -m 'chore(deps): update FSDK appliance sources' + basic="$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 -w0)" + echo "::add-mask::$basic" + git -c http.https://github.com/.extraheader="AUTHORIZATION: basic $basic" \ + push --force "https://github.com/${REPOSITORY}.git" "HEAD:refs/heads/$branch" + if existing="$(gh pr list --head "$branch" --state open --json number --jq '.[0].number')" && [[ -n "$existing" ]]; then + printf 'Updated dependency PR #%s\n' "$existing" + else + gh pr create \ + --base main \ + --head "$branch" \ + --title 'chore(deps): update FSDK appliance sources' \ + --body 'Automated atomic update of the freedesktop-sdk junction and repository-owned BuildStream sources. The workflow completed the CUPS patch-chain gate and full appliance verification before opening this PR. Auto-merge is intentionally disabled.' + fi diff --git a/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md b/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md new file mode 100644 index 0000000..6bd149d --- /dev/null +++ b/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md @@ -0,0 +1,33 @@ +# FSDK Dependency Update Automation Plan + +**Goal:** Propose atomic, fully verified updates for the FSDK junction and every repository-owned external BuildStream source while preserving the Snap updater as an independent lane. + +**Architecture:** Give every Git source a deliberate moving selector and immutable resolved ref, keep CPAN name/suffix/checksum contracts, and keep the plugin tarball's version/checksum explicit. A standard-library Python coordinator runs BuildStream tracking for the complete root graph, refreshes the PyPI plugin tarball, derives the application version from the newly selected FSDK Ghostscript source, synchronizes the IJS source and OCI FSDK labels, and validates the source inventory. A scheduled workflow runs the CUPS patch-chain gate and complete appliance gate before minting a short-lived Mergeraptor token and opening one non-auto-merged PR containing every related version/ref change. The existing updater becomes Snap-only. + +## Task 1: Make every source trackable + +- [ ] Use bounded wildcard selectors for release-series sources and moving branch selectors only where upstream has no releases. +- [ ] Retain immutable Git describe refs, CPAN SHA-256 sums, and the plugin tarball SHA-256 sum. +- [ ] Add a source-inventory check that rejects external Git sources without `track`/`ref`, CPAN sources without name/suffix/checksum, or tar sources without URL/checksum. + +## Task 2: Coordinate atomic metadata updates + +- [ ] Track every root-project source without crossing into the FSDK junction project. +- [ ] Refresh the buildstream-plugins-community PyPI sdist URL and checksum from PyPI metadata. +- [ ] Read the selected FSDK Ghostscript element, derive `-`, update `VERSION`, and synchronize the IJS track/ref. +- [ ] Synchronize the OCI FSDK release/ref labels with the selected junction. +- [ ] Fail without writing partial metadata when upstream responses are missing or malformed. + +## Task 3: Automate verified proposals + +- [ ] Add a scheduled/manual FSDK-source workflow with read-only permissions during tracking and verification. +- [ ] Run `just verify-cups-patch-chain` and `just verify` before minting write credentials. +- [ ] Mint a short-lived Mergeraptor token only after verification, then commit all changes atomically and create or update one dependency PR. +- [ ] Never enable auto-merge in the updater; native PR CI remains the merge gate. +- [ ] Reduce the existing updater to the independent Snap dependency lane. + +## Task 4: Verify and publish + +- [ ] Run source-inventory checks, a no-change updater smoke, `just verify`, workflow lint, script syntax checks, and `git diff --check`. +- [ ] Review the full diff from `feat/fsdk-secure-releases` and resolve all blocking findings. +- [ ] Resolve issue 09, commit and push `feat/fsdk-dependency-updates`, and open a stacked PR based on `feat/fsdk-secure-releases`. diff --git a/elements/printer-app/brlaser.bst b/elements/printer-app/brlaser.bst index d58d1ea..9cc49d2 100644 --- a/elements/printer-app/brlaser.bst +++ b/elements/printer-app/brlaser.bst @@ -4,7 +4,7 @@ description: Build the brlaser Brother laser raster filter. sources: - kind: git_repo url: github:pdewacht/brlaser.git - track: v6 + track: v* ref: v6-0-g23117fe9e0266396e4791cdae84d979928aed135 build-depends: diff --git a/elements/printer-app/c2050.bst b/elements/printer-app/c2050.bst index b23d619..957f82f 100644 --- a/elements/printer-app/c2050.bst +++ b/elements/printer-app/c2050.bst @@ -4,7 +4,7 @@ description: Build the c2050 Lexmark raster converter. sources: - kind: git_repo url: salsa:printing-team/c2050.git - track: debian/0.3-7 + track: debian/* ref: debian/0.3-7-0-ge2ee50d2da58e552b1ac70ae0b4d8913436921e1 - kind: patch_queue path: patches/c2050 diff --git a/elements/printer-app/c2esp.bst b/elements/printer-app/c2esp.bst index 13c58a9..a2799c5 100644 --- a/elements/printer-app/c2esp.bst +++ b/elements/printer-app/c2esp.bst @@ -4,7 +4,7 @@ description: Build the c2esp Kodak raster filters and driver descriptions. sources: - kind: git_repo url: salsa:printing-team/c2esp.git - track: debian/27-11 + track: debian/* ref: debian/27-11-0-g90a09ca4927fca5e1ae08307cd3ca9c0f0ca5b5b - kind: local path: patches/c2esp-libcupsfilters2-api.patch diff --git a/elements/printer-app/cjet.bst b/elements/printer-app/cjet.bst index 83ec5bd..1f418af 100644 --- a/elements/printer-app/cjet.bst +++ b/elements/printer-app/cjet.bst @@ -4,7 +4,7 @@ description: Build the cjet Canon CaPSL raster converter. sources: - kind: git_repo url: salsa:printing-team/cjet.git - track: debian/0.8.9-11 + track: debian/* ref: debian/0.8.9-11-0-g2de422f1b08cdf8d18a2e2ea8ae0b14751702fba build-depends: diff --git a/elements/printer-app/dymo-cups-drivers.bst b/elements/printer-app/dymo-cups-drivers.bst index a387cb4..d528212 100644 --- a/elements/printer-app/dymo-cups-drivers.bst +++ b/elements/printer-app/dymo-cups-drivers.bst @@ -4,7 +4,7 @@ description: Build the DYMO CUPS raster filters and PPD archive. sources: - kind: git_repo url: salsa:printing-team/dymo-cups-drivers.git - track: debian/1.4.0-12 + track: debian/* ref: debian/1.4.0-12-0-ge69dceba792dc23d2894d57b0468009e52109625 build-depends: diff --git a/elements/printer-app/foo2zjs.bst b/elements/printer-app/foo2zjs.bst index a0b5b0f..7e8bc37 100644 --- a/elements/printer-app/foo2zjs.bst +++ b/elements/printer-app/foo2zjs.bst @@ -4,7 +4,7 @@ description: Build foo2zjs drivers with persistent color-profile paths. sources: - kind: git_repo url: salsa:printing-team/foo2zjs.git - track: debian/20200505dfsg0-5 + track: debian/* ref: debian/20200505dfsg0-5-0-g816bcd01d5fa0b3171fe6d09c093a9290ccf7c25 build-depends: diff --git a/elements/printer-app/fxlinuxprint.bst b/elements/printer-app/fxlinuxprint.bst index c7166ba..d8747b1 100644 --- a/elements/printer-app/fxlinuxprint.bst +++ b/elements/printer-app/fxlinuxprint.bst @@ -4,7 +4,7 @@ description: Build the Fuji Xerox PDF printer CUPS filters and PPD archive. sources: - kind: git_repo url: salsa:printing-team/fxlinuxprint.git - track: debian/1.1.0+ds-4 + track: debian/* ref: debian/1.1.0+ds-4-0-g3e640d5cf2881b01b6dab16e7d899290e61b0417 build-depends: diff --git a/elements/printer-app/hpijs.bst b/elements/printer-app/hpijs.bst index 6a8b68b..b93cf70 100644 --- a/elements/printer-app/hpijs.bst +++ b/elements/printer-app/hpijs.bst @@ -4,7 +4,7 @@ description: Build the HPLIP HPIJS driver with persistent appliance configuratio sources: - kind: git_repo url: salsa:printing-team/hplip.v2.git - track: debian/3.26.4+dfsg0-3 + track: debian/* ref: debian/3.26.4+dfsg0-3-0-gf27f786d5f1060cce7c92e42488bd5e5d00ba135 build-depends: diff --git a/elements/printer-app/m2300w.bst b/elements/printer-app/m2300w.bst index 7a7c3e6..fab4e76 100644 --- a/elements/printer-app/m2300w.bst +++ b/elements/printer-app/m2300w.bst @@ -4,7 +4,7 @@ description: Build the m2300w drivers with persistent color-profile paths. sources: - kind: git_repo url: salsa:printing-team/m2300w.git - track: debian/0.51-15 + track: debian/* ref: debian/0.51-15-0-gf4ebe31fe93e8f2f56ecce2f927e59bb41432d53 build-depends: diff --git a/elements/printer-app/min12xxw.bst b/elements/printer-app/min12xxw.bst index 370b3c2..ba8c81a 100644 --- a/elements/printer-app/min12xxw.bst +++ b/elements/printer-app/min12xxw.bst @@ -4,7 +4,7 @@ description: Build the min12xxw Minolta raster converter. sources: - kind: git_repo url: salsa:printing-team/min12xxw.git - track: debian/0.0.9-11 + track: debian/* ref: debian/0.0.9-11-0-g9a1fae0987e757c8d04e728eaec6e7adc1efca89 build-depends: diff --git a/elements/printer-app/pappl.bst b/elements/printer-app/pappl.bst index a24d137..443cbd2 100644 --- a/elements/printer-app/pappl.bst +++ b/elements/printer-app/pappl.bst @@ -4,7 +4,7 @@ description: Build PAPPL for the Ghostscript Printer Application. sources: - kind: git_repo url: github:michaelrsweet/pappl.git - track: v1.4.12 + track: v1.4.* ref: v1.4.12-0-g6db8e137557ad84662e78d24fdb2a591c621f4ac - kind: patch_queue path: patches/pappl diff --git a/elements/printer-app/pnm2ppa.bst b/elements/printer-app/pnm2ppa.bst index e2b0efc..f86a2f7 100644 --- a/elements/printer-app/pnm2ppa.bst +++ b/elements/printer-app/pnm2ppa.bst @@ -4,7 +4,7 @@ description: Build the pnm2ppa HP PPA raster converter. sources: - kind: git_repo url: salsa:printing-team/pnm2ppa.git - track: debian/1.13-14 + track: debian/* ref: debian/1.13-14-0-gca990cac53fb605d827f707e5f2d0c490639705c build-depends: diff --git a/elements/printer-app/printer-driver-oki.bst b/elements/printer-app/printer-driver-oki.bst index c50b865..b5b3ff7 100644 --- a/elements/printer-app/printer-driver-oki.bst +++ b/elements/printer-app/printer-driver-oki.bst @@ -4,7 +4,7 @@ description: Package the OKI shell filters and PPD archive. sources: - kind: git_repo url: github:rbalint/printer-driver-oki.git - track: 1.0.2 + track: 1.* ref: 1.0.2-0-g9f48d5a6c5938126a5aa91a902668aa877c3c22b build-depends: diff --git a/elements/printer-app/psutils.bst b/elements/printer-app/psutils.bst index e62f92c..528a68a 100644 --- a/elements/printer-app/psutils.bst +++ b/elements/printer-app/psutils.bst @@ -4,7 +4,7 @@ description: Build the legacy psnup helper required by stateful driver wrappers. sources: - kind: git_repo url: salsa:debian/psutils.git - track: debian/1.17.dfsg-5 + track: debian/* ref: debian/1.17.dfsg-5-0-gafa3fccb4b77165a22b783f21feac35a082d8137 build-depends: diff --git a/elements/printer-app/ptouch-driver.bst b/elements/printer-app/ptouch-driver.bst index 8ae70c2..189d865 100644 --- a/elements/printer-app/ptouch-driver.bst +++ b/elements/printer-app/ptouch-driver.bst @@ -4,7 +4,7 @@ description: Build the P-Touch CUPS raster filter and Foomatic PPD archive. sources: - kind: git_repo url: salsa:printing-team/ptouch-driver.git - track: debian/1.7-1 + track: debian/* ref: debian/1.7-1-0-gccfa92351be3ce601f212048b174147a5496d8be build-depends: diff --git a/elements/printer-app/pxljr.bst b/elements/printer-app/pxljr.bst index 971973d..e2a175b 100644 --- a/elements/printer-app/pxljr.bst +++ b/elements/printer-app/pxljr.bst @@ -4,7 +4,7 @@ description: Build the pxljr HP Color LaserJet IJS driver and PPD archive. sources: - kind: git_repo url: salsa:printing-team/pxljr.git - track: debian/1.4+repack0-6 + track: debian/* ref: debian/1.4+repack0-6-0-g6b0dafe66965e5fa398733c04160bd6436d13254 build-depends: diff --git a/elements/printer-app/pyppd.bst b/elements/printer-app/pyppd.bst index aa360f8..7d17983 100644 --- a/elements/printer-app/pyppd.bst +++ b/elements/printer-app/pyppd.bst @@ -4,7 +4,7 @@ description: Build the pyppd self-extracting PPD archive generator. sources: - kind: git_repo url: github:OpenPrinting/pyppd.git - track: release-1-1-0 + track: release-* ref: release-1-1-0-0-g29ccf6cf85781315a696774e7458a2f1f61aac57 build-depends: diff --git a/elements/printer-app/rastertosag-gdi.bst b/elements/printer-app/rastertosag-gdi.bst index 7591ec3..7ee2e4b 100644 --- a/elements/printer-app/rastertosag-gdi.bst +++ b/elements/printer-app/rastertosag-gdi.bst @@ -4,7 +4,7 @@ description: Package the rastertosag-gdi Ricoh raster filter and PPDs. sources: - kind: git_repo url: salsa:printing-team/rastertosag-gdi.git - track: debian/0.1-8 + track: debian/* ref: debian/0.1-8-0-g6f4028f36f015692a602077b7060438d0bf9f634 build-depends: diff --git a/elements/printer-app/splix.bst b/elements/printer-app/splix.bst index 2114761..bae80d8 100644 --- a/elements/printer-app/splix.bst +++ b/elements/printer-app/splix.bst @@ -4,7 +4,7 @@ description: Build the SpliX QPDL printer filters and PPD archive. sources: - kind: git_repo url: salsa:printing-team/splix.git - track: debian/2.0.1-1 + track: debian/* ref: debian/2.0.1-1-0-g505f24d43fdb86eaa41a44486e7ef88226bb2c0c build-depends: diff --git a/scripts/update-fsdk-sources.py b/scripts/update-fsdk-sources.py new file mode 100755 index 0000000..21ed2cf --- /dev/null +++ b/scripts/update-fsdk-sources.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +"""Track repository-owned BuildStream sources and synchronize release metadata.""" + +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import urllib.request +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +ELEMENTS = ROOT / "elements" +FSDK_ELEMENT = ELEMENTS / "freedesktop-sdk.bst" +IJS_ELEMENT = ELEMENTS / "printer-app" / "ijs.bst" +OCI_ELEMENT = ELEMENTS / "oci" / "ghostscript-printer-app.bst" +PLUGIN_ELEMENT = ELEMENTS / "plugins" / "buildstream-plugins-community.bst" +VERSION_FILE = ROOT / "VERSION" + +SOURCE_BLOCK = re.compile( + r"(?ms)^ - kind: (?Pgit_repo|cpan|tar)\n(?P.*?)(?=^ - kind:|\Z)" +) +FSDK_REF = re.compile(r"^\s*ref: freedesktop-sdk-(.+?)-0-g([0-9a-f]{40})$", re.MULTILINE) +GHOSTSCRIPT_REF = re.compile(r"^\s*ref: ghostpdl-([0-9][^-\s]*)-\d+-g[0-9a-f]{40}$", re.MULTILINE) + + +def replace_one(path: Path, pattern: str, replacement: str) -> None: + text = path.read_text() + updated, count = re.subn(pattern, replacement, text, count=1, flags=re.MULTILINE) + if count != 1: + raise RuntimeError(f"expected one metadata field in {path}: {pattern}") + if updated != text: + path.write_text(updated) + + +def read_fsdk_ref() -> tuple[str, str]: + match = FSDK_REF.search(FSDK_ELEMENT.read_text()) + if match is None: + raise RuntimeError("freedesktop-sdk ref is not a release plus full commit") + return match.group(1), match.group(2) + + +def fetch_ghostscript_version(fsdk_ref: str) -> str: + url = ( + "https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/raw/" + f"{fsdk_ref}/elements/components/ghostscript.bst" + ) + with urllib.request.urlopen(url, timeout=30) as response: + element = response.read().decode() + match = GHOSTSCRIPT_REF.search(element) + if match is None: + raise RuntimeError("selected freedesktop-sdk has no parseable Ghostscript release ref") + return match.group(1) + + +def refresh_plugin_tarball() -> None: + with urllib.request.urlopen( + "https://pypi.org/pypi/buildstream-plugins-community/json", timeout=30 + ) as response: + metadata = json.load(response) + version = metadata["info"]["version"] + sdists = [item for item in metadata["releases"][version] if item["packagetype"] == "sdist"] + if len(sdists) != 1: + raise RuntimeError(f"expected one buildstream-plugins-community {version} sdist") + sdist = sdists[0] + url = f"pypi:source/b/buildstream-plugins-community/{sdist['filename']}" + replace_one(PLUGIN_ELEMENT, r"^\s*url: pypi:.*$", f" url: {url}") + replace_one(PLUGIN_ELEMENT, r"^\s*ref: [0-9a-f]{{64}}$", f" ref: {sdist['digests']['sha256']}") + + +def track_sources() -> None: + subprocess.run( + [ + "just", + "bst", + "source", + "track", + "--deps", + "all", + "oci/ghostscript-printer-app.bst", + ], + cwd=ROOT, + check=True, + ) + + +def sync_fsdk_metadata() -> None: + fsdk_version, fsdk_ref = read_fsdk_ref() + ghostscript_version = fetch_ghostscript_version(fsdk_ref) + current_version = VERSION_FILE.read_text().strip() + match = re.fullmatch(r".+-([0-9]+)", current_version) + if match is None: + raise RuntimeError("VERSION must end in a numeric packaging revision") + VERSION_FILE.write_text(f"{ghostscript_version}-{match.group(1)}\n") + replace_one(IJS_ELEMENT, r"^\s*track: ghostpdl-.*$", f" track: ghostpdl-{ghostscript_version}") + replace_one( + OCI_ELEMENT, + r"^(\s*'io\.projectbluefin\.fsdk\.version': )'[^']+'$", + rf"\1'{fsdk_version}'", + ) + replace_one( + OCI_ELEMENT, + r"^(\s*'io\.projectbluefin\.fsdk\.ref': )'[^']+'$", + rf"\1'{fsdk_ref}'", + ) + subprocess.run( + ["just", "bst", "source", "track", "printer-app/ijs.bst"], + cwd=ROOT, + check=True, + ) + + +def check_source_inventory() -> None: + errors: list[str] = [] + count = 0 + for path in sorted(ELEMENTS.rglob("*.bst")): + text = path.read_text() + for source in SOURCE_BLOCK.finditer(text): + count += 1 + kind = source.group("kind") + body = source.group("body") + relative = path.relative_to(ROOT) + required = { + "git_repo": ("url", "track", "ref"), + "cpan": ("name", "suffix", "sha256sum"), + "tar": ("url", "ref"), + }[kind] + missing = [key for key in required if re.search(rf"^\s*{key}:", body, re.MULTILINE) is None] + if missing: + errors.append(f"{relative}: {kind} source missing {', '.join(missing)}") + ref = re.search(r"^\s*(?:ref|sha256sum):\s*([^\s]+)$", body, re.MULTILINE) + if ref is None or re.search(r"[0-9a-f]{40,64}$", ref.group(1)) is None: + errors.append(f"{relative}: {kind} source has no immutable integrity ref") + if count == 0: + errors.append("no external BuildStream sources found") + if errors: + raise RuntimeError("\n".join(errors)) + print(f"OK: {count} external BuildStream sources expose update and integrity metadata") + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("--update", action="store_true", help="track sources and synchronize metadata") + args = parser.parse_args() + if args.update: + refresh_plugin_tarball() + track_sources() + sync_fsdk_metadata() + check_source_inventory() + + +if __name__ == "__main__": + main() From 7f4f20743c6970ed516ad0c5eb68ba335a32f62d Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 12:29:30 -0400 Subject: [PATCH 2/7] fix: make source updates atomic Assisted-by: GitHub Copilot GPT-5.6 via pi --- scripts/update-fsdk-sources.py | 56 ++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/scripts/update-fsdk-sources.py b/scripts/update-fsdk-sources.py index 21ed2cf..41b0153 100755 --- a/scripts/update-fsdk-sources.py +++ b/scripts/update-fsdk-sources.py @@ -17,12 +17,9 @@ OCI_ELEMENT = ELEMENTS / "oci" / "ghostscript-printer-app.bst" PLUGIN_ELEMENT = ELEMENTS / "plugins" / "buildstream-plugins-community.bst" VERSION_FILE = ROOT / "VERSION" - -SOURCE_BLOCK = re.compile( - r"(?ms)^ - kind: (?Pgit_repo|cpan|tar)\n(?P.*?)(?=^ - kind:|\Z)" -) +SOURCE_KIND = re.compile(r"^\s*-\s+kind:\s+(git_repo|cpan|tar)$", re.MULTILINE) FSDK_REF = re.compile(r"^\s*ref: freedesktop-sdk-(.+?)-0-g([0-9a-f]{40})$", re.MULTILINE) -GHOSTSCRIPT_REF = re.compile(r"^\s*ref: ghostpdl-([0-9][^-\s]*)-\d+-g[0-9a-f]{40}$", re.MULTILINE) +GHOSTSCRIPT_REF = re.compile(r"^\s*ref: (ghostpdl-(.+?)-\d+-g[0-9a-f]{40})$", re.MULTILINE) def replace_one(path: Path, pattern: str, replacement: str) -> None: @@ -41,7 +38,7 @@ def read_fsdk_ref() -> tuple[str, str]: return match.group(1), match.group(2) -def fetch_ghostscript_version(fsdk_ref: str) -> str: +def fetch_ghostscript_ref(fsdk_ref: str) -> tuple[str, str]: url = ( "https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/raw/" f"{fsdk_ref}/elements/components/ghostscript.bst" @@ -51,7 +48,7 @@ def fetch_ghostscript_version(fsdk_ref: str) -> str: match = GHOSTSCRIPT_REF.search(element) if match is None: raise RuntimeError("selected freedesktop-sdk has no parseable Ghostscript release ref") - return match.group(1) + return match.group(2), match.group(1) def refresh_plugin_tarball() -> None: @@ -66,10 +63,15 @@ def refresh_plugin_tarball() -> None: sdist = sdists[0] url = f"pypi:source/b/buildstream-plugins-community/{sdist['filename']}" replace_one(PLUGIN_ELEMENT, r"^\s*url: pypi:.*$", f" url: {url}") - replace_one(PLUGIN_ELEMENT, r"^\s*ref: [0-9a-f]{{64}}$", f" ref: {sdist['digests']['sha256']}") + replace_one(PLUGIN_ELEMENT, r"^\s*ref: [0-9a-f]{64}$", f" ref: {sdist['digests']['sha256']}") def track_sources() -> None: + subprocess.run( + ["just", "bst", "source", "track", "freedesktop-sdk.bst"], + cwd=ROOT, + check=True, + ) subprocess.run( [ "just", @@ -87,13 +89,14 @@ def track_sources() -> None: def sync_fsdk_metadata() -> None: fsdk_version, fsdk_ref = read_fsdk_ref() - ghostscript_version = fetch_ghostscript_version(fsdk_ref) + ghostscript_version, ghostscript_ref = fetch_ghostscript_ref(fsdk_ref) current_version = VERSION_FILE.read_text().strip() match = re.fullmatch(r".+-([0-9]+)", current_version) if match is None: raise RuntimeError("VERSION must end in a numeric packaging revision") VERSION_FILE.write_text(f"{ghostscript_version}-{match.group(1)}\n") replace_one(IJS_ELEMENT, r"^\s*track: ghostpdl-.*$", f" track: ghostpdl-{ghostscript_version}") + replace_one(IJS_ELEMENT, r"^\s*ref: (?:ghostpdl-)?[^\s]+$", f" ref: {ghostscript_ref}") replace_one( OCI_ELEMENT, r"^(\s*'io\.projectbluefin\.fsdk\.version': )'[^']+'$", @@ -104,11 +107,6 @@ def sync_fsdk_metadata() -> None: r"^(\s*'io\.projectbluefin\.fsdk\.ref': )'[^']+'$", rf"\1'{fsdk_ref}'", ) - subprocess.run( - ["just", "bst", "source", "track", "printer-app/ijs.bst"], - cwd=ROOT, - check=True, - ) def check_source_inventory() -> None: @@ -116,20 +114,23 @@ def check_source_inventory() -> None: count = 0 for path in sorted(ELEMENTS.rglob("*.bst")): text = path.read_text() - for source in SOURCE_BLOCK.finditer(text): + kinds = SOURCE_KIND.findall(text) + if not kinds: + continue + relative = path.relative_to(ROOT) + if len(kinds) != 1: + errors.append(f"{relative}: expected one external source, found {len(kinds)}") + for kind in kinds: count += 1 - kind = source.group("kind") - body = source.group("body") - relative = path.relative_to(ROOT) required = { "git_repo": ("url", "track", "ref"), "cpan": ("name", "suffix", "sha256sum"), "tar": ("url", "ref"), }[kind] - missing = [key for key in required if re.search(rf"^\s*{key}:", body, re.MULTILINE) is None] + missing = [key for key in required if re.search(rf"^\s*{key}:", text, re.MULTILINE) is None] if missing: errors.append(f"{relative}: {kind} source missing {', '.join(missing)}") - ref = re.search(r"^\s*(?:ref|sha256sum):\s*([^\s]+)$", body, re.MULTILINE) + ref = re.search(r"^\s*(?:ref|sha256sum):\s*([^\s]+)$", text, re.MULTILINE) if ref is None or re.search(r"[0-9a-f]{40,64}$", ref.group(1)) is None: errors.append(f"{relative}: {kind} source has no immutable integrity ref") if count == 0: @@ -144,9 +145,18 @@ def main() -> None: parser.add_argument("--update", action="store_true", help="track sources and synchronize metadata") args = parser.parse_args() if args.update: - refresh_plugin_tarball() - track_sources() - sync_fsdk_metadata() + paths = [VERSION_FILE, *ELEMENTS.rglob("*.bst")] + original = {path: path.read_bytes() for path in paths} + try: + refresh_plugin_tarball() + track_sources() + sync_fsdk_metadata() + check_source_inventory() + except BaseException: + for path, contents in original.items(): + path.write_bytes(contents) + raise + return check_source_inventory() From 4eef295f50a9ce590f42ee607f6174cbd6afb668 Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 12:36:47 -0400 Subject: [PATCH 3/7] docs: record dependency automation completion Assisted-by: GitHub Copilot GPT-5.6 via pi --- .../2026-09-16-fsdk-dependency-updates.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md b/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md index 6bd149d..6af773f 100644 --- a/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md +++ b/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md @@ -6,28 +6,28 @@ ## Task 1: Make every source trackable -- [ ] Use bounded wildcard selectors for release-series sources and moving branch selectors only where upstream has no releases. -- [ ] Retain immutable Git describe refs, CPAN SHA-256 sums, and the plugin tarball SHA-256 sum. -- [ ] Add a source-inventory check that rejects external Git sources without `track`/`ref`, CPAN sources without name/suffix/checksum, or tar sources without URL/checksum. +- [x] Use bounded wildcard selectors for release-series sources and moving branch selectors only where upstream has no releases. +- [x] Retain immutable Git describe refs, CPAN SHA-256 sums, and the plugin tarball SHA-256 sum. +- [x] Add a source-inventory check that rejects external Git sources without `track`/`ref`, CPAN sources without name/suffix/checksum, or tar sources without URL/checksum. ## Task 2: Coordinate atomic metadata updates -- [ ] Track every root-project source without crossing into the FSDK junction project. -- [ ] Refresh the buildstream-plugins-community PyPI sdist URL and checksum from PyPI metadata. -- [ ] Read the selected FSDK Ghostscript element, derive `-`, update `VERSION`, and synchronize the IJS track/ref. -- [ ] Synchronize the OCI FSDK release/ref labels with the selected junction. -- [ ] Fail without writing partial metadata when upstream responses are missing or malformed. +- [x] Track every root-project source without crossing into the FSDK junction project. +- [x] Refresh the buildstream-plugins-community PyPI sdist URL and checksum from PyPI metadata. +- [x] Read the selected FSDK Ghostscript element, derive `-`, update `VERSION`, and synchronize the IJS track/ref. +- [x] Synchronize the OCI FSDK release/ref labels with the selected junction. +- [x] Fail without writing partial metadata when upstream responses are missing or malformed. ## Task 3: Automate verified proposals -- [ ] Add a scheduled/manual FSDK-source workflow with read-only permissions during tracking and verification. -- [ ] Run `just verify-cups-patch-chain` and `just verify` before minting write credentials. -- [ ] Mint a short-lived Mergeraptor token only after verification, then commit all changes atomically and create or update one dependency PR. -- [ ] Never enable auto-merge in the updater; native PR CI remains the merge gate. -- [ ] Reduce the existing updater to the independent Snap dependency lane. +- [x] Add a scheduled/manual FSDK-source workflow with read-only permissions during tracking and verification. +- [x] Run `just verify-cups-patch-chain` and `just verify` before minting write credentials. +- [x] Mint a short-lived Mergeraptor token only after verification, then commit all changes atomically and create or update one dependency PR. +- [x] Never enable auto-merge in the updater; native PR CI remains the merge gate. +- [x] Reduce the existing updater to the independent Snap dependency lane. ## Task 4: Verify and publish -- [ ] Run source-inventory checks, a no-change updater smoke, `just verify`, workflow lint, script syntax checks, and `git diff --check`. -- [ ] Review the full diff from `feat/fsdk-secure-releases` and resolve all blocking findings. +- [x] Run source-inventory checks, an isolated updater smoke, `just verify`, workflow lint, script syntax checks, and `git diff --check`. +- [x] Review the full diff from `feat/fsdk-secure-releases` and resolve all blocking findings. - [ ] Resolve issue 09, commit and push `feat/fsdk-dependency-updates`, and open a stacked PR based on `feat/fsdk-secure-releases`. From 47dc1228f7591f8d5f71d213e680d810aedd5c07 Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 12:37:40 -0400 Subject: [PATCH 4/7] docs: record dependency update completion Assisted-by: GitHub Copilot GPT-5.6 via pi --- docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md b/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md index 6af773f..a6ed6fb 100644 --- a/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md +++ b/docs/superpowers/plans/2026-09-16-fsdk-dependency-updates.md @@ -30,4 +30,4 @@ - [x] Run source-inventory checks, an isolated updater smoke, `just verify`, workflow lint, script syntax checks, and `git diff --check`. - [x] Review the full diff from `feat/fsdk-secure-releases` and resolve all blocking findings. -- [ ] Resolve issue 09, commit and push `feat/fsdk-dependency-updates`, and open a stacked PR based on `feat/fsdk-secure-releases`. +- [x] Resolve issue 09, commit and push `feat/fsdk-dependency-updates`, and open a stacked PR based on `feat/fsdk-secure-releases`. From bd428a379a7484cf66468d2370b3e2b6b1600ba0 Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 15:15:10 -0400 Subject: [PATCH 5/7] fix: normalize numeric application versions --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index ba31b6d..2164569 100644 --- a/Makefile +++ b/Makefile @@ -31,16 +31,16 @@ CFLAGS += `pkg-config --cflags pappl` `cups-config --cflags` `pkg-config --cfla ifdef VERSION CFLAGS += -DSYSTEM_VERSION_STR="\"$(VERSION)\"" ifndef MAJOR -MAJOR = `echo $(VERSION) | perl -p -e 's/^(\d+).*$$/\1/'` +MAJOR = `echo $(VERSION) | perl -p -e 's/^(\d+).*$$/\1/; s/^0+(\d)/\1/'` endif ifndef MINOR -MINOR = `echo $(VERSION) | perl -p -e 's/^\d+\D+(\d+).*$$/\1/'` +MINOR = `echo $(VERSION) | perl -p -e 's/^\d+\D+(\d+).*$$/\1/; s/^0+(\d)/\1/'` endif ifndef PATCH -PATCH = `echo $(VERSION) | perl -p -e 's/^\d+\D+\d+\D+(\d+).*$$/\1/'` +PATCH = `echo $(VERSION) | perl -p -e 's/^\d+\D+\d+\D+(\d+).*$$/\1/; s/^0+(\d)/\1/'` endif ifndef PACKAGE -PACKAGE = `echo $(VERSION) | perl -p -e 's/^\d+\D+\d+\D+\d+\D+(\d+).*$$/\1/'` +PACKAGE = `echo $(VERSION) | perl -p -e 's/^\d+\D+\d+\D+\d+\D+(\d+).*$$/\1/; s/^0+(\d)/\1/'` endif endif ifdef MAJOR From 49dd49b05ed12cb4889acd8f47e33e647a535c0a Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 15:34:05 -0400 Subject: [PATCH 6/7] fix: build pnm2ppa with C23 compilers --- snap/snapcraft.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 1c558a0..fe130f5 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -897,6 +897,7 @@ parts: - --sysconfdir=/etc override-build: | set -eux + sed -i '1i#include ' global.h # "Unlock" config file perl -p -i -e 's/^\s*version\s+0\s+/version 710 /' pnm2ppa.conf # Set config file path (we copy the config file to an editable place From bc4451f62eb19d1309431d4e790ebb75c27bf85b Mon Sep 17 00:00:00 2001 From: castrojo Date: Wed, 16 Sep 2026 15:43:07 -0400 Subject: [PATCH 7/7] revert: keep Snap packaging unchanged --- snap/snapcraft.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fe130f5..1c558a0 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -897,7 +897,6 @@ parts: - --sysconfdir=/etc override-build: | set -eux - sed -i '1i#include ' global.h # "Unlock" config file perl -p -i -e 's/^\s*version\s+0\s+/version 710 /' pnm2ppa.conf # Set config file path (we copy the config file to an editable place