Die Core-Migrationen von EmDash aktualisieren die eigenen Tabellen von EmDash und die Standardspalten der Inhaltstabellen. Sie erstellen, entfernen oder benennen Ihre Sammlungen und Felder nicht um; Änderungen am Inhaltsmodell finden Sie unter Das Schema einer bereitgestellten Site weiterentwickeln.
Der Laufzeit-Migrationsmodus ist standardmäßig auto, sodass bestehende Deployments ausstehende Core-Migrationen weiterhin beim Start anwenden. Mit Deployment-verwalteten Migrationen kann ein Build seine Datenbank migrieren, bevor neuer Anwendungscode Traffic erhält, und die Laufzeit prüft anschließend diesen Deployment-Schritt oder vertraut darauf.
Core-Migrationen laufen nur vorwärts. Sie sind so geschrieben, dass ein Befehl nach Anweisungen, die definitiv abgeschlossen wurden, wiederholt werden kann, aber ein unterbrochener Remote-Befehl kann ein mehrdeutiges Ergebnis hinterlassen. Die sichere Reaktion ist, dieselbe Datenbank mit emdash migrate --status zu untersuchen, statt anzunehmen, dass entweder die gesamte Migration oder gar nichts davon ausgeführt wurde.
Bauen, migrieren, deployen, prüfen
Ein Astro-Build oder eine Synchronisierung schreibt .emdash/migrations.json. Dieses Manifest enthält keine Geheimnisse und hält die exakte EmDash-Version, die geordnete Menge der Migrationen, die Locale-Konfiguration und den Migrations-Executor des Adapters fest, die dieser Build verwendet hat.
Führen Sie diese Befehle in dem Projekt aus, dessen Abhängigkeiten das Manifest erzeugt haben. Bauen Sie zuerst und prüfen Sie das Ziel.
pnpm build
pnpm emdash migrate --status
Nachdem Sie bestätigt haben, dass das gemeldete Ziel die beabsichtigte Datenbank ist, starten Sie die interaktive Migration. Prüfen Sie das Ziel an der Eingabeaufforderung noch einmal, bevor Sie bestätigen. Deployen Sie anschließend denselben Build und prüfen Sie das bereitgestellte Schema.
pnpm emdash migrate
pnpm wrangler deploy
pnpm emdash migrate --check
emdash migrate --status meldet angewendete, ausstehende und unbekannte Migrationen, ohne die Datenbank zu verändern. Der einfache Befehl emdash migrate zeigt das Ziel an und fragt nach einer Bestätigung, bevor er ausstehende Migrationen anwendet.
--check wendet nie Migrationen an und beendet sich mit einem Wert ungleich null, wenn bekannte Migrationen ausstehen oder die Datenbank Migrationseinträge enthält, die dem Build unbekannt sind. Verwenden Sie --status, wenn Sie dieselben Migrationsmengen untersuchen möchten, ohne den Exit-Status ungleich null von check für „Handlungsbedarf“. Die CLI-Referenz unterscheidet die Exit-Codes für ausstehend, unbekannt, Bestätigung, Unterbrechung und Betriebsfehler.
Nicht interaktives Anwenden und jedes Anwenden mit --json erfordern --expected-target-fingerprint; der Befehl schlägt fehl, wenn das aufgelöste Ziel nicht übereinstimmt. Verwenden Sie diese Optionen in automatisierten Deployment-Jobs, nicht im oben beschriebenen interaktiven Ablauf.
Verwenden Sie --manifest path/to/migrations.json für ein Manifest, das anderswo liegt. Für lokale Untersuchungen wertet --from-config [--config astro.config.mjs] die vertrauenswürdige Projektkonfiguration ausdrücklich aus, ohne Astro-Hooks auszuführen oder einen Server zu starten. Deployment-Pipelines sollten das Build-Manifest verwenden.
Die Datenbank explizit auswählen
Der konfigurierte Adapter liefert dem Manifest Zielinformationen ohne Geheimnisse. Zugangsdaten bleiben in Umgebungsvariablen und werden nur vom Migrationsbefehl gelesen.
| Adapter | Manifest-Ziel | Standardvariable für Zugangsdaten | Nützliche Überschreibung |
|---|---|---|---|
| SQLite | Datenbankpfad oder file:-URL | — | --database <path> |
| libSQL | Öffentliche URL | TURSO_AUTH_TOKEN | migrationAuthTokenEnv konfigurieren |
| PostgreSQL | Name der Verbindungsvariable | DATABASE_URL | --database-url-env <name> |
| Cloudflare D1 | Name des Wrangler-Bindings | CLOUDFLARE_API_TOKEN | --d1, --account-id, --wrangler-config, --wrangler-env |
| Hyperdrive | Primäres Binding und Name der Origin-Variable | Bindungsspezifische Direct-Origin-Variable | migrationConnectionStringEnv konfigurieren |
Relative SQLite-Pfade werden vom Projektstamm aus aufgelöst, nicht vom installierten EmDash-Paket und nicht vom aktuellen Unterverzeichnis der Shell. Die Zielbezeichnungen für PostgreSQL, libSQL und Hyperdrive enthalten keine Zugangsdaten und keine URL-Parameter.
D1 vor der Migration bereitstellen
Das Erstellen einer D1-Datenbank und das Migrieren ihres Schemas sind getrennte Vorgänge. emdash migrate erstellt nie eine fehlende Datenbank.
-
Stellen Sie die Datenbank bereit und notieren Sie ihre Produktions-UUID.
pnpm wrangler d1 create my-site-production -
Tragen Sie diese UUID in das vorgesehene Binding und die vorgesehene Umgebung in
wrangler.jsoncein. -
Bauen Sie die Site, damit das D1-Binding in
.emdash/migrations.jsonfestgehalten wird. -
Legen Sie die Konto-ID und ein eng begrenztes API-Token mit D1-Edit-Berechtigung fest. Prüfen Sie das ausgewählte Ziel und führen Sie dann die interaktive Migration aus. Bestätigen Sie die Eingabeaufforderung nur, wenn Konto und Datenbank mit der beabsichtigten Produktionsdatenbank übereinstimmen.
export CLOUDFLARE_ACCOUNT_ID="..." export CLOUDFLARE_API_TOKEN="..." pnpm emdash migrate \ --status \ --wrangler-config wrangler.jsonc \ --wrangler-env production pnpm emdash migrate \ --wrangler-config wrangler.jsonc \ --wrangler-env production
Sie können stattdessen --account-id zusammen mit --d1 <database-uuid-or-name> angeben. Die Suche nach dem Namen muss genau eine Datenbank ergeben. Vorschau-IDs, Platzhalter-IDs, widersprüchliche Konten und mehrdeutige Bindings schlagen sicherheitshalber fehl (fail closed).
D1-Migrationen in CI konfigurieren
EmDash hält in der D1-Datenbank eine Migrationssperre, während es Migrationen anwendet, sowohl bei emdash migrate als auch bei Laufzeitmigrationen im Modus auto. Ein zweiter Lauf, der startet, während die Sperre gehalten wird, wartet bis zu 10 Sekunden. Beendet sich der erste Lauf in dieser Zeit, ist der zweite Lauf erfolgreich, ohne etwas anzuwenden; andernfalls schlägt er fehl, ohne Migrationen anzuwenden. Führen Sie pro Konto und D1-Datenbank-UUID jeweils nur einen Migrationsjob gleichzeitig aus, damit ein zweiter Job in der CI-Warteschlange wartet, statt fehlzuschlagen.
Legen Sie in der CI-Umgebung das folgende Secret und die folgenden Variablen fest:
- Secret
CLOUDFLARE_API_TOKEN: ein eng begrenztes Token mit D1-Edit-Berechtigung. - Variable
CLOUDFLARE_ACCOUNT_ID: die ID des Cloudflare-Kontos, dem die Datenbank gehört. - Variable
D1_DATABASE_ID: die UUID der D1-Produktionsdatenbank. - Variable
EMDASH_TARGET_FINGERPRINT: der Fingerprint, denemdash migrate --statusausgibt, nachdem Sie Konto und Datenbank lokal geprüft haben.
Der folgende GitHub-Actions-Workflow verwendet diese Werte und bildet den Schlüssel der Concurrency-Gruppe aus beiden unveränderlichen D1-Kennungen. Sein Anwendungsschritt ist nicht interaktiv, daher übergibt er den geprüften Ziel-Fingerprint ausdrücklich.
name: Deploy
on:
workflow_dispatch:
concurrency:
group: emdash-migrations-${{ vars.CLOUDFLARE_ACCOUNT_ID }}-${{ vars.D1_DATABASE_ID }}
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm build
- name: Inspect EmDash migration target
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
pnpm emdash migrate --status --json \
--account-id "${{ vars.CLOUDFLARE_ACCOUNT_ID }}" \
--d1 "${{ vars.D1_DATABASE_ID }}"
- name: Apply EmDash migrations
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
EMDASH_TARGET_FINGERPRINT: ${{ vars.EMDASH_TARGET_FINGERPRINT }}
run: |
pnpm emdash migrate \
--account-id "${{ vars.CLOUDFLARE_ACCOUNT_ID }}" \
--d1 "${{ vars.D1_DATABASE_ID }}" \
--expected-target-fingerprint "$EMDASH_TARGET_FINGERPRINT"
- run: pnpm wrangler deploy
- name: Check EmDash migrations
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: |
pnpm emdash migrate --check \
--account-id "${{ vars.CLOUDFLARE_ACCOUNT_ID }}" \
--d1 "${{ vars.D1_DATABASE_ID }}"
Aktualisieren Sie EMDASH_TARGET_FINGERPRINT erst, nachdem Sie ein geändertes Ziel lokal geprüft haben. Der Fingerprint enthält keine Zugangsdaten, aber wenn Sie ihn ändern, ohne Konto und Datenbank zu prüfen, entfällt der Schutz davor, die falsche Datenbank zu migrieren.
Eine hängende Migrationssperre freigeben
Ein D1-Migrationslauf, der stoppt, bevor er die Migrationssperre freigibt, lässt die Sperre gehalten zurück. Das passiert, wenn ein CI-Job während emdash migrate abgebrochen wird, wenn ein Worker im Modus auto während einer Laufzeitmigration stoppt oder wenn ein Entwicklungsserver beendet wird, während er Migrationen anwendet. Ein Lauf, der mit einem Migrationsfehler fehlschlägt, gibt die Sperre frei. EmDash gibt keine Sperre frei, die es nicht hält, weil der Halter möglicherweise noch Migrationen anwendet oder mitten in einer Migration gestoppt ist. Solange die Sperre nicht freigegeben ist, kann die Site ihre ausstehenden Migrationen nicht anwenden, und im Modus auto schlägt die Initialisierung von EmDash fehl.
Wenn die Sperre länger als eine Minute gehalten wurde, warten emdash migrate und Laufzeitmigrationen nicht mehr darauf und melden den folgenden Fehler:
The migration lock has been held since 2026-09-01T12:00:00.000Z (lock 1788264000000). A migration may still be running; if none is, check the database and release the lock: https://docs.emdashcms.com/deployment/core-migrations/#release-a-stuck-migration-lock
Das Freigeben der Sperre einer entfernten D1-Datenbank verwendet emdash migrate. Dafür benötigen Sie einen Build, der .emdash/migrations.json geschrieben hat, und ein API-Token mit D1-Edit-Berechtigung, wie unter D1 vor der Migration bereitstellen beschrieben.
-
Vergewissern Sie sich, dass kein Migrationsjob, kein Deployment und kein anderer
emdash migrate-Befehl auf die Datenbank zugreift. -
Prüfen Sie die Sperre und die Migrationsmengen mit denselben Zieloptionen, die die Migration verwendet hat.
pnpm emdash migrate --statusDer Bericht beginnt mit der Sperre und ihrer ID. Wenn der gestoppte Lauf gerade eine Migration anwendete, war das die erste ausstehende Migration, und sie ist möglicherweise teilweise angewendet.
Migration lock: held since 2026-09-01T12:00:00.000Z (id 1788264000000)Auch ein Worker im Modus
autokann die Sperre halten, während er Migrationen anwendet. Führen Sie den Befehl nach einer Minute oder mehr erneut aus, und warten Sie, statt die Sperre freizugeben, solange sich die bekannten angewendeten Migrationen weiter ändern. -
Geben Sie die Sperre mit dieser ID frei. Der Befehl bittet Sie, das Ziel zu bestätigen, und gibt die Sperre nur frei, solange sie noch diese ID hat. Fügen Sie in einer nicht interaktiven Shell
--expected-target-fingerprintmit dem Ziel-Fingerprint hinzu, den--statusausgegeben hat.pnpm emdash migrate --release-lock 1788264000000 -
Wenden Sie die ausstehenden Migrationen erneut an.
pnpm emdash migrateSchlägt das Anwenden bei der ersten ausstehenden Migration fehl, behandeln Sie diese Migration als halb ausgeführt und folgen Sie dem Eintrag zu einem mehrdeutigen D1-Schreibvorgang unter Fehlerbehebung.
emdash migrate erreicht nur entfernte D1-Datenbanken. Wird die Sperre in der lokalen D1-Datenbank eines Entwicklungsservers gehalten, beenden Sie den Server und löschen Sie die Sperre mit Wrangler. Ersetzen Sie dabei DB durch den Binding-Namen und die Zahl durch die Sperr-ID aus dem Fehler.
pnpm wrangler d1 execute DB --local --command "UPDATE _emdash_migrations_lock SET is_locked = 0 WHERE is_locked = 1788264000000"
Hyperdrive verbindet sich mit dem Ursprung
Der Migrations-Executor von Hyperdrive öffnet eine direkte PostgreSQL-Verbindung zum Ursprung (Origin). Er leitet keinen Migrationsverkehr über Hyperdrive, verwendet nicht das optionale Cache-Binding und erbt vom Worker keine Erreichbarkeit im privaten Netzwerk.
Der Deployment-Runner muss den Ursprung erreichen können. Setzen Sie migrationConnectionStringEnv auf hyperdrive(), wenn die standardmäßige bindungsspezifische Variable ungeeignet ist, und stellen Sie diese Variable nur dem Migrationsjob bereit. Halten Sie die Hyperdrive-Zugangsdaten der Laufzeit und die Deployment-Zugangsdaten für den direkten Ursprung getrennt.
Laufzeitdurchsetzung schrittweise einführen
Die folgende Konfiguration der EmDash-Integration aktiviert die Laufzeitdurchsetzung und behält in der Entwicklung automatische Migrationen bei.
emdash({
database,
migrations: {
runtime: "check",
dev: "auto",
},
});
autoist der abwärtskompatible Standard. Der Laufzeitstart prüft ausstehende Migrationen und wendet sie an.checkführt eine gerichtete Statusabfrage aus und liefert 503, bevor eine Anfrage bedient wird, wenn bekannte Migrationen ausstehen. Es toleriert Einträge eines neueren kompatiblen Builds während eines Rolling-Deployments. Anfragen an den Bildendpunkt, den EmDash installiert, werden weiterhin bedient, weil sie den Laufzeitstart überspringen, der diese Prüfung ausführt.manualführt keine Laufzeitmigration und keine Statusabfrage aus. Verwenden Sie es erst, wenn die Deployment-Pipeline jeden Build zuverlässig anwendet und prüft.
EMDASH_MIGRATIONS_MODE kann den Laufzeitmodus überschreiben, wenn dasselbe Artefakt durch mehrere Umgebungen befördert wird. Die Umgehungsrouten für Einrichtung und Entwicklung befolgen den tatsächlich gültigen Modus; sie können nicht stillschweigend hinter check oder manual migrieren.
Ein vorsichtiges Vorgehen ist auto, während Sie den Deployment-Job einführen, dann check, sobald der Job zuverlässig ist, und schließlich manual, wenn für jedes Deployment eine externe Prüfung erzwungen wird.
Kompatibilität während Rolling-Deploys
Core-Migrationen folgen der Abfolge Expand/Deploy/Contract. Ein Deployment kann vorübergehend alte und neue Anwendungs-Isolates gegen die erweiterte Datenbank laufen lassen, und ein Backfill kann noch laufen. Verkleinern Sie ein Schema (Contract) erst, wenn jede bereitgestellte Version aufgehört hat, es zu verwenden.
Unbekannte Einträge angewendeter Migrationen werden vom Laufzeit-check nur für diese Richtung des Rolling-Deployments toleriert. Die exakte Prüfung der CLI meldet sie, und das Anwenden verweigert Änderungen, weil die Datenbank neuer sein oder eine abweichende Migrationshistorie haben kann.
Rollback-Grenze
Das Deployen des vorherigen Anwendungsartefakts macht eine Core-Migration nicht rückgängig. Erstellen Sie, bevor Sie ausstehende Migrationen anwenden, ein wiederherstellbares Datenbank-Backup und notieren Sie das dazu passende Anwendungsartefakt. Kann die vorherige Anwendung nicht gegen das migrierte Schema laufen, stellen Sie die Datenbank vor der Migration und die Anwendung gemeinsam wieder her. Löschen Sie keine Zeilen aus _emdash_migrations und führen Sie die interne down()-Funktion einer Migration nicht als betrieblichen Rollback aus.
Gemischte PostgreSQL-Eigentümerschaft reparieren
Verwenden Sie dieses Runbook, wenn eine bestehende PostgreSQL-Site EmDash-Objekte mit mehr als einem Eigentümer erstellt hat und spätere Migrationen mit Fehlern wie must be owner of table fehlschlagen. Wählen Sie die kanonische Rolle, die die primäre EmDash-Verbindung weiterhin verwenden wird. Erstellen Sie ein wiederherstellbares Datenbank-Backup und stoppen Sie den Anwendungsverkehr und Schemaänderungen, bevor Sie die Eigentümerschaft ändern.
Prüfen Sie jede Tabelle im aktiven Schema:
SELECT
n.nspname AS schema_name,
c.relname AS table_name,
pg_get_userbyid(c.relowner) AS owner
FROM pg_class AS c
JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE n.nspname = current_schema()
AND c.relkind IN ('r', 'p')
ORDER BY c.relname;
Zu den EmDash-Objekten gehören die Systemtabellen _emdash_* und _plugin_*, die Sammlungstabellen ec_* sowie Tabellen ohne Präfix wie content_taxonomies, media, options, revisions und taxonomies. In einem dedizierten EmDash-Schema sollte jede Anwendungstabelle den kanonischen Eigentümer haben.
EmDash erstellt außerdem PostgreSQL-Funktionen, die von den Triggern zur Mediennutzung verwendet werden. Prüfen Sie die Eigentümerschaft der Funktionen und merken Sie sich die Argumentsignatur jeder Funktion für den Reparaturbefehl:
SELECT
n.nspname AS schema_name,
p.proname AS function_name,
pg_get_function_identity_arguments(p.oid) AS arguments,
pg_get_userbyid(p.proowner) AS owner
FROM pg_proc AS p
JOIN pg_namespace AS n ON n.oid = p.pronamespace
WHERE n.nspname = current_schema()
ORDER BY p.proname, arguments;
Übertragen Sie jedes abweichende Objekt mit einem Superuser oder einer Rolle des Anbieters, die die Eigentümerschaft ändern kann. Verwenden Sie das tatsächliche Schema, Objekt, die Rolle und die Funktionssignatur aus der Bestandsaufnahme, statt die Beispielnamen unverändert zu kopieren:
ALTER TABLE emdash.content_taxonomies OWNER TO emdash_app;
ALTER TABLE emdash.ec_posts OWNER TO emdash_app;
ALTER FUNCTION emdash.emdash_media_usage_capture_work() OWNER TO emdash_app;
Das Ändern des Eigentümers einer Tabelle erfasst auch ihre zugehörigen Indizes, Constraints und Trigger, nicht aber eigenständige Trigger-Funktionen. Wiederholen Sie beide Abfragen der Bestandsaufnahme, bis jede EmDash-Tabelle und -Funktion den kanonischen Eigentümer meldet. Verbinden Sie sich dann als diese Rolle und prüfen Sie current_database(), current_schema() und den Migrationsstatus, bevor Sie den Verkehr wieder starten.
Damit ein Nicht-Superuser die Eigentümerschaft übertragen kann, muss er das Objekt besitzen oder die Eigentümerschaft erben, per SET ROLE zum neuen Eigentümer wechseln können, und der neue Eigentümer muss CREATE auf dem Schema haben. Verwaltete PostgreSQL-Anbieter verlangen möglicherweise, dass ihre administrative Rolle die Übertragung vornimmt.
Fehlerbehebung
- Kein Migrationsmanifest gefunden. Bauen oder synchronisieren Sie zuerst das Projekt. Verwenden Sie
--manifestfür einen nicht standardmäßigen Speicherort des Artefakts oder wählen Sie für lokale Untersuchungen ausdrücklich--from-config. - Das Artefakt passt nicht zum EmDash des Projekts. Bauen und deployen Sie Anwendung und Manifest gemeinsam neu. Führen Sie die CLI des Projekts aus statt einer globalen Installation.
- Das Ziel fehlt oder ist mehrdeutig. Stellen Sie es zuerst bereit und geben Sie dann einen expliziten Datenbankpfad, den Namen der Verbindungsvariable, einen D1-Selektor oder die ausgewählte Wrangler-Konfiguration samt Umgebung an. EmDash rät nicht anhand unzusammenhängender Umgebungsvariablen oder Bindings.
- Der Ziel-Fingerprint hat sich geändert. Halten Sie an und prüfen Sie das angezeigte Konto, die Umgebung, den Datenbanknamen, die UUID oder den Pfad. Aktualisieren Sie den erwarteten Fingerprint erst, nachdem Sie das beabsichtigte Ziel bestätigt haben.
- Unbekannte Migrationseinträge sind vorhanden. Löschen Sie die Einträge nicht und wiederholen Sie das Anwenden nicht. Vergewissern Sie sich, dass das Anwendungsartefakt die beabsichtigte Version ist, und untersuchen Sie, ob ein neuerer oder abweichender Build die Datenbank migriert hat.
- Das Ergebnis eines D1-Schreibvorgangs ist mehrdeutig. Wiederholen Sie den Migrationsbefehl nicht. Führen Sie
emdash migrate --statusfür dasselbe Konto und dieselbe Datenbank-UUID aus, prüfen Sie das Ergebnis und eskalieren Sie, wenn die Migration mittendrin gestoppt ist. - Die Normalisierung von Datum und Uhrzeit erfordert eine manuelle Prüfung. Ein älterer Datum-Uhrzeit-Wert fällt in eine wiederholte oder übersprungene Stunde der Sommerzeit in der konfigurierten Zeitzone der Site. Der Fehler listet jede betroffene Inhaltszeile oder Revision auf. Korrigieren Sie diese Werte mit einem expliziten UTC-Offset und wiederholen Sie dann die Migration. Der Preflight der Migration schreibt keinen Datum-Uhrzeit-Wert, bevor sich nicht jeder gespeicherte Wert auflösen lässt.
- Hyperdrive kann keine Verbindung herstellen. Testen Sie die Erreichbarkeit des PostgreSQL-Ursprungs vom Deployment-Runner aus und prüfen Sie die Direct-Origin-Variable. Die Konnektivität zwischen Worker und Hyperdrive beweist nicht, dass der Runner den Ursprung erreichen kann.