stoke/scripts/publish-deb.sh
kimi-reviewer-andresmgsl 33079afb33 Harden publish/build/install scripts and fix audit findings
- publish-deb: keep the token out of the process list (curl -K config
  file via mktemp, no JSON round-trip through node argv), mktemp the
  response file with trap cleanup, add --max-time to the upload
- build-deb: umask 022 + chmod -R a+rX so the payload is world-readable
  even when built with umask 077
- install-apt: only fall back to [trusted=yes] on an actual signature
  verification failure; other apt-get update failures stay fatal
- auth logout: warn that a manually supplied token stays active on the
  server and point at the web UI revocation page
- repo import-batch: resolve the source token inside the per-item try so
  one bad item no longer aborts the whole batch
- auth status: print me.login (the /user response has no username field)
  and exit 1 when not authenticated
2026-07-26 23:07:16 +00:00

70 lines
2.5 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# Publish a .deb to the Forgejo Debian package registry.
#
# Usage: scripts/publish-deb.sh <path-to-deb> [owner] [distribution] [component]
#
# owner registry owner (user or org), default: heavy-duty
# distribution APT distribution, default: stable
# component APT component, default: main
#
# Authentication (first match wins):
# 1. STOKE_TOKEN environment variable
# 2. The token stored by `stoke auth login`
#
# The Forgejo URL defaults to the instance in the stoke config, falling back
# to https://forgejo.heavyduty.builders. Override with FORGE_URL.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DEB="${1:?usage: publish-deb.sh <path-to-deb> [owner] [distribution] [component]}"
OWNER="${2:-heavy-duty}"
DISTRIBUTION="${3:-stable}"
COMPONENT="${4:-main}"
[ -f "$DEB" ] || { echo "error: no such file: $DEB" >&2; exit 1; }
# Read the config field inside node so the values (token included) never
# pass through this script's argv or environment, where they would show up
# in the process list. A corrupt config makes node fail with the real parse
# error — surfaced by `set -e` — instead of a misleading "no token" message.
read_config_field() {
node -e "
const c = require('$ROOT/src/config').loadConfig();
process.stdout.write(String((c && c.$1) || ''));
"
}
TOKEN="${STOKE_TOKEN:-$(read_config_field token)}"
FORGE_URL="${FORGE_URL:-$(read_config_field url)}"
FORGE_URL="${FORGE_URL:-https://forgejo.heavyduty.builders}"
[ -n "$TOKEN" ] || { echo "error: no token. Set STOKE_TOKEN or run: stoke auth login" >&2; exit 1; }
# The token goes to curl through a config file (passed with -K) instead of a
# -H argument so it never appears in the process list; response body and
# config file are mktemp'd and cleaned up on exit.
CURL_CONFIG="$(mktemp)"
RESPONSE="$(mktemp)"
trap 'rm -f "$CURL_CONFIG" "$RESPONSE"' EXIT
chmod 0600 "$CURL_CONFIG"
printf 'header = "Authorization: token %s"\n' "$TOKEN" > "$CURL_CONFIG"
URL="$FORGE_URL/api/packages/$OWNER/debian/pool/$DISTRIBUTION/$COMPONENT/upload"
echo "Uploading $(basename "$DEB") to $URL"
STATUS="$(curl -sS -o "$RESPONSE" -w '%{http_code}' --max-time 300 \
-X PUT -K "$CURL_CONFIG" \
--upload-file "$DEB" "$URL")"
case "$STATUS" in
201) echo "Published." ;;
409) echo "Already published (409): this exact version already exists in the registry." ;;
*)
echo "error: upload failed with HTTP $STATUS" >&2
cat "$RESPONSE" >&2 || true
exit 1
;;
esac