#!/usr/bin/env bash set -euo pipefail # Fenris XBPS publication script (issue #83). # Builds, signs, and publishes XBPS packages to the Fenris-xbps repository. # # Usage: # scripts/xbps-publish.sh --dry-run # Print commands without executing # scripts/xbps-publish.sh --publish # Execute the full publication flow # # Environment: # XBPS_SIGNING_KEY - Path to SSH RSA private key for XBPS signing # (default: ~/.ssh/id_xbps) # SIGNED_BY - Signature identity string # (default: "Fenris Packaging ") # # Spec: native-void-support.md, ADR 0008 # ── Defaults ───────────────────────────────────────────────────────────── DRY_RUN=false PUBLISH=false GITEA_URL="https://git.bongbetic.com" GITEA_OWNER="xavierk" GITEA_REPO="Fenris-xbps" GITEA_BRANCH="stable" ARCH="x86_64" XBPS_SIGNING_KEY="${XBPS_SIGNING_KEY:-$HOME/.ssh/id_xbps}" SIGNED_BY="${SIGNED_BY:-Fenris Packaging }" # ── Parse arguments ────────────────────────────────────────────────────── for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=true ;; --publish) PUBLISH=true ;; --help|-h) echo "Usage: $0 [--dry-run | --publish]" echo "" echo "Modes:" echo " --dry-run Print commands without executing (default)" echo " --publish Execute the full publication flow" echo "" echo "Environment:" echo " XBPS_SIGNING_KEY Path to SSH RSA private key (default: ~/.ssh/id_xbps)" echo " SIGNED_BY Signature identity (default: Fenris Packaging )" exit 0 ;; *) echo "Unknown argument: $arg" >&2 echo "Usage: $0 [--dry-run | --publish]" >&2 exit 1 ;; esac done if ! $DRY_RUN && ! $PUBLISH; then DRY_RUN=true fi # ── Helpers ────────────────────────────────────────────────────────────── _version() { sed -n 's/^version = "\(.*\)"/\1/p' pyproject.toml } _run() { if $DRY_RUN; then echo " $*" else eval "$@" fi } # ── Pre-flight checks ─────────────────────────────────────────────────── if [[ ! -f "$XBPS_SIGNING_KEY" ]]; then echo "ERROR: Signing key not found at $XBPS_SIGNING_KEY" >&2 echo "Generate one with: ssh-keygen -t rsa -b 3072 -f $XBPS_SIGNING_KEY" >&2 exit 1 fi for tool in xbps-create xbps-rindex git; do if ! command -v "$tool" &>/dev/null; then echo "ERROR: Required tool not found: $tool" >&2 exit 1 fi done # ── Main ───────────────────────────────────────────────────────────────── VERSION=$(_version) REVISION="${XBPS_REVISION:-1}" PKGVER="fenris-${VERSION}_${REVISION}" XBPS_FILE="${PKGVER}.${ARCH}.xbps" SOURCE_DIR=$(pwd) XBPS_PATH="${SOURCE_DIR}/${XBPS_FILE}" GIT_USER_NAME=$(git config user.name || true) GIT_USER_EMAIL=$(git config user.email || true) if [[ -z "${GIT_USER_NAME}" || -z "${GIT_USER_EMAIL}" ]]; then echo "ERROR: Configure git user.name and user.email in the source repository before publishing" >&2 exit 1 fi echo "=== Fenris XBPS Publication v${VERSION} ===" echo "" if $DRY_RUN; then echo "[dry-run] Commands below will be executed in --publish mode." echo "" fi # ── Step 1: Build XBPS package ─────────────────────────────────────────── echo "--- Build XBPS package ---" _run "make XBPS_REVISION=${REVISION} package-xbps" echo "" # ── Step 2: Sign package ───────────────────────────────────────────────── echo "--- Sign XBPS package ---" _run "rm -f ${XBPS_PATH}.sig2" _run "xbps-rindex --sign-pkg --privkey ${XBPS_SIGNING_KEY} ${XBPS_PATH}" echo "" # ── Step 3: Clone/update distribution repository ───────────────────────── echo "--- Prepare distribution repository ---" WORK_DIR=$(mktemp -d) _run "git clone ${GITEA_URL}/${GITEA_OWNER}/${GITEA_REPO}.git ${WORK_DIR}" _run "git -C ${WORK_DIR} config user.name '${GIT_USER_NAME}'" _run "git -C ${WORK_DIR} config user.email '${GIT_USER_EMAIL}'" _run "git -C ${WORK_DIR} checkout ${GITEA_BRANCH}" _run "mkdir -p ${WORK_DIR}/${ARCH}" echo "" # ── Step 4: Copy artifacts and update index ────────────────────────────── echo "--- Update repository index ---" _run "cp ${XBPS_PATH} ${XBPS_PATH}.sig2 ${WORK_DIR}/${ARCH}/" _run "(cd ${WORK_DIR} && xbps-rindex --add ${ARCH}/${XBPS_FILE})" _run "(cd ${WORK_DIR} && xbps-rindex --sign --privkey ${XBPS_SIGNING_KEY} --signedby '${SIGNED_BY}' ${ARCH})" echo "" # ── Step 5: Commit and push ────────────────────────────────────────────── echo "--- Commit and push ---" _run "git -C ${WORK_DIR} add -A" _run "git -C ${WORK_DIR} commit -m 'Release fenris ${VERSION}'" _run "git -C ${WORK_DIR} push origin ${GITEA_BRANCH}" echo "" # ── Step 6: Verify publication ─────────────────────────────────────────── echo "--- Verify publication ---" RAW_BASE="${GITEA_URL}/${GITEA_OWNER}/${GITEA_REPO}/raw/branch/${GITEA_BRANCH}/${ARCH}" if $PUBLISH; then # Compare every served byte with the artifact that was indexed and pushed. # A successful HEAD request alone can still hide a stale or incomplete # publication behind the raw endpoint's cache. # Repository signatures are embedded in x86_64-repodata by xbps-rindex; # only package signatures are separate .sig2 files. for artifact in "${XBPS_FILE}" "${XBPS_FILE}.sig2" "x86_64-repodata"; do case "${artifact}" in "${XBPS_FILE}") local_path="${XBPS_PATH}" ;; "${XBPS_FILE}.sig2") local_path="${XBPS_PATH}.sig2" ;; *) local_path="${WORK_DIR}/${ARCH}/${artifact}" ;; esac downloaded="${WORK_DIR}/.${artifact}.download" curl --fail --silent --show-error --location \ --output "${downloaded}" "${RAW_BASE}/${artifact}" cmp -- "${local_path}" "${downloaded}" rm -f "${downloaded}" done else _run "curl --fail --silent --show-error --location --output ${WORK_DIR}/.${XBPS_FILE}.download ${RAW_BASE}/${XBPS_FILE}" _run "cmp -- ${XBPS_PATH} ${WORK_DIR}/.${XBPS_FILE}.download" _run "curl --fail --silent --show-error --location --output ${WORK_DIR}/.${XBPS_FILE}.sig2.download ${RAW_BASE}/${XBPS_FILE}.sig2" _run "cmp -- ${XBPS_PATH}.sig2 ${WORK_DIR}/.${XBPS_FILE}.sig2.download" _run "curl --fail --silent --show-error --location --output ${WORK_DIR}/.x86_64-repodata.download ${RAW_BASE}/x86_64-repodata" _run "cmp -- ${WORK_DIR}/${ARCH}/x86_64-repodata ${WORK_DIR}/.x86_64-repodata.download" _run "rm -f ${WORK_DIR}/.${XBPS_FILE}.download ${WORK_DIR}/.${XBPS_FILE}.sig2.download ${WORK_DIR}/.x86_64-repodata.download" fi echo "" # ── Cleanup ────────────────────────────────────────────────────────────── if $PUBLISH; then rm -rf "${WORK_DIR}" fi # ── Done ───────────────────────────────────────────────────────────────── echo "=== XBPS Publication v${VERSION} complete ===" echo "" echo "Summary:" echo " Package: ${XBPS_FILE}" echo " Repository: ${GITEA_URL}/${GITEA_OWNER}/${GITEA_REPO}" echo " Branch: ${GITEA_BRANCH}" echo " Repository URL: https://git.bongbetic.com/${GITEA_OWNER}/${GITEA_REPO}/raw/branch/${GITEA_BRANCH}/${ARCH}" echo "" echo "Client installation:" echo " echo 'repository=https://git.bongbetic.com/${GITEA_OWNER}/${GITEA_REPO}/raw/branch/${GITEA_BRANCH}/${ARCH}' | sudo tee /etc/xbps.d/fenris.conf" echo " sudo xbps-install -M -S fenris" echo "" echo "Key ceremony: delete the private key after publication." echo " See docs/install/signing-key-ceremony.md"