SonicDE hard fork and upgrade to 6.7

This commit is contained in:
Anders da Silva Rytter Hansen 2026-08-25 01:26:42 +02:00
commit ca95bace9a
167 changed files with 16629 additions and 419 deletions

85
ci/build-tier.sh Executable file
View file

@ -0,0 +1,85 @@
#!/bin/bash
# Build every package of one tier from build-order.txt for a single mock chroot
# and upload the resulting RPMs to the Forgejo package registry.
#
# Usage: ci/build-tier.sh <tier> <mock-config>
#
# Environment:
# FORGEJO_URL, FORGEJO_OWNER, FORGEJO_TOKEN upload target (upload is skipped
# when FORGEJO_TOKEN is empty)
set -euo pipefail
TIER=${1:?tier name, e.g. tier1}
MOCK_CONFIG=${2:?mock config, e.g. alma+epel-10-x86_64}
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ORDER_FILE="$REPO_DIR/build-order.txt"
RESULT_DIR="${RESULT_DIR:-$HOME/mock-results}"
packages() {
sed -n "/^\[$TIER\]$/,/^\[/p" "$ORDER_FILE" |
grep -v '^\[' | grep -v '^#' | grep -v '^$'
}
write_mock_config() {
cat > /etc/mock/sonicde.cfg <<MOCKEOF
include('/etc/mock/${MOCK_CONFIG}.cfg')
config_opts['root'] = 'sonicde-${MOCK_CONFIG}'
config_opts['yum.conf'] += """
[sonicde-rpm]
name=SonicDE RPM
baseurl=${SONICDE_REPO_URL:-https://pc-rytteren.dk/forge/api/packages/anders/rpm}
enabled=1
gpgcheck=0
[xlibre-xserver]
name=Copr xlibre-xserver
baseurl=${XLIBRE_REPO_URL:-https://download.copr.fedorainfracloud.org/results/@xlibre/xlibre-xserver/rhel+epel-10-\$basearch/}
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://download.copr.fedorainfracloud.org/results/@xlibre/xlibre-xserver/pubkey.gpg
repo_gpgcheck=0
enabled=1
"""
MOCKEOF
}
upload() {
local rpm=$1
if [ -z "${FORGEJO_TOKEN:-}" ]; then
echo " (springer upload over: FORGEJO_TOKEN ikke sat)"
return 0
fi
curl --fail-with-body --silent --show-error \
--user "${FORGEJO_OWNER}:${FORGEJO_TOKEN}" \
--upload-file "$rpm" \
"${FORGEJO_URL}/api/packages/${FORGEJO_OWNER}/rpm/upload"
}
rpmdev-setuptree
write_mock_config
mock --root sonicde --init
for pkg in $(packages); do
spec="$REPO_DIR/$pkg/$pkg.spec"
echo "=== $pkg ($MOCK_CONFIG) ==="
find "$REPO_DIR/$pkg" -maxdepth 1 -type f ! -name '*.spec' \
-exec cp -p {} "$HOME/rpmbuild/SOURCES/" \;
( cd "$REPO_DIR/$pkg" && spectool -g -C "$HOME/rpmbuild/SOURCES/" "$pkg.spec" )
rpmbuild -bs \
--define "_topdir $HOME/rpmbuild" \
--define "_disable_source_fetch 0" \
"$spec"
srpm="$HOME/rpmbuild/SRPMS/$(rpmspec -q --srpm \
--queryformat '%{NAME}-%{VERSION}-%{RELEASE}.src.rpm' "$spec")"
rm -rf "$RESULT_DIR/$pkg"
mock --root sonicde --resultdir "$RESULT_DIR/$pkg" --no-clean --rebuild "$srpm"
find "$RESULT_DIR/$pkg" -name '*.rpm' ! -name '*.src.rpm' | while read -r rpm; do
echo " uploader $(basename "$rpm")"
upload "$rpm"
done
done

120
ci/generate-build-order.py Executable file
View file

@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""Recompute build-order.txt tiers from the BuildRequires of every spec.
The tiers generated from the SonicDE builder dependency graph only describe
source-level dependencies; RPM build dependencies are wider (devel subpackages,
cmake config files, pkgconfig files). This derives the tiers from the specs
themselves so a tier only needs packages from earlier tiers.
"""
import glob
import os
import re
import subprocess
import sys
from collections import defaultdict
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
specs = sorted(glob.glob(os.path.join(REPO, "*", "*.spec")))
specs = [s for s in specs if os.path.basename(s)[:-5] == os.path.basename(os.path.dirname(s))]
provides = {} # capability -> package dir name
brs = defaultdict(set)
def parse(spec):
pkg = os.path.basename(spec)[:-5]
out = subprocess.run(["rpmspec", "-P", spec], capture_output=True, text=True).stdout
if not out:
out = open(spec, errors="replace").read()
names = {pkg}
in_files = False
for line in out.splitlines():
s = line.strip()
m = re.match(r"%package\s+(?:-n\s+(\S+)|(\S+))", s)
if m:
names.add(m.group(1) or f"{pkg}-{m.group(2)}")
continue
if s.startswith("%files"):
in_files = True
elif s.startswith("%") and re.match(r"%(prep|build|install|conf|check|changelog|description|post|pre)", s):
in_files = s.startswith("%files")
m = re.match(r"(Provides|BuildRequires)\s*:\s*(.+)", s)
if m:
for dep in re.split(r"\s+(?=[A-Za-z_%(/])", m.group(2).strip()):
cap = dep.split()[0].replace("%{?_isa}", "").strip()
if not cap or cap.startswith(("%", "(")):
continue
if m.group(1) == "Provides":
names.add(cap)
else:
brs[pkg].add(cap)
if in_files:
for cm in re.findall(r"/cmake/([A-Za-z0-9_.+-]+)/?", s):
names.add(f"cmake({cm})")
for pc in re.findall(r"/([A-Za-z0-9_.+-]+)\.pc\b", s):
names.add(f"pkgconfig({pc})")
for n in names:
provides.setdefault(n, pkg)
return pkg
pkgs = [parse(s) for s in specs]
edges = defaultdict(set)
for pkg in pkgs:
for cap in brs[pkg]:
owner = provides.get(cap)
if owner and owner != pkg:
edges[pkg].add(owner)
# longest-path tier assignment, ignoring back edges of cycles
tier = {}
state = {}
def depth(pkg, stack=()):
if pkg in tier:
return tier[pkg]
if pkg in stack:
return 0
d = 0
for dep in edges[pkg]:
d = max(d, depth(dep, stack + (pkg,)) + 1)
tier[pkg] = d
return d
sys.setrecursionlimit(10000)
for pkg in pkgs:
depth(pkg)
cycles = []
for pkg in pkgs:
for dep in edges[pkg]:
if tier[dep] >= tier[pkg]:
cycles.append((pkg, dep))
groups = defaultdict(list)
for pkg in pkgs:
groups[tier[pkg]].append(pkg)
lines = [
"# Build order for the SonicDE packages, derived from the BuildRequires of",
"# every spec in this repository (see ci/generate-build-order.py).",
"#",
"# Packages inside one tier are independent and can be built in parallel;",
"# the tiers themselves must be built in order.",
"",
]
for i, t in enumerate(sorted(groups), start=1):
lines.append(f"[tier{i}]")
lines += sorted(groups[t])
lines.append("")
open(os.path.join(REPO, "build-order.txt"), "w").write("\n".join(lines).rstrip() + "\n")
print("tiers:", len(groups), "packages:", len(pkgs))
if cycles:
print("dependency cycles (built with the earlier tier's package from the repo):")
for a, b in sorted(set(cycles)):
print(f" {a} <- {b}")

21
ci/install-macros.sh Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Installer for the SonicDE RPM macros without building the package. Used by
# CI so specs referring to macros like %majmin_ver_kf6 parse the same way they
# do on a system with sonic-rpm-macros installed.
set -euo pipefail
cd "$(dirname "$0")/.."
spec=sonic-rpm-macros/sonic-rpm-macros.spec
frameworks=$(rpmspec -q --srpm --queryformat '%{version}\n' "$spec")
plasma=$(sed -n 's/^%global sonicde_plasma_version \(.*\)$/\1/p' "$spec")
target=$(rpm --eval '%{_rpmconfigdir}')/macros.d/macros.kf6
install -Dpm 644 sonic-rpm-macros/macros.kf6 "$target"
sed -i \
-e "s|@@kf6_VERSION@@|$frameworks|g" \
-e "s|@@sonicde_frameworks_VERSION@@|$frameworks|g" \
-e "s|@@sonicde_plasma_VERSION@@|$plasma|g" \
"$target"
echo "installed $target (frameworks $frameworks, plasma $plasma)"

49
ci/parse-specs.sh Executable file
View file

@ -0,0 +1,49 @@
#!/bin/bash
# Parse every spec in the repository and check that all packages listed in
# build-order.txt have a spec (and vice versa).
set -uo pipefail
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_DIR"
fail=0
for spec in */*.spec; do
dir=${spec%%/*}
base=$(basename "$spec" .spec)
if [ "$dir" != "$base" ]; then
echo "FEJL: $spec ligger ikke i mappen $base"
fail=1
continue
fi
if ! out=$(rpmspec -q --srpm --queryformat '%{name} %{version}-%{release}\n' "$spec" 2>&1); then
echo "FEJL: kan ikke parse $spec"
echo "$out"
fail=1
continue
fi
echo "$out"
if warn=$(rpmspec -P "$spec" 2>&1 >/dev/null) && [ -n "$warn" ]; then
echo "ADVARSEL i $spec:"
echo "$warn"
fi
done
ordered=$(grep -v '^\[' build-order.txt | grep -v '^#' | grep -v '^$' | sort)
present=$(for spec in */*.spec; do basename "$spec" .spec; done | sort)
missing=$(comm -13 <(echo "$ordered") <(echo "$present"))
extra=$(comm -23 <(echo "$ordered") <(echo "$present"))
if [ -n "$missing" ]; then
echo "FEJL: pakker uden plads i build-order.txt:"
echo "$missing"
fail=1
fi
if [ -n "$extra" ]; then
echo "FEJL: build-order.txt nævner pakker uden spec:"
echo "$extra"
fail=1
fi
exit $fail