diff --git a/CLAUDE.md b/CLAUDE.md index 59fda48..a8035fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -180,20 +180,48 @@ go vet ./... # 1. Alle Änderungen committen git add -p && git commit -m "feat: ..." -# 2. Release-Skript (macht Tests → Build → Tag → Push) +# 2. Release-Skript (macht Tests → .deb-Build → Gitea-Upload → Tag → Push) ./scripts/release.sh 1.2.0 # Danach liegt in dist/: -# deklarix_1.2.0_amd64 -# deklarix_1.2.0_arm64 +# deklarix_1.2.0_amd64.deb +# deklarix_1.2.0_arm64.deb ``` ### Was das Release-Skript tut 1. Prüft: sauberer Git-Status (keine uncommitted changes) -2. Führt `./scripts/test.sh` aus (vet + race tests + build-check) -3. Kompiliert für `linux/amd64` und `linux/arm64` -4. Setzt Git-Tag `v` mit Annotierung -5. Pusht `main` + Tag nach `origin` +2. Führt `./scripts/test.sh` aus (vet + race tests + build-check, braucht `DATABASE_URL`) +3. Baut `.deb`-Pakete für `linux/amd64` und `linux/arm64` (`scripts/build.sh`) +4. Lädt die Pakete in Giteas Debian-Paketregistrierung hoch (Kanal `testing`, + Suites `bookworm` + `trixie`) — siehe „Deployment“ unten +5. Setzt Git-Tag `v` mit Annotierung +6. Pusht `main` + Tag nach `origin` + +### Deployment (apt, Infrastruktur-Standard wie bei enconf) + +Updates laufen ausschließlich über ein apt-Paket, nie über manuelles +Kopieren eines Binaries. `.deb`-Pakete landen in Giteas eingebauter +Debian-Paketregistrierung unter der Gitea-Org `projekte` (gleicher +Mechanismus wie beim Schwesterprojekt enconf-webpanel). Zielserver +richten sich einmalig ein: + +```bash +sudo mkdir -p /etc/apt/keyrings +sudo curl -fsSL https://git.netcell-it.de/api/packages/projekte/debian/repository.key \ + -o /etc/apt/keyrings/deklarix-gitea.asc +echo "deb [signed-by=/etc/apt/keyrings/deklarix-gitea.asc] https://git.netcell-it.de/api/packages/projekte/debian $(lsb_release -cs) testing" \ + | sudo tee /etc/apt/sources.list.d/deklarix.list +sudo apt-get update && sudo apt-get install deklarix +``` + +Danach: `apt upgrade` für Updates, `systemctl {start,stop,status} deklarix` +für den Dienst. Konfiguration liegt in `/etc/deklarix/deklarix.env` +(aus `deklarix.env.example` beim Erstinstall kopiert, `DATABASE_URL` ist +dort absichtlich auskommentiert — der Dienst startet erst, wenn sie +gesetzt ist; `postinst` startet ihn sonst bewusst nicht, um keine +Crash-Restart-Schleife gegen einen Platzhalter-Host zu erzeugen). +Aktuell gibt es nur den Kanal `testing` (kein `stable` — es gibt noch +keine Kunden, für die eine Trennung nötig wäre). --- @@ -298,13 +326,23 @@ git push origin main ### Server-Prozess ```bash -# Start (manuell) -PORT=8080 DATABASE_URL=postgres://... ANTHROPIC_API_KEY=... ./dist/deklarix_latest_amd64 & +# Dienst läuft über systemd (installiert per apt, siehe Deployment oben) +sudo systemctl start deklarix +sudo systemctl status deklarix + +# Config: /etc/deklarix/deklarix.env (DATABASE_URL, PORT, ANTHROPIC_API_KEY) # Logs prüfen journalctl -u deklarix -f ``` +### Bekannte Server + +| Rolle | Host | Zugang | +|---|---|---| +| Testsystem (DB-Integrationstests, apt-Verifikation) | 89.163.205.4 | SSH `noroot`, sudo passwortlos | +| Installer-Endpoint (`get.deklarix.com`) | 89.163.205.110 | noch nicht eingerichtet | + --- ## Arbeitsweise für Claude Code diff --git a/packaging/DEBIAN/conffiles b/packaging/DEBIAN/conffiles new file mode 100644 index 0000000..7d3abb5 --- /dev/null +++ b/packaging/DEBIAN/conffiles @@ -0,0 +1 @@ +/etc/deklarix/deklarix.env.example diff --git a/packaging/DEBIAN/control.tmpl b/packaging/DEBIAN/control.tmpl index de79ceb..8f9a32c 100644 --- a/packaging/DEBIAN/control.tmpl +++ b/packaging/DEBIAN/control.tmpl @@ -2,6 +2,10 @@ Package: deklarix Version: VERSION Architecture: ARCH Maintainer: NetCell IT -Description: Deklarix — deklarix.de / deklarix.com - Deklarix Software Package. -Depends: +Homepage: https://deklarix.de +Section: web +Priority: optional +Depends: ca-certificates +Description: Deklarix — Prüfung und Nachweis für Werbe-Content + Pre-Publish-Kennzeichnungsprüfung und revisionssichere Archivierung + veröffentlichter Beiträge für Creator, Agenturen, Marken und Kanzleien. diff --git a/packaging/DEBIAN/postinst b/packaging/DEBIAN/postinst new file mode 100755 index 0000000..788ec59 --- /dev/null +++ b/packaging/DEBIAN/postinst @@ -0,0 +1,65 @@ +#!/bin/bash +# Deklarix — postinst +set -e + +SERVICE_USER="deklarix" +CONFIG_DIR="/etc/deklarix" +DATA_DIR="/var/lib/deklarix" +LOG_DIR="/var/log/deklarix" + +case "$1" in + configure) + # ─── apt-Quelle einrichten/aktualisieren ──────────────────── + # Gleiches Muster wie enconf: Gitea-Debian-Registry, Suite = + # OS-Codename, Komponente = Kanal (aktuell nur "testing"). Ein + # bereits gewählter Kanal bleibt über Upgrades hinweg erhalten. + mkdir -p /etc/apt/keyrings + curl -fsSL "https://git.netcell-it.de/api/packages/projekte/debian/repository.key" \ + -o /etc/apt/keyrings/deklarix-gitea.asc 2>/dev/null || true + if [ -f /etc/apt/keyrings/deklarix-gitea.asc ]; then + DX_CODENAME="$(. /etc/os-release 2>/dev/null; echo "${VERSION_CODENAME:-}")" + case "$DX_CODENAME" in bookworm|trixie) ;; *) DX_CODENAME="trixie" ;; esac + DX_CHANNEL="$(grep -oE 'projekte/debian[[:space:]]+[a-z]+[[:space:]]+(stable|testing)' \ + /etc/apt/sources.list.d/deklarix.list 2>/dev/null | awk '{print $NF}' | head -1)" + case "$DX_CHANNEL" in stable|testing) ;; *) DX_CHANNEL="testing" ;; esac + echo "deb [signed-by=/etc/apt/keyrings/deklarix-gitea.asc] https://git.netcell-it.de/api/packages/projekte/debian $DX_CODENAME $DX_CHANNEL" \ + > /etc/apt/sources.list.d/deklarix.list + fi + + # ─── System-User + Verzeichnisse ──────────────────────────── + if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then + useradd -r -s /usr/sbin/nologin -d "$DATA_DIR" "$SERVICE_USER" + fi + mkdir -p "$DATA_DIR" "$LOG_DIR" "$CONFIG_DIR" + chown "$SERVICE_USER:$SERVICE_USER" "$DATA_DIR" "$LOG_DIR" + + # ─── Konfiguration ─────────────────────────────────────────── + # Erstinstall: Vorlage kopieren, nicht scharf schalten (Secrets + # fehlen noch). Upgrade: bestehende deklarix.env bleibt unberührt. + if [ ! -f "$CONFIG_DIR/deklarix.env" ]; then + cp "$CONFIG_DIR/deklarix.env.example" "$CONFIG_DIR/deklarix.env" + chmod 640 "$CONFIG_DIR/deklarix.env" + chown "root:$SERVICE_USER" "$CONFIG_DIR/deklarix.env" + fi + + systemctl daemon-reload 2>/dev/null || true + systemctl enable deklarix.service >/dev/null 2>&1 || true + + # Nicht blind starten — ohne gesetzte DATABASE_URL würde der Dienst + # nur in eine Restart-Schleife laufen (main.go bricht sonst bewusst + # mit log.Fatal ab, siehe CLAUDE.md "keine stillen Fallbacks"). Die + # Vorlage liefert DATABASE_URL auskommentiert aus — ein Treffer hier + # bedeutet also wirklich "vom Admin gesetzt", nicht den Platzhalter. + if grep -qE '^DATABASE_URL=.+' "$CONFIG_DIR/deklarix.env" 2>/dev/null; then + systemctl restart deklarix.service + else + echo "" + echo " → Deklarix installiert, aber noch nicht gestartet." + echo " DATABASE_URL in $CONFIG_DIR/deklarix.env setzen, dann:" + echo " systemctl start deklarix" + echo "" + fi + ;; +esac + +exit 0 diff --git a/packaging/DEBIAN/prerm b/packaging/DEBIAN/prerm new file mode 100755 index 0000000..0be1bec --- /dev/null +++ b/packaging/DEBIAN/prerm @@ -0,0 +1,11 @@ +#!/bin/bash +# Deklarix — prerm +set -e + +case "$1" in + remove|deconfigure) + systemctl stop deklarix.service 2>/dev/null || true + ;; +esac + +exit 0 diff --git a/packaging/etc/deklarix/deklarix.env.example b/packaging/etc/deklarix/deklarix.env.example new file mode 100644 index 0000000..8eef9c6 --- /dev/null +++ b/packaging/etc/deklarix/deklarix.env.example @@ -0,0 +1,15 @@ +# Deklarix — Konfiguration +# Wird bei der Erstinstallation nach /etc/deklarix/deklarix.env kopiert. +# Diese Datei selbst wird von apt-Updates verwaltet — Anpassungen gehören +# in deklarix.env, nicht hierhin. + +PORT=8080 + +# Pflicht — der Dienst startet nicht ohne gültige DATABASE_URL. Auskommentiert +# lassen, bis eine echte Postgres-Verbindung eingetragen ist: postinst prüft +# genau diese Zeile, um den Dienst nicht blind in eine Restart-Schleife gegen +# einen Platzhalter-Host laufen zu lassen. +#DATABASE_URL=postgres://user:password@host:5432/deklarix?sslmode=require + +# Für internal/extract (Claude-API-Extraktion), sobald angebunden. +#ANTHROPIC_API_KEY= diff --git a/packaging/etc/systemd/system/deklarix.service b/packaging/etc/systemd/system/deklarix.service new file mode 100644 index 0000000..7b41b02 --- /dev/null +++ b/packaging/etc/systemd/system/deklarix.service @@ -0,0 +1,23 @@ +[Unit] +Description=Deklarix +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=deklarix +Group=deklarix +EnvironmentFile=/etc/deklarix/deklarix.env +ExecStart=/usr/bin/deklarix +WorkingDirectory=/var/lib/deklarix +Restart=on-failure +RestartSec=5 + +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +ReadWritePaths=/var/lib/deklarix /var/log/deklarix + +[Install] +WantedBy=multi-user.target diff --git a/scripts/build.sh b/scripts/build.sh index 1beb93d..e78eb2f 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,23 +1,19 @@ #!/usr/bin/env bash -# Deklarix — Build-Skript (amd64 + arm64) +# Deklarix — .deb-Pakete bauen (amd64 + arm64) # # Verwendung: # ./scripts/build.sh [version] # Beide Architekturen # ./scripts/build.sh [version] amd64 # Nur amd64 # ./scripts/build.sh [version] arm64 # Nur arm64 # -# Ausgabe: dist/deklarix__ -# -# Release-Prozess: -# 1. VERSION erhöhen (Semantic Versioning: MAJOR.MINOR.PATCH) -# 2. ./scripts/build.sh -# 3. ./scripts/test.sh -# 4. git tag v && git push origin v +# Ausgabe: dist/deklarix__.deb set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +PACKAGING_DIR="$REPO_DIR/packaging" +BUILD_DIR="$REPO_DIR/dist/build" VERSION="${1:-0.0.1}" ARCH_FILTER="${2:-both}" @@ -26,9 +22,8 @@ log() { echo -e "${GRN}[build]${NC} $*"; } export PATH=$PATH:/usr/local/go/bin -command -v go >/dev/null || { echo "Go nicht gefunden — PATH: $PATH"; exit 1; } - -mkdir -p "$REPO_DIR/dist" +command -v go >/dev/null || { echo "Go nicht gefunden — PATH: $PATH"; exit 1; } +command -v dpkg-deb >/dev/null || { echo "dpkg-deb nicht gefunden"; exit 1; } log "Version: $VERSION | Go: $(go version)" @@ -39,14 +34,40 @@ case "$ARCH_FILTER" in *) ARCHS=("amd64" "arm64") ;; esac +rm -rf "$BUILD_DIR" +mkdir -p "$REPO_DIR/dist" + for ARCH in "${ARCHS[@]}"; do - OUT="$REPO_DIR/dist/deklarix_${VERSION}_${ARCH}" - log "Baue $ARCH → $OUT" - GOARCH=$ARCH GOOS=linux go build \ - -ldflags="-X main.Version=${VERSION} -s -w" \ - -o "$OUT" \ + DEB_NAME="deklarix_${VERSION}_${ARCH}" + DEB_DIR="$BUILD_DIR/$DEB_NAME" + + log "Baue Binary ($ARCH)..." + mkdir -p "$DEB_DIR/DEBIAN" \ + "$DEB_DIR/usr/bin" \ + "$DEB_DIR/etc/systemd/system" \ + "$DEB_DIR/etc/deklarix" + CGO_ENABLED=0 GOARCH=$ARCH GOOS=linux go build \ + -trimpath -ldflags="-X main.Version=${VERSION} -s -w" \ + -o "$DEB_DIR/usr/bin/deklarix" \ ./cmd/deklarix/ - log "$ARCH fertig: $(du -sh "$OUT" | cut -f1)" + chmod 755 "$DEB_DIR/usr/bin/deklarix" + + log "Stelle Paket zusammen ($ARCH)..." + sed "s/^Version: VERSION/Version: $VERSION/; s/^Architecture: ARCH/Architecture: $ARCH/" \ + "$PACKAGING_DIR/DEBIAN/control.tmpl" > "$DEB_DIR/DEBIAN/control" + cp "$PACKAGING_DIR/DEBIAN/postinst" "$DEB_DIR/DEBIAN/postinst" + cp "$PACKAGING_DIR/DEBIAN/prerm" "$DEB_DIR/DEBIAN/prerm" + cp "$PACKAGING_DIR/DEBIAN/conffiles" "$DEB_DIR/DEBIAN/conffiles" + chmod 755 "$DEB_DIR/DEBIAN/postinst" "$DEB_DIR/DEBIAN/prerm" + + cp "$PACKAGING_DIR/etc/systemd/system/deklarix.service" "$DEB_DIR/etc/systemd/system/" + cp "$PACKAGING_DIR/etc/deklarix/deklarix.env.example" "$DEB_DIR/etc/deklarix/" + + dpkg-deb --build --root-owner-group "$DEB_DIR" "$REPO_DIR/dist/$DEB_NAME.deb" + + log "$ARCH fertig: dist/$DEB_NAME.deb ($(du -sh "$REPO_DIR/dist/$DEB_NAME.deb" | cut -f1))" done +rm -rf "$BUILD_DIR" + log "Build abgeschlossen ✓" diff --git a/scripts/release.sh b/scripts/release.sh index 7bfa341..98496e2 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -6,13 +6,14 @@ # # Was passiert: # 1. Tests laufen -# 2. Build für amd64 + arm64 -# 3. git tag v wird gesetzt -# 4. git push origin main + git push origin v +# 2. .deb-Pakete bauen (amd64 + arm64) +# 3. Pakete in Giteas Debian-Paketregistrierung hochladen (Kanal "testing") +# 4. git tag v wird gesetzt +# 5. git push origin main + git push origin v # # Voraussetzungen: # - Sauberer Git-Status (keine uncommitted changes) -# - Alle Tests grün +# - Alle Tests grün (inkl. DATABASE_URL für die Store-Integrationstests) set -euo pipefail @@ -24,8 +25,9 @@ if [[ -z "$VERSION" ]]; then exit 1 fi -GRN='\033[0;32m'; BLD='\033[1m'; NC='\033[0m' -log() { echo -e "${GRN}[release]${NC} $*"; } +GRN='\033[0;32m'; YLW='\033[0;33m'; BLD='\033[1m'; NC='\033[0m' +log() { echo -e "${GRN}[release]${NC} $*"; } +warn() { echo -e "${YLW}[release]${NC} $*"; } cd "$REPO_DIR" @@ -43,9 +45,46 @@ log "Tests laufen..." bash "$REPO_DIR/scripts/test.sh" # Build -log "Build für alle Architekturen..." +log "Pakete bauen..." bash "$REPO_DIR/scripts/build.sh" "$VERSION" +# ─── apt-Kanal-Modell ──────────────────────────────────────────────────────── +# Suite = OS-Codename, Komponente = Kanal. Deklarix hat aktuell nur einen +# Kanal (testing) — es gibt noch keine Kunden, für die "stable" vom +# aktuellen Entwicklungsstand getrennt werden müsste. Sobald das relevant +# wird: gleiches Promote-Modell wie enconf-webpanel (release.sh stable). +GITEA_URL="https://git.netcell-it.de" +GITEA_ORG="projekte" +GITEA_CHANNEL="testing" +APT_SUITES=(bookworm trixie) + +GITEA_TOKEN="$(git -C "$REPO_DIR" remote get-url origin | grep -oP '://[^:]+:\K[^@]+' 2>/dev/null || echo '')" +if [ -z "$GITEA_TOKEN" ]; then + echo "FEHLER: Gitea-Token nicht im Git-Remote gefunden." + exit 1 +fi + +upload_deb() { + curl -sk -w "%{http_code}" -o /dev/null -X PUT \ + "$GITEA_URL/api/packages/$GITEA_ORG/debian/pool/$2/$3/upload" \ + -H "Authorization: token $GITEA_TOKEN" --upload-file "$1" 2>/dev/null +} + +log "Pakete → Gitea-Debian-Registry (${APT_SUITES[*]} / $GITEA_CHANNEL)..." +for deb in "$REPO_DIR"/dist/deklarix_"${VERSION}"_*.deb; do + [ -f "$deb" ] || { echo "FEHLER: kein .deb für Version $VERSION in dist/ gefunden"; exit 1; } + b="$(basename "$deb")" + for suite in "${APT_SUITES[@]}"; do + code=$(upload_deb "$deb" "$suite" "$GITEA_CHANNEL") + if [ "$code" = "201" ] || [ "$code" = "409" ]; then + log " ✅ $b → $suite/$GITEA_CHANNEL" + else + echo "FEHLER: Upload $b → $suite/$GITEA_CHANNEL fehlgeschlagen (HTTP $code)" + exit 1 + fi + done +done + # Git Tag log "Tag v${VERSION} setzen..." git tag -a "v${VERSION}" -m "Release v${VERSION}" @@ -57,4 +96,5 @@ git push origin "v${VERSION}" log "" log "Release v${VERSION} fertig ✓" -log "Binaries: dist/deklarix_${VERSION}_amd64 + _arm64" +log "Pakete: dist/deklarix_${VERSION}_amd64.deb + _arm64.deb" +log "Installation/Update auf Zielservern: apt install/upgrade deklarix"