mirror of
https://github.com/archr-linux/Arch-R.git
synced 2026-07-12 18:19:42 -07:00
faa9c6f6c4
Installing the linux package through pacman overwrote modules.dep with the copy generated at kernel build time, which predates every out-of-tree module: the joypad and the WiFi drivers vanished from the index and the console booted with dead input and no network (reproduced on real hardware). Two independent layers fix it: the repo generator no longer ships any depmod-generated index inside packages (modules.builtin*/order stay, they are kernel-owned depmod inputs), and a new alpm hook rebuilds the index over the full overlay tree after any transaction that touches kernel modules. linux goes to rel 3 so updated devices pick the clean package.
241 lines
10 KiB
Bash
Executable File
241 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
# Copyright (C) 2026 ArchR (https://github.com/archr-linux/Arch-R)
|
|
#
|
|
# gen-pacman-repo: build a pacman repo (archr-core.db + .pkg.tar.zst per
|
|
# package) out of a finished ArchR build tree.
|
|
#
|
|
# Inputs:
|
|
# BUILD_DIR - the build.* dir that has install_pkg/<pkg>/ trees
|
|
# (defaults to the most recent build.*-RK3326 under cwd)
|
|
# OUT_DIR - where the .pkg.tar.zst and .db get written
|
|
# (defaults to ${BUILD_DIR}/../repo-out)
|
|
# CHANNEL - stable|next|dev. Goes into PKGINFO and the .db name
|
|
# (defaults to dev)
|
|
# SIGNER - GPG key id to sign packages and the .db with.
|
|
# If empty, packages are not signed (good for local CI).
|
|
#
|
|
# Output:
|
|
# ${OUT_DIR}/archr.db (+.sig if signed)
|
|
# ${OUT_DIR}/archr.files
|
|
# ${OUT_DIR}/<pkg>-<ver>-<rel>-aarch64.pkg.tar.zst (+.sig)
|
|
#
|
|
# This is the GitHub-Releases-as-mirror path: after gen-pacman-repo,
|
|
# `gh release create repo-${CHANNEL} ${OUT_DIR}/*` uploads everything
|
|
# and pacman clients pick it up via the mirrorlist URL.
|
|
|
|
set -euo pipefail
|
|
|
|
BUILD_DIR="${BUILD_DIR:-$(ls -1dt build.*-RK3326*aarch64* 2>/dev/null | head -1)}"
|
|
[ -z "${BUILD_DIR:-}" ] && { echo "BUILD_DIR not set and no build.*-RK3326*aarch64* found"; exit 2; }
|
|
[ -d "${BUILD_DIR}/install_pkg" ] || { echo "no install_pkg/ under ${BUILD_DIR}"; exit 2; }
|
|
|
|
OUT_DIR="${OUT_DIR:-${BUILD_DIR}/../repo-out}"
|
|
CHANNEL="${CHANNEL:-dev}"
|
|
SIGNER="${SIGNER:-}"
|
|
|
|
REPO_NAME="archr"
|
|
ARCH="aarch64"
|
|
|
|
mkdir -p "${OUT_DIR}"
|
|
echo "gen-pacman-repo: build=${BUILD_DIR} out=${OUT_DIR} channel=${CHANNEL} signer=${SIGNER:-none}"
|
|
|
|
# Per-package metadata. Today the package.mk format does not carry a
|
|
# clean semver, so we synthesize: PKG_VERSION if numeric, else the
|
|
# build date. Real PKGBUILDs replace this when Arch-ification 2.3 lands.
|
|
|
|
build_one_pkg() {
|
|
local src="$1"
|
|
local dirname
|
|
dirname="$(basename "${src}")"
|
|
|
|
# Skip anything that is not an actually built package tree.
|
|
[ -d "${src}" ] || return 0
|
|
|
|
# The install_pkg dir name is "<pkgname>-<version_or_githash>". Strip
|
|
# the trailing suffix to recover the package name; the suffix is
|
|
# either a 40-char git hash or starts with a digit (semver).
|
|
local name version
|
|
name="$(echo "${dirname}" | sed -E 's/-[0-9a-f]{40}$//; s/-[0-9].*$//')"
|
|
|
|
# Try to recover the exact PKG_VERSION from a matching package.mk
|
|
# under either the ArchR overlay or the top-level packages tree.
|
|
# Search any depth; multiple categories share the same package name
|
|
# in some cases, take the ArchR overlay first.
|
|
local pkgmk=""
|
|
pkgmk="$(find projects/ArchR/packages -mindepth 2 -maxdepth 6 \
|
|
-path "*/${name}/package.mk" -print -quit 2>/dev/null)"
|
|
if [ -z "${pkgmk}" ]; then
|
|
pkgmk="$(find packages -mindepth 2 -maxdepth 6 \
|
|
-path "*/${name}/package.mk" -print -quit 2>/dev/null)"
|
|
fi
|
|
|
|
version=""
|
|
if [ -n "${pkgmk}" ]; then
|
|
version="$(awk -F'"' '/^PKG_VERSION/ {print $2; exit}' "${pkgmk}")"
|
|
fi
|
|
# Some package.mk files set PKG_VERSION dynamically, e.g.:
|
|
# PKG_VERSION="$(get_pkg_version gstreamer)"
|
|
# awk pulls the unresolved shell expansion as a string, which then
|
|
# turns into garbage like "..get_pkg_version.gstreamer." after the
|
|
# charset filter. Detect any $( ... ), :- or ${ and bail to the
|
|
# dirname fallback below, which holds the version the build system
|
|
# actually substituted.
|
|
case "${version}" in
|
|
*'$('* | *'${'* ) version="" ;;
|
|
esac
|
|
# Fallback: pull the suffix back out of the dirname.
|
|
if [ -z "${version}" ]; then
|
|
version="${dirname#${name}-}"
|
|
fi
|
|
version="${version:-$(date +%Y%m%d)}"
|
|
|
|
# pacman version must match [a-zA-Z0-9.+_]+. Long hashes are fine
|
|
# by this charset, but '/' or '~' would break: tr the rest. Use
|
|
# printf instead of echo to avoid the trailing newline being
|
|
# turned into a stray '.' by tr -c.
|
|
version="$(printf '%s' "${version}" | tr -c 'a-zA-Z0-9.+_' '.')"
|
|
|
|
# pkgrel comes from the package's PKG_REV (the standard knob for "same
|
|
# upstream version, new package build", e.g. a changed local overlay
|
|
# like a joypad autoconfig). Hardcoding 1 here meant any local-only
|
|
# change never produced a newer package, so pacman never saw it as an
|
|
# update. Default to 1 when PKG_REV is unset or dynamic.
|
|
local pkgrel=1
|
|
if [ -n "${pkgmk}" ]; then
|
|
local rev
|
|
rev="$(awk -F'"' '/^PKG_REV/ {print $2; exit}' "${pkgmk}")"
|
|
case "${rev}" in
|
|
''|*'$('* | *'${'* ) : ;;
|
|
*) pkgrel="${rev}" ;;
|
|
esac
|
|
fi
|
|
local outfile="${OUT_DIR}/${name}-${version}-${pkgrel}-${ARCH}.pkg.tar.zst"
|
|
|
|
# .PKGINFO sidecar that pacman reads on install. pkgbase is
|
|
# required by repo-add (pacman 7+); xdata pkgtype=pkg matches
|
|
# makepkg output so clients treat us as a normal split-package.
|
|
local pkginfo="${src}/.PKGINFO"
|
|
cat >"${pkginfo}" <<EOF
|
|
# Generated by gen-pacman-repo
|
|
pkgname = ${name}
|
|
pkgbase = ${name}
|
|
xdata = pkgtype=pkg
|
|
pkgver = ${version}-${pkgrel}
|
|
pkgdesc = ArchR ${name} (channel: ${CHANNEL})
|
|
url = https://arch-r.io
|
|
builddate = $(date +%s)
|
|
packager = ArchR Build <build@arch-r.io>
|
|
size = $(du -sb "${src}" | cut -f1)
|
|
arch = ${ARCH}
|
|
license = GPL
|
|
EOF
|
|
|
|
# Pack with .PKGINFO as the FIRST tar entry; pacman/repo-add stream
|
|
# the archive and need .PKGINFO up front to validate. Drop the "./"
|
|
# prefix so listings match what makepkg produces.
|
|
(
|
|
cd "${src}"
|
|
filelist="$(mktemp)"
|
|
{
|
|
echo ".PKGINFO"
|
|
# .image is build-system plumbing (raw kernel + symbols the
|
|
# image script consumes); shipping it would dump ~25MB at
|
|
# /.image on every kernel update for nothing.
|
|
find . -mindepth 1 -maxdepth 1 -not -name '.PKGINFO' -not -name '.image' -printf '%P\n'
|
|
} > "${filelist}"
|
|
# The depmod-generated indexes are stale by construction inside a
|
|
# package (the kernel builds them before any out-of-tree module
|
|
# exists); shipping them wiped the joypad and WiFi drivers from
|
|
# modules.dep on every linux update. The device rebuilds them via
|
|
# the archr-depmod hook; modules.builtin*/order stay (kernel-owned
|
|
# depmod inputs).
|
|
tar --zstd -cf "${outfile}" --owner=0 --group=0 \
|
|
--exclude='*/modules/*/modules.dep' \
|
|
--exclude='*/modules/*/modules.dep.bin' \
|
|
--exclude='*/modules/*/modules.alias' \
|
|
--exclude='*/modules/*/modules.alias.bin' \
|
|
--exclude='*/modules/*/modules.symbols' \
|
|
--exclude='*/modules/*/modules.symbols.bin' \
|
|
--exclude='*/modules/*/modules.devname' \
|
|
--exclude='*/modules/*/modules.softdep' \
|
|
--exclude='*/modules/*/modules.weakdep' \
|
|
--exclude='*/modules/*/modules.builtin.bin' \
|
|
--exclude='*/modules/*/modules.builtin.alias.bin' \
|
|
-T "${filelist}"
|
|
rm -f "${filelist}"
|
|
)
|
|
rm -f "${pkginfo}"
|
|
|
|
# Per-package signing is intentionally NOT done here. The repo db
|
|
# carries the SHA256 of every pkg.tar.zst and is itself GPG-signed,
|
|
# which gives pacman an unbroken trust chain without doubling the
|
|
# asset count on GitHub Releases (1000-per-release cap).
|
|
# The signer is still passed to repo-add below so the db gets its
|
|
# own .sig and trust flows from there.
|
|
|
|
echo " + ${name} ${version}-${pkgrel}"
|
|
}
|
|
|
|
echo "gen-pacman-repo: packaging install_pkg/* into .pkg.tar.zst..."
|
|
for src in "${BUILD_DIR}/install_pkg/"*; do
|
|
build_one_pkg "${src}"
|
|
done
|
|
|
|
# repo-add walks the .pkg.tar.zst files and produces archr.db.tar.gz
|
|
# (which pacman renames to archr.db at fetch time) plus archr.files.
|
|
echo "gen-pacman-repo: building repo db..."
|
|
local_db="${OUT_DIR}/${REPO_NAME}.db.tar.gz"
|
|
local_files="${OUT_DIR}/${REPO_NAME}.files.tar.gz"
|
|
rm -f "${local_db}" "${local_files}"
|
|
|
|
if [ -n "${SIGNER}" ]; then
|
|
repo-add --sign --key "${SIGNER}" "${local_db}" "${OUT_DIR}"/*.pkg.tar.zst
|
|
else
|
|
repo-add "${local_db}" "${OUT_DIR}"/*.pkg.tar.zst
|
|
fi
|
|
|
|
# Pacman fetches archr.db (no .tar.gz); the conventional way is to ship
|
|
# the .tar.gz and a .db symlink alongside.
|
|
( cd "${OUT_DIR}" && \
|
|
ln -sf "${REPO_NAME}.db.tar.gz" "${REPO_NAME}.db" && \
|
|
ln -sf "${REPO_NAME}.files.tar.gz" "${REPO_NAME}.files" )
|
|
|
|
# Seed an installed (local) db. Without it the shipped image's
|
|
# /var/lib/pacman/local is empty: `pacman -Q` returns nothing and
|
|
# `pacman -Syu` (archr-update) can only install user addons, never upgrade
|
|
# the base. We register every just-built package as installed (db entry
|
|
# only; the files already live in the image squashfs) into a staging dbpath
|
|
# and tar the resulting local/ tree. It is published as a repo asset and
|
|
# pulled in on first boot by the 004-seed-pacmandb autostart hook (so no
|
|
# image-build wiring is needed; the seed always matches this build).
|
|
echo "gen-pacman-repo: seeding installed (local) db..."
|
|
seed_dbpath="${OUT_DIR}/.seed-dbpath"
|
|
rm -rf "${seed_dbpath}" "${OUT_DIR}/.seed-root"
|
|
mkdir -p "${seed_dbpath}/local" "${seed_dbpath}/sync" "${OUT_DIR}/.seed-root"
|
|
cp "${local_db}" "${seed_dbpath}/sync/${REPO_NAME}.db"
|
|
# The host pacman runs with the host architecture and signature policy,
|
|
# which rejects our unsigned aarch64 payload; give it a minimal config
|
|
# scoped to this seed operation.
|
|
seed_conf="${seed_dbpath}/pacman.conf"
|
|
printf '[options]\nArchitecture = aarch64\nSigLevel = Never\n' > "${seed_conf}"
|
|
if fakeroot pacman -U --dbonly --noconfirm --config "${seed_conf}" \
|
|
--root "${OUT_DIR}/.seed-root" --dbpath "${seed_dbpath}" \
|
|
--cachedir "${OUT_DIR}" \
|
|
"${OUT_DIR}"/*.pkg.tar.zst >/dev/null 2>&1; then
|
|
tar -C "${seed_dbpath}" -czf "${OUT_DIR}/${REPO_NAME}-localdb.tar.gz" local
|
|
echo " wrote ${REPO_NAME}-localdb.tar.gz ($(ls -1 "${seed_dbpath}/local" | wc -l) entries)"
|
|
else
|
|
echo " WARNING: local-db seed failed; base will not be pacman-upgradable" >&2
|
|
fi
|
|
rm -rf "${seed_dbpath}" "${OUT_DIR}/.seed-root"
|
|
|
|
echo ""
|
|
echo "gen-pacman-repo: ready at ${OUT_DIR}"
|
|
ls -1sh "${OUT_DIR}" | head -20
|
|
echo ""
|
|
echo "Next: gh release create repo-${CHANNEL} \\"
|
|
echo " --repo archr-linux/archr-repo \\"
|
|
echo " --title \"ArchR repo ${CHANNEL} $(date +%Y%m%d)\" \\"
|
|
echo " \"${OUT_DIR}\"/*"
|