Les migrations du noyau EmDash mettent à jour les tables propres à EmDash et les colonnes standard des tables de contenu. Elles ne créent, ne suppriment ni ne renomment vos collections et champs ; voir Faire évoluer un site déployé pour les changements du modèle de contenu.
Le mode de migration d’exécution est auto par défaut, de sorte que les déploiements existants continuent d’appliquer les migrations du noyau en attente au démarrage. Les migrations gérées par le déploiement permettent à une compilation de migrer sa base de données avant que le nouveau code d’application ne reçoive du trafic, puis à l’exécution de vérifier ou de faire confiance à cette étape de déploiement.
Les migrations du noyau sont uniquement vers l’avant. Elles sont écrites pour qu’une commande puisse être réessayée après des instructions qui ont définitivement abouti, mais une commande distante interrompue peut laisser un résultat ambigu. La réponse sûre est d’inspecter la même base de données avec emdash migrate --status, non de supposer que toute la migration ou aucune n’a été exécutée.
Compiler, migrer, déployer, vérifier
Une compilation ou synchronisation Astro écrit .emdash/migrations.json. Ce manifeste sans secret enregistre la version exacte d’EmDash, l’ensemble de migrations ordonné, la configuration de locale et l’exécuteur de migrations de l’adaptateur utilisé par cette compilation.
Exécutez ces commandes depuis le projet dont les dépendances ont produit le manifeste. Compilez et inspectez d’abord la cible.
pnpm build
pnpm emdash migrate --status
Après avoir confirmé que la cible signalée est la base de données prévue, démarrez la migration interactive. Relisez la cible à l’invite avant de confirmer. Déployez ensuite la même compilation et vérifiez le schéma déployé.
pnpm emdash migrate
pnpm wrangler deploy
pnpm emdash migrate --check
emdash migrate --status signale les migrations appliquées, en attente et inconnues sans modifier la base de données. La commande simple emdash migrate affiche la cible et demande confirmation avant d’appliquer les migrations en attente.
--check n’applique jamais de migrations et se termine avec un code non nul lorsque des migrations connues sont en attente ou que la base de données contient des enregistrements de migration inconnus de la compilation. Utilisez --status pour inspecter les mêmes ensembles de migrations sans le statut de sortie non nul « travail requis » de check. La référence CLI distingue les codes de sortie en attente, inconnus, de confirmation, d’interruption et opérationnels.
L’application non interactive et chaque application --json exigent --expected-target-fingerprint ; la commande échoue si la cible résolue ne correspond pas. Utilisez ces options dans les tâches de déploiement automatisées, pas pour le flux interactif ci-dessus.
Utilisez --manifest path/to/migrations.json pour un manifeste stocké ailleurs. Pour une investigation locale, --from-config [--config astro.config.mjs] évalue explicitement la configuration de projet de confiance sans exécuter les hooks Astro ni démarrer un serveur. Les pipelines de déploiement doivent consommer le manifeste de compilation.
Sélectionner la base de données explicitement
L’adaptateur configuré contribue des informations de cible sans secret au manifeste. Les identifiants restent dans les variables d’environnement et ne sont lus que par la commande de migration.
| 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 |
Les chemins SQLite relatifs se résolvent depuis la racine du projet, non depuis le paquet EmDash installé ni le sous-répertoire actuel du shell. Les étiquettes de cible PostgreSQL, libSQL et Hyperdrive omettent les identifiants et les paramètres d’URL.
Provisionner D1 avant de le migrer
Créer une base de données D1 et migrer son schéma sont des opérations distinctes. emdash migrate ne crée jamais une base de données manquante.
-
Provisionnez la base de données et enregistrez son UUID de production.
pnpm wrangler d1 create my-site-production -
Ajoutez cet UUID à la liaison et à l’environnement prévus dans
wrangler.jsonc. -
Compilez le site pour que la liaison D1 soit enregistrée dans
.emdash/migrations.json. -
Définissez l’ID de compte et un jeton d’API à portée limitée avec l’autorisation D1 Edit. Inspectez la cible sélectionnée, puis exécutez la migration interactive. Confirmez l’invite uniquement lorsque le compte et la base de données correspondent à la base de données de production prévue.
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
Vous pouvez à la place fournir --account-id avec --d1 <database-uuid-or-name>. La recherche par nom doit résoudre exactement une base de données. Les ID de prévisualisation, ID de placeholder, comptes en conflit et liaisons ambiguës échouent de façon fermée.
Configurer les migrations D1 en CI
EmDash détient un verrou de migration dans la base de données D1 pendant qu’il applique les migrations, depuis emdash migrate et depuis les migrations d’exécution en mode auto également. Une deuxième exécution qui démarre pendant que le verrou est détenu attend jusqu’à 10 secondes. Si la première exécution se termine dans ce délai, la deuxième réussit sans rien appliquer ; sinon, elle échoue sans appliquer de migrations. Exécutez une tâche de migration à la fois pour un compte et un UUID de base de données afin qu’une deuxième tâche attende dans la file CI au lieu d’échouer.
Définissez le secret et les variables suivants dans l’environnement CI :
- Secret
CLOUDFLARE_API_TOKEN: un jeton à portée limitée avec l’autorisation D1 Edit. - Variable
CLOUDFLARE_ACCOUNT_ID: l’ID de compte Cloudflare qui possède la base de données. - Variable
D1_DATABASE_ID: l’UUID de la base de données D1 de production. - Variable
EMDASH_TARGET_FINGERPRINT: l’empreinte affichée paremdash migrate --statusaprès avoir examiné le compte et la base de données localement.
Le flux de travail GitHub Actions suivant utilise ces valeurs et regroupe la concurrence par les deux identifiants D1 immuables. Son étape d’application est non interactive, elle fournit donc explicitement l’empreinte de cible examinée.
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 }}"
Mettez à jour EMDASH_TARGET_FINGERPRINT uniquement après avoir examiné une cible modifiée localement. L’empreinte ne contient aucun identifiant, mais la changer sans vérifier le compte et la base de données retire la protection contre la migration de la mauvaise base de données.
Libérer un verrou de migration bloqué
Une exécution de migration D1 qui s’arrête avant de libérer le verrou de migration laisse le verrou détenu. Cela se produit lorsqu’une tâche CI est annulée pendant emdash migrate, lorsqu’un Worker en mode auto s’arrête pendant une migration d’exécution, ou lorsqu’un serveur de développement est arrêté pendant qu’il applique des migrations. Une exécution qui échoue avec une erreur de migration libère le verrou. EmDash ne libère pas un verrou qu’il ne détient pas, car le détenteur peut encore appliquer des migrations ou s’être arrêté au milieu d’une. Jusqu’à la libération du verrou, le site ne peut pas appliquer ses migrations en attente et, en mode auto, EmDash ne s’initialise pas.
Après que le verrou a été détenu plus d’une minute, emdash migrate et les migrations d’exécution cessent d’attendre et signalent l’erreur suivante :
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
Libérer le verrou d’une base de données D1 distante utilise emdash migrate, il faut donc une compilation qui a écrit .emdash/migrations.json et un jeton d’API avec l’autorisation D1 Edit, comme décrit dans Provisionner D1 avant de le migrer.
-
Confirmez qu’aucune tâche de migration, déploiement ou autre commande
emdash migratene s’exécute contre la base de données. -
Inspectez le verrou et les ensembles de migrations avec les mêmes options de cible que la migration a utilisées.
pnpm emdash migrate --statusLe rapport commence par le verrou et son id. Si l’exécution arrêtée appliquait une migration, c’était la première migration en attente, et elle peut être partiellement appliquée.
Migration lock: held since 2026-09-01T12:00:00.000Z (id 1788264000000)Un Worker en mode
autopeut aussi détenir le verrou pendant qu’il applique des migrations. Relancez la commande une minute ou plus tard, et attendez au lieu de libérer le verrou tant que les migrations appliquées connues continuent de changer. -
Libérez le verrou avec cet id. La commande vous demande de confirmer la cible et ne libère le verrou que tant qu’il a encore cet id. Dans un shell non interactif, ajoutez
--expected-target-fingerprintavec l’empreinte de cible que--statusa affichée.pnpm emdash migrate --release-lock 1788264000000 -
Appliquez à nouveau les migrations en attente.
pnpm emdash migrateSi l’application échoue dans la première migration en attente, traitez cette migration comme laissée à moitié et suivez l’entrée pour une écriture D1 ambiguë dans Dépannage.
emdash migrate n’atteint que les bases de données D1 distantes. Lorsque le verrou est détenu dans la base de données D1 locale d’un serveur de développement, arrêtez le serveur et effacez le verrou avec Wrangler, en remplaçant DB par le nom de la liaison et le nombre par l’id du verrou de l’erreur.
pnpm wrangler d1 execute DB --local --command "UPDATE _emdash_migrations_lock SET is_locked = 0 WHERE is_locked = 1788264000000"
Hyperdrive se connecte à l’origine
L’exécuteur de migrations d’Hyperdrive ouvre une connexion PostgreSQL directe à l’origine. Il n’envoie pas le trafic de migration via Hyperdrive, n’utilise pas la liaison en cache optionnelle et n’hérite pas de la portée réseau privée du Worker.
Le runner de déploiement doit pouvoir atteindre l’origine. Définissez migrationConnectionStringEnv sur hyperdrive() lorsque la variable par défaut spécifique à la liaison est inadaptée, et fournissez cette variable uniquement à la tâche de migration. Séparez les identifiants Hyperdrive d’exécution et les identifiants de déploiement d’origine directe.
Adopter l’application d’exécution progressivement
La configuration d’intégration EmDash suivante active l’application d’exécution tout en conservant les migrations automatiques en développement.
emdash({
database,
migrations: {
runtime: "check",
dev: "auto",
},
});
autoest la valeur par défaut rétrocompatible. Le démarrage d’exécution vérifie et applique les migrations en attente.checkeffectue une requête d’état directionnelle et renvoie 503 avant de servir une requête lorsque des migrations connues sont en attente. Il tolère les enregistrements d’une compilation compatible plus récente pendant un déploiement progressif.manualn’effectue aucune migration ni requête d’état d’exécution. Utilisez-le uniquement après que le pipeline de déploiement applique et vérifie chaque compilation de façon fiable.
EMDASH_MIGRATIONS_MODE peut remplacer le mode d’exécution lorsque le même artefact est promu dans plusieurs environnements. Les routes de configuration et de contournement de développement obéissent au mode effectif ; elles ne peuvent pas migrer silencieusement derrière check ou manual.
Un déploiement conservateur est auto pendant l’introduction de la tâche de déploiement, puis check lorsque la tâche est fiable, puis manual lorsqu’une vérification externe est imposée pour chaque déploiement.
Compatibilité pendant les déploiements progressifs
Les migrations du noyau suivent la séquence expand/deploy/contract. Un déploiement peut exécuter temporairement d’anciens et de nouveaux isolats d’application contre la base de données étendue, et un backfill peut encore être en cours. Ne contractez pas un schéma tant que chaque version déployée a cessé de l’utiliser.
Les enregistrements de migration appliqués inconnus sont tolérés par le check d’exécution uniquement pour cette direction de déploiement progressif. La vérification exacte de la CLI les signale et apply refuse de muter, car la base de données peut être plus récente ou avoir un historique de migration divergent.
Limite de restauration
Déployer l’artefact d’application précédent ne renverse pas une migration du noyau. Avant d’appliquer les migrations en attente, prenez une sauvegarde de base de données restaurable et enregistrez l’artefact d’application qui lui correspond. Si l’application précédente ne peut pas s’exécuter contre le schéma migré, restaurez ensemble la base de données pré-migration et l’application. Ne supprimez pas de lignes de _emdash_migrations et n’exécutez pas la fonction interne down() d’une migration comme restauration opérationnelle.
Réparer la propriété PostgreSQL mixte
Utilisez ce runbook lorsqu’un site PostgreSQL existant a créé des objets EmDash avec plus d’un propriétaire et que des migrations ultérieures échouent avec des erreurs telles que must be owner of table. Choisissez le rôle canonique que la connexion EmDash principale continuera d’utiliser. Prenez une sauvegarde de base de données restaurable et arrêtez le trafic d’application et les changements de schéma avant de modifier la propriété.
Inspectez chaque table dans le schéma actif :
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;
Les objets EmDash incluent les tables système _emdash_* et _plugin_*, les tables de collection ec_* et des tables sans préfixe telles que content_taxonomies, media, options, revisions et taxonomies. Dans un schéma EmDash dédié, chaque table d’application doit avoir le propriétaire canonique.
EmDash crée aussi des fonctions PostgreSQL utilisées par les déclencheurs d’usage média. Inspectez la propriété des fonctions et conservez la signature d’arguments de chaque fonction pour la commande de réparation :
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;
Transférez chaque objet non correspondant avec un superutilisateur ou un rôle fournisseur pouvant changer sa propriété. Utilisez le schéma, l’objet, le rôle et la signature de fonction réels de l’inventaire au lieu de copier les noms d’exemple inchangés :
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;
Changer le propriétaire d’une table couvre aussi ses index, contraintes et déclencheurs attachés, mais pas les fonctions de déclencheur indépendantes. Répétez les deux requêtes d’inventaire jusqu’à ce que chaque table et fonction EmDash signale le propriétaire canonique. Connectez-vous ensuite en tant que ce rôle et vérifiez current_database(), current_schema() et le statut de migration avant de redémarrer le trafic.
Pour qu’un non-superutilisateur transfère la propriété, il doit posséder ou hériter la propriété de l’objet, pouvoir SET ROLE vers le nouveau propriétaire, et le nouveau propriétaire doit avoir CREATE sur le schéma. Les fournisseurs PostgreSQL gérés peuvent exiger leur rôle administratif pour effectuer le transfert.
Dépannage
- 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.