#!/usr/bin/env bash # # release.sh – baut ein sauberes XPI, lädt es als Gitea-Release hoch und trägt es # in updates.json fürs Auto-Update ein. # # KEIN Signieren: Thunderbird verlangt für self-distributed Add-ons keine # Mozilla-Signatur, und addons.thunderbird.net (ATN) zeichnet Thunderbird-Add-ons # ohnehin nicht gegen (jede Version bleibt `signed=false`; "web-ext sign" scheitert # mit "no signed files were found"). Wir verteilen also das unsignierte XPI direkt # über Gitea – genau wie schon 0.8.0, das nie bei ATN war. # # WICHTIG fürs Auto-Update: update_url und Release-Asset müssen ANONYM (ohne Login) # erreichbar sein. Bei Gitea heißt das: Repo öffentlich UND instanzweit # REQUIRE_SIGNIN_VIEW=false (in app.ini). Sonst bekommt Thunderbird beim Update-Check # nur die Login-Seite und aktualisiert nie. # # Benötigt: bash, node, curl, git. Zum Bauen zusätzlich npx (web-ext); im --xpi-Modus # nicht nötig. # # Secrets kommen aus der Umgebung bzw. aus scripts/.env (gitignored, NIE committen): # GITEA_TOKEN – Gitea Personal Access Token (Scope: write:repository) # Optionale Overrides (sonst aus dem git-Remote "origin" abgeleitet): # GITEA_BASE, GITEA_OWNER, GITEA_REPO, REPO_DIR # # Aufruf: # scripts/release.sh # baut sauberes XPI, Version aus manifest.json # scripts/release.sh 0.9.0 # setzt zuerst diese Version, dann bauen # scripts/release.sh --xpi # fertiges XPI/ZIP veröffentlichen (kein Build) # scripts/release.sh --xpi 0.9.0 # # Verifikation nach jedem Release (anonym, wie Thunderbird): # curl -L .../raw/branch/main/updates.json → muss JSON liefern, nicht die Login-Seite # curl -L .../releases/download/vX/...xpi → Content-Type application/octet-stream # set -euo pipefail # --- Argumente: optional vorsigniertes XPI (--xpi) -------------------------- PRESIGNED="" if [ "${1:-}" = "--xpi" ]; then PRESIGNED="${2:-}"; shift 2 [ -n "$PRESIGNED" ] && [ -f "$PRESIGNED" ] || { echo "FEHLER: --xpi braucht eine existierende Datei." >&2; exit 1; } fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # .env (falls vorhanden) laden – liefert die Secrets, ohne sie ins Repo zu schreiben. if [ -f "$SCRIPT_DIR/.env" ]; then set -a; . "$SCRIPT_DIR/.env"; set +a fi # Projektwurzel: Standard = ein Ordner über diesem Skript. Bei abweichendem Ort # REPO_DIR setzen. Es muss eine manifest.json enthalten. REPO_DIR="${REPO_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}" MANIFEST="$REPO_DIR/manifest.json" UPDATES="$REPO_DIR/updates.json" ARTIFACTS="$REPO_DIR/web-ext-artifacts" die() { echo "FEHLER: $*" >&2; exit 1; } need() { command -v "$1" >/dev/null 2>&1 || die "'$1' nicht gefunden. Bitte installieren."; } # --- Vorbedingungen ----------------------------------------------------------- need node; need curl; need git [ -f "$MANIFEST" ] || die "manifest.json nicht gefunden ($MANIFEST). Ggf. REPO_DIR setzen." [ -f "$UPDATES" ] || die "updates.json nicht gefunden ($UPDATES)." : "${GITEA_TOKEN:?GITEA_TOKEN fehlt (scripts/.env oder Umgebung)}" if [ -z "$PRESIGNED" ]; then # Sauberes Paket wird mit "web-ext build" erzeugt (kein Signieren, daher # versionsunabhängig). Per WEBEXT_CMD überschreibbar. need npx WEBEXT="${WEBEXT_CMD:-npx --yes web-ext}" fi # --- Version setzen / lesen --------------------------------------------------- if [ "${1:-}" ]; then node -e 'const fs=require("fs"),p=process.argv[1],v=process.argv[2]; const m=JSON.parse(fs.readFileSync(p,"utf8"));m.version=v; fs.writeFileSync(p,JSON.stringify(m,null,2)+"\n");' "$MANIFEST" "$1" echo "manifest.json → Version $1" fi VERSION="$(node -p "require('$MANIFEST').version")" ADDON_ID="$(node -p "require('$MANIFEST').applications.gecko.id")" [ -n "$VERSION" ] && [ -n "$ADDON_ID" ] || die "Version/Add-on-ID konnten nicht gelesen werden." TAG="v${VERSION}" XPI_NAME="docuware-ablage-${VERSION}.xpi" # --- Gitea-Koordinaten aus dem Remote ableiten (oder per Env überschreiben) --- REMOTE_URL="$(git -C "$REPO_DIR" remote get-url origin)" case "$REMOTE_URL" in git@*) host="${REMOTE_URL#git@}"; host="${host%%:*}"; path="${REMOTE_URL#*:}";; ssh://*) rest="${REMOTE_URL#ssh://}"; rest="${rest#*@}"; host="${rest%%/*}"; path="${rest#*/}";; http://*|https://*) rest="${REMOTE_URL#*://}"; host="${rest%%/*}"; path="${rest#*/}";; *) die "Remote-URL-Form nicht erkannt: $REMOTE_URL";; esac path="${path%.git}" GITEA_BASE="${GITEA_BASE:-https://$host}" GITEA_OWNER="${GITEA_OWNER:-${path%%/*}}" GITEA_REPO="${GITEA_REPO:-${path#*/}}" API="$GITEA_BASE/api/v1/repos/$GITEA_OWNER/$GITEA_REPO" BRANCH="$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD)" UPDATE_LINK="$GITEA_BASE/$GITEA_OWNER/$GITEA_REPO/releases/download/$TAG/$XPI_NAME" echo "Release $VERSION (Add-on $ADDON_ID)" echo " Gitea : $GITEA_BASE/$GITEA_OWNER/$GITEA_REPO (Branch $BRANCH)" echo " Asset : $UPDATE_LINK" # --- 1) XPI besorgen: fertiges Paket (--xpi) oder sauber selbst bauen ----------- mkdir -p "$ARTIFACTS" if [ -n "$PRESIGNED" ]; then echo "→ Nutze fertiges Paket: $PRESIGNED" cp -f "$PRESIGNED" "$ARTIFACTS/$XPI_NAME" else echo "→ Baue sauberes XPI (web-ext build) …" rm -f "$ARTIFACTS"/*.zip "$ARTIFACTS"/*.xpi 2>/dev/null || true # --ignore-files hält Secrets/Build-Kram raus: scripts/ (mit .env!), docs/, # web-ext-artifacts/, *.md, *.xpi, git-Kram und updates.json (gehört NICHT ins XPI). $WEBEXT build \ --source-dir="$REPO_DIR" \ --artifacts-dir="$ARTIFACTS" \ --overwrite-dest \ --ignore-files "web-ext-artifacts/**" "scripts/**" "docs/**" "*.md" "*.xpi" \ ".git/**" ".gitignore" "*.env" ".env" "updates.json" BUILT="$(ls -t "$ARTIFACTS"/*.zip 2>/dev/null | head -1)" [ -n "${BUILT:-}" ] && [ -f "$BUILT" ] || die "Kein Paket in $ARTIFACTS gebaut." cp -f "$BUILT" "$ARTIFACTS/$XPI_NAME" fi echo "✓ XPI bereit: $ARTIFACTS/$XPI_NAME" # --- 2) updates.json patchen -------------------------------------------------- node -e 'const fs=require("fs"); const [p,id,ver,link]=process.argv.slice(1); const j=JSON.parse(fs.readFileSync(p,"utf8")); j.addons=j.addons||{}; j.addons[id]=j.addons[id]||{updates:[]}; const u=j.addons[id].updates=j.addons[id].updates||[]; const e={version:ver,update_link:link}, i=u.findIndex(x=>x.version===ver); if(i>=0)u[i]=e; else u.push(e); fs.writeFileSync(p, JSON.stringify(j,null,2)+"\n");' \ "$UPDATES" "$ADDON_ID" "$VERSION" "$UPDATE_LINK" echo "✓ updates.json aktualisiert" # --- 3) Version + updates.json committen und pushen --------------------------- git -C "$REPO_DIR" add manifest.json updates.json if ! git -C "$REPO_DIR" diff --cached --quiet; then git -C "$REPO_DIR" commit -m "Release $VERSION" fi git -C "$REPO_DIR" push origin "$BRANCH" echo "✓ committed & gepusht" # --- 4) Gitea-Release anlegen (oder vorhandenes nehmen) + XPI hochladen ------- echo "→ Lege Gitea-Release $TAG an …" gitea() { curl -fsS -H "Authorization: token $GITEA_TOKEN" "$@"; } REL_JSON="$(gitea -X POST "$API/releases" -H "Content-Type: application/json" \ -d "{\"tag_name\":\"$TAG\",\"target_commitish\":\"$BRANCH\",\"name\":\"$TAG\",\"body\":\"DocuWare Ablage $VERSION\"}" \ 2>/dev/null || true)" if [ -z "$REL_JSON" ]; then echo " (Release/Tag existiert evtl. schon – hole vorhandenes)" REL_JSON="$(gitea "$API/releases/tags/$TAG")" fi RELEASE_ID="$(printf '%s' "$REL_JSON" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).id")" [ -n "$RELEASE_ID" ] || die "Konnte Release-ID nicht ermitteln." echo "→ Lade $XPI_NAME hoch …" gitea -X POST "$API/releases/$RELEASE_ID/assets?name=$XPI_NAME" \ -F "attachment=@$ARTIFACTS/$XPI_NAME;type=application/x-xpinstall" >/dev/null \ || die "Asset-Upload fehlgeschlagen (existiert der Asset-Name schon? Dann altes Asset löschen)." echo echo "✓ Release $VERSION fertig." echo " Download : $UPDATE_LINK" echo " Auto-Update zieht beim nächsten Thunderbird-Check."