GitHub Actions による GitHub Pages への自動デプロイ

GitHub Actions の登場により GitHub Pages へのデプロイがとても簡単になりました。手順を書いた YAML ファイルを Push するだけでビルド・デプロイの CI/CD を構築できます。この記事では GitHub Actions を用いて GitHub Pages へのデプロイを自動化する方法を紹介します。

[<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285675/f446d6e0-1428-9b57-3028-58b63831c316.jpeg" alt="GitHub Actions による GitHub Pages への自動デプロイのためのアクション" width="500px">](https://github.com/peaceiris/actions-gh-pages)



## Hugo のブログ・サイトの場合

具体的には以下のような YAML ファイルをデフォルトブランチに `.github/workflows/gh-pages.yml` として Push するだけで GitHub Pages へのデプロイが始まり、サイトが公開されます。

```yaml:.github/workflows/gh-pages.yml
name: GitHub Pages

on:
  push:
    branches:
      - main  # Set a branch name to trigger deployment
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: true  # Fetch Hugo themes (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v3
        with:
          hugo-version: '0.85.0'

      - name: Build
        run: hugo --minify

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public
```

一応補足しておくと `GITHUB_TOKEN` は GitHub Actions runner で自動生成されるトークンであり、 Personal Access Token ではないのでご注意を。何も設定は必要ありません。



## 使用している Actions

最新の情報は各 Action のリポジトリにて確認してください。Issue, Pull Request, GitHub Star など、フィードバックをお待ちしてます。

### GitHub Pages Action

https://github.com/peaceiris/actions-gh-pages

### Hugo Setup Action

https://github.com/peaceiris/actions-hugo

[<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285675/bd312ad9-1723-fbfb-3678-fb4708832907.png" alt="GitHub Actions による GitHub Pages への自動デプロイのためのアクション" width="500px">](https://github.blog/2020-03-22-github-action-hero-shohei-ueda/)

- [GitHub Action Hero: Shohei Ueda - The GitHub Blog](https://github.blog/2020-03-22-github-action-hero-shohei-ueda/)

GitHub Pages Action, Hugo Action, mdBook Action のメンテナーとして GitHub Blog で紹介していただきました。ありがとうございます。



## User and Organization リポジトリの場合

~~`username/username.github.io` リポジトリの場合は master ブランチに GitHub Pages で公開するファイルを配置する必要がありますので、YAML ファイルを少し修正して Push する必要があります。~~ (2020-10-16 追記) 現在の [GitHub Pages では任意のブランチをデプロイ対象として指定できるようになった](https://github.blog/changelog/2020-07-31-build-and-deploy-github-pages-from-any-branch-beta/)ため、User and Organization リポジトリでも `gh-pages` ブランチを GitHub Pages 用途で使うことが可能となりました。



## その他 Static Site Generator の例

Hugo プロジェクトの例を既に紹介しましたが、他の静的サイトジェネレーターでも同様に使えます。

### Node.js (JavaScript) 系 

Node.js (JavaScript) 製の静的サイトジェネレーターの場合は以下のような YAML にすればオッケーです。依存関係は `package.json` と `package-lock.json` で管理されているものとします。

[next.js], [nuxt.js], [gatsby], [hexo], [gitbook], [vuepress], [react-static], [gridsome], etc.

[next.js]: https://github.com/zeit/next.js
[nuxt.js]: https://github.com/nuxt/nuxt.js
[gatsby]: https://github.com/gatsbyjs/gatsby
[hexo]: https://github.com/hexojs/hexo
[gitbook]: https://github.com/GitbookIO/gitbook
[vuepress]: https://github.com/vuejs/vuepress
[react-static]: https://github.com/react-static/react-static
[gridsome]: https://github.com/gridsome/gridsome

Gatsby の例を以下記事で解説しています。

- [Gatsby のサイトを GitHub Actions で GitHub Pages にデプロイ - Qiita](https://qiita.com/peaceiris/items/2f6d83802f2aefa66f9d)

Vue.js, Nuxt.js の例を以下記事で解説しています。

- [Vue Nuxt アプリを GitHub Actions で GitHub Pages にデプロイ - Qiita](https://qiita.com/peaceiris/items/154fc3f9bccf9e4eb137)

React.js, Next.js の例を以下記事で解説しています。

- [React Next アプリを GitHub Actions で GitHub Pages にデプロイ - Qiita](https://qiita.com/peaceiris/items/9c569125b25fc090c515)

```yaml:.github/workflows/gh-pages.yml
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci
      - run: npm run build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public
```

### Python 系

Python 製の静的サイトジェネレーターの場合は以下のような YAML にすればオッケーです。依存関係は `requirements.txt` で管理されているものとします。

[pelican], [MkDocs], [sphinx], etc.

[pelican]: https://github.com/getpelican/pelican
[MkDocs]: https://github.com/mkdocs/mkdocs
[sphinx]: https://github.com/sphinx-doc/sphinx

```yaml:.github/workflows/gh-pages.yml
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.8'

      - name: Upgrade pip
        run: |
          # install pip=>20.1 to use "pip cache dir"
          python3 -m pip install --upgrade pip

      - name: Get pip cache dir
        id: pip-cache
        run: echo "::set-output name=dir::$(pip cache dir)"

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.pip-cache.outputs.dir }}
          key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
          restore-keys: |
            ${{ runner.os }}-pip-

      - name: Install dependencies
        run: python3 -m pip install -r ./requirements.txt

      - run: mkdocs build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./site
```

### mdBook (Rust)

GitHub Actions 上で Rust 製静的サイトジェネレーター mdBook を実行し、生成したサイトを GitHub Pages へデプロイするワークフローの例です。

https://github.com/peaceiris/actions-mdbook

```yaml
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup mdBook
        uses: peaceiris/actions-mdbook@v2
        with:
          mdbook-version: '0.4.8'
          # mdbook-version: 'latest'

      - run: mdbook build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./book
```

### Flutter Web

[Flutter web project] のワークフローの例です。

[Flutter web project]: https://flutter.dev/docs/get-started/web

```yaml
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup Flutter
        run: |
          git clone https://github.com/flutter/flutter.git --depth 1 -b beta _flutter
          echo "${GITHUB_WORKSPACE}/_flutter/bin" >> ${GITHUB_PATH}

      - name: Install
        run: |
          flutter config --enable-web
          flutter pub get

      - name: Build
        run: flutter build web

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build/web
```

### Elm

[Elm] のワークフロー例です。

[Elm]: https://elm-lang.org

```yaml
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '14'

      - name: Setup Elm
        run: npm install elm --global

      - name: Make
        run: elm make --optimize src/Main.elm

      - name: Move files
        run: |
          mkdir ./public
          mv ./index.html ./public/
        # If you have non-minimal setup with some assets and separate html/js files,
        # provide --output=<output-file> option for `elm make` and remove this step

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public
```

### github/personal-website

GitHub Pages built-in Jekyll を使う例です。

- [github/personal-website](https://github.com/github/personal-website) - Code that'll help you kickstart a personal website that showcases your work as a software developer.

```yaml:.github/workflows/github-pages.yml
name: GitHub Pages

on:
  push:
    branches:
      - master
  schedule:
    - cron: '24 */24 * * *'  # Once a day

jobs:
  deploy:
    runs-on: ubuntu-22.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./
          allow_empty_commit: true
          enable_jekyll: true
          cname: github.peaceiris.com
```


### Swift Publish

Swift 製の静的サイトジェネレーター [JohnSundell/Publish] のワークフロー例です。

[JohnSundell/Publish]: https://github.com/JohnSundell/Publish

```yaml
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: macos-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v4

      - uses: actions/cache@v4
        with:
          path: |
            ~/Publish_build
            .build
          key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
          restore-keys: |
            ${{ runner.os }}-spm-

      - name: Setup JohnSundell/Publish
        run: |
          cd ${HOME}
          export PUBLISH_VERSION="0.7.0"
          git clone https://github.com/JohnSundell/Publish.git
          cd ./Publish && git checkout ${PUBLISH_VERSION}
          mv ~/Publish_build .build || true
          swift build -c release
          cp -r .build ~/Publish_build || true
          echo "${HOME}/Publish/.build/release" >> ${GITHUB_PATH}

      - run: publish-cli generate

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v4
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./Output
```



## その他のオプションやサンプル

利用可能なオプションの定義は [action.yml] で確認できます。

(もしリクエストがあればオプションの日本語解説も頑張って書きます。)

最新の [README.md] を確認してください。

[action.yml]: https://github.com/peaceiris/actions-gh-pages/blob/master/action.yml
[README.md]: https://github.com/peaceiris/actions-gh-pages



## 個人的に面白いと思った活用事例

### [fastai/fastpages](https://github.com/fastai/fastpages)

fastpages は Jupyter Notebook からブログを生成して GitHub Pages へデプロイできるツールであり、内部で `peaceiris/actions-gh-pages@v3` を使っています。初期セットアップをガイドしてくれる Pull Request を発行してくれるなど、なかなか面白い GitHub Actions の使い方をしていて勉強になりました。

### GitHub Pages にデプロイしない用途

- [tokyo-metropolitan-gov/covid19](https://github.com/tokyo-metropolitan-gov/covid19)
- [codeforsapporo/covid19](https://github.com/codeforsapporo/covid19)

東京都が始めて、北海道版もある新型コロナウイルス対策サイトですが、ここでも弊 Action を活用いただいています。最初見た時に GitHub Pages でサイトを公開している様子がなかったので、なぜ使っているのか疑問でしたが、どうやら「ビルドした成果物をブランチで共有する」という目的で使っているようです。

例えば `publish_branch: production` を指定しておき `production` ブランチに生成物を push するようにします。こうしておけば、デプロイ先に Netlify を利用する場合には production を公開ブランチとして指定することでビルドを省略できます。VPS やレンタルサーバーなどでサイトを公開する場合には定期的に git pull するだけで済むようになります。