| #!/usr/bin/env python3 |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| """Patch providers.json to include backfilled versions in `provider.versions`. |
| |
| Backfill writes per-version `metadata.json` to disk for the targeted version, |
| but the `Download data files from S3 for build` workflow step then overwrites |
| `registry/src/_data/providers.json` with the cached S3 copy. That cached copy |
| predates the backfilled version (that's why we're backfilling). Without |
| patching, `providerVersions.js`'s ``provider.versions ∩ availableSet`` filter |
| drops the on-disk metadata and the Eleventy build emits no page. |
| |
| This script runs after the S3 download, in the per-matrix backfill job, and |
| appends the backfilled version(s) into the targeted ``provider.versions`` |
| array. It also defensively re-includes ``provider.version`` (the "latest" |
| field) -- otherwise patching into a previously-empty ``versions: []`` could |
| leave the latest field excluded once the array becomes the authoritative |
| filter. |
| |
| Backfill-only tool. ``registry-build.yml`` (the full-build workflow) doesn't |
| need this -- ``extract_metadata.py`` regenerates ``providers.json`` from |
| scratch there. |
| """ |
| |
| from __future__ import annotations |
| |
| import argparse |
| import json |
| import sys |
| from pathlib import Path |
| |
| from packaging.version import InvalidVersion, Version |
| |
| |
| def patch(providers_json_path: Path, provider_id: str, versions: list[str]) -> int: |
| """Append `versions` into the targeted provider's `versions[]`. Returns exit code.""" |
| data = json.loads(providers_json_path.read_text()) |
| target = next((p for p in data["providers"] if p["id"] == provider_id), None) |
| if target is None: |
| print( |
| f"ERROR: provider '{provider_id}' not found in {providers_json_path}. " |
| f"This script is for backfilling versions of providers already in the " |
| f"registry; for new providers run a full registry-build.yml dispatch." |
| ) |
| return 1 |
| |
| existing = list(target.get("versions") or []) |
| # Always keep `provider.version` (latest) in `versions[]`. Without this, |
| # patching into a previously-empty list could leave the latest field out |
| # of providerVersions.js's authoritative filter. |
| latest = target.get("version") |
| if latest and latest not in existing: |
| existing.append(latest) |
| |
| added: list[str] = [] |
| for v in versions: |
| if v not in existing: |
| existing.append(v) |
| added.append(v) |
| |
| try: |
| existing.sort(key=Version, reverse=True) |
| except InvalidVersion: |
| existing.sort(reverse=True) |
| |
| target["versions"] = existing |
| providers_json_path.write_text(json.dumps(data, indent=2) + "\n") |
| print(f"Patched {provider_id}: added {added or []}; versions list now has {len(existing)} entries") |
| return 0 |
| |
| |
| def main() -> int: |
| parser = argparse.ArgumentParser(description=__doc__.splitlines()[0] if __doc__ else "") |
| parser.add_argument( |
| "--providers-json", |
| required=True, |
| type=Path, |
| help="Path to providers.json to modify in place.", |
| ) |
| parser.add_argument( |
| "--provider", |
| required=True, |
| help="Provider ID (e.g. 'amazon', 'common-compat').", |
| ) |
| parser.add_argument( |
| "--version", |
| required=True, |
| action="append", |
| help="Version(s) to add (repeat the flag for multiple).", |
| ) |
| args = parser.parse_args() |
| return patch(args.providers_json, args.provider, args.version) |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main()) |