2026-06-03 13:14:15 +00:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
#
|
|
|
|
|
|
# release.sh – signiert die Extension bei addons.thunderbird.net (Kanal "unlisted",
|
|
|
|
|
|
# also signiert ABER nicht öffentlich gelistet), lädt das signierte XPI als
|
|
|
|
|
|
# Gitea-Release hoch und trägt es in updates.json fürs Auto-Update ein.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Benötigt: bash, node, npx, curl, git. web-ext wird genutzt, wenn global
|
|
|
|
|
|
# installiert; sonst automatisch via "npx web-ext" (kein globales Install nötig).
|
|
|
|
|
|
#
|
|
|
|
|
|
# Secrets kommen aus der Umgebung bzw. aus scripts/.env (gitignored, NIE committen):
|
|
|
|
|
|
# ATN_API_KEY – API-Schlüssel von addons.thunderbird.net (Entwicklerbereich)
|
|
|
|
|
|
# ATN_API_SECRET – zugehöriges Secret
|
|
|
|
|
|
# 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:
|
2026-06-03 13:50:06 +00:00
|
|
|
|
# scripts/release.sh # signiert via ATN, nutzt Version aus manifest.json
|
|
|
|
|
|
# scripts/release.sh 0.9.0 # setzt zuerst diese Version, dann signieren
|
|
|
|
|
|
# scripts/release.sh --xpi <datei.xpi> # bereits signiertes XPI veröffentlichen
|
|
|
|
|
|
# scripts/release.sh --xpi <datei.xpi> 0.9.0
|
|
|
|
|
|
#
|
|
|
|
|
|
# Hinweis ATN: addons.thunderbird.net gibt das signierte XPI nicht zuverlässig über
|
|
|
|
|
|
# die API zurück. In der Praxis: einmal mit dem Skript hochladen (legt die Version an),
|
|
|
|
|
|
# das signierte XPI aus dem ATN-Entwicklerbereich herunterladen, dann mit --xpi
|
|
|
|
|
|
# veröffentlichen. Der --xpi-Modus überspringt das Signieren komplett.
|
2026-06-03 13:14:15 +00:00
|
|
|
|
#
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
2026-06-03 13:50:06 +00:00
|
|
|
|
# --- 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
|
|
|
|
|
|
|
2026-06-03 13:14:15 +00:00
|
|
|
|
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)."
|
2026-06-03 13:50:06 +00:00
|
|
|
|
: "${GITEA_TOKEN:?GITEA_TOKEN fehlt (scripts/.env oder Umgebung)}"
|
|
|
|
|
|
if [ -z "$PRESIGNED" ]; then
|
|
|
|
|
|
# web-ext: ATN nutzt die Signing-API v4. Das geht nur mit web-ext 7.x – ab 8.x
|
|
|
|
|
|
# spricht web-ext ausschließlich die AMO-v5-API und ATN antwortet mit 404.
|
|
|
|
|
|
# Daher fest auf 7.x via npx (kein globales Install nötig); per WEBEXT_CMD überschreibbar.
|
|
|
|
|
|
need npx
|
|
|
|
|
|
WEBEXT="${WEBEXT_CMD:-npx --yes web-ext@^7}"
|
|
|
|
|
|
: "${ATN_API_KEY:?ATN_API_KEY fehlt (scripts/.env oder Umgebung)}"
|
|
|
|
|
|
: "${ATN_API_SECRET:?ATN_API_SECRET fehlt}"
|
|
|
|
|
|
fi
|
2026-06-03 13:14:15 +00:00
|
|
|
|
|
|
|
|
|
|
# --- 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"
|
|
|
|
|
|
|
2026-06-03 13:50:06 +00:00
|
|
|
|
# --- 1) XPI besorgen: entweder bereits signiert (--xpi) oder via ATN signieren --
|
|
|
|
|
|
mkdir -p "$ARTIFACTS"
|
|
|
|
|
|
if [ -n "$PRESIGNED" ]; then
|
|
|
|
|
|
echo "→ Nutze vorsigniertes XPI: $PRESIGNED"
|
|
|
|
|
|
cp -f "$PRESIGNED" "$ARTIFACTS/$XPI_NAME"
|
|
|
|
|
|
else
|
|
|
|
|
|
echo "→ Signiere bei addons.thunderbird.net (unlisted) …"
|
|
|
|
|
|
rm -f "$ARTIFACTS"/*.xpi 2>/dev/null || true
|
|
|
|
|
|
$WEBEXT sign \
|
|
|
|
|
|
--channel=unlisted \
|
|
|
|
|
|
--api-url-prefix="https://addons.thunderbird.net/api/v4" \
|
|
|
|
|
|
--api-key="$ATN_API_KEY" \
|
|
|
|
|
|
--api-secret="$ATN_API_SECRET" \
|
|
|
|
|
|
--source-dir="$REPO_DIR" \
|
|
|
|
|
|
--artifacts-dir="$ARTIFACTS" \
|
|
|
|
|
|
--ignore-files "web-ext-artifacts/**" "scripts/**" "docs/**" "*.md" "*.xpi" \
|
|
|
|
|
|
".git/**" ".gitignore" "*.env" ".env"
|
|
|
|
|
|
SIGNED="$(ls -t "$ARTIFACTS"/*.xpi 2>/dev/null | head -1)"
|
|
|
|
|
|
[ -n "${SIGNED:-}" ] && [ -f "$SIGNED" ] || die "Kein signiertes XPI in $ARTIFACTS gefunden."
|
|
|
|
|
|
cp -f "$SIGNED" "$ARTIFACTS/$XPI_NAME"
|
|
|
|
|
|
fi
|
|
|
|
|
|
echo "✓ XPI bereit: $ARTIFACTS/$XPI_NAME"
|
2026-06-03 13:14:15 +00:00
|
|
|
|
|
|
|
|
|
|
# --- 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."
|