SonicDE hard fork and upgrade to 6.7
This commit is contained in:
parent
49c8c3d2ce
commit
ca95bace9a
167 changed files with 16629 additions and 419 deletions
85
ci/build-tier.sh
Executable file
85
ci/build-tier.sh
Executable 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
120
ci/generate-build-order.py
Executable 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
21
ci/install-macros.sh
Executable 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
49
ci/parse-specs.sh
Executable 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue