Files
openwrt-sdk-rust/ci/matrix.py
T

109 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Resolve Rust-supported SDK tags for each stable/oldstable release."""
import json
import os
import re
import sys
import urllib.parse
import urllib.request
# OpenWrt packages/lang/rust/rust-values.mk: RUST_ARCH_DEPENDS.
RUST_ARCHES = {
"aarch64", "arm", "i386", "loongarch64", "mips", "mips64",
"mips64el", "mipsel", "powerpc", "powerpc64", "riscv64", "x86_64",
}
def fetch(url):
with urllib.request.urlopen(url, timeout=60) as response:
return response.read().decode()
def sdk_tags():
token = json.loads(fetch("https://ghcr.io/token?service=ghcr.io&scope=repository:openwrt/sdk:pull"))["token"]
url = "https://ghcr.io/v2/openwrt/sdk/tags/list?n=1000"
tags = set()
while url:
request = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"})
with urllib.request.urlopen(request, timeout=60) as response:
tags.update(json.load(response)["tags"])
link = response.headers.get("Link", "")
next_page = re.search(r'<([^>]+)>;\s*rel="next"', link)
url = urllib.parse.urljoin(url, next_page[1]) if next_page else None
return tags
def release_targets(version, tags):
base = f"https://downloads.openwrt.org/releases/{version}"
# Directory names distinguish target/subtarget tags from architecture aliases.
boards = re.findall(r'href="([a-z0-9_+-]+)/"', fetch(f"{base}/targets/"))
suffix = f"-{version}"
targets = sorted({f"{board}/{tag[len(board) + 1:-len(suffix)]}"
for board in boards for tag in tags
if tag.startswith(f"{board}-") and tag.endswith(suffix)})
if not targets:
raise ValueError(f"No SDK images available for OpenWrt {version}")
architectures = json.loads(fetch(f"{base}/.targets.json"))
result = {}
for target in targets:
if not re.fullmatch(r"[a-z0-9_+-]+/[a-z0-9_+-]+", target):
raise ValueError(f"Invalid SDK target: {target!r}")
if target in architectures:
arch = architectures[target]
arch = arch if arch in RUST_ARCHES else arch.split("_", 1)[0]
else:
# SDK-only targets (e.g. Malta) have no profiles in .targets.json.
board, subtarget = target.split("/")
source = f"https://raw.githubusercontent.com/openwrt/openwrt/v{version}/target/linux"
definition = fetch(f"{source}/{board}/{subtarget}/target.mk")
match = re.search(r"^ARCH\s*:?=\s*([a-z0-9_]+)\s*$", definition, re.MULTILINE)
if not match:
definition = fetch(f"{source}/{board}/Makefile")
match = re.search(r"^ARCH\s*:?=\s*([a-z0-9_]+)\s*$", definition, re.MULTILINE)
if not match:
raise ValueError(f"Cannot determine architecture for {target} on {version}")
arch = match[1]
result[target] = arch
return result
def resolve(versions, targets, tags):
versions = [versions[k] for k in ("stable_version", "oldstable_version") if versions.get(k)]
if not versions:
raise ValueError("OpenWrt returned no stable or oldstable version")
targets = [t.strip() for t in targets.split(",") if t.strip()]
if not targets:
raise ValueError("No targets selected")
if "all" in targets and targets != ["all"]:
raise ValueError("Select all or a comma-separated target list")
for target in targets:
if target != "all" and not re.fullmatch(r"[a-z0-9_+-]+/[a-z0-9_+-]+", target):
raise ValueError(f"Invalid target: {target!r}")
for version in versions:
if not re.fullmatch(r"[a-zA-Z0-9_.-]+", version):
raise ValueError(f"Invalid version: {version!r}")
matrix = []
for version in dict.fromkeys(versions):
available = release_targets(version, tags)
selected = available if targets == ["all"] else dict.fromkeys(targets)
supported = [t for t in selected if available.get(t) in RUST_ARCHES]
skipped = [t for t in selected if t not in supported]
print(f"OpenWrt {version}: {len(supported)} targets selected; "
f"{len(available)} SDK images available", file=sys.stderr)
if skipped:
print(f"Skipped unavailable or Rust-unsupported targets: {', '.join(skipped)}", file=sys.stderr)
matrix.extend({"target": t.replace("/", "-"), "version": version} for t in supported)
if not matrix:
raise ValueError("No available Rust-supported SDK targets selected")
return {"include": matrix}
if __name__ == "__main__":
versions = json.loads(fetch("https://downloads.openwrt.org/.versions.json"))
targets = os.environ.get("TARGETS", "all")
matrix = json.dumps(resolve(versions, targets, sdk_tags()), separators=(",", ":"))
print(matrix)
with open(os.environ["GITHUB_OUTPUT"], "a") as output:
output.write(f"matrix={matrix}\n")