Git-Deploy per Tag, Betriebsüberwachung und Release-Seeder für den Livegang
- dev/betrieb/deploy.sh: Deploy nur per Tag, mit --seed (Seeder im Wartungsmodus) und --initial (Vorschau/Live einmalig an Ort und Stelle auf Git umstellen) - dev/betrieb/vorschau-auffrischen.sh: Live-DB und Live-Medien in die Vorschau (Generalprobe) - InteriorReleaseSeeder: alle Inhaltsschritte des Relaunchs in einem Lauf, idempotent; Displays, Playlists und Mediathek bleiben unberührt - DisplayInteriorBrandingSeeder überschreibt keinen inzwischen gepflegten Claim mehr - Monitoring: GlitchTip (Server + Browser), /health mit Token, Scheduler-Herzschlag, Alarme per Mail und ntfy, Version in about und /health - public/build versioniert; .gitignore: macOS-Muster Icon traf resources/views/flux/icon/ - Runbook: dev/betrieb/README.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fcb10be120
commit
b5ed756d67
98 changed files with 3947 additions and 82 deletions
|
|
@ -2492,3 +2492,28 @@ korrigiert – u. a. Hinweisleiste (über B2IN_ANNOUNCEMENT_BAR aus, Feld „ena
|
|||
**Nachbesserung 08.10.:** Hero mobil – die Infokarte steht unter dem Bild (Bild 4:3 oben, Karte als weißer
|
||||
Streifen darunter), ab lg schwebt sie wie bisher unten links. Karte weiß statt `bg-card`, damit die
|
||||
Glass-Flow-Regel `[class*="card"]` nicht greift; `absolute!` entfällt.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Nachtrag 32 · Git-Deploy, Monitoring und Ablauf für den Livegang (08.10.2026)
|
||||
|
||||
- **Git-Deploy per Tag:** `dev/betrieb/deploy.sh` (aus `dev/git-deploy-monitoring/`) liefert nur Tags aus,
|
||||
`origin` = forge.adametz.media, `NAS` = Sicherung. `public/build` ist jetzt versioniert. Neu:
|
||||
`--seed=<Klasse>` (Seeder im Wartungsmodus) und `--initial` (Vorschau und Live einmalig an Ort und
|
||||
Stelle auf Git umstellen, mit Code-Sicherung und Parken der Nur-Server-Dateien).
|
||||
`.gitignore`: Das macOS-Muster `Icon` traf `resources/views/flux/icon/`, die vier Icons wären mit
|
||||
Git-Deploy auf Live verloren gegangen; das Muster heißt jetzt `Icon[\r]`.
|
||||
- **Monitoring:** GlitchTip (Server und Browser, Website und Portal), `/health` mit Token (jede Domain,
|
||||
ohne Basic Auth), Scheduler-Herzschlag, Alarme bei fehlgeschlagenen Tasks per Mail und ntfy,
|
||||
Version in `php artisan about` und `/health`. Pakete: `sentry/sentry-laravel`, `@sentry/browser`.
|
||||
- **Livegang ohne Datenmigration:** Die Live-Datenbank bleibt die Grundlage (Displays, Playlists,
|
||||
Mediathek, Benutzer), `InteriorReleaseSeeder` bringt die Website-Inhalte in einem Lauf auf den neuen
|
||||
Stand. Er fasst alle Seeder aus 8.2 und „Seeder für das nächste Deployment“ zusammen, ist idempotent
|
||||
(Test) und lässt Display-Daten unberührt. Ausnahme: Branding, nur bei Modulen ohne Interior-Logo;
|
||||
`DisplayInteriorBrandingSeeder` überschreibt seit heute keinen inzwischen gepflegten Claim mehr.
|
||||
Generalprobe: `dev/betrieb/vorschau-auffrischen.sh` kopiert Live-DB und Live-Medien in die Vorschau.
|
||||
- **Ablauf:** `dev/betrieb/README.md`. Er ersetzt für den Livegang die Einzelbefehle aus 8.2 und die
|
||||
Seeder-Liste oben. Die Prüfliste 8.3 gilt weiter.
|
||||
- **Offen:** Datenschutzerklärung um die Fehlerprotokollierung ergänzen, bevor die DSNs auf Live gesetzt
|
||||
werden (Textvorschlag in `dev/betrieb/README.md`, Abschnitt 6).
|
||||
|
|
|
|||
235
dev/betrieb/README.md
Normal file
235
dev/betrieb/README.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
# Betrieb B2in: Git-Deploy, Livegang, Monitoring
|
||||
|
||||
**Stand:** 08.10.2026. Grundlage ist der Baukasten unter `dev/git-deploy-monitoring/`. Dort steht,
|
||||
warum etwas so gebaut ist. Hier steht, wie es für B2in läuft.
|
||||
|
||||
| Was | Wo |
|
||||
|---|---|
|
||||
| Deploy-Skript | `dev/betrieb/deploy.sh`, liefert nur Git-Tags `vX.Y.Z` aus |
|
||||
| Vorschau auffrischen | `dev/betrieb/vorschau-auffrischen.sh` kopiert Live-DB und Live-Medien in die Vorschau |
|
||||
| Inhaltsschritte Relaunch | `InteriorReleaseSeeder`, alle 29 Seeder in einem Lauf, idempotent |
|
||||
| Remote `origin` | lokal `ssh://git@forge.adametz.media:222/kadmin/b2in.git`, auf dem Server `forge-b2in:kadmin/b2in.git` (Host-Alias, Abschnitt 2): Deploy-Quelle für Vorschau und Live |
|
||||
| Remote `NAS` | `ssh://git@git.adametz.media:222/gitmedia/b2in.git`: lokale Sicherung, wird nie deployt |
|
||||
| Fehlerprotokoll | GlitchTip `https://errors.adametz.media`, Organisation `adametz-media`, Projekte `server-b2in` (ID 8) und `browser-b2in` (ID 7) |
|
||||
| Push-Alarme | ntfy `https://push.adametz.media`, Topic `b2in-ops` |
|
||||
| Externer Monitor | Uptime Kuma auf `/health` (mit Token) und `/up` je Domain |
|
||||
|
||||
---
|
||||
|
||||
## 1. Release lokal (jedes Mal)
|
||||
|
||||
```bash
|
||||
git status # nichts offen
|
||||
php artisan test --compact
|
||||
npm ci && npm run build:web && npm run build:portal # public/build ist versioniert, der Server hat kein Node
|
||||
git add public/build && git diff --cached --quiet public/build || git commit -m "build: Assets für vX.Y.Z"
|
||||
|
||||
git tag -a vX.Y.Z -m "vX.Y.Z – <Inhalt>
|
||||
|
||||
Deploy: <keine Migration | N Migrationen>, <Seeder>, <neue .env-Werte>
|
||||
bash dev/betrieb/deploy.sh vX.Y.Z [--migrations] [--seed=…]"
|
||||
git push origin <branch> --follow-tags
|
||||
git push NAS <branch> --follow-tags # Sicherung
|
||||
```
|
||||
|
||||
Versionen folgen SemVer: Ein Feature-Release erhöht die mittlere Stelle, ein Hotfix die letzte.
|
||||
Der Interior-Relaunch ist `v2.0.0`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Einmalig auf dem Server
|
||||
|
||||
Als App-Benutzer, gilt für Vorschau und Live (gleicher Server):
|
||||
|
||||
1. **Deploy-Key**, nur Leserecht am Repository auf forge.adametz.media. Auf dem Server liegen mehrere
|
||||
Deploy-Keys für denselben Forgejo (B2in, Breco), deshalb gibt es je Projekt einen Host-Alias.
|
||||
Die Repository-Adresse heißt dann **`forge-b2in:kadmin/b2in.git`**, nicht `ssh://git@forge.adametz.media:222/…`;
|
||||
mit der vollen Adresse greift kein Alias, und ssh bietet keinen Schlüssel an („Permission denied (publickey)“).
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/deploy_b2in -N "" -C "b2in-server"
|
||||
cat ~/.ssh/deploy_b2in.pub # in Forgejo: kadmin/b2in ▸ Einstellungen ▸ Deploy-Schlüssel, ohne Schreibrecht
|
||||
```
|
||||
`~/.ssh/config`:
|
||||
```
|
||||
Host forge-b2in
|
||||
HostName forge.adametz.media
|
||||
Port 222
|
||||
User git
|
||||
IdentityFile ~/.ssh/deploy_b2in
|
||||
IdentitiesOnly yes
|
||||
HostKeyAlias forge.adametz.media
|
||||
```
|
||||
Test: `git ls-remote forge-b2in:kadmin/b2in.git` zeigt die Tags.
|
||||
2. **Flux Pro für Composer:** `deploy.sh` führt `composer install` aus. Dafür braucht der Server die
|
||||
Flux-Zugangsdaten, also `auth.json` im App-Verzeichnis (ignoriert, bleibt liegen) oder
|
||||
`composer config --global http-basic.composer.fluxui.dev <e-mail> <lizenz>`.
|
||||
3. **PHP-FPM-Reload ohne Passwort testen:** `echo "" | sudo -S service php8.4-fpm reload; echo $?`.
|
||||
Ergibt das Exit 0, lädt das Skript FPM selbst neu. Fragt sudo nach einem Passwort, beim Deploy
|
||||
`DEPLOY_FPM_RELOAD=` setzen; dann wartet das Skript, bis FPM im Panel neu geladen ist. Heißt
|
||||
der Dienst anders: `DEPLOY_PHP_FPM_SERVICE=…`. **Immer Reload, nie Restart.**
|
||||
4. **Cron für den Scheduler:** `* * * * * cd <app> && php artisan schedule:run >> /dev/null 2>&1`.
|
||||
Er trägt den Herzschlag für `/health` und `cabinet:reset-overrides`. Eine Queue braucht B2in
|
||||
nicht; der Worker-Test im Skript ist aus.
|
||||
5. **Server-Panel:** „Quick Deploy“ und Push-Webhooks bleiben aus. Ein Push ist noch kein Release.
|
||||
|
||||
### `.env`-Werte je Umgebung
|
||||
|
||||
| Schlüssel | Live | Vorschau |
|
||||
|---|---|---|
|
||||
| `SENTRY_LARAVEL_DSN`, `SENTRY_BROWSER_DSN` | wie lokal (Projekte server-b2in / browser-b2in), **erst nach der Datenschutzerklärung (Abschnitt 6)** | gleich, optional |
|
||||
| `SENTRY_ENVIRONMENT` | `production` | `preview` |
|
||||
| `MONITORING_NTFY_URL`, `_TOPIC`, `_TOKEN` | wie lokal | leer (keine Push-Alarme aus der Vorschau) |
|
||||
| `MONITORING_ALERT_EMAIL` | Ops-Postfach | leer |
|
||||
| `MONITORING_HEALTH_TOKEN` | **eigener Wert** (`openssl rand -hex 24`), nicht der lokale | eigener Wert oder leer |
|
||||
| `MAIL_MAILER` | wie bisher | `log` (die Vorschau hat Live-Daten) |
|
||||
|
||||
`APP_VERSION` bleibt leer. Die Version kommt aus der Datei `RELEASE`, die das Skript schreibt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Livegang Interior-Relaunch (v2.0.0)
|
||||
|
||||
**Grundidee:** Auf Live hat der Kunde Displays, Playlists und die Mediathek gepflegt, an der Website
|
||||
im CMS aber nichts geändert. Deshalb kopieren wir **nicht** die Vorschau-Datenbank nach Live und
|
||||
übertragen Display-Daten einzeln. Stattdessen bleibt die Live-Datenbank die Grundlage, und
|
||||
`InteriorReleaseSeeder` bringt deren Website-Inhalte auf den neuen Stand. Displays,
|
||||
Display-Mediathek, CMS-Mediathek, Benutzer und hochgeladene Dateien bleiben unberührt. Angepasst
|
||||
werden nur:
|
||||
|
||||
- das Display-Branding, und zwar nur bei Modulen, die noch kein Interior-Logo tragen
|
||||
- absolute Medien-URLs, die auf `b2in.test` oder `b2in.eu` zeigen; sie werden host-unabhängig
|
||||
|
||||
Damit der Kunde genau dieses Ergebnis vorher sieht, läuft derselbe Ablauf zuerst auf der Vorschau
|
||||
mit einer frischen Kopie der Live-Daten (Generalprobe).
|
||||
|
||||
### 3.1 Generalprobe auf der Vorschau
|
||||
|
||||
```bash
|
||||
# Skripte aus dem Tag holen (Vorschau und Live sind noch keine Git-Checkouts)
|
||||
git clone -q --depth 1 --branch v2.0.0 forge-b2in:kadmin/b2in.git /tmp/b2in-v2.0.0
|
||||
|
||||
# 1. Live-Daten und Live-Medien in die Vorschau (Live wird nur gelesen; legt ~/b2in-live-<zeit>.sql.gz an)
|
||||
bash /tmp/b2in-v2.0.0/dev/betrieb/vorschau-auffrischen.sh <live> <vorschau>
|
||||
|
||||
# 2. Vorschau einmalig auf Git umstellen und v2.0.0 ausliefern, mit allen Inhaltsschritten
|
||||
cd <vorschau>
|
||||
bash /tmp/b2in-v2.0.0/dev/betrieb/deploy.sh v2.0.0 --initial --migrations --seed=InteriorReleaseSeeder
|
||||
```
|
||||
|
||||
`--initial` sichert vorher den Code nach `~/<verzeichnis>-vor-git-<zeit>.tgz`. Dann zeigt es die
|
||||
Dateien, die es nur auf dem Server gibt (Reste aus SFTP- und Archiv-Updates). Erst nach einem
|
||||
`ja` verschiebt es sie nach `~/<verzeichnis>-vor-git-<zeit>/`. Danach ist `git status` leer.
|
||||
|
||||
Vorschau prüfen: die Liste unter 8.3 im Entwicklungsplan, dazu im Portal der Vorschau die
|
||||
**Displays und Playlists mit Live vergleichen**. Dort müssen dieselben Module, Bilder und Videos
|
||||
erscheinen. Neue Generalprobe, etwa nach einem Hotfix-Tag oder weil auf Live weiter gepflegt
|
||||
wurde:
|
||||
|
||||
```bash
|
||||
cd <vorschau> && bash dev/betrieb/deploy.sh v2.0.1 --migrations # nur bei neuem Tag
|
||||
bash dev/betrieb/vorschau-auffrischen.sh <live> <vorschau> --seed=InteriorReleaseSeeder
|
||||
```
|
||||
|
||||
### 3.2 Livegang nach der Freigabe
|
||||
|
||||
1. Wartungsfenster mit Marcel abstimmen. Die Displays holen ihre Konfiguration zyklisch, laufen
|
||||
also weiter, ein Wechsel im Laden ist aber sichtbar.
|
||||
2. **DB-Backup von Live:**
|
||||
`mysqldump --single-transaction --no-tablespaces -u … -p b2in_live | gzip > ~/b2in-live-vor-v2.sql.gz`
|
||||
3. Live-`.env` ergänzen (Tabelle oben). `SENTRY_*` erst nach Abschnitt 6.
|
||||
4. Gleicher Befehl wie auf der Vorschau, nur im Live-Verzeichnis:
|
||||
```bash
|
||||
cd <live>
|
||||
bash /tmp/b2in-v2.0.0/dev/betrieb/deploy.sh v2.0.0 --initial --migrations --seed=InteriorReleaseSeeder
|
||||
rm -rf /tmp/b2in-v2.0.0
|
||||
```
|
||||
5. Prüfen: `php artisan about --only=b2in` zeigt `v2.0.0`. `/health` mit Token liefert
|
||||
`"status":"ok"` (der Herzschlag braucht nach dem Deploy bis zu einer Minute). Danach die
|
||||
Liste 8.3 im Entwicklungsplan, die vier Displays sichten, `storage/logs` und GlitchTip auf neue
|
||||
Fehler prüfen.
|
||||
|
||||
**Rückweg** (nur Live, falls etwas grundlegend nicht passt): Seite in den Wartungsmodus nehmen,
|
||||
`.git` entfernen, die Code-Sicherung `~/<live>-vor-git-<zeit>.tgz` zurückspielen, das DB-Backup
|
||||
aus Schritt 2 einspielen und FPM neu laden. Die geparkten Nur-Server-Dateien aus
|
||||
`~/<live>-vor-git-<zeit>/` liegen ebenfalls noch vor.
|
||||
|
||||
---
|
||||
|
||||
## 4. Folge-Deploys (ab v2.0.0)
|
||||
|
||||
```bash
|
||||
cd <app>
|
||||
bash dev/betrieb/deploy.sh vX.Y.Z # ohne Migrationen
|
||||
bash dev/betrieb/deploy.sh vX.Y.Z --migrations # mit Migrationen, vorher DB-Backup
|
||||
bash dev/betrieb/deploy.sh vX.Y.Z --seed=<Seeder> # Inhaltsschritt laut Release-Notiz
|
||||
bash dev/betrieb/deploy.sh <vorheriger Tag> # Rollback
|
||||
```
|
||||
|
||||
Das Skript bricht ab, **bevor** es etwas ändert, wenn:
|
||||
|
||||
- im Verzeichnis jemand von Hand etwas geändert hat
|
||||
- der Tag fehlt
|
||||
- ein Build-Manifest fehlt
|
||||
- neue Migrationen ohne `--migrations` kommen
|
||||
|
||||
Auf dem Server wird nichts von Hand geändert, auch Hotfixes laufen über Commit, Tag und Deploy.
|
||||
Exit-Codes: `0` fertig · `1` abgebrochen · `2` falscher Aufruf.
|
||||
|
||||
---
|
||||
|
||||
## 5. Monitoring
|
||||
|
||||
Eingebaut nach `dev/git-deploy-monitoring/` (02 und 03), Tests unter `tests/Feature/Monitoring/`.
|
||||
|
||||
| Ausfall | Wer meldet | Kanal |
|
||||
|---|---|---|
|
||||
| Exception (Request, Command, Livewire) | Sentry-SDK | GlitchTip `server-b2in` |
|
||||
| JavaScript-Fehler (Website und Portal) | `resources/js/error-reporting.js` | GlitchTip `browser-b2in` (Tag `area`: `web`/`portal`) |
|
||||
| Toter interner Link, 419, 403/429 für Angemeldete | `ReportErrorSignals` | GlitchTip (Warnung) |
|
||||
| Geplanter Task fehlgeschlagen | `onFailure` in `routes/console.php` | Log `monitoring`, Mail, ntfy |
|
||||
| Cron steht, DB/Cache weg, Platte voll | `/health` | Uptime Kuma, dann ntfy und Mail |
|
||||
| Server, FPM, Nginx oder SSL weg | `/up` nicht erreichbar | Uptime Kuma |
|
||||
|
||||
**Uptime Kuma** (status.adametz.media):
|
||||
|
||||
| Monitor | Ziel | Intervall |
|
||||
|---|---|---|
|
||||
| B2in /health | `https://b2in.eu/health`, Header `{"Authorization": "Bearer <MONITORING_HEALTH_TOKEN von Live>"}`, Status 200–299 | 60 s, 2 Wiederholungen |
|
||||
| B2in /up | `https://b2in.eu/up` und `https://portal.b2in.eu/up` | 5 min |
|
||||
| Zertifikate | Ablaufwarnung 14 Tage vorher | – |
|
||||
|
||||
`/health` antwortet auf jeder Domain, ist von der Basic Auth ausgenommen und liefert ohne oder
|
||||
mit falschem Token 404.
|
||||
|
||||
**Abnahme auf Live:**
|
||||
|
||||
- `php artisan tinker --execute 'App\Support\OperationalAlert::send("Test B2in");'`: Push und Mail kommen an
|
||||
- `php artisan tinker --execute 'report(new RuntimeException("Test B2in"));'`: erscheint in `server-b2in`, Umgebung `production`, Release `v2.0.0`
|
||||
- Browser-Konsole auf b2in.eu: `setTimeout(() => { throw new Error('Test test@example.com') })` erscheint in `browser-b2in`, die E-Mail als `[email]`
|
||||
- Uptime Kuma: Ein Test-Monitor mit falschem Token löst einen Alarm aus, danach löschen
|
||||
|
||||
**GlitchTip auswerten** (lokal, Lese-Token `GLITCHTIP_API_TOKEN` in der `.env`): Die Befehle stehen
|
||||
in `dev/git-deploy-monitoring/02-fehlerprotokoll-glitchtip.md`, Abschnitt 7, mit
|
||||
`B=https://errors.adametz.media/api/0`, `ORG=adametz-media`, Projekt `8` (Server) bzw. `7` (Browser).
|
||||
|
||||
Lokal sind die DSNs gesetzt (`SENTRY_ENVIRONMENT=local`). Fehler beim Entwickeln landen also in
|
||||
GlitchTip, aber getrennt nach Umgebung. Die Testsuite meldet nie etwas; `phpunit.xml` leert DSNs
|
||||
und ntfy.
|
||||
|
||||
---
|
||||
|
||||
## 6. Datenschutzerklärung (vor dem Einschalten der DSNs auf Live)
|
||||
|
||||
Die Fehlerprotokollierung muss in die Datenschutzerklärung, bevor auf Live `SENTRY_*` gesetzt wird.
|
||||
Hier ein Textvorschlag, den Marcel bzw. die Rechtsberatung freigeben muss:
|
||||
|
||||
> **Fehlerprotokollierung.** Tritt beim Aufruf unserer Website ein technischer Fehler auf,
|
||||
> speichern wir dazu die aufgerufene Adresse (ohne Formularinhalte und ohne Zugangs-Token), Browser
|
||||
> und Betriebssystem, die Fehlermeldung und, bei angemeldeten Nutzern, deren interne Nutzer-ID.
|
||||
> IP-Adressen und Cookies werden nicht übermittelt. Die Daten verarbeiten wir auf einem eigenen
|
||||
> Server in Deutschland, um Fehler zu erkennen und zu beheben. Rechtsgrundlage ist unser
|
||||
> berechtigtes Interesse am sicheren und fehlerfreien Betrieb (Art. 6 Abs. 1 lit. f DSGVO). Die
|
||||
> Einträge werden nach 90 Tagen gelöscht.
|
||||
|
||||
Vor der Freigabe prüfen: Serverstandort und Aufbewahrungsfrist (`GLITCHTIP_RETENTION_DAYS`).
|
||||
Eingetragen wird der Text in `resources/lang/de/b2in_legal.php` und im CMS unter „Rechtliches“.
|
||||
294
dev/betrieb/deploy.sh
Executable file
294
dev/betrieb/deploy.sh
Executable file
|
|
@ -0,0 +1,294 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Deploy eines Git-Tags auf Vorschau oder Live (B2in). Grundlage: Baukasten
|
||||
# dev/git-deploy-monitoring, Ablauf für B2in: dev/betrieb/README.md.
|
||||
#
|
||||
# Aufruf im App-Verzeichnis auf dem Server:
|
||||
#
|
||||
# bash dev/betrieb/deploy.sh v2.0.0 # ohne neue Migrationen
|
||||
# bash dev/betrieb/deploy.sh v2.0.0 --migrations # mit Migrationen, DB-Backup vorher!
|
||||
# bash dev/betrieb/deploy.sh v2.0.0 --migrations --seed=InteriorReleaseSeeder
|
||||
#
|
||||
# Optionen:
|
||||
# --migrations Neue Migrationen ausführen. Ohne diese Option bricht das
|
||||
# Skript ab, sobald der Tag neue Migrationen mitbringt.
|
||||
# --clear-schedule php artisan schedule:clear-cache (alte withoutOverlapping-Sperren).
|
||||
# --seed=<Klasse> Seeder im Wartungsmodus ausführen (nach den Migrationen,
|
||||
# vor den Caches) – für Inhaltsschritte, die die Release-Notiz nennt.
|
||||
# --initial Einmalig: ein Verzeichnis ohne Git (bisher SFTP/Archiv) an Ort und
|
||||
# Stelle zum Checkout des Tags machen. Sichert vorher den Code nach
|
||||
# ~/<verzeichnis>-vor-git-<zeit>.tgz, zeigt Dateien, die es nur auf dem
|
||||
# Server gibt, und verschiebt sie nach Rückfrage nach ~/<verzeichnis>-vor-git-<zeit>/.
|
||||
# Verlangt --migrations (vorher DB-Backup).
|
||||
#
|
||||
# Umgebung (optional):
|
||||
# DEPLOY_FPM_RELOAD Befehl zum Neuladen von PHP-FPM (Standard siehe unten).
|
||||
# Leer setzen, wenn FPM von Hand im Server-Panel neu geladen
|
||||
# wird — dann wartet das Skript auf Enter. Nie „Restart“:
|
||||
# der entfernt kurz den Socket, Nginx liefert 502.
|
||||
# DEPLOY_PHP_FPM_SERVICE systemd-Name des FPM-Dienstes (Standard php8.4-fpm).
|
||||
# DEPLOY_REPOSITORY Repository für --initial (Standard: Host-Alias forge-b2in aus ~/.ssh/config).
|
||||
#
|
||||
# Exit-Codes: 0 fertig, 1 abgebrochen (siehe Ausgabe), 2 falscher Aufruf,
|
||||
# 3 live, aber kein Queue-Worker hat den Test-Job abgeholt.
|
||||
#
|
||||
# Rollback: dasselbe Skript mit dem vorherigen Tag. Nach --initial: Sicherung
|
||||
# zurückspielen (Pfad steht in der Ausgabe) und DB-Backup einspielen.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---- Projekt-Einstellungen --------------------------------------------------
|
||||
DEPLOY_SCRIPT="dev/betrieb/deploy.sh" # Pfad dieses Skripts im Repository
|
||||
PHP_FPM_SERVICE="${DEPLOY_PHP_FPM_SERVICE:-php8.4-fpm}" # systemd-Name des FPM-Dienstes
|
||||
ABOUT_SECTION="b2in" # AboutCommand::add('B2in', …) im AppServiceProvider
|
||||
QUEUE_PROBE=0 # B2in stellt keine Jobs in die Queue (Mails gehen sofort raus)
|
||||
REPOSITORY="${DEPLOY_REPOSITORY:-forge-b2in:kadmin/b2in.git}"
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Git ersetzt diese Datei beim Checkout. Bash liest Skripte stückweise, deshalb
|
||||
# läuft der Deploy aus einer Kopie.
|
||||
if [[ -z "${DEPLOY_COPY:-}" ]]; then
|
||||
copy="$(mktemp)"
|
||||
cp "$0" "$copy"
|
||||
DEPLOY_COPY="$copy" exec bash "$copy" "$@"
|
||||
fi
|
||||
trap 'rm -f "$DEPLOY_COPY"' EXIT
|
||||
|
||||
tag="${1:-}"
|
||||
shift || true
|
||||
run_migrations=0
|
||||
clear_schedule=0
|
||||
initial=0
|
||||
seeders=()
|
||||
|
||||
for option in "$@"; do
|
||||
case "$option" in
|
||||
--migrations) run_migrations=1 ;;
|
||||
--clear-schedule) clear_schedule=1 ;;
|
||||
--initial) initial=1 ;;
|
||||
--seed=*)
|
||||
seeder="${option#--seed=}"
|
||||
if [[ ! "$seeder" =~ ^[A-Za-z][A-Za-z0-9]*$ ]]; then
|
||||
echo "Ungültiger Seeder: $seeder" >&2; exit 2
|
||||
fi
|
||||
seeders+=("$seeder") ;;
|
||||
*) echo "Unbekannte Option: $option" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "Aufruf: bash $DEPLOY_SCRIPT vX.Y.Z [--migrations] [--seed=<Klasse>] [--clear-schedule] [--initial]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ ! -f artisan ]]; then
|
||||
echo "Im App-Verzeichnis aufrufen (artisan fehlt hier)." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
step() { printf '\n==> %s\n' "$*"; }
|
||||
|
||||
if [[ "$initial" -eq 1 ]]; then
|
||||
if [[ -d .git ]]; then
|
||||
echo "--initial nur für ein Verzeichnis ohne Git – hier gibt es .git schon." >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ "$run_migrations" -ne 1 ]]; then
|
||||
echo "--initial verlangt --migrations (vorher DB-Backup ziehen)." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
stamp="$(date +%Y-%m-%d-%H%M)"
|
||||
site="$(basename "$PWD")"
|
||||
backup="$HOME/$site-vor-git-$stamp.tgz"
|
||||
parked="$HOME/$site-vor-git-$stamp"
|
||||
|
||||
step "Sicherung des Codes nach $backup (ohne storage/, node_modules/)"
|
||||
# Exit 1 = „Datei hat sich beim Lesen geändert“ (Logs o. Ä.) – die Sicherung ist trotzdem brauchbar.
|
||||
tar czf "$backup" --exclude=./storage --exclude=./node_modules --exclude=./public/storage . || [[ $? -eq 1 ]]
|
||||
ls -lh "$backup"
|
||||
|
||||
step "Git einrichten, Tag $tag holen"
|
||||
git init -q
|
||||
git remote add origin "$REPOSITORY"
|
||||
if ! git fetch --quiet --tags origin || ! git rev-parse --quiet --verify "refs/tags/$tag^{commit}" > /dev/null; then
|
||||
rm -rf .git
|
||||
echo "Tag $tag nicht abrufbar (Deploy-Key? Tag gepusht?). .git wieder entfernt, nichts verändert." >&2
|
||||
exit 1
|
||||
fi
|
||||
git update-ref --no-deref HEAD "$(git rev-parse "$tag^{commit}")"
|
||||
git read-tree HEAD
|
||||
# Ignorier-Regeln des Tags gelten schon jetzt (die .gitignore im Verzeichnis ist noch die alte).
|
||||
git show "$tag:.gitignore" > .git/info/exclude
|
||||
|
||||
changed="$(git status --porcelain --untracked-files=no | wc -l | tr -d ' ')"
|
||||
server_only="$(git -c core.quotePath=false ls-files --others --exclude-standard)"
|
||||
echo "$changed Dateien weichen vom Tag ab oder fehlen – der Checkout ersetzt sie."
|
||||
if [[ -n "$server_only" ]]; then
|
||||
echo
|
||||
echo "Diese $(echo "$server_only" | wc -l | tr -d ' ') Dateien gibt es nur auf dem Server (nicht im Tag, nicht ignoriert):"
|
||||
echo "$server_only" | sed 's/^/ /' | head -200
|
||||
echo
|
||||
echo "Sie werden nach $parked/ verschoben (Pfade bleiben erhalten). Wird eine davon gebraucht,"
|
||||
echo "jetzt abbrechen und sie ins Repository oder in die .gitignore aufnehmen (neuer Tag)."
|
||||
fi
|
||||
read -r -p "Weiter mit dem Deploy von $tag? [ja/nein] " answer
|
||||
if [[ "$answer" != "ja" ]]; then
|
||||
rm -rf .git
|
||||
echo "Abgebrochen. .git wieder entfernt, nichts verändert (Sicherung bleibt liegen)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
previous="vor-git (Sicherung: $backup)"
|
||||
new_migrations="(Umstellung: alle offenen Migrationen)"
|
||||
else
|
||||
if [[ ! -d .git ]]; then
|
||||
echo "Hier gibt es kein .git – für die einmalige Umstellung --initial verwenden." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
step "Arbeitsverzeichnis prüfen"
|
||||
drift="$(git status --porcelain)"
|
||||
if [[ -n "$drift" ]]; then
|
||||
echo "Das Arbeitsverzeichnis weicht vom ausgecheckten Stand ab:" >&2
|
||||
echo "$drift" >&2
|
||||
echo "Erst klären (git diff), dann deployen. Nichts wurde verändert." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
step "Tags von origin holen"
|
||||
git fetch --quiet --tags origin
|
||||
|
||||
if ! git rev-parse --quiet --verify "refs/tags/$tag^{commit}" > /dev/null; then
|
||||
echo "Tag $tag gibt es auf origin nicht." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
previous="$(cat RELEASE 2>/dev/null || git describe --tags --always)"
|
||||
|
||||
if [[ "$(git rev-parse HEAD)" == "$(git rev-parse "$tag^{commit}")" ]]; then
|
||||
echo "$tag ist bereits ausgecheckt. Nichts zu tun."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Bisher: $previous → Ziel: $tag"
|
||||
|
||||
for build in b2in b2a stileigentum style2own portal; do
|
||||
if ! git cat-file -e "$tag:public/build/$build/manifest.json" 2> /dev/null; then
|
||||
echo "public/build/$build/manifest.json fehlt im Tag $tag – Assets lokal bauen und committen. Nichts wurde verändert." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$initial" -ne 1 ]]; then
|
||||
new_migrations="$(git diff --name-only --diff-filter=A HEAD "$tag" -- database/migrations)"
|
||||
fi
|
||||
if [[ -n "$new_migrations" ]]; then
|
||||
echo "Neue Migrationen:"
|
||||
echo "$new_migrations" | sed 's/^/ /'
|
||||
if [[ "$run_migrations" -ne 1 ]]; then
|
||||
echo "Abbruch: DB-Backup ziehen und mit --migrations erneut aufrufen. Nichts wurde verändert." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$initial" -eq 1 ]]; then
|
||||
trap 'echo; echo "!! Umstellung abgebrochen. Die Seite bleibt im Wartungsmodus." >&2; echo "!! Rückweg: Verzeichnis aus $backup wiederherstellen (vorher .git entfernen), DB-Backup einspielen." >&2; rm -f "$DEPLOY_COPY"' ERR
|
||||
else
|
||||
trap 'echo; echo "!! Deploy abgebrochen. Die Seite bleibt im Wartungsmodus." >&2; echo "!! Rollback: bash $DEPLOY_SCRIPT $previous" >&2; rm -f "$DEPLOY_COPY"' ERR
|
||||
fi
|
||||
|
||||
step "Wartungsmodus"
|
||||
php artisan down --retry=60
|
||||
|
||||
step "Checkout $tag"
|
||||
if [[ "$initial" -eq 1 ]]; then
|
||||
if [[ -n "$server_only" ]]; then
|
||||
while IFS= read -r file; do
|
||||
mkdir -p "$parked/$(dirname "$file")"
|
||||
mv "$file" "$parked/$file"
|
||||
done <<< "$server_only"
|
||||
echo "Nur-Server-Dateien verschoben nach $parked/"
|
||||
fi
|
||||
git checkout --quiet --force --detach "$tag"
|
||||
: > .git/info/exclude
|
||||
else
|
||||
git checkout --quiet --detach "$tag"
|
||||
fi
|
||||
echo "$tag" > RELEASE
|
||||
|
||||
step "Composer"
|
||||
composer install --no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
if [[ -n "$new_migrations" ]]; then
|
||||
step "Migrationen"
|
||||
php artisan migrate --force
|
||||
fi
|
||||
|
||||
for seeder in "${seeders[@]}"; do
|
||||
step "Seeder $seeder"
|
||||
php artisan db:seed --class="$seeder" --force
|
||||
done
|
||||
|
||||
step "Öffentliche Assets"
|
||||
if [[ ! -e public/storage ]]; then
|
||||
php artisan storage:link
|
||||
fi
|
||||
if [[ -d public/vendor/livewire ]]; then
|
||||
php artisan vendor:publish --tag=livewire:assets --force
|
||||
fi
|
||||
# Statisch ausgelieferte Flux-Skripte passend zur installierten Flux-Version
|
||||
if [[ -d public/flux ]]; then
|
||||
php artisan flux:publish-assets --force
|
||||
fi
|
||||
|
||||
step "Caches"
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:clear
|
||||
php artisan view:cache
|
||||
php artisan event:cache
|
||||
|
||||
if [[ "$clear_schedule" -eq 1 ]]; then
|
||||
php artisan schedule:clear-cache
|
||||
fi
|
||||
|
||||
step "Worker und PHP-FPM"
|
||||
php artisan queue:restart
|
||||
default_fpm_reload="echo '' | sudo -S service $PHP_FPM_SERVICE reload"
|
||||
fpm_reload="${DEPLOY_FPM_RELOAD-$default_fpm_reload}"
|
||||
if [[ -n "$fpm_reload" ]]; then
|
||||
bash -c "$fpm_reload"
|
||||
else
|
||||
echo "PHP-FPM jetzt im Server-Panel neu laden (Reload, nicht Restart), danach Enter."
|
||||
read -r
|
||||
fi
|
||||
|
||||
step "Wartungsmodus aus"
|
||||
php artisan up
|
||||
|
||||
step "Version"
|
||||
if [[ -n "$ABOUT_SECTION" ]]; then
|
||||
php artisan about --only="$ABOUT_SECTION"
|
||||
else
|
||||
cat RELEASE
|
||||
fi
|
||||
|
||||
trap - ERR
|
||||
|
||||
# Läuft ein Worker? `queue:restart` oben beendet ihn; neu starten muss ihn
|
||||
# Supervisor. Läuft Supervisor nicht, steht die Queue still, ohne dass es
|
||||
# jemand merkt.
|
||||
if [[ "$QUEUE_PROBE" -eq 1 ]]; then
|
||||
step "Queue-Worker"
|
||||
if ! php artisan monitoring:queue-probe --wait=60; then
|
||||
echo
|
||||
echo "!! Deploy $tag ist live, aber KEIN Queue-Worker läuft." >&2
|
||||
echo "!! Jetzt Supervisor und den Worker im Server-Panel prüfen — sonst bleiben Jobs und Mails liegen." >&2
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Deploy $tag fertig (vorher $previous). Weiter mit den Prüfungen aus der Deploy-Anleitung."
|
||||
175
dev/betrieb/vorschau-auffrischen.sh
Executable file
175
dev/betrieb/vorschau-auffrischen.sh
Executable file
|
|
@ -0,0 +1,175 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Vorschau mit den aktuellen Live-Daten auffrischen (B2in). Ablauf: dev/betrieb/README.md.
|
||||
#
|
||||
# Kopiert die komplette Live-Datenbank und die hochgeladenen Medien (storage/app/public:
|
||||
# Display-Mediathek, CMS-Mediathek, Videos, QR-Codes) in die Vorschau. Danach steht auf der
|
||||
# Vorschau genau das, was der Kunde auf Live gepflegt hat – Displays, Playlists, Mediathek –,
|
||||
# und der Release-Seeder kann darauf zur Probe laufen.
|
||||
#
|
||||
# Live wird nur gelesen (mysqldump --single-transaction, rsync als Quelle). Die Vorschau-
|
||||
# Datenbank wird vollständig ersetzt.
|
||||
#
|
||||
# Aufruf (auf dem Server, als App-Benutzer):
|
||||
#
|
||||
# bash dev/betrieb/vorschau-auffrischen.sh <live-verzeichnis> <vorschau-verzeichnis> [--seed=<Klasse>]
|
||||
#
|
||||
# --seed=<Klasse> nach dem Import (und den Migrationen) diesen Seeder auf der Vorschau ausführen,
|
||||
# z. B. --seed=InteriorReleaseSeeder für eine erneute Generalprobe.
|
||||
#
|
||||
# Die Live-Sicherung bleibt als ~/b2in-live-<zeit>.sql.gz liegen.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
live="${1:-}"
|
||||
preview="${2:-}"
|
||||
shift 2 || true
|
||||
seeders=()
|
||||
|
||||
for option in "$@"; do
|
||||
case "$option" in
|
||||
--seed=*)
|
||||
seeder="${option#--seed=}"
|
||||
if [[ ! "$seeder" =~ ^[A-Za-z][A-Za-z0-9]*$ ]]; then
|
||||
echo "Ungültiger Seeder: $seeder" >&2; exit 2
|
||||
fi
|
||||
seeders+=("$seeder") ;;
|
||||
*) echo "Unbekannte Option: $option" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$live" || -z "$preview" || ! -f "$live/artisan" || ! -f "$preview/artisan" ]]; then
|
||||
echo "Aufruf: bash $0 <live-verzeichnis> <vorschau-verzeichnis> [--seed=<Klasse>]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
live="$(cd "$live" && pwd)"
|
||||
preview="$(cd "$preview" && pwd)"
|
||||
|
||||
if [[ "$live" == "$preview" ]]; then
|
||||
echo "Live- und Vorschau-Verzeichnis sind dasselbe." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
step() { printf '\n==> %s\n' "$*"; }
|
||||
|
||||
# Wert aus einer .env lesen (ohne Anführungszeichen, ohne Kommentar am Zeilenende bei unquotierten Werten)
|
||||
env_value() {
|
||||
local file="$1" key="$2" line value
|
||||
line="$(grep -E "^${key}=" "$file" | tail -n 1 || true)"
|
||||
value="${line#*=}"
|
||||
if [[ "$value" =~ ^\"(.*)\"$ || "$value" =~ ^\'(.*)\'$ ]]; then
|
||||
value="${BASH_REMATCH[1]}"
|
||||
else
|
||||
value="${value%% #*}"
|
||||
fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
# MySQL-Zugangsdaten als Datei (chmod 600), damit das Passwort nicht in der Prozessliste steht
|
||||
credentials() {
|
||||
local env="$1/.env" file
|
||||
file="$(mktemp)"
|
||||
chmod 600 "$file"
|
||||
{
|
||||
echo "[client]"
|
||||
echo "host=$(env_value "$env" DB_HOST)"
|
||||
echo "port=$(env_value "$env" DB_PORT)"
|
||||
echo "user=$(env_value "$env" DB_USERNAME)"
|
||||
echo "password=\"$(env_value "$env" DB_PASSWORD)\""
|
||||
} > "$file"
|
||||
printf '%s' "$file"
|
||||
}
|
||||
|
||||
live_db="$(env_value "$live/.env" DB_DATABASE)"
|
||||
preview_db="$(env_value "$preview/.env" DB_DATABASE)"
|
||||
live_host="$(env_value "$live/.env" DB_HOST)"
|
||||
preview_host="$(env_value "$preview/.env" DB_HOST)"
|
||||
|
||||
if [[ -z "$live_db" || -z "$preview_db" ]]; then
|
||||
echo "DB_DATABASE fehlt in einer der beiden .env-Dateien." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$live_db" == "$preview_db" && "$live_host" == "$preview_host" ]]; then
|
||||
echo "STOPP: Vorschau und Live zeigen auf dieselbe Datenbank ($live_db). Nichts wurde verändert." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$(env_value "$preview/.env" APP_ENV)" == "production" ]]; then
|
||||
echo "STOPP: Die Vorschau läuft mit APP_ENV=production – ist das wirklich die Vorschau? Nichts wurde verändert." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mailer="$(env_value "$preview/.env" MAIL_MAILER)"
|
||||
if [[ "$mailer" != "log" && "$mailer" != "array" ]]; then
|
||||
echo "Hinweis: MAIL_MAILER der Vorschau ist „$mailer“. Mit den Live-Daten könnte die Vorschau echte Mails verschicken."
|
||||
echo "Empfohlen: MAIL_MAILER=log in $preview/.env."
|
||||
fi
|
||||
|
||||
echo "Live: $live (Datenbank $live_db)"
|
||||
echo "Vorschau: $preview (Datenbank $preview_db – wird vollständig ersetzt)"
|
||||
echo "Medien: $live/storage/app/public/ → $preview/storage/app/public/ (Vorschau-eigene Dateien werden entfernt)"
|
||||
read -r -p "Weiter? [ja/nein] " answer
|
||||
if [[ "$answer" != "ja" ]]; then
|
||||
echo "Abgebrochen, nichts verändert."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
live_credentials="$(credentials "$live")"
|
||||
preview_credentials="$(credentials "$preview")"
|
||||
trap 'rm -f "$live_credentials" "$preview_credentials"' EXIT
|
||||
|
||||
dump="$HOME/b2in-live-$(date +%Y-%m-%d-%H%M).sql.gz"
|
||||
|
||||
step "Live-Datenbank sichern nach $dump"
|
||||
mysqldump --defaults-extra-file="$live_credentials" --single-transaction --quick --no-tablespaces \
|
||||
--routines --triggers --default-character-set=utf8mb4 "$live_db" | gzip > "$dump"
|
||||
ls -lh "$dump"
|
||||
|
||||
step "Vorschau in den Wartungsmodus"
|
||||
(cd "$preview" && php artisan down --retry=60)
|
||||
|
||||
trap 'echo; echo "!! Abgebrochen. Die Vorschau bleibt im Wartungsmodus; Live ist unverändert." >&2; rm -f "$live_credentials" "$preview_credentials"' ERR
|
||||
|
||||
step "Vorschau-Datenbank leeren und Live-Stand einspielen"
|
||||
(cd "$preview" && php artisan db:wipe --force)
|
||||
gunzip -c "$dump" | mysql --defaults-extra-file="$preview_credentials" --default-character-set=utf8mb4 "$preview_db"
|
||||
|
||||
# Sitzungen und Cache von Live gehören nicht in die Vorschau
|
||||
for table in sessions cache cache_locks jobs; do
|
||||
if mysql --defaults-extra-file="$preview_credentials" -N -e "SHOW TABLES LIKE '$table'" "$preview_db" | grep -q .; then
|
||||
mysql --defaults-extra-file="$preview_credentials" -e "TRUNCATE TABLE \`$table\`" "$preview_db"
|
||||
fi
|
||||
done
|
||||
|
||||
step "Medien übernehmen"
|
||||
mkdir -p "$preview/storage/app/public"
|
||||
if [[ "$(realpath "$live/storage/app/public")" == "$(realpath "$preview/storage/app/public")" ]]; then
|
||||
# Vorschau verlinkt auf die Live-Medien (preview-deployment.md, Schritt 3) – nichts zu kopieren
|
||||
echo "Die Vorschau nutzt die Live-Medien per Link – übersprungen."
|
||||
else
|
||||
rsync -a --delete "$live/storage/app/public/" "$preview/storage/app/public/"
|
||||
du -sh "$preview/storage/app/public"
|
||||
fi
|
||||
|
||||
step "Migrationen und Seeder"
|
||||
cd "$preview"
|
||||
php artisan migrate --force
|
||||
for seeder in "${seeders[@]}"; do
|
||||
php artisan db:seed --class="$seeder" --force
|
||||
done
|
||||
|
||||
step "Caches"
|
||||
[[ -e public/storage ]] || php artisan storage:link
|
||||
php artisan optimize:clear
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
|
||||
step "Vorschau wieder online"
|
||||
php artisan up
|
||||
|
||||
trap - ERR
|
||||
echo
|
||||
echo "Vorschau aufgefrischt: Daten und Medien von Live, Stand $(date '+%d.%m.%Y %H:%M')."
|
||||
|
|
@ -82,28 +82,28 @@ Auf dem Server als App-Benutzer (bei Ploi `ploi`):
|
|||
2. Deploy-Key erzeugen und im Git-Server **nur lesend** am Repository eintragen:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/deploy_meineapp -N "" -C "meineapp-live"
|
||||
cat ~/.ssh/deploy_meineapp.pub
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/deploy_b2inapp -N "" -C "b2inapp-live"
|
||||
cat ~/.ssh/deploy_b2inapp.pub
|
||||
```
|
||||
|
||||
3. `~/.ssh/config`:
|
||||
|
||||
```
|
||||
Host git.example.com
|
||||
Host forge.adametz.media
|
||||
Port 222
|
||||
User git
|
||||
IdentityFile ~/.ssh/deploy_meineapp
|
||||
IdentityFile ~/.ssh/deploy_b2inapp
|
||||
IdentitiesOnly yes
|
||||
```
|
||||
|
||||
4. Host-Key aufnehmen, Fingerprint mit dem im Git-Server angezeigten vergleichen:
|
||||
|
||||
```bash
|
||||
ssh-keyscan -p 222 git.example.com >> ~/.ssh/known_hosts
|
||||
ssh-keygen -lf <(ssh-keyscan -p 222 git.example.com 2>/dev/null)
|
||||
ssh-keyscan -p 222 forge.adametz.media >> ~/.ssh/known_hosts
|
||||
ssh-keygen -lf <(ssh-keyscan -p 222 forge.adametz.media 2>/dev/null)
|
||||
```
|
||||
|
||||
5. Test: `git ls-remote ssh://git@git.example.com:222/team/meineapp.git` zeigt die Tags.
|
||||
5. Test: `git ls-remote ssh://git@forge.adametz.media :222/kadmin/b2in.git` zeigt die Tags.
|
||||
6. **FPM-Reload ohne Passwort testen** (Ploi erlaubt das dem Benutzer `ploi`):
|
||||
|
||||
```bash
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue