#!/usr/bin/env bash set -euo pipefail # Create and publish a Gogs release through the web form. # # Gogs's API only exposes GET /repos/:owner/:repo/releases; POST returns 404 on # this instance. The web form still accepts a normal user/password session, so # this helper logs in locally and posts the release. Credentials are read from # a mode-600 file or prompt and are never written to the repository or printed. # # Usage: # ./tool/gogs_release.sh <body-file> # # Example: # ./tool/gogs_release.sh v1.0.7 "CarDash 1.0.7 — Truly disable Impeller" releases/1.0.7.md # # Optional environment: # GOGS_URL default https://git.krasi.net # GOGS_REPO default kkazakov/car-interface-android # GOGS_TARGET default main # GOGS_USER skip the username prompt # GOGS_PASSWORD skip the password prompt (prefer the credentials file) # GOGS_CREDENTIALS credentials file path (default: $HOME/.config/cardash/gogs_credentials, # then android/keystore/gogs_credentials) # # Credentials file format (mode 600, gitignored): # username=kkazakov # password=... # # If 2FA is enabled, log in through the browser and use the web UI instead. BASE="${GOGS_URL:-https://git.krasi.net}" REPO="${GOGS_REPO:-kkazakov/car-interface-android}" TARGET="${GOGS_TARGET:-main}" if [[ $# -lt 2 || $# -gt 3 ]]; then echo "Usage: $0 <tag> <title> [body-file]" >&2 echo "Example: $0 v1.0.7 'CarDash 1.0.7' releases/1.0.7.md" >&2 exit 2 fi TAG="$1" TITLE="$2" BODY_FILE="${3:-releases/${TAG#v}.md}" if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Tag must look like v1.2.3 (got: $TAG)." >&2 exit 2 fi if [[ ! -f "$BODY_FILE" ]]; then echo "Release body file not found: $BODY_FILE" >&2 exit 2 fi USER_NAME="${GOGS_USER:-}" PASSWORD="${GOGS_PASSWORD:-}" CRED_FILE="${GOGS_CREDENTIALS:-}" if [[ -z "$CRED_FILE" ]]; then for candidate in \ "$HOME/.config/cardash/gogs_credentials" \ "android/keystore/gogs_credentials"; do if [[ -f "$candidate" ]]; then CRED_FILE="$candidate" break fi done fi if [[ -n "$CRED_FILE" ]]; then if [[ ! -r "$CRED_FILE" ]]; then echo "Credentials file is not readable: $CRED_FILE" >&2 exit 2 fi if [[ -z "$USER_NAME" ]]; then USER_NAME="$(sed -n 's/^username=//p' "$CRED_FILE" | head -n 1)" fi if [[ -z "$PASSWORD" ]]; then PASSWORD="$(sed -n 's/^password=//p' "$CRED_FILE" | head -n 1)" fi fi if [[ -z "$USER_NAME" ]]; then read -r -p "Gogs user: " USER_NAME fi if [[ -z "$PASSWORD" ]]; then read -r -s -p "Gogs password for $USER_NAME: " PASSWORD echo fi if [[ -z "$PASSWORD" ]]; then echo "No password supplied." >&2 exit 2 fi umask 077 TMP_DIR="$(mktemp -d)" cleanup() { rm -rf "$TMP_DIR" } trap cleanup EXIT COOKIES="$TMP_DIR/cookies.txt" PASS_FILE="$TMP_DIR/password" printf '%s' "$PASSWORD" > "$PASS_FILE" unset PASSWORD csrf_from() { sed -n 's/.*name="_csrf" value="\([^"]*\)".*/\1/p' "$1" | head -n 1 } LOGIN_PAGE="$TMP_DIR/login.html" if ! curl -fsS -c "$COOKIES" "$BASE/user/login" -o "$LOGIN_PAGE"; then echo "Could not reach $BASE/user/login." >&2 exit 1 fi LOGIN_CSRF="$(csrf_from "$LOGIN_PAGE")" if [[ -z "$LOGIN_CSRF" ]]; then echo "Could not read the Gogs login form. Is GOGS_URL correct?" >&2 exit 1 fi LOGIN_CODE="$(curl -sS -b "$COOKIES" -c "$COOKIES" \ -o "$TMP_DIR/login-result.html" -w '%{http_code}' \ -X POST "$BASE/user/login" \ --data-urlencode "_csrf=$LOGIN_CSRF" \ --data-urlencode "user_name=$USER_NAME" \ --data-urlencode "password@$PASS_FILE" \ --data-urlencode 'remember=on')" if [[ "$LOGIN_CODE" != "302" ]]; then echo "Gogs login failed (HTTP $LOGIN_CODE). Check the username/password or 2FA." >&2 exit 1 fi NEW_PAGE="$TMP_DIR/new-release.html" if ! curl -fsS -b "$COOKIES" -c "$COOKIES" \ "$BASE/$REPO/releases/new" -o "$NEW_PAGE"; then echo "Could not open $BASE/$REPO/releases/new. Is the session valid?" >&2 exit 1 fi if ! grep -q 'name="tag_name"' "$NEW_PAGE"; then echo "The release form did not load; the account may lack write access." >&2 exit 1 fi RELEASE_CSRF="$(csrf_from "$NEW_PAGE")" if [[ -z "$RELEASE_CSRF" ]]; then echo "Could not read the release form CSRF token." >&2 exit 1 fi RELEASE_CODE="$(curl -sS -b "$COOKIES" -c "$COOKIES" \ -D "$TMP_DIR/release-headers.txt" -o "$TMP_DIR/release-result.html" \ -w '%{http_code}' \ -X POST "$BASE/$REPO/releases/new" \ --data-urlencode "_csrf=$RELEASE_CSRF" \ --data-urlencode "tag_name=$TAG" \ --data-urlencode "tag_target=$TARGET" \ --data-urlencode "title=$TITLE" \ --data-urlencode "content@$BODY_FILE")" if [[ "$RELEASE_CODE" != "302" ]]; then echo "Gogs rejected the release (HTTP $RELEASE_CODE)." >&2 echo "Check that $TAG does not already exist and that $TARGET is a branch." >&2 exit 1 fi if grep -q "\"tag_name\": *\"$TAG\"" <(curl -fsS "$BASE/api/v1/repos/$REPO/releases"); then echo "Published $TAG" else echo "Release request accepted; verify at $BASE/$REPO/releases" fi