EmDash 核心遷移會更新 EmDash 自身的資料表,以及內容資料表上的標準欄位。它們不會建立、移除或重新命名你的集合與欄位;內容模型的變更請參閱演進已部署站台的結構描述。
執行階段遷移模式預設為 auto,因此既有的部署會繼續在啟動時套用待處理的核心遷移。由部署管理的遷移,可以讓建置在新的應用程式碼接收流量之前先遷移它的資料庫,然後讓執行階段驗證或信任這個部署步驟。
核心遷移只能向前執行。它們的撰寫方式保證:在確定已完成的陳述式之後,指令可以重試;但被中斷的遠端指令可能留下含糊不清的結果。安全的做法是用 emdash migrate --status 檢查同一個資料庫,而不是假定整個遷移或完全沒有遷移已經執行。
建置、遷移、部署、檢查
Astro 的建置或同步會寫出 .emdash/migrations.json。這份不含密鑰的資訊清單記錄了該次建置所使用的確切 EmDash 版本、有序的遷移集合、語系設定,以及介接器的遷移執行器。
請在產生該資訊清單的相依套件所在的專案中執行這些指令。先建置,並檢查目標。
pnpm build
pnpm emdash migrate --status
確認所回報的目標就是預期的資料庫後,開始互動式遷移。在確認之前,請在提示處再次檢查目標。然後部署同一份建置,並檢查已部署的結構描述。
pnpm emdash migrate
pnpm wrangler deploy
pnpm emdash migrate --check
emdash migrate --status 會回報已套用、待處理和未知的遷移,而不會變更資料庫。一般的 emdash migrate 指令會顯示目標,並在套用待處理遷移之前要求確認。
--check 從不套用遷移,並且當存在已知的待處理遷移,或資料庫中包含建置不認識的遷移紀錄時,會以非零狀態結束。如果你想在不觸發 check 那種表示「需要處理」的非零結束狀態的情況下檢查同樣的遷移集合,請使用 --status。CLI 參考區分了待處理、未知、確認、中斷和作業錯誤這幾種結束碼。
非互動式的套用,以及每一次帶 --json 的套用,都要求提供 --expected-target-fingerprint;如果解析出的目標與之不符,指令會失敗。請在自動化部署作業中使用這些選項,而不要用於上面的互動式工作流程。
如果資訊清單存放在其他位置,請使用 --manifest path/to/migrations.json。進行本機排查時,--from-config [--config astro.config.mjs] 會明確地對受信任的專案設定求值,而不會執行 Astro 勾點或啟動伺服器。部署管線應當使用建置資訊清單。
明確選取資料庫
所設定的介接器會向資訊清單提供不含密鑰的目標資訊。憑證保留在環境變數中,並且只由遷移指令讀取。
| 介接器 | 資訊清單中的目標 | 預設憑證變數 | 常用覆寫項 |
|---|---|---|---|
| SQLite | 資料庫路徑或 file: URL | — | --database <path> |
| libSQL | 公開 URL | TURSO_AUTH_TOKEN | 設定 migrationAuthTokenEnv |
| PostgreSQL | 連線變數名稱 | DATABASE_URL | --database-url-env <name> |
| Cloudflare D1 | Wrangler 繫結名稱 | CLOUDFLARE_API_TOKEN | --d1、--account-id、--wrangler-config、--wrangler-env |
| Hyperdrive | 主繫結和來源變數名稱 | 與繫結對應的直連來源變數 | 設定 migrationConnectionStringEnv |
相對的 SQLite 路徑從專案根目錄解析,而不是從已安裝的 EmDash 套件或 shell 目前所在的子目錄解析。PostgreSQL、libSQL 和 Hyperdrive 的目標標籤會省略憑證和 URL 參數。
遷移前佈建 D1
建立 D1 資料庫和遷移它的結構描述是兩個獨立的操作。emdash migrate 從不建立缺少的資料庫。
-
佈建資料庫,並記錄它的正式環境 UUID。
pnpm wrangler d1 create my-site-production -
把該 UUID 加入
wrangler.jsonc中預期的繫結和環境。 -
建置站台,使 D1 繫結被記錄到
.emdash/migrations.json中。 -
設定帳戶 ID,以及具有 D1 Edit 權限的限定範圍 API 權杖。檢查所選的目標,然後執行互動式遷移。只有當帳戶和資料庫與預期的正式環境資料庫一致時,才在提示處確認。
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
你也可以改為同時提供 --account-id 和 --d1 <database-uuid-or-name>。依名稱查詢必須恰好解析到一個資料庫。預覽 ID、預留位置 ID、互相衝突的帳戶和含糊不清的繫結都會以失敗告終(fail closed)。
在 CI 中設定 D1 遷移
EmDash 在套用遷移期間會在 D1 資料庫中持有一把遷移鎖,emdash migrate 和 auto 模式下的執行階段遷移都是如此。當鎖被持有時,如果又啟動了第二次執行,它最多會等待 10 秒。如果第一次執行在這段時間內完成,第二次執行會成功,但不套用任何內容;否則,它會失敗,並且不套用任何遷移。請針對同一個帳戶和資料庫 UUID,每次只執行一個遷移作業,這樣第二個作業會在 CI 佇列中等待,而不是失敗。
請在 CI 環境中設定以下密鑰和變數:
- 密鑰
CLOUDFLARE_API_TOKEN:具有 D1 Edit 權限的限定範圍權杖。 - 變數
CLOUDFLARE_ACCOUNT_ID:擁有該資料庫的 Cloudflare 帳戶 ID。 - 變數
D1_DATABASE_ID:正式環境 D1 資料庫的 UUID。 - 變數
EMDASH_TARGET_FINGERPRINT:你在本機審核過帳戶和資料庫之後,由emdash migrate --status列印的指紋。
下面這個 GitHub Actions 工作流程使用了這些值,並同時用這兩個不可變的 D1 識別碼為並行群組命名。它的套用步驟是非互動式的,因此會明確提供已審核過的目標指紋。
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 }}"
只有在本機審核過變更後的目標之後,才更新 EMDASH_TARGET_FINGERPRINT。指紋中不含任何憑證,但如果不檢查帳戶和資料庫就修改它,就會失去防止遷移錯資料庫的保護。
釋放卡住的遷移鎖
如果某次 D1 遷移執行在釋放遷移鎖之前就停止了,鎖就會一直被持有。在以下情況會發生這種事:CI 作業在 emdash migrate 期間被取消,auto 模式下的 Worker 在執行階段遷移期間停止,或者開發伺服器在套用遷移時被停止。因遷移錯誤而失敗的執行會釋放鎖。EmDash 不會釋放不是由它持有的鎖,因為持有者可能仍在套用遷移,也可能在遷移中途停止了。在鎖被釋放之前,站台無法套用它待處理的遷移,而在 auto 模式下,EmDash 會初始化失敗。
鎖被持有超過一分鐘之後,emdash migrate 和執行階段遷移會停止等待它,並回報下面的錯誤:
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
釋放遠端 D1 資料庫的鎖要用到 emdash migrate,因此需要一份寫出了 .emdash/migrations.json 的建置,以及具有 D1 Edit 權限的 API 權杖,如遷移前佈建 D1中所述。
-
確認沒有任何遷移作業、部署或其他
emdash migrate指令正在針對該資料庫執行。 -
使用與該遷移相同的目標選項,檢查鎖和遷移集合。
pnpm emdash migrate --status報告以鎖及其 id 開頭。如果停止的那次執行正在套用某個遷移,那麼它就是第一個待處理的遷移,並且可能只套用了一部分。
Migration lock: held since 2026-09-01T12:00:00.000Z (id 1788264000000)auto模式下的 Worker 在套用遷移時也可能持有該鎖。請在一分鐘或更長時間之後再次執行該指令,並且當已知的已套用遷移仍在變化時,應當等待,而不要釋放鎖。 -
使用該 id 釋放鎖。指令會要求你確認目標,並且只有當鎖仍然是該 id 時才會釋放它。在非互動式 shell 中,請加入
--expected-target-fingerprint,並使用--status列印的目標指紋。pnpm emdash migrate --release-lock 1788264000000 -
再次套用待處理的遷移。
pnpm emdash migrate如果套用在第一個待處理的遷移中失敗,請把該遷移視為執行到一半,並按照疑難排解中關於含糊不清的 D1 寫入的條目處理。
emdash migrate 只能存取遠端 D1 資料庫。當鎖被持有在開發伺服器的本機 D1 資料庫中時,請停止伺服器,並用 Wrangler 清除該鎖,把 DB 替換為繫結名稱,把其中的數字替換為錯誤訊息中的鎖 id。
pnpm wrangler d1 execute DB --local --command "UPDATE _emdash_migrations_lock SET is_locked = 0 WHERE is_locked = 1788264000000"
Hyperdrive 連線到來源
Hyperdrive 的遷移執行器會直接開啟一條到來源的 PostgreSQL 連線。它不會讓遷移流量經過 Hyperdrive,不會使用選用的快取繫結,也不會從 Worker 繼承私有網路的可達性。
部署執行器必須能夠存取該來源。當預設的、與繫結對應的變數不合適時,請在 hyperdrive() 上設定 migrationConnectionStringEnv,並且只把該變數提供給遷移作業。請把執行階段的 Hyperdrive 憑證和直連來源的部署憑證分開保管。
逐步採用執行階段強制
下面的 EmDash 整合設定啟用了執行階段強制,同時在開發環境中保留自動遷移。
emdash({
database,
migrations: {
runtime: "check",
dev: "auto",
},
});
auto是向後相容的預設值。執行階段啟動時會檢查並套用待處理的遷移。check執行一次有方向的狀態查詢,並在存在已知的待處理遷移時,於處理請求之前傳回 503。在滾動部署期間,它能容忍來自更新的相容建置的紀錄。對 EmDash 所安裝的圖片端點的請求仍然會被處理,因為它們會略過執行這項檢查的執行階段啟動程序。manual不執行任何執行階段遷移或狀態查詢。只有在部署管線能夠可靠地為每個建置執行套用和檢查之後,才使用它。
當同一個產出物被升級到多個環境時,可以用 EMDASH_MIGRATIONS_MODE 覆寫執行階段模式。設定和開發的繞過路由遵循實際生效的模式;它們不能在 check 或 manual 之下悄悄地執行遷移。
一種穩妥的推行方式是:在引入部署作業時先使用 auto,在作業可靠之後改用 check,在每次部署都強制執行外部檢查時再改用 manual。
滾動部署期間的相容性
核心遷移遵循擴展/部署/收縮(expand/deploy/contract)的順序。部署期間,舊的和新的應用程式 isolate 可能會暫時同時針對已擴展的資料庫執行,並且回填可能仍在進行。在每個已部署的版本都停止使用某個結構描述之前,請不要收縮它。
對於未知的已套用遷移紀錄,執行階段的 check 只在這種滾動部署的方向上容忍它們。CLI 的精確檢查會回報它們,而套用會拒絕修改,因為資料庫可能更新,或者可能有分岔的遷移歷程。
回滾邊界
部署先前的應用程式產出物並不會復原核心遷移。在套用待處理遷移之前,請先做一份可還原的資料庫備份,並記錄與之相符的應用程式產出物。如果先前的應用程式無法針對已遷移的結構描述執行,請把遷移前的資料庫和應用程式一起還原。不要從 _emdash_migrations 中刪除資料列,也不要把遷移內部的 down() 函式當作維運層面的回滾來執行。
修復混合的 PostgreSQL 擁有權
當既有的 PostgreSQL 站台建立的 EmDash 物件有多個擁有者,並且之後的遷移因 must be owner of table 之類的錯誤而失敗時,請使用本手冊。請選擇 EmDash 主連線今後繼續使用的標準角色。在變更擁有權之前,請先做一份可還原的資料庫備份,並停止應用程式流量和結構描述變更。
檢查目前結構描述中的每個資料表:
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 的物件包括 _emdash_* 和 _plugin_* 系統資料表、ec_* 集合資料表,以及 content_taxonomies、media、options、revisions 和 taxonomies 之類不帶前綴的資料表。在專用的 EmDash 結構描述中,每個應用程式資料表都應當由標準角色擁有。
EmDash 還會建立供媒體使用情況觸發器使用的 PostgreSQL 函式。請檢查函式的擁有權,並保留每個函式的引數簽章,供修復指令使用:
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;
使用超級使用者,或使用能夠變更擁有權的服務供應商角色,轉移每個不一致的物件。請使用清單中真實的結構描述、物件、角色和函式簽章,而不要原樣照搬範例中的名稱:
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;
變更資料表的擁有者同時也會涵蓋它所附帶的索引、限制式和觸發器,但不包括獨立的觸發器函式。請重複執行這兩個清單查詢,直到每個 EmDash 資料表和函式回報的擁有者都是標準角色。然後以該角色連線,並在重新放入流量之前驗證 current_database()、current_schema() 和遷移狀態。
對於非超級使用者,要轉移擁有權,它必須擁有或繼承該物件的擁有權,能夠 SET ROLE 到新的擁有者,並且新的擁有者必須在該結構描述上擁有 CREATE 權限。受管理的 PostgreSQL 服務供應商可能要求使用它們的管理員角色來執行轉移。
疑難排解
- 找不到遷移資訊清單。 請先建置或同步專案。對於非標準的產出物位置,請使用
--manifest,或者在本機排查時明確選擇--from-config。 - 產出物與專案的 EmDash 不相符。 請把應用程式和資訊清單一起重新建置並部署。請執行專案自帶的 CLI,而不是全域安裝的版本。
- 目標缺少或含糊不清。 請先佈建它,然後提供明確的資料庫路徑、連線變數名稱、D1 選擇器,或所選的 Wrangler 設定和環境。EmDash 不會根據無關的環境變數或繫結去猜測。
- 目標指紋發生了變化。 請停下來,審核所顯示的帳戶、環境、資料庫名稱、UUID 或路徑。只有在確認了預期的目標之後,才更新期望的指紋。
- 存在未知的遷移紀錄。 不要刪除這些紀錄,也不要重新執行套用。請確認應用程式產出物是預期的版本,並調查是否有更新的或分岔的建置遷移過該資料庫。
- D1 寫入的結果含糊不清。 不要重放遷移指令。請針對同一個帳戶和資料庫 UUID 執行
emdash migrate --status,檢查結果,並在遷移中途停止時升級處理。 - 日期時間正規化需要人工審核。 某個舊版日期時間落在了站台所設定時區中重複或被跳過的夏令時間小時內。錯誤訊息會列出每個受影響的內容資料列或修訂版本。請用明確的 UTC 位移量更正這些值,然後重試遷移。在每個已儲存的值都能被解析之前,遷移的預檢不會寫入任何日期時間。
- Hyperdrive 無法連線。 請測試從部署執行器到 PostgreSQL 來源的可達性,並驗證直連來源變數。Worker 到 Hyperdrive 的連通性並不能證明執行器能夠存取該來源。