EmDash-Core-Migrationen aktualisieren EmDashs eigene Tabellen und die Standardspalten auf Content-Tabellen. Sie erstellen, entfernen oder benennen Ihre Collections und Felder nicht um; siehe Eine deployte Site weiterentwickeln für Content-Modell-Änderungen.
Der Runtime-Migrationsmodus ist standardmäßig auto, sodass bestehende Deployments ausstehende Core-Migrationen beim Start weiterhin anwenden. Deployment-verwaltete Migrationen lassen einen Build seine Datenbank migrieren, bevor neuer Anwendungscode Traffic erhält, und lassen die Runtime diesen Deployment-Schritt dann verifizieren oder vertrauen.
Core-Migrationen sind nur vorwärts gerichtet. Sie sind so geschrieben, dass ein Befehl nach Statements, die definitiv abgeschlossen sind, erneut versucht werden kann, aber ein unterbrochener Remote-Befehl kann ein mehrdeutiges Ergebnis hinterlassen. Die sichere Reaktion ist, dieselbe Datenbank mit emdash migrate --status zu prüfen, nicht anzunehmen, dass entweder die gesamte Migration oder keine davon gelaufen ist.
Bauen, migrieren, deployen, prüfen
Ein Astro-Build oder Sync schreibt .emdash/migrations.json. Dieses geheimnisfreie Manifest zeichnet die genaue EmDash-Version, den geordneten Migrationssatz, die Locale-Konfiguration und den Adapter-Migrations-Executor auf, den dieser Build verwendet.
Führen Sie diese Befehle aus dem Projekt aus, dessen Abhängigkeiten das Manifest erzeugt haben. Bauen und prüfen Sie zuerst 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 erneut an der Eingabeaufforderung, bevor Sie bestätigen. Deployen Sie dann denselben Build und prüfen Sie das deployte Schema.
pnpm emdash migrate
pnpm wrangler deploy
pnpm emdash migrate --check
emdash migrate --status meldet angewendete, ausstehende und unbekannte Migrationen, ohne die Datenbank zu ändern. Der einfache Befehl emdash migrate zeigt das Ziel an und fordert vor dem Anwenden ausstehender Migrationen eine Bestätigung an.
--check wendet nie Migrationen an und beendet mit Nicht-Null, wenn bekannte Migrationen ausstehen oder die Datenbank Migrationsdatensätze enthält, die dem Build unbekannt sind. Verwenden Sie --status, wenn Sie dieselben Migrationssätze prüfen möchten, ohne den Nicht-Null-Exit-Status „Arbeit erforderlich“ von check. Die CLI-Referenz unterscheidet ausstehende, unbekannte, Bestätigungs-, Unterbrechungs- und operative Exit-Codes.
Nicht-interaktives Anwenden und jedes --json-Anwenden 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 für den interaktiven Workflow oben.
Verwenden Sie --manifest path/to/migrations.json für ein Manifest, das anderswo gespeichert ist. Für lokale Untersuchung wertet --from-config [--config astro.config.mjs] explizit vertrauenswürdige Projektkonfiguration aus, ohne Astro-Hooks auszuführen oder einen Server zu starten. Deployment-Pipelines sollten das Build-Manifest konsumieren.
Die Datenbank explizit auswählen
Der konfigurierte Adapter trägt geheimnisfreie Zielinformationen zum Manifest bei. Anmeldedaten bleiben in Umgebungsvariablen und werden nur vom Migrationsbefehl gelesen.
| Adapter | Manifest target | Default credential variable | Useful override |
|---|---|---|---|
| SQLite | Database path or file: URL | — | --database <path> |
| libSQL | Public URL | TURSO_AUTH_TOKEN | Configure migrationAuthTokenEnv |
| PostgreSQL | Connection variable name | DATABASE_URL | --database-url-env <name> |
| Cloudflare D1 | Wrangler binding name | CLOUDFLARE_API_TOKEN | --d1, --account-id, --wrangler-config, --wrangler-env |
| Hyperdrive | Primary binding and origin variable name | Binding-specific direct-origin variable | Configure migrationConnectionStringEnv |
Relative SQLite-Pfade werden vom Projektstamm aufgelöst, nicht vom installierten EmDash-Paket oder dem aktuellen Unterverzeichnis der Shell. PostgreSQL-, libSQL- und Hyperdrive-Zielbeschriftungen lassen Anmeldedaten und URL-Parameter weg.
D1 vor der Migration bereitstellen
Das Erstellen einer D1-Datenbank und das Migrieren ihres Schemas sind getrennte Operationen. 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 -
Fügen Sie diese UUID dem beabsichtigten Binding und der Umgebung in
wrangler.jsonchinzu. -
Bauen Sie die Site, damit das D1-Binding in
.emdash/migrations.jsonaufgezeichnet wird. -
Setzen Sie die Account-ID und ein eingeschränktes API-Token mit D1-Edit-Berechtigung. Prüfen Sie das ausgewählte Ziel und führen Sie dann die interaktive Migration aus. Bestätigen Sie die Eingabeaufforderung nur, wenn Account und Datenbank der beabsichtigten Produktionsdatenbank entsprechen.
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 mit --d1 <database-uuid-or-name> angeben. Die Namensauflösung muss genau eine Datenbank ergeben. Preview-IDs, Platzhalter-IDs, widersprüchliche Accounts und mehrdeutige Bindings scheitern geschlossen.
D1-Migrationen in CI konfigurieren
EmDash hält eine Migrationssperre in der D1-Datenbank, während es Migrationen anwendet — sowohl von emdash migrate als auch von Runtime-Migrationen im Modus auto. Ein zweiter Lauf, der startet, während die Sperre gehalten wird, wartet bis zu 10 Sekunden. Wenn der erste Lauf in dieser Zeit endet, gelingt der zweite Lauf, ohne etwas anzuwenden; andernfalls scheitert er, ohne Migrationen anzuwenden. Führen Sie jeweils einen Migrationsjob für einen Account und eine Datenbank-UUID aus, damit ein zweiter Job in der CI-Warteschlange wartet, statt zu scheitern.
Setzen Sie das folgende Secret und die folgenden Variablen in der CI-Umgebung:
- Secret
CLOUDFLARE_API_TOKEN: ein eingeschränktes Token mit D1-Edit-Berechtigung. - Variable
CLOUDFLARE_ACCOUNT_ID: die Cloudflare-Account-ID, der die Datenbank gehört. - Variable
D1_DATABASE_ID: die Produktions-D1-Datenbank-UUID. - Variable
EMDASH_TARGET_FINGERPRINT: der Fingerabdruck, denemdash migrate --statusausgibt, nachdem Sie Account und Datenbank lokal geprüft haben.
Der folgende GitHub-Actions-Workflow verwendet diese Werte und gruppiert die Concurrency nach beiden unveränderlichen D1-Identifikatoren. Sein Apply-Schritt ist nicht interaktiv und liefert daher den geprüften Zielfingerabdruck explizit.
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 nur, nachdem Sie ein geändertes Ziel lokal geprüft haben. Der Fingerabdruck enthält keine Anmeldedaten, aber ihn ohne Prüfung von Account und Datenbank zu ändern entfernt die Absicherung gegen die Migration der falschen Datenbank.
Eine hängende Migrationssperre freigeben
Ein D1-Migrationslauf, der stoppt, bevor er die Migrationssperre freigibt, lässt die Sperre gehalten. Das geschieht, wenn ein CI-Job während emdash migrate abgebrochen wird, wenn ein Worker im Modus auto während einer Runtime-Migration stoppt oder wenn ein Entwicklungsserver gestoppt wird, während er Migrationen anwendet. Ein Lauf, der mit einem Migrationsfehler scheitert, 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 gestoppt hat. Bis die Sperre freigegeben ist, kann die Site ihre ausstehenden Migrationen nicht anwenden, und im Modus auto initialisiert EmDash nicht.
Nachdem die Sperre länger als eine Minute gehalten wurde, warten emdash migrate und Runtime-Migrationen 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 Remote-D1-Datenbank verwendet emdash migrate, benötigt also einen Build, der .emdash/migrations.json geschrieben hat, und ein API-Token mit D1-Edit-Berechtigung, wie in D1 vor der Migration bereitstellen beschrieben.
-
Bestätigen Sie, dass kein Migrationsjob, Deployment oder anderer
emdash migrate-Befehl gegen die Datenbank läuft. -
Prüfen Sie die Sperre und die Migrationssätze mit denselben Zieloptionen, die die Migration verwendet hat.
pnpm emdash migrate --statusDer Bericht beginnt mit der Sperre und ihrer ID. Wenn der gestoppte Lauf eine Migration anwendete, war das die erste ausstehende Migration, und sie kann teilweise angewendet sein.
Migration lock: held since 2026-09-01T12:00:00.000Z (id 1788264000000)Ein Worker im Modus
autokann die Sperre auch halten, während er Migrationen anwendet. Führen Sie den Befehl eine Minute oder später erneut aus und warten Sie, statt die Sperre freizugeben, während sich die bekannten angewendeten Migrationen weiter ändern. -
Geben Sie die Sperre mit dieser ID frei. Der Befehl fordert Sie auf, das Ziel zu bestätigen, und gibt die Sperre nur frei, solange die Sperre noch diese ID hat. In einer nicht-interaktiven Shell fügen Sie
--expected-target-fingerprintmit dem Zielfingerabdruck hinzu, den--statusausgegeben hat.pnpm emdash migrate --release-lock 1788264000000 -
Wenden Sie die ausstehenden Migrationen erneut an.
pnpm emdash migrateWenn das Anwenden in der ersten ausstehenden Migration scheitert, behandeln Sie diese Migration als teilweise durchgelaufen und folgen Sie dem Eintrag für einen mehrdeutigen D1-Schreibvorgang in Fehlerbehebung.
emdash migrate erreicht nur Remote-D1-Datenbanken. Wenn die Sperre in der lokalen D1-Datenbank eines Entwicklungsservers gehalten wird, stoppen Sie den Server und löschen Sie die Sperre mit Wrangler; ersetzen Sie 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. Er sendet keinen Migrationsverkehr durch Hyperdrive, verwendet nicht das optionale gecachte Binding und erbt keine private Netzwerkreichweite vom Worker.
Der Deployment-Runner muss den Ursprung erreichen können. Setzen Sie migrationConnectionStringEnv auf hyperdrive(), wenn die standardmäßige binding-spezifische Variable ungeeignet ist, und stellen Sie diese Variable nur dem Migrationsjob bereit. Halten Sie Runtime-Hyperdrive-Anmeldedaten und direkte Ursprungs-Deployment-Anmeldedaten getrennt.
Laufzeitdurchsetzung schrittweise einführen
Die folgende EmDash-Integrationskonfiguration aktiviert die Runtime-Durchsetzung und behält automatische Migrationen in der Entwicklung bei.
emdash({
database,
migrations: {
runtime: "check",
dev: "auto",
},
});
autoist die abwärtskompatible Standardeinstellung. Der Runtime-Start prüft und wendet ausstehende Migrationen an.checkführt eine gerichtete Statusabfrage aus und gibt 503 zurück, bevor eine Anfrage bedient wird, wenn bekannte Migrationen ausstehen. Es toleriert Datensätze eines neueren kompatiblen Builds während eines Rolling Deployments.manualführt keine Runtime-Migration oder Statusabfrage durch. Verwenden Sie es nur, nachdem die Deployment-Pipeline jeden Build zuverlässig anwendet und prüft.
EMDASH_MIGRATIONS_MODE kann den Runtime-Modus überschreiben, wenn dasselbe Artefakt durch mehrere Umgebungen befördert wird. Setup- und Entwicklungs-Bypass-Routen gehorchen dem effektiven Modus; sie können hinter check oder manual nicht still migrieren.
Ein konservativer Rollout ist auto, während der Deployment-Job eingeführt wird, dann check, nachdem der Job zuverlässig ist, dann manual, wenn eine externe Prüfung für jedes Deployment erzwungen wird.
Kompatibilität während Rolling-Deploys
Core-Migrationen folgen der Expand/Deploy/Contract-Sequenz. Ein Deployment kann vorübergehend alte und neue Anwendungs-Isolate gegen die erweiterte Datenbank ausführen, und ein Backfill kann noch laufen. Kontrahieren Sie ein Schema nicht, bis jede deployte Version es nicht mehr verwendet.
Unbekannte angewendete Migrationsdatensätze werden vom Runtime-check nur für diese Rolling-Deployment-Richtung toleriert. Die exakte CLI-Prüfung meldet sie, und Apply weigert sich zu mutieren, weil die Datenbank neuer sein oder eine abweichende Migrationshistorie haben kann.
Rollback-Grenze
Das Deployen des vorherigen Anwendungsartefakts kehrt eine Core-Migration nicht um. Bevor Sie ausstehende Migrationen anwenden, erstellen Sie ein wiederherstellbares Datenbank-Backup und notieren Sie das Anwendungsartefakt, das dazu passt. Wenn die vorherige Anwendung nicht gegen das migrierte Schema laufen kann, stellen Sie die Pre-Migrations-Datenbank und die Anwendung zusammen wieder her. Löschen Sie keine Zeilen aus _emdash_migrations und führen Sie die interne down()-Funktion einer Migration nicht als operativen Rollback aus.
Gemischte PostgreSQL-Eigentümerschaft reparieren
Verwenden Sie dieses Runbook, wenn eine bestehende PostgreSQL-Site EmDash-Objekte mit mehr als einem Owner erstellt hat und spätere Migrationen mit Fehlern wie must be owner of table scheitern. Wählen Sie die kanonische Rolle, die die primäre EmDash-Verbindung weiter verwenden wird. Erstellen Sie ein wiederherstellbares Datenbank-Backup und stoppen Sie Anwendungs-Traffic 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;
EmDash-Objekte umfassen _emdash_*- und _plugin_*-Systemtabellen, ec_*-Collection-Tabellen und unpräfixierte Tabellen wie content_taxonomies, media, options, revisions und taxonomies. In einem dedizierten EmDash-Schema sollte jede Anwendungstabelle den kanonischen Owner haben.
EmDash erstellt auch PostgreSQL-Funktionen, die von Media-Usage-Triggern verwendet werden. Prüfen Sie die Funktionseigentümerschaft und behalten Sie die Argument-Signatur 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 nicht übereinstimmende Objekt mit einem Superuser oder einer Provider-Rolle, die die Eigentümerschaft ändern kann. Verwenden Sie das echte Schema, Objekt, Rolle und Funktionssignatur aus dem Inventar, 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 Owners einer Tabelle deckt auch ihre angehängten Indizes, Constraints und Trigger ab, aber nicht unabhängige Triggerfunktionen. Wiederholen Sie beide Inventarabfragen, bis jede EmDash-Tabelle und -Funktion den kanonischen Owner meldet. Verbinden Sie sich dann als diese Rolle und prüfen Sie current_database(), current_schema() und den Migrationsstatus, bevor Sie den Traffic neu starten.
Damit ein Nicht-Superuser Eigentümerschaft übertragen kann, muss er das Objekt besitzen oder die Eigentümerschaft erben, SET ROLE auf den neuen Owner ausführen können, und der neue Owner muss CREATE im Schema haben. Verwaltete PostgreSQL-Anbieter können ihre administrative Rolle für die Übertragung verlangen.
Fehlerbehebung
- No migration manifest found. Build or sync the project first. Use
--manifestfor a non-standard artifact location or explicitly choose--from-configfor local investigation. - The artifact does not match project EmDash. Rebuild and deploy the application and manifest together. Run the project’s CLI instead of a global installation.
- The target is missing or ambiguous. Provision it first, then supply an explicit database path, connection-variable name, D1 selector, or selected Wrangler config and environment. EmDash does not guess from unrelated environment variables or bindings.
- The target fingerprint changed. Stop and review the displayed account, environment, database name, UUID, or path. Update the expected fingerprint only after confirming the intended target.
- Unknown migration records are present. Do not delete the records or rerun apply. Confirm that the application artifact is the intended version and investigate whether a newer or divergent build migrated the database.
- A D1 write outcome is ambiguous. Do not replay the migration command. Run
emdash migrate --statusagainst the same account and database UUID, inspect the result, and escalate if the migration stopped part-way through. - Datetime normalization requires manual review. A legacy datetime falls in a repeated or skipped daylight-saving hour in the site’s configured timezone. The error lists each affected content row or revision. Correct those values with an explicit UTC offset, then retry the migration. The migration preflight does not write any datetime until every stored value can be resolved.
- Hyperdrive cannot connect. Test reachability from the deployment runner to the PostgreSQL origin and verify the direct-origin variable. Worker-to-Hyperdrive connectivity does not prove the runner can reach the origin.