presseportale/dev/betrieb/deploy-v1.1.0.md

10 KiB
Raw Blame History

Deploy v1.1.0 — Checkliste

Live seit 05.10.2026 — Abschluss vom Betreiber bestätigt. Protokoll am Ende.

Stand: 05.10.2026 · Live bisher: Stand 98c465b (Deploy 28.09.2026, ca. 14:34) Inhalt: alles danach — Blöcke 9 und 10 aus dem Audit (ENTWICKLUNGSPLAN.md, Deploy-Plan dort), Betriebsüberwachung Phase 1 (monitoring-plan.md), Composer-Update, Versionsnummer.

Umfang gegenüber Live: 8 additive Migrationen, 2 neue Composer-Pakete (sentry/sentry-laravel, Abhängigkeiten), 48 Paket-Updates, neue .env-Werte, neue Assets (beide Builds), 2 neue Scheduler-Tasks (21 statt 19). Keine gelöschten Code-Dateien.

Die Schritte aus dem Deploy-Plan der Blöcke 9/10 sind hier eingeordnet und mit (B9/10 Nr.) gekennzeichnet.

Auf dem Server wird kein Git gebraucht. Git läuft nur lokal (Abschnitt A): Tag, Versionsdatei, Paket. Der Server bekommt das fertige Paket und braucht nur tar, php und composer.

A — Lokal vorbereiten

  1. Arbeitskopie committen, main per Fast-Forward nachziehen, Tag setzen: git tag -a v1.1.0 -m "v1.1.0 — Audit-Blöcke 9/10, Betriebsüberwachung Phase 1". Push von main und Tag nach origin.

  2. Auf dem Tag: composer install, npm ci && npm run build, volle Testsuite (php artisan test --compact --parallel), Larastan.

  3. Versionsdatei und Paket bauen (im Projektverzeichnis, auf dem Tag):

    git describe --tags --always --dirty > RELEASE          # muss genau "v1.1.0" ergeben
    git -c core.quotepath=off diff --name-only --diff-filter=AMR 98c465b v1.1.0 \
      -- . ':!dev' ':!docs' ':!tests' ':!.mcp.json' ':!.codex' ':!phpunit.xml' ':!phpstan-baseline.neon' \
      > deploy-files.txt
    tar czf deploy-v1.1.0.tgz -T deploy-files.txt public/build RELEASE
    

    Ergibt RELEASE etwas anderes als v1.1.0 (z. B. -dirty), ist die Arbeitskopie nicht sauber — erst klären. Im Devcontainer fehlt docs/ (nicht eingebunden), das allein macht -dirty; dort nach Prüfung mit git describe --tags --always schreiben.

    Erledigt 05.10.2026: Paket deploy-v1.1.0.tgz gebaut (148 Dateien + public/build + RELEASE = v1.1.0), liegt im Projektverzeichnis.

  4. Paket per SFTP nach ~/ auf den Server laden.

B — Live vorbereiten (vor dem Wartungsfenster)

  1. DB-Backup ziehen; ARCHIVE_KEY liegt getrennt gesichert vor. (B9/10 Nr. 4)

  2. Dateibackup für den Rollback (inkl. vendor/, ohne storage/):

    cd ~ && tar czf backup-vor-v1.1.0.tgz --exclude=./storage --exclude=./node_modules -C ~/pressekonto.com .
    
  3. .env auf Live ergänzen:

    Variable Wert Herkunft
    NEWSROOM_API_ASSIGNMENT_USER_IDS 13993 (vorher prüfen: select id from users where email = 'info@connektar.de') E19
    NEWSLETTER_SENDING_ENABLED false E23
    BOUNCE_UNSIGNED_KIND policy E25/O01
    SENTRY_LARAVEL_DSN DSN Projekt 1 (errors.adametz.media) M1
    SENTRY_BROWSER_DSN DSN Projekt 2 M1
    SENTRY_ENVIRONMENT production
    SENTRY_RELEASE / APP_VERSION leer lassen — kommt aus RELEASE
    MONITORING_NTFY_URL https://push.adametz.media M3
    MONITORING_NTFY_TOPIC pressekonto-ops
    MONITORING_NTFY_TOKEN Token von pressekonto-alerts
    MONITORING_HEALTH_TOKEN neuer Zufallswert, nicht der lokale (openssl rand -hex 24) 1.5
    MONITORING_ALERT_EMAIL Ops-Postfach prüfen (leer = LEGAL_CONTACT_EMAIL)
    UMAMI_PORTAL_ENABLED false — erst nach DSE-Anpassung M6
    UMAMI_IDENTIFY_USERS false — erst nach DSE-Anpassung M6

    Optionale Werte (Standards greifen) siehe B9/10 Nr. 1.

  4. Hauptkategorien gegen config/canonical.php prüfen: select id, slug from category_translations where locale = 'de' and category_id between 1 and 14. (B9/10 Nr. 2)

  5. Uploadgrenzen PHP upload_max_filesize/post_max_size und nginx client_max_body_size ≥ 20M (Ploi: PHP-Einstellungen bzw. nginx-Konfiguration). (B9/10 Nr. 3)

  6. Ausgehend erreichbar vom Server: curl -sI https://errors.adametz.media und curl -sI https://push.adametz.media (beide antworten).

C — Deploy (SSH, Wartungsfenster ca. 3–5 Minuten)

cd ~/pressekonto.com
php artisan down --retry=60
tar xzf ~/deploy-v1.1.0.tgz
composer install --no-dev --optimize-autoloader --no-interaction
php artisan migrate --force                         # 8 Migrationen, additiv (B9/10 Nr. 6)
[ -d public/vendor/livewire ] && php artisan vendor:publish --tag=livewire:assets --force
php artisan config:cache
php artisan route:cache
php artisan view:clear && php artisan view:cache
php artisan event:cache
php artisan schedule:clear-cache                    # alte 24-h-Sperren (B9/10 Nr. 7)
php artisan queue:restart
# PHP-FPM neu laden (Ploi: Server ▸ PHP ▸ Restart) — OPcache hält sonst alten Code
php artisan up

Schlägt composer install oder migrate fehl: nicht up, sondern Rollback (Abschnitt G).

D — Direkt danach (Prüfungen)

  1. php artisan about --only=pressekonto → Version v1.1.0.
  2. Admin ▸ Reports ▸ Betriebsüberwachung: Version v1.1.0; nach 1–2 Minuten Scheduler grün, Plattenplatz grün; archive_key, archive_pending, archive_sweep grün (nach Nr. 14).
  3. curl -s -H "Authorization: Bearer <MONITORING_HEALTH_TOKEN>" https://pressekonto.com/health → "status":"ok", "version":"v1.1.0". Direkt nach dem Deploy kann der Scheduler noch fehlen (503) — eine Minute warten.
  4. php artisan archive:retry-pending einmal von Hand. (B9/10 Nr. 8)
  5. php artisan company-memberships:api-assignments sichten; --remove nur bei Treffern ohne Freischaltung. (B9/10 Nr. 9)
  6. php artisan press-releases:assign-canonicals. (B9/10 Nr. 10)
  7. php artisan press-release-images:sweep-orphan-originals (Trockenlauf) und php artisan archive:sweep-orphans --dry-run. (B9/10 Nr. 11, 12)
  8. php artisan schedule:list → 21 Tasks, darunter monitoring:heartbeat (minütlich), queue:prune-failed (03:55), press-release-images:sweep-orphan-originals (So 04:45), press-releases:purge-events (03:50). (B9/10 Nr. 13)
  9. GlitchTip: php artisan tinker --execute 'report(new RuntimeException("Deploy-Test v1.1.0"));' → erscheint in Projekt 1 mit Umgebung production, Release v1.1.0.
  10. ntfy: php artisan tinker --execute 'App\Support\OperationalAlert::send("Deploy v1.1.0 live");' → Push auf dem Handy (und Mail an das Ops-Postfach).
  11. storage/logs/laravel-*.log und monitoring-*.log auf neue Fehler prüfen.

E — Abnahme mit dem Testkonto

  1. Anmelden per Passwort, Magic Link und Google; danach einige Seiten aufrufen. Ein eingeloggter Nutzer, der einen eigenen Magic Link öffnet, landet auf dem Ziel. (B9/10 Nr. 14)
  2. Einmal absichtlich falsches Passwort → Admin ▸ Reports ▸ Verlauf zeigt „Anmeldung fehlgeschlagen … falsches Passwort“. (1.7)
  3. Titelbild mit 13–15 MiB hochladen. (B9/10 Nr. 15)
  4. sitemap.xml: englische Meldungen unter /en/press-release/…; article:modified_time = Veröffentlichung/letzte Textänderung. (B9/10 Nr. 16)
  5. Admin ▸ Newsletter: Sperrhinweis, „Senden“ aus. (B9/10 Nr. 17)
  6. Nach dem nächsten mail:fetch-bounces: unsignierte Rückläufer als Richtlinie „(unsigniert)“, ohne Sperre. (B9/10 Nr. 18)
  7. Der Smoke-Test aus dem Runbook (Abschnitt 8): drei Domains, robots/sitemap, Test-PM durch die Klassifikation, Formulare mit Mail.

F — Danach

  1. Erledigt 06.10.2026 (status.adametz.media). Externer Monitor (Uptime Kuma o. Ä.) auf https://pressekonto.com/health, Header Authorization: Bearer …, 60 s, 2 Wiederholungen, Benachrichtigung über ntfy (push.adametz.media, pressekonto-ops). (1.5)
  2. KAS-Passwort rotieren (B9/10 Nr. 19) — erledigt 06.10.2026, SSH-Zugang deaktiviert.
  3. Datenschutzerklärung (Betreiber/Anwalt): Rückläufer-Protokoll (B9/10 Nr. 20), Fehlerprotokollierung GlitchTip (User-ID, URL, Browser, Fehlermeldung, 90 Tage), fehlgeschlagene Anmeldungen im Verlauf (IP nur als HMAC). Danach — und erst dann — UMAMI_PORTAL_ENABLED/UMAMI_IDENTIFY_USERS per .env + config:cache.
  4. Restore-Übung (B9/10 Nr. 21).
  5. Protokolle ergänzen: Deploy-Datum und Ergebnisse in ENTWICKLUNGSPLAN.md (B9/10 Nr. 22) und monitoring-plan.md.
  6. Backups ~/deploy-v1.1.0.tgz und ~/backup-vor-v1.1.0.tgz nach ein paar stabilen Tagen löschen.

G — Rollback

cd ~/pressekonto.com
php artisan down
tar xzf ~/backup-vor-v1.1.0.tgz -C ~/pressekonto.com    # alter Code inkl. vendor/
php artisan config:cache && php artisan route:cache && php artisan view:clear && php artisan view:cache && php artisan event:cache
php artisan queue:restart          # + PHP-FPM neu laden
php artisan up

Die Migrationen sind additiv; der alte Code läuft mit den neuen Spalten und Tabellen. Nur bei Datenproblemen das DB-Backup aus Nr. 5 einspielen und danach die Archiv-Schritte aus Runbook Abschnitt 9 befolgen.

Protokoll

05.10.2026 — Deploy v1.1.0 live (Paket aus Abschnitt A, Commit df7ebdc = Tag v1.1.0).

Abschnitt D, Ausgaben auf Live:

Nr. Ergebnis
11 Zunächst Version „—“: Datei RELEASE fehlte bzw. Config-Cache war älter. Behoben mit echo v1.1.0 > RELEASE + config:cache. Eine lokal neu erzeugte RELEASE hatte v1.1.0-2-g8a64bd7-dirty ergeben (Doku-Commits nach dem Tag, docs/ nicht eingebunden) — Lehre: RELEASE aus dem Paket nehmen, nicht neu erzeugen
14 archive:retry-pending: 0 Aufträge, 0 offen
15 company-memberships:api-assignments: 3 Mitgliedschaften (connektar, User 13993), alle freigeschaltet — kein --remove
16 assign-canonicals: 9.887 Paare, 342 geändert, 13 veraltete Verweise zurückgesetzt, Caches geleert
17 sweep-orphan-originals (Trockenlauf): 6 geprüft, 0 verwaist; archive:sweep-orphans --dry-run: 0
18 schedule:list: 21 Tasks inkl. monitoring:heartbeat, queue:prune-failed 03:55, sweep-orphan-originals So 04:45
19, 20 GlitchTip- und ntfy-Test ausgeführt; nach config:cache wiederholt

Abschnitt E (Abnahme Testkonto) vom Betreiber nicht einzeln gemeldet. Abschnitt F offen: externer Monitor auf /health (29), Datenschutzerklärung (31), Restore-Übung (32); dazu Context7-Schlüssel rotieren (stand bis df7ebdc im Repository). KAS-Passwort (30) erledigt 06.10.2026.