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/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 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..a6ed6fb --- /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 + +- [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 + +- [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 + +- [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 + +- [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. +- [x] 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..41b0153 --- /dev/null +++ b/scripts/update-fsdk-sources.py @@ -0,0 +1,164 @@ +#!/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_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-(.+?)-\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_ref(fsdk_ref: str) -> tuple[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(2), 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", "freedesktop-sdk.bst"], + cwd=ROOT, + check=True, + ) + 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, 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': )'[^']+'$", + rf"\1'{fsdk_version}'", + ) + replace_one( + OCI_ELEMENT, + r"^(\s*'io\.projectbluefin\.fsdk\.ref': )'[^']+'$", + rf"\1'{fsdk_ref}'", + ) + + +def check_source_inventory() -> None: + errors: list[str] = [] + count = 0 + for path in sorted(ELEMENTS.rglob("*.bst")): + text = path.read_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 + 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}:", 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]+)$", 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: + 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: + 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() + + +if __name__ == "__main__": + main()