docker/sync_helpers.py hinzugefügt

This commit is contained in:
2026-08-26 11:44:59 +02:00
parent bc0760089a
commit 4d4c546709
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""
sync_helpers.py - small helper used by sync.sh
Two jobs, on purpose kept out of bash because reliable YAML parsing and XML
escaping in pure bash/sed is a losing game:
parse-yaml <file>
Reads the "sources:" list from the YAML file and prints it as JSON
on stdout, so sync.sh can loop over it safely (no word-splitting
issues, handles quoting etc.).
gen-appstream <origin> <entries_json>
entries_json is a JSON array of {"name":..., "summary":...} objects
(only the *explicitly requested* top-level packages, never pulled-in
dependencies). Prints a minimal AppStream "generic component" catalog
on stdout, which sync.sh gzips and injects into the repo metadata via
modifyrepo_c.
NOTE: "generic" components (no icon/screenshots/.desktop file) make a
package installable from GNOME Software / Discover and show up in
search, but will NOT appear on the curated "Explore" front page -
that's a GNOME Software behaviour for non-desktop-app components, not
something fixable at the repo-metadata level.
"""
import sys
import json
import argparse
from xml.sax.saxutils import escape
def cmd_parse_yaml(path: str) -> None:
try:
import yaml
except ImportError:
sys.stderr.write(
"ERROR: python3-pyyaml (PyYAML) is not installed.\n"
"Install it with: dnf install -y python3-pyyaml\n"
)
sys.exit(1)
with open(path, "r", encoding="utf-8") as fh:
data = yaml.safe_load(fh) or {}
sources = data.get("sources") or []
if not isinstance(sources, list):
sys.stderr.write("ERROR: 'sources' in YAML is not a list\n")
sys.exit(1)
for i, src in enumerate(sources):
for field in ("name", "baseurl", "packages"):
if field not in src:
sys.stderr.write(
f"ERROR: source #{i} is missing required field '{field}'\n"
)
sys.exit(1)
if not isinstance(src["packages"], list) or not src["packages"]:
sys.stderr.write(
f"ERROR: source '{src.get('name')}' has an empty/invalid packages list\n"
)
sys.exit(1)
json.dump(sources, sys.stdout)
def cmd_gen_appstream(origin: str, entries_json: str) -> None:
entries = json.loads(entries_json)
print('<?xml version="1.0" encoding="UTF-8"?>')
print(f'<components version="0.14" origin="{escape(origin)}">')
for e in entries:
name = e.get("name", "")
summary = e.get("summary") or f"{name} (mirrored package)"
cid = f"{name}.generic"
print(' <component type="generic">')
print(f" <id>{escape(cid)}</id>")
print(f" <pkgname>{escape(name)}</pkgname>")
print(f" <name>{escape(name)}</name>")
print(f" <summary>{escape(summary)}</summary>")
print(" <metadata_license>CC0-1.0</metadata_license>")
print(" <project_license>unknown</project_license>")
print(" </component>")
print("</components>")
def main() -> None:
ap = argparse.ArgumentParser()
sub = ap.add_subparsers(dest="cmd", required=True)
p1 = sub.add_parser("parse-yaml")
p1.add_argument("path")
p2 = sub.add_parser("gen-appstream")
p2.add_argument("origin")
p2.add_argument("entries_json")
args = ap.parse_args()
if args.cmd == "parse-yaml":
cmd_parse_yaml(args.path)
elif args.cmd == "gen-appstream":
cmd_gen_appstream(args.origin, args.entries_json)
if __name__ == "__main__":
main()