GitHub Pages でソースリポジトリと公開用リポジトリを分ける
前提 #
Configuring a publishing source for your GitHub Pages site - GitHub DocsYou can configure your GitHub Pages site to publish when changes are pushed to a specific branch, or you can write a GitHub Actions workflow to publish your site.GitHub Docs現在利用できる GitHub Pages のデプロイ手法は 2 種類あります。
- Publish from branch
- 従来から利用できる手法で、特定ブランチに対するプッシュをトリガーとして、そのブランチのコンテンツを Pages にデプロイする手法です。
- Publish from GitHub Actions
- 最近利用できるようになった手法で、GitHub Actions ワークフローから Pages に直接デプロイする手法です。
- 技術的には、ワークフローで公開したいコンテンツをビルド、それを workflow artifact としてアップロードし、Pages API でこの artifact を指定してデプロイする、ということをしています。
GitHub は後者の GitHub Actions によるデプロイを推奨しています。こちらはデプロイのタイミングや手続きを GitHub Actions ワークフローの表現力で柔軟に構成でき、成果物 (HTML など) をリポジトリにプッシュする必要がないため、より汎用的かつ効率的で第一選択肢となるでしょう。
ソースリポジトリの分離 #
一般的な Pages のデプロイ戦略は、SSG ツールの構成とソースコード (JSX, Markdown など) を含む単一のリポジトリにおいて GitHub Actions ワークフローを構成する方法です。他方、いくつかのシナリオにおいては、このようなソースを含むリポジトリと、Pages の公開設定を行うリポジトリを分離したい場合もあります。例えば、単純にセキュリティレベルや責務を分離するためや、複数リポジトリに依存関係をもつビルドプロセスを独立させるため、などが考えられます。このようなケースでは、前述のデプロイ手法のいずれを用いるかによって、実装方針が異なります。
なお、以降では便宜上、Pages の公開設定を行わないリポジトリを「ソースリポジトリ」、公開設定を行うリポジトリを「公開用リポジトリ」と呼称します。
- Publish from branch
- このデプロイ手法においては、成果物を公開用リポジトリにプッシュする必要があります。
- ソースリポジトリでビルドを行ったのち、公開用リポジトリへその内容をプッシュするようなワークフローを実装することになるでしょう。
- 利点:
- 成果物(すなわち Web ページとして HTTP リクエスト可能なリソース)以外のすべてをソースリポジトリに分離できること
- 公開用リポジトリにワークフロー実装が不要であり、結果として、公開用リポジトリからソースリポジトリへのアクセスが不要であること
- Publish from GitHub Actions
- このデプロイ手法においては、公開用リポジトリで pages 用の workflow artifact を作成し、それをデプロイする必要があります。
- デプロイ手法固有の利点として、成果物のリポジトリへの push が不要である性質は受け継がれます。
- アプローチは 2 種類考えられます:
- a. 公開用リポジトリのワークフローでソースリポジトリを clone し、その内容を用いてビルドおよび pages のデプロイを行う
- この手法の欠点は、公開用リポジトリにおいてソースリポジトリコンテンツへのアクセスが必要であること、また、ビルドプロセスを公開用リポジトリに実装する必要があることです。
- b. ソースリポジトリのワークフローで成果物を workflow artifact をアップロードし、公開用リポジトリのデプロイワークフローを呼び出す。呼び出されたデプロイワークフローでは、ソースリポジトリの workflow artifact をダウンロードし、それを pages にデプロイする
- この手法の利点は、2-a. の手法の欠点を解消し、公開用リポジトリのワークフローで取り扱われる情報は公開される成果物のみとすることができることです。
- a. 公開用リポジトリのワークフローでソースリポジトリを clone し、その内容を用いてビルドおよび pages のデプロイを行う
ソースリポジトリを分離する動機に照らして、とりうる実装方針は実質的に 1. か 2-b. のいずれかになるでしょう。以降ではこの 2 種類の実装をそれぞれ例示します。
“Publish from branch” の場合でソースリポジトリを分離する #
- GitHub App
- Contents: write 権限を持つ GitHub App を作成します。
- [ソースリポジトリ] ghp-src
- 次のリポジトリのシークレットと変数を設定します。
secrets.APP_ID: 作成した GitHub App の App IDsecrets.APP_PRIVATE_KEY: 作成した GitHub App で発行した Private Keyvars.PAGES_REPO_OWNER: 公開用リポジトリの所有者vars.PAGES_REPO_NAME: 公開用リポジトリ名 (=ghp-pub)
- 後述する
push.ymlワークフローを配置します。
- 次のリポジトリのシークレットと変数を設定します。
- [公開用リポジトリ] ghp-pub
- Pages の設定で、main ブランチの
/docsディレクトリを公開するように設定します。 - 作成した GitHub App をインストールします。
- Pages の設定で、main ブランチの
.github/workflows/push.yml
name: Push
on:
push:
branches:
- main
jobs:
push:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
# ソースリポジトリを取得します。
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# 公開する Web ページをビルドします。
# ここでは例として HTML を作成しているだけですが、実際には SSG ツールの呼び出しなどを行うでしょう。
- name: Build
run: |
mkdir -p docs
touch docs/.nojekyll
echo '<h1>Hello GitHub Pages ${{ github.sha }}</h1>' > docs/index.html
# GitHub App Installation Access Token を取得します。
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: auth
with:
app-id: ${{ secrets.APP_ID }}
owner: ${{ vars.PAGES_REPO_OWNER }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
repositories: ${{ vars.PAGES_REPO_NAME }}
# 公開用リポジトリを取得します。
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
path: public
persist-credentials: false
repository: ${{ vars.PAGES_REPO_OWNER }}${{ '/' }}${{ vars.PAGES_REPO_NAME }}
token: ${{ steps.auth.outputs.token }}
# 公開用リポジトリにビルド成果物をプッシュします。
- name: Configure git user info
env:
GH_TOKEN: ${{ steps.auth.outputs.token }}
run: |
USER_NAME='${{ steps.auth.outputs.app-slug }}[bot]'
USER_ID="$(gh api "/users/$USER_NAME" --jq .id)"
git config user.name "$USER_NAME"
git config user.email "$USER_ID+$USER_NAME@users.noreply.github.com"
- name: Push
working-directory: public
run: |
rm -fr docs
cp -r ../docs .
git add .
git commit -m 'publish'
git remote set-url origin 'https://x-access-token:${{ steps.auth.outputs.token }}@github.com/${{ vars.PAGES_REPO_OWNER }}/${{ vars.PAGES_REPO_NAME }}.git'
git push
この実装例では、ghp-src の main ブランチにプッシュするたび push.yml ワークフローが実行され、ghp-pub の main ブランチに成果物がプッシュされます。Pages へのデプロイは ghp-pub の設定に基づいて暗黙的に行われ、ワークフローの実装はありません。
なお、GitHub App を利用した他リポジトリへのプッシュについては GitHub Actions でコミットに署名する で詳しく議論しているためあわせて参照してください。
“Publish from GitHub Actions” の場合でソースリポジトリを分離する #
- GitHub App
- Actions: write 権限を持つ GitHub App を作成します。
- [ソースリポジトリ] ghp-src
- 作成した GitHub App をインストールします。
- 次のリポジトリのシークレットと変数を設定します。
secrets.APP_ID: 作成した GitHub App の App IDsecrets.APP_PRIVATE_KEY: 作成した GitHub App で発行した Private Keyvars.PAGES_REPO_OWNER: 公開用リポジトリの所有者vars.PAGES_REPO_NAME: 公開用リポジトリ名 (=ghp-pub)vars.PAGES_REPO_BRANCH: 公開用リポジトリのブランチ (mainなど)
- 後述する
push.ymlワークフローを配置します。
- [公開用リポジトリ] ghp-pub
- 作成した GitHub App をインストールします。
- Pages の設定で、GitHub Actions によるデプロイを利用するように設定します。
- 次のリポジトリのシークレットと変数を設定します。
secrets.APP_ID: 作成した GitHub App の App IDsecrets.APP_PRIVATE_KEY: 作成した GitHub App で発行した Private Keysecrets.PAGES_SOURCE_REPO_OWNER: ソースリポジトリの所有者secrets.PAGES_SOURCE_REPO_NAME: ソースリポジトリ名 (=ghp-src)
- 後述する
deploy.ymlワークフローを配置します。
(ghp-src) .github/workflows/push.yml
name: Push
on:
push:
branches:
- main
jobs:
build:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
# ソースリポジトリを取得します。
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
# 公開する Web ページをビルドします。
# ここでは例として HTML を作成しているだけですが、実際には SSG ツールの呼び出しなどを行うでしょう。
- name: Build
run: |
mkdir -p docs
touch docs/.nojekyll
echo '<h1>Hello GitHub Pages ${{ github.sha }}</h1>' > docs/index.html
# 成果物をアップロードします。
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
id: artifact
with:
path: _site/
retention-days: 1
# GitHub App Installation Access Token を取得します。
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: auth
with:
app-id: ${{ secrets.APP_ID }}
owner: ${{ vars.PAGES_REPO_OWNER }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
repositories: ${{ vars.PAGES_REPO_NAME }}
# 公開用リポジトリのデプロイワークフローを呼び出します。
- name: Dispatch deploy workflow
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ steps.auth.outputs.token }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: '${{ vars.PAGES_REPO_OWNER }}',
repo: '${{ vars.PAGES_REPO_NAME }}',
workflow_id: 'deploy.yml',
ref: '${{ vars.PAGES_REPO_BRANCH }}',
inputs: {
artifact_id: '${{ steps.artifact.outputs.artifact-id }}',
run_id: '${{ github.run_id }}'
}
})
(ghp-pub) .github/workflows/deploy.yml
name: Deploy
on:
workflow_dispatch:
inputs:
artifact_id:
description: The ID of the source artifact
required: true
type: string
run_id:
description: The ID of the run containing the source artifact
required: true
type: string
jobs:
prepare:
runs-on: ubuntu-latest
steps:
# GitHub App Installation Access Token を取得します。
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
id: auth
with:
app-id: ${{ secrets.APP_ID }}
owner: ${{ secrets.PAGES_SOURCE_REPO_OWNER }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
repositories: ${{ secrets.PAGES_SOURCE_REPO_NAME }}
# ソースリポジトリでビルドした成果物をダウンロードします。
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
artifact-ids: ${{ inputs.artifact_id }}
github-token: ${{ steps.auth.outputs.token }}
path: _site/
repository: ${{ secrets.PAGES_SOURCE_REPO_OWNER }}${{ '/' }}${{ secrets.PAGES_SOURCE_REPO_NAME }}
run-id: ${{ inputs.run_id }}
# 改めて、このリポジトリの workflow artifact としてアップロードします。
- uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0
# Pages へデプロイします。このジョブは単一リポジトリの場合とまったく同じです。
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: prepare
permissions:
id-token: write
pages: write
runs-on: ubuntu-latest
steps:
- uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
id: deployment
この実装例では、ghp-src の main ブランチにプッシュするたび、push.yml ワークフローが実行され、ページのビルドおよび成果物のアップロードが行われます。さらに、そこから GitHub App の権限を利用して ghp-pub の deploy.yml ワークフローを起動します。deploy.yml ワークフローでは、ソースリポジトリのビルド済み成果物をダウンロードし、それを ghp-pub の pages デプロイ用 workflow artifact として改めてアップロードし、これを pages にデプロイします。
なお、push.yml ワークフローから直接 ghp-pub の workflow artifact をアップロードすると冗長さが減少できそうに思いますが、現状では API 的に不可能なようでした。
まとめ #
GitHub Pages のソースリポジトリと公開用リポジトリを分離したデプロイ戦略について 2 パターンを提案しました。
- Publish from branch の手法で Pages をデプロイする手法では、GitHub App を利用してリポジトリを跨いで成果物をプッシュすることで、ソースリポジトリのコンテンツを完全に分離し、公開用リポジトリには Web ページとして公開されるリソースのみが含まれるようにすることができます。
- この手法は次項よりも構成要素が少なくシンプルですが、リポジトリに成果物をプッシュする必要があるという根本的な手法の課題があり、特別な理由がなければ次項の手法の採用を検討したいところです。
- Publish from GitHub Actions の手法では、GitHub App を利用してリポジトリ間で workflow artifact を受け渡すことで、ソースリポジトリのコンテンツを完全に分離し、公開用リポジトリから成果物自体を artifact に分離することができます。
- デプロイ手法として効率的かつ柔軟であるため、ソースリポジトリの分離の有無にかかわらず、こちらの手法が推薦されます。
- セットアップの手間は少し多いですが、初回だけなので大きな課題とはならないでしょう。