#!/usr/bin/env bash
# /srv/repo/bin/publish.sh — forced-command script for the ciupload SSH key.
#
# Receives a gzipped tar of signed .deb / .rpm artifacts on stdin from the
# packages.yml CI workflow. Validates signatures, integrates each artifact
# into the matching repo tree, regenerates + re-signs the index metadata.
#
# Runs as user `ciupload` (locked-down: nologin shell, no sudo, no groups).
# The only mutation it can make is writing into the staging dir under
# /srv/repo/incoming and whatever paths aptly/createrepo_c rewrite — and
# even those are gated on signature verification before any state change.
#
# Failure modes (all leave the public repo state unchanged):
#   - tar extraction fails → exit 1
#   - any .deb missing its .asc → exit 1
#   - any signature verification fails → exit 1
#   - aptly/createrepo_c failure → exit 1; partial state possible. Acceptable
#     because publish.sh is idempotent: re-running with the same tar overwrites.

set -euo pipefail
umask 022

GPG_KEY='release@interposed.ai'
GPG_PASSPHRASE_FILE="$HOME/.gpg-passphrase"
APT_REPO_NAME='interposed-stable'
APT_DISTRIBUTION='stable'
DNF_DIR='/srv/repo/dnf'
LOG_FILE='/var/log/repo-publishes.log'

log() {
    local ts
    ts="$(date -u +%FT%TZ)"
    printf '[%s] %s\n' "$ts" "$*" | tee -a "$LOG_FILE" >&2
}

die() {
    log "FATAL: $*"
    exit 1
}

# 1. Receive the tar bundle on stdin
TMP="$(mktemp -d -p /srv/repo/incoming staging.XXXXXX)"
trap 'rm -rf "$TMP"' EXIT

log "publish.sh starting; tmp=$TMP"

if ! tar -xzf - -C "$TMP"; then
    die "tar extraction failed (bundle malformed or empty)"
fi

shopt -s nullglob
DEBS=("$TMP"/*.deb)
RPMS=("$TMP"/*.rpm)
shopt -u nullglob

if [ ${#DEBS[@]} -eq 0 ] && [ ${#RPMS[@]} -eq 0 ]; then
    die "no .deb or .rpm files in the bundle"
fi

log "received ${#DEBS[@]} .deb and ${#RPMS[@]} .rpm files"

# 2. Validate signatures BEFORE touching any repo state
for deb in "${DEBS[@]}"; do
    [ -f "${deb}.asc" ] || die "missing detached signature for $(basename "$deb")"
    if ! gpg --batch --verify "${deb}.asc" "$deb" 2>&1 | grep -q "Good signature"; then
        die "signature verification failed for $(basename "$deb")"
    fi
done

for rpm in "${RPMS[@]}"; do
    if ! rpm --checksig "$rpm" 2>&1 | grep -qiE "pgp.*ok|rsa.*ok|signatures ok|digests signatures ok"; then
        die "rpm signature check failed for $(basename "$rpm")"
    fi
done

log "all signatures verified"

# 3. Integrate the .deb files into the apt repo via aptly
if [ ${#DEBS[@]} -gt 0 ]; then
    for deb in "${DEBS[@]}"; do
        aptly repo add -force-replace "$APT_REPO_NAME" "$deb" >&2
    done
    aptly publish update \
        -batch \
        -passphrase-file="$GPG_PASSPHRASE_FILE" \
        -gpg-key="$GPG_KEY" \
        "$APT_DISTRIBUTION" >&2
    log "apt repo updated with ${#DEBS[@]} package(s)"
fi

# 4. Integrate the .rpm files into the dnf tree, regenerate + sign metadata
if [ ${#RPMS[@]} -gt 0 ]; then
    mkdir -p "$DNF_DIR/packages"
    for rpm in "${RPMS[@]}"; do
        cp -f "$rpm" "$DNF_DIR/packages/"
    done
    createrepo_c --update "$DNF_DIR" >&2

    # Sign repomd.xml detached so dnf clients with repo_gpgcheck=1 verify it
    gpg --batch --yes --pinentry-mode loopback \
        --passphrase-file "$GPG_PASSPHRASE_FILE" \
        --detach-sign --armor --local-user "$GPG_KEY" \
        --output "$DNF_DIR/repodata/repomd.xml.asc" \
        "$DNF_DIR/repodata/repomd.xml"
    log "dnf repo updated with ${#RPMS[@]} package(s)"
fi

log "publish.sh complete: ${#DEBS[@]} deb + ${#RPMS[@]} rpm"
exit 0
