Rust + Yew で静的ページをGitHub Pagesにデプロイ

[Yew](https://yew.rs/ja/)は、Rustとwasmを使ってReactのようなコンポーネントベースのWebフロントエンドを構築できるフレームワークです。

https://yew.rs/ja/

Yewを使って、静的ページをGitHub Pagesにデプロイしてみます。

## 環境
- Windows 11 WSL2 Debian bullseye
- Docker version 20.10.11
- docker-compose version 1.29.2
- Rust 1.58
- Yew 0.19
- Trunk 0.14.0

### ソースコード
今回使用したソースコードは、環境構築に利用したDockerfile等も含めて下記のリポジトリにあります。

https://github.com/s2terminal/yew-static-pages

## 静的ページの構築
まずはDockerを使ってローカル環境上にYewプロジェクトを構築していきます。

```bash
$ docker run \
  --interactive --tty --rm \
  --volume $(pwd):/app \
  --workdir /app \
  rust:1.58-slim \
  cargo new yew-static-pages
$ cd $_
```

`Dockerfile`と`docker-compose.yml`を作成します。

```Dockerfile:Dockerfile
FROM rust:1.58-slim
WORKDIR /app

RUN apt-get update && apt-get install -y \
    git \
    curl \
    build-essential \
 && rm -rf /var/lib/apt/lists/*

# setup yew
RUN rustup target add wasm32-unknown-unknown
RUN cargo install trunk

COPY ./ ./
RUN cargo build

CMD trunk serve
```

```docker-compose.yml
version: '3'
services:
  app:
    build: .
    volumes:
      - ./:/app
    environment:
      - USER=root
    ports:
      - 8080:8080
```

`Cargo.toml`に、Yewへの依存を追記します。

```toml:Cargo.toml（抜粋）
[dependencies]
yew = "0.19"
```

[Yew公式のテンプレート](https://github.com/yewstack/yew-trunk-minimal-template/blob/2b41d1b968a5de1378f9c9d2a24c728f00be5cf4/src/app.rs)を参考に、とりあえずHello Worldを表示するページを作ります。

```rust:src/main.rs
use yew::prelude::*;

fn main() {
    yew::start_app::<App>();
}

#[function_component(App)]
pub fn app() -> Html {
    html! {
        <main>
            <img class="logo" src="https://yew.rs/img/logo.png" alt="Yew logo" />
            <h1>{ "Hello World!" }</h1>
            <span class="subtitle">{ "from Yew with " }<i class="heart" /></span>
        </main>
    }
}
```

`index.html`を配置します。

```index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Yew App</title>
  </head>
</html>
```

`$ docker-compose up --build` を実行して、 localhost:8080 でWebサイトが表示されれば成功です。

## GitHub Pagesへのデプロイ
`$ trunk build --release`で生成される`dist/index.html`を、GitHub Pages上で閲覧できるようにデプロイします。[`--public-url`オプション](https://trunkrs.dev/assets/#directives)でGitHub Pagesのパスを指定する点に注意です。

```.github/workflows/gh-pages.yml
name: Deploy

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - run: rustup target add wasm32-unknown-unknown
    - run: cargo install trunk
    # 「yew-static-pages」は実際のリポジトリ名に変更
    - run: trunk build --release --public-url yew-static-pages
    - run: cp -r ./dist/ ./public/
    - uses: peaceiris/actions-gh-pages@v3
      if: ${{ github.ref == 'refs/heads/main' }}
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./public
```

GitHubにpushした後、しばらくしてデプロイが完了し、`https://<ユーザ名>.github.io/<リポジトリ名>`でページが表示されたら成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50391/ad5404ff-9535-9ab5-2424-50b7c701d30a.png)

## wasm-packを使う場合
wasmのビルドにはTrunkではなくwasm-packも選択できます。Trunkとの比較など詳細は公式ドキュメントを読んでください。

https://yew.rs/ja/docs/0.18.0/getting-started/introduction

wasm-packを使う場合、ビルド時に`.gitignore`が自動的に配置され、GitHub上でコミットに含まれずGitHub Pagesにリリースされません。GitHub Pagesに反映するためには`.gitignore`を削除する必要があります。

```yml:.github/workflows/gh-pages.yml（抜粋）
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - run: cargo install wasm-pack
    - run: wasm-pack build --release --target web --out-name wasm --out-dir ./public
    - run: cp ./static/index.html ./public/index.html
    - run: rm -rf ./public/.gitignore

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

## 参考
- [\`build \-\-target web\`: have an option not to add \.gitignore to pkg output folder · Issue \#999 · rustwasm/wasm\-pack](https://github.com/rustwasm/wasm-pack/issues/999)
- [\`build\` doesn't work but \`serve\` does · Issue \#268 · thedodd/trunk](https://github.com/thedodd/trunk/issues/268)