Deployment
Auf dieser Seite
- Der Befehl deploy
- Alle Ziele auf einmal deployen
- local
- github-pages
- s3
- azure
- gcs
- firebase
- ftp / sftp
- rsync
- netlify
- vercel
- cloudflare-pages
- Der Befehl package
- GitHub Actions (Multi-Versions-Veröffentlichung)
- Was er tut
- Einmalige Einrichtung
- Zwei Versionen gleichzeitig veröffentlichen
- Für dein eigenes Projekt verwenden
- Aus einem Projekt-Pages-Unterpfad ausliefern
- Zugriff auf deine Website einschränken
Deployment
site/ ist eine reine statische Website - hoste sie überall, wo statische
Dateien ausgeliefert werden können. bxSites deploy
bringt sie mit einem einzigen Befehl direkt dorthin: S3 (und jeder
S3-kompatible Dienst - DigitalOcean Spaces, Cloudflare R2, Backblaze B2,
MinIO), Azure Blob Storage, Google Cloud Storage, Firebase Hosting, FTP,
SFTP, rsync über SSH, Netlify, Vercel, Cloudflare Pages, ein lokales
Verzeichnis oder GitHub Pages.
Der Befehl deploy
Jedes Ziel außer den beiden einfachsten (local/github-pages, die allein
mit Flags auskommen - siehe CLI-Referenz)
wird über eine deployments/<name>.json-Datei im Projekt-Wurzelverzeichnis
konfiguriert - eine Datei pro tatsächlich genutztem Deploy-Ziel, jede
benennt, welches target es ist, plus die eigenen Felder dieses Ziels:
bxSites deploy --entry=production
{ "target": "s3", "bucket": "my-docs-site", "accessKeyIdEnvVar": "AWS_ACCESS_KEY_ID", "secretAccessKeyEnvVar": "AWS_SECRET_ACCESS_KEY" }
Secrets kommen immer aus einer Umgebungsvariable, nie aus einem
literalen Wert in deployments/*.json. Jedes Feld, das auf EnvVar
endet, benennt die Umgebungsvariable, die das eigentliche Secret enthält
(ein Access Key, ein Passwort, ein API-Token) - zur Deploy-Zeit live
aufgelöst, sodass deployments/*.json selbst immer gefahrlos committet
werden kann. Ein Feld, das ein Pfad zu einer Credential-Datei ist, die
du bereits selbst verwaltest (ein privater SSH-Schlüssel, ein
heruntergeladener GCP-Service-Account-JSON-Key), ist die eine Ausnahme -
ein einfaches Feld, da die Datei selbst aus der Versionskontrolle
herausgehalten wird, nicht ihr Pfad. Lokal können diese
Umgebungsvariablen auch aus einer .env-Datei stammen (BoxLang lädt
automatisch eine, und getSystemSetting() - was jedes Ziel zu ihrer
Auflösung nutzt - prüft sie transparent), statt sie von Hand in deine
Shell zu exportieren; in CI werden sie als echte Secrets auf dem Runner
gesetzt.
Alle Ziele auf einmal deployen
bxSites deploy ohne --entry und ohne --target ausgeführt, deployt
jeden deployments/*.json-Eintrag der Reihe nach, ausgehend von einem
einzigen gemeinsamen Build:
bxSites deploy
Die Website wird nur einmal gebaut, egal wie viele Einträge du hast.
Schlägt ein Ziel fehl, stoppt das die übrigen nicht - jeder Eintrag wird
versucht, und der Befehl liefert nur dann einen von null verschiedenen
Exit-Code, wenn mindestens einer davon fehlgeschlagen ist; die
Zusammenfassung meldet, wie viele erfolgreich waren (z. B. Deployed to 2/3 target(s) (1 failed)). Mit --verbose (funktioniert auch mit
--entry/--target) wird eine Fortschrittszeile ausgegeben, wenn der
Build und jedes Ziel starten und enden, statt nur der abschließenden
Zusammenfassung.
local
Kopiert die gebaute Website in ein beliebiges Verzeichnis - ein
Netzlaufwerk, einen Staging-Ordner, egal wohin. Das einzige Ziel, das
überhaupt keinen deployments/-Eintrag braucht.
bxSites deploy --target=local --destination=/path/to/somewhere
github-pages
Derselbe Push, den gh-deploy macht,
auch über diesen einheitlichen Befehl erreichbar - braucht ebenfalls
keinen deployments/-Eintrag:
bxSites deploy --target=github-pages [--branch=gh-pages] [--remote=origin] [--message="..."]
s3
Echtes AWS S3, oder jeder S3-kompatible Dienst - setze endpoint für
alles außer AWS selbst, und forcePathStyle: true für die meisten
Nicht-AWS-Provider.
{
"target": "s3",
"bucket": "my-docs-site",
"region": "us-east-1",
"prefix": "",
"accessKeyIdEnvVar": "AWS_ACCESS_KEY_ID",
"secretAccessKeyEnvVar": "AWS_SECRET_ACCESS_KEY"
}
{
"target": "s3",
"bucket": "my-docs-site",
"endpoint": "https://nyc3.digitaloceanspaces.com",
"forcePathStyle": true,
"accessKeyIdEnvVar": "SPACES_KEY",
"secretAccessKeyEnvVar": "SPACES_SECRET"
}
Dieselbe Form (eigener endpoint + forcePathStyle: true) deckt auch
Cloudflare R2 (https://<accountid>.r2.cloudflarestorage.com), Backblaze
B2 und MinIO/Wasabi ab.
azure
Azure Blob Storage, authentifiziert mit einem SAS-Token, einem Account-Key oder einem vollständigen Connection String - genau eines der drei.
{
"target": "azure",
"account": "mystorageaccount",
"container": "site",
"accountKeyEnvVar": "AZURE_STORAGE_KEY"
}
gcs
Google Cloud Storage, authentifiziert mit einem heruntergeladenen Service-Account-JSON-Key (Google Cloud Console -> IAM & Admin -> Service Accounts -> Keys).
{
"target": "gcs",
"bucket": "my-docs-site",
"serviceAccountKeyPath": "/path/to/service-account.json"
}
firebase
Firebase Hosting, mit derselben Art von Service-Account-Key wie gcs.
{
"target": "firebase",
"siteId": "my-firebase-site",
"serviceAccountKeyPath": "/path/to/service-account.json"
}
ftp / sftp
Lädt die gesamte Website per FTP oder SFTP auf einen entfernten Server hoch, unter Beibehaltung ihrer Ordnerstruktur. SFTP akzeptiert ein Passwort oder einen SSH-Schlüssel.
{
"target": "sftp",
"host": "example.com",
"username": "deploy",
"remotePath": "/var/www/html",
"key": "/home/me/.ssh/id_rsa"
}
rsync
Synchronisiert die Website per SSH mit der echten rsync-Binary auf
einen entfernten Server - schneller als FTP/SFTP bei einem vollständigen
Rebuild, da nur die Änderungen übertragen werden. Erfordert rsync und
ssh auf der Maschine, die bxSites ausführt.
{
"target": "rsync",
"host": "example.com",
"username": "deploy",
"remotePath": "/var/www/html",
"identityFile": "/home/me/.ssh/id_rsa"
}
netlify
{
"target": "netlify",
"siteId": "my-site-id-or-name.netlify.app",
"authTokenEnvVar": "NETLIFY_AUTH_TOKEN"
}
vercel
{
"target": "vercel",
"projectId": "my-project",
"authTokenEnvVar": "VERCEL_TOKEN"
}
cloudflare-pages
Cloudflare hat keine offiziell dokumentierte REST-API für
Direct-Upload-Deploys - nur seine wrangler-CLI. Dieses Ziel
reverse-engineert Wranglers eigenen Upload-Flow und braucht eine
BLAKE3-Hash-Implementierung auf dem JVM-Classpath, die die meisten
Standard-Java-Installationen nicht mitbringen - siehe
CLI-Referenz und den eigenen Quellcode
dieses Ziels für das vollständige, ehrliche Detail zu diesen rauen Kanten.
{
"target": "cloudflare-pages",
"accountId": "your-account-id",
"projectName": "my-project",
"apiTokenEnvVar": "CLOUDFLARE_API_TOKEN"
}
Der Befehl package
Bevorzugst du ein reines Archiv gegenüber einem der obigen Ziele - einen
Build an ein GitHub-Release anhängen, ihn an einen Host übergeben, der nur
einen Zip-Upload akzeptiert, oder ihn irgendwohin ausliefern, wo keines
der anbindbaren Ziele hinreicht? bxSites package
baut die Website und packt sie dann in eine einzige Datei, deren Wurzel
der Inhalt der gebauten Website selbst ist (kein umschließender
site/-Ordner):
bxSites package
bxSites package --output=dist/my-site.zip
--output ist standardmäßig <projectRoot>/site.zip; ein relativer Wert
wird gegenüber dem Projekt-Root aufgelöst, und seine übergeordneten
Verzeichnisse werden automatisch angelegt, falls sie noch nicht
existieren.
GitHub Actions (Multi-Versions-Veröffentlichung)
Für eine automatische Veröffentlichung bei jedem Push, statt eines manuell
ausgeführten bxSites deploy/gh-deploy, bringt dieses Modul einen
einsatzbereiten GitHub-Actions-Workflow mit
(.github/workflows/pages.yml), der main und development als zwei
unabhängig live geschaltete Versionen derselben Website auf GitHub Pages
veröffentlicht. Der Rest dieser Anleitung behandelt diesen Workflow, den
die eigenen Docs dieses Repos verwenden.
Was er tut
Bei jedem Push nach main oder development, der docs/, bxsites.yaml
oder die eigene Quelle des Moduls (Theme-/Pipeline-Änderungen) berührt,
macht der Workflow Folgendes:
- Installiert BoxLang + bx-markdown
- Registriert dieses Repo als Modul, damit
boxlang bxSites buildaufgelöst werden kann - Setzt auf jedem Branch außer
mainbaseURLauf.../<branch-name>/nur für diesen einen Build (siehe unten) - Führt
boxlang bxSites buildaus - Pusht
site/in dengh-pages-Branch -mainin die Wurzel der Website,developmentnach/development/- ohne die jeweils andere Version anzurühren
Er ist außerdem als manueller Auslöser (workflow_dispatch) über den
Reiter Actions verfügbar, für eine einmalige Neuveröffentlichung ohne
neuen Commit.
Einmalige Einrichtung
GitHub Pages muss auf den gh-pages-Branch verweisen, bevor der Workflow
etwas veröffentlichen kann - das ist eine Repository-Einstellung, kein
Schalter, den eine Workflow-Datei von selbst umlegen kann. Der erste
erfolgreiche Lauf erstellt gh-pages für dich, tu dies also, nachdem
der Workflow mindestens einmal gelaufen ist:
- Settings -> Pages
- Unter Build and deployment -> Source wähle Deploy from a branch
- Unter Branch wähle gh-pages und / (root)
Danach wird jeder passende Push automatisch gebaut und veröffentlicht. Die veröffentlichte URL erscheint unter Settings -> Pages, sobald das erste Deploy abgeschlossen ist.
Zwei Versionen gleichzeitig veröffentlichen
main wird in die Wurzel der Website veröffentlicht
(https://<user>.github.io/<repo>/) - betrachte das als die
stabilen/veröffentlichten Docs. development wird nach /development/
veröffentlicht (https://<user>.github.io/<repo>/development/) - die
neuesten, unveröffentlichten Docs. Beide bleiben gleichzeitig live: der
Job jedes Branches pusht nur mit keep_files: true und seinem eigenen
destination_dir nach gh-pages, sodass ein development-Deploy nie den
Inhalt von main überschreibt und umgekehrt.
Die eigene bxsites.yaml von main sollte baseURL auf die Wurzel der
Website gesetzt haben (https://<user>.github.io/<repo>/); der Workflow
überschreibt das für jeden anderen Branch zur Build-Zeit, sodass die
bxsites.yaml von development dafür keinen eigenen baseURL-Eintrag
braucht.
Um einen dritten Branch hinzuzufügen (z. B. eine release/2.0-Vorschau),
füge ihn zur on.push.branches-Liste hinzu und gib ihm einen eigenen
if: github.ref_name == '...'-Deploy-Schritt mit
destination_dir: release-2.0 (oder ähnlich) - das Muster ist dasselbe
wie bei development.
Für dein eigenes Projekt verwenden
Kopiere .github/workflows/pages.yml in dein eigenes Projekt (passe die
modules:-Zeile an, falls dein Projekt mehr als bx-markdown braucht),
aktiviere Pages wie oben, und Pushes nach main/development werden auf
dieselbe Weise veröffentlicht. Willst du nur eine einzige veröffentlichte
Version, lösche den nicht benötigten Branch aus on.push.branches und
seinen passenden Deploy-Schritt.
Aus einem Projekt-Pages-Unterpfad ausliefern
Eine GitHub-Projekt-Pages-Website (im Gegensatz zu einer
<user>.github.io-Benutzer-Website) wird unter
https://<user>.github.io/<repo>/ ausgeliefert, nicht von der
Domain-Wurzel. Setze baseURL in bxsites.yaml auf diese vollständige
URL, damit jeder interne Link, jedes Asset und jeder Navigationseintrag
das nötige /<repo>/-Präfix bekommt - und damit auch eine echte
sitemap.xml erzeugt wird:
baseURL: "https://<user>.github.io/<repo>/"
{ "baseURL": "https://<user>.github.io/<repo>/" }
Siehe Konfiguration für die vollständige
Aufschlüsselung dessen, was baseURL bewirkt. Eine
<user>.github.io-Benutzer-Website, oder jede eigene Domain, die auf die
Wurzel der Website zeigt, kann baseURL bei seinem Standardwert (/)
belassen.
Zugriff auf deine Website einschränken
Eine eingebaute Zugriffskontrolle gibt es hier nicht - bx-sites erzeugt
immer nur ein schlichtes statisches site/, und eine statische Datei hat
kein Konzept von "wer fragt hier gerade an". Das
robots: false von bxsites.json sagt
wohlerzogenen Crawlern, ein Build nicht zu indexieren (nützlich für ein
Staging-/Vorschau-Deploy, das nicht in Suchergebnissen auftauchen soll),
aber das ist eine höfliche Bitte, kein Schloss - die URL funktioniert
weiterhin für jeden, der sie hat. Musst du den Zugriff tatsächlich
sperren, muss das vor den statischen Dateien passieren, bei welchem Host
auch immer sie ausliefert - ein paar gängige, für statische Websites
geeignete Optionen:
- Cloudflare Pages/Access - stelle die veröffentlichte Website hinter eine Cloudflare-Access-Richtlinie (E-Mail-Allowlist, SSO oder eine Einmal-PIN) - ohne eigenen Anwendungscode.
- Netlify - eingebauter Passwortschutz pro Website oder pro Deploy, allein aus den Website-Einstellungen heraus.
- Ein kleiner Reverse-Proxy (beliebiger Host) - HTTP-Basic-Auth vor den
statischen Dateien (eine
.htpasswd-artige Regel, oder ein Ein-Datei-Cloudflare-Worker/Netlify-Edge-Function) reicht, um Suchmaschinen und Zufallsbesucher fernzuhalten, auch wenn das keine echte Pro-Benutzer-Identität ist, wie sie eine eingeloggte Anwendung hätte.
Nichts davon sind bx-sites-Funktionen - es sind Host-Einstellungen, die du
dort einschaltest, wo site/ am Ende ausgeliefert wird.