#!/usr/bin/env bash # publish-kernel.sh # # Package an entry from the local banger kernel catalog as a tar.zst, # upload it to the public R2 bucket, and patch internal/kernelcat/catalog.json # with the resulting URL + sha256 + size. Run after `banger kernel import`. # # Usage: # scripts/publish-kernel.sh [--description "..."] # # Environment overrides: # RCLONE_REMOTE rclone remote to upload through (default: r2) # RCLONE_BUCKET R2 bucket name (default: banger-kernels) # BASE_URL public URL prefix for the bucket (default: https://kernels.thaloco.com) # BANGER_KERNELS_DIR local catalog directory (default: ~/.local/state/banger/kernels) set -euo pipefail log() { printf '[publish-kernel] %s\n' "$*" >&2; } die() { log "$*"; exit 1; } usage() { cat < [--description ""] Reads the locally-imported kernel at \$BANGER_KERNELS_DIR//, packages it as -.tar.zst, uploads to R2, and updates internal/kernelcat/catalog.json. Run \`banger kernel import --from --distro --arch \` first. EOF } RCLONE_REMOTE="${RCLONE_REMOTE:-r2}" RCLONE_BUCKET="${RCLONE_BUCKET:-banger-kernels}" BASE_URL="${BASE_URL:-https://kernels.thaloco.com}" BANGER_KERNELS_DIR="${BANGER_KERNELS_DIR:-$HOME/.local/state/banger/kernels}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" CATALOG_FILE="$REPO_ROOT/internal/kernelcat/catalog.json" NAME="" DESCRIPTION="" while [[ $# -gt 0 ]]; do case "$1" in -d|--description) DESCRIPTION="${2:-}"; shift 2;; -h|--help) usage; exit 0;; --) shift; break;; -*) die "unknown flag: $1";; *) if [[ -z "$NAME" ]]; then NAME="$1"; shift else die "unexpected positional arg: $1" fi ;; esac done [[ -n "$NAME" ]] || { usage; exit 1; } for tool in jq rclone tar zstd sha256sum stat curl; do command -v "$tool" >/dev/null 2>&1 || die "missing required tool: $tool" done [[ -f "$CATALOG_FILE" ]] || die "catalog file not found: $CATALOG_FILE" SRC="$BANGER_KERNELS_DIR/$NAME" [[ -d "$SRC" ]] || die "$SRC does not exist; run 'banger kernel import $NAME --from ' first" [[ -f "$SRC/vmlinux" ]] || die "$SRC/vmlinux missing" [[ -f "$SRC/manifest.json" ]] || die "$SRC/manifest.json missing" DISTRO="$(jq -r '.distro // ""' "$SRC/manifest.json")" ARCH="$(jq -r '.arch // ""' "$SRC/manifest.json")" KERNEL_VERSION="$(jq -r '.kernel_version // ""' "$SRC/manifest.json")" [[ -n "$ARCH" ]] || ARCH="x86_64" STAGE="$(mktemp -d)" trap 'rm -rf "$STAGE"' EXIT TARBALL_NAME="${NAME}-${ARCH}.tar.zst" TARBALL="$STAGE/$TARBALL_NAME" INCLUDES=(vmlinux) [[ -f "$SRC/initrd.img" ]] && INCLUDES+=(initrd.img) [[ -d "$SRC/modules" ]] && INCLUDES+=(modules) log "packaging ${INCLUDES[*]} from $SRC" ( cd "$SRC" && tar -cf - "${INCLUDES[@]}" ) | zstd -19 --long -T0 -q -o "$TARBALL" SHA256="$(sha256sum "$TARBALL" | awk '{print $1}')" SIZE="$(stat -c '%s' "$TARBALL")" HUMAN_SIZE="$(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "${SIZE}B")" log "tarball $TARBALL_NAME: $HUMAN_SIZE, sha256 $SHA256" log "uploading to $RCLONE_REMOTE:$RCLONE_BUCKET/$TARBALL_NAME" rclone copyto "$TARBALL" "$RCLONE_REMOTE:$RCLONE_BUCKET/$TARBALL_NAME" URL="$BASE_URL/$TARBALL_NAME" log "verifying $URL is reachable" HEAD_STATUS="$(curl -fsSI -o /dev/null -w '%{http_code}' "$URL" || true)" if [[ "$HEAD_STATUS" != "200" ]]; then die "uploaded tarball is not publicly reachable at $URL (HTTP $HEAD_STATUS); check bucket public-access config" fi log "patching $CATALOG_FILE" NEW_ENTRY="$(jq -n \ --arg name "$NAME" \ --arg distro "$DISTRO" \ --arg arch "$ARCH" \ --arg kver "$KERNEL_VERSION" \ --arg url "$URL" \ --arg sha "$SHA256" \ --argjson size "$SIZE" \ --arg desc "$DESCRIPTION" \ '{ name: $name, distro: $distro, arch: $arch, kernel_version: $kver, tarball_url: $url, tarball_sha256: $sha, size_bytes: $size, description: $desc } | with_entries(select(.value != null and .value != ""))')" CATALOG_TMP="$(mktemp)" jq --arg name "$NAME" --argjson new "$NEW_ENTRY" ' .version = (.version // 1) | .entries = (((.entries // []) | map(select(.name != $name))) + [$new]) | .entries |= sort_by(.name) ' "$CATALOG_FILE" > "$CATALOG_TMP" mv "$CATALOG_TMP" "$CATALOG_FILE" log "done" log "next steps:" log " git diff -- $CATALOG_FILE" log " git add $CATALOG_FILE && git commit -m 'kernel catalog: add/update $NAME'" log " make build # rebuild banger so the new catalog is embedded"