跳至主要内容

Docusaurus 跨 Repo 部署到 GitHub Pages:從零到自動化的五個坑

· 閱讀時間約 3 分鐘
Ignikah Technology

用 Docusaurus 建官方網站很快,但把它接上 GitHub Pages 的自動部署,尤其是「source repo 與 Pages repo 分開」的架構,踩了不少坑。這篇把整個過程與解法記下來,下次不用再查。

架構目標

ignikah-dev/official-website (private, source)
↓ GitHub Actions build
ignikah-dev/ignikah-dev.github.io (public, GitHub Pages)

source repo 保持 private,只有靜態輸出推到 Pages repo。這樣可以把草稿、設計稿、內部文件留在 source,不對外曝光。

坑一:peaceiris/actions-gh-pages 需要 Personal Token,不能用 GITHUB_TOKEN

GITHUB_TOKEN 的 scope 只限於 當前 repo,無法推送到外部的 ignikah-dev.github.io

必須用 Fine-grained Personal Access Token,在 workflow 裡用 personal_token 參數:

- uses: peaceiris/actions-gh-pages@v4
with:
personal_token: ${{ secrets.GH_PAGES_DEPLOY_TOKEN }}
external_repository: ignikah-dev/ignikah-dev.github.io
publish_branch: main
publish_dir: ./build
force_orphan: true

坑二:Fine-grained PAT 的 Resource Owner 必須是 Org,不是個人帳號

第一次建 token 時,Resource Owner 選了個人帳號(balnibarbian),結果:

remote: Permission to ignikah-dev/ignikah-dev.github.io.git denied to balnibarbian.
fatal: unable to access '...': The requested URL returned error: 403

即使把 balnibarbian 加為 collaborator 也沒用。根本原因是 fine-grained PAT 的權限是「token 持有者在指定 owner 下的資源」——選個人帳號只能操作個人 repo,組織 repo 需要選 org 作為 Resource Owner

正確設定:

  • Resource owner → ignikah-dev(org)
  • Repository access → Only ignikah-dev/ignikah-dev.github.io
  • Permissions → Contents: Read and write

坑三:只開 pages 權限不夠,必須開 contents

第一版 token 設了 pages: read,結果還是 403。要能推 commit 到目標 repo,必須開:

Repository permissions
└─ Contents: Read and write ← 這個才是關鍵

pages 權限是控制 GitHub Pages 設定頁,跟推 code 無關。

坑四:Secret 名稱要跟 workflow 對得上

workflow 寫 secrets.PAGES_DEPLOY_TOKEN,但 repo 裡存的是 GH_PAGES_DEPLOY_TOKEN——結果一直出現:

Error: Action failed with "not found deploy key or tokens"

兩邊要一致,選一個名字就好:

personal_token: ${{ secrets.GH_PAGES_DEPLOY_TOKEN }}

坑五:navbar backdrop-filter 讓手機選單消失

這個跟部署無關,但是 CSS 坑,值得記一下。

Docusaurus 的手機側邊選單(.navbar-sidebar)使用 position: fixed。CSS 規範裡,backdrop-filterfiltertransform 加在祖先元素上,會讓該元素成為 fixed 子元素的新 containing block——導致側邊選單的定位從 viewport 變成 navbar 的邊界,整個跑掉。

錯誤做法:

.navbar {
backdrop-filter: saturate(180%) blur(12px); /* 破壞 fixed 子元素定位 */
}

正確做法——把 blur 移到 ::before 偽元素,不影響後代的定位:

.navbar {
position: sticky;
top: 0;
z-index: 300;
background: transparent !important;
}
.navbar::before {
content: '';
position: absolute;
inset: 0;
background: rgba(248, 250, 252, 0.88);
backdrop-filter: saturate(180%) blur(12px);
-webkit-backdrop-filter: saturate(180%) blur(12px);
z-index: -1;
pointer-events: none;
}

最終設定清單

項目設定位置
Deploy tokenofficial-website → SecretsGH_PAGES_DEPLOY_TOKEN
Token scopeFine-grained PATResource owner: org;Contents: R/W
Chatwoot tokenofficial-website → SecretsCHATWOOT_WEBSITE_TOKEN
Workflow.github/workflows/deploy.ymlpersonal_token: ${{ secrets.GH_PAGES_DEPLOY_TOKEN }}

設定完成後每次 push 到 main,CI 自動 build + 推到 Pages,約 90 秒後生效。