#!/usr/bin/env bash # Generate Android release signing material + upload Forgejo Actions secrets for # Obtainium APK builds — without printing secret values. # # Usage (from anywhere): # bash /home/yuna/git/frosty/scripts/setup-forgejo-release-secrets.sh # # What it does: # 1. Writes secrets to ~/.config/frosty/forgejo-release-secrets.env (mode 600) # — generates a new upload keystore + passwords if the file is missing # — merges CLIENT_ID / SECRET from frosty/.env when present (never echoes them) # 2. PUTs each secret to Forgejo Actions on copilot/frosty # # Re-run safely: existing secrets file is reused (keystore not regenerated) unless # FORCE_REGENERATE=1 bash …/setup-forgejo-release-secrets.sh # # Twitch Helix: if CLIENT_ID/SECRET are still empty after merge, the script stops # before upload and tells you to edit the secrets file (add the two lines) then re-run. set -euo pipefail REPO="${REPO:-copilot/frosty}" FORGEJO_URL="${FORGEJO_URL:-https://forgejo.fsociety00.cc}" TOKEN_FILE="${TOKEN_FILE:-${HOME}/.config/forgejo/token}" SECRETS_DIR="${SECRETS_DIR:-${HOME}/.config/frosty}" SECRETS_FILE="${SECRETS_FILE:-${SECRETS_DIR}/forgejo-release-secrets.env}" KEYSTORE_FILE="${KEYSTORE_FILE:-${SECRETS_DIR}/upload-keystore.jks}" FROSTY_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ENV_FILE="${ENV_FILE:-${FROSTY_ROOT}/.env}" KEY_ALIAS_DEFAULT="${KEY_ALIAS_DEFAULT:-upload}" need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing command: $1" >&2 exit 1 } } need_cmd curl need_cmd python3 need_cmd openssl need_cmd keytool need_cmd base64 if [[ ! -f "$TOKEN_FILE" ]]; then echo "ERROR: Forgejo token not found: $TOKEN_FILE" >&2 exit 1 fi TOKEN="$(tr -d '[:space:]' <"$TOKEN_FILE")" [[ -n "$TOKEN" ]] || { echo "ERROR: empty token in $TOKEN_FILE" >&2 exit 1 } mkdir -p "$SECRETS_DIR" chmod 700 "$SECRETS_DIR" rand_pw() { # URL/shell-safe password, no quotes openssl rand -base64 24 | tr -d '\n/+=\r' | head -c 32 } write_secrets_file() { local store_pw key_pw alias b64 store_pw="$1" key_pw="$2" alias="$3" b64="$4" # Atomic write; never echo values local tmp tmp="$(mktemp "${SECRETS_DIR}/.secrets.XXXXXX")" umask 077 cat >"$tmp" <&2 # Response may contain error text only — still avoid dumping request body python3 -c 'import pathlib; p=pathlib.Path("/tmp/frosty-forgejo-secret-resp.json"); print(p.read_text()[:500] if p.exists() else "")' >&2 || true exit 1 fi echo " set ${name} (HTTP ${code})" } # --- generate if needed --- if [[ ! -f "$SECRETS_FILE" || "${FORCE_REGENERATE:-0}" == "1" ]]; then echo "→ Generating new upload keystore + passwords → ${SECRETS_FILE}" STORE_PW="$(rand_pw)" KEY_PW="$(rand_pw)" ALIAS="$KEY_ALIAS_DEFAULT" rm -f "$KEYSTORE_FILE" keytool -genkeypair \ -keystore "$KEYSTORE_FILE" \ -storetype JKS \ -storepass "$STORE_PW" \ -keypass "$KEY_PW" \ -alias "$ALIAS" \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ -dname "CN=Frosty Forgejo Release, OU=Newas, O=fsociety00, L=Home, ST=NA, C=DE" \ >/dev/null 2>&1 chmod 600 "$KEYSTORE_FILE" B64="$(base64 -w0 "$KEYSTORE_FILE" 2>/dev/null || base64 "$KEYSTORE_FILE" | tr -d '\n')" write_secrets_file "$STORE_PW" "$KEY_PW" "$ALIAS" "$B64" # Drop locals unset STORE_PW KEY_PW B64 echo " wrote keystore ${KEYSTORE_FILE}" echo " wrote secrets file (mode 600)" else echo "→ Reusing existing secrets file: ${SECRETS_FILE}" echo " (set FORCE_REGENERATE=1 to mint a new keystore)" fi echo "→ Merging Twitch CLIENT_ID/SECRET from ${ENV_FILE} if present…" merge_twitch_from_dotenv || true if ! status="$(secrets_complete)"; then echo >&2 echo "Twitch Helix secrets still missing in ${SECRETS_FILE}." >&2 echo "Edit that file and set CLIENT_ID=… and SECRET=… (from https://dev.twitch.tv/console)," >&2 echo "or put them in ${ENV_FILE}, then re-run this script." >&2 echo "Signing secrets are already generated; they will be uploaded together." >&2 exit 3 fi echo "→ Secrets file complete (${status})" echo "→ Uploading Actions secrets to ${FORGEJO_URL}/${REPO} (values not printed)" for name in RELEASE_KEYSTORE_BASE64 STORE_PASSWORD KEY_ALIAS KEY_PASSWORD CLIENT_ID SECRET; do put_secret "$name" done echo echo "Done. Secrets live only in:" echo " ${SECRETS_FILE}" echo " ${KEYSTORE_FILE}" echo " Forgejo Actions → ${REPO}" echo echo "Next: Forgejo → Actions → Release APK → Run workflow" echo "Obtainium: https://forgejo.fsociety00.cc/api/v1/repos/copilot/frosty/releases" echo "APK filter: frosty-arm64-v8a-v.*\\.apk"