GitLab pagesでReactアプリを無料ホスティングする方法(GitHub/GitLab自動連携+CI/CD)

React.jsを勉強している内に、成果物を無性に上げたくなり無料のホスティング先を探してみました。
いくつか候補はありましたが、その中でもGitHub pagesとGitLab pagesを比較して、以下の理由から今回はGitLabを選択しました。

- GitLab pagesでは独自ドメインが設定可能
- GitHub側でコードを管理して、成果物のみGitLabに上げる形にしたい

GitLabの場合、無料だとCI/CDを使ったビルド時間に400分という制限([こちら](https://about.gitlab.com/blog/2020/09/01/ci-minutes-update-free-users/)を参照)があるので、使い方によっては無料にならない点はご注意下さい。

## 前提条件
- GitHubアカウント
- GitLabアカウント
- Git
- Nodeとyarn(またはnpm)

すでにGitHubとGitLabアカウントを持っていることを前提とします。

## ReactアプリをGitHubに上げる

この記事では、アプリの内容は無視して設定を優先したいので、`create-react-app`で作られるデフォルトのアプリを使用します。

### Reactアプリを作る

GitHubリモートリポジトリを作成し、ローカルにクローンします。

```zsh
git clone git@github.com:USER_NAME/REPOSITORY.git
```
USER_NAME, REPOSITORYは自身のユーザー名とリポジトリ名で置き換える。

クローンしたローカルリポジトリに移動し、以下のコマンドでReactアプリを作ります。

```zsh
npx create-react-app .
```

作ったら早速動作を確認します。

```zsh
yarn start
```

### GitHubに上げる前の設定

動いたらGitHubに上げる前に2つ設定を行います。  

1つ目は`package.json`の編集です。
GitLab pagesでは次のようなpage URLがデフォルトです。

`https://USER_NAME.gitlab.io/PROJECT_NAME`  

URLに適切に紐付けるために`package.json`ファイルの先頭に`homepage`プロパティを追加して、値としてプロジェクト名を入れて下さい。
次の`my-react-app`がプロジェクト名にあたります。

```package.json
	{
	 "homepage": "my-react-app",
	 "name": "my-react-app",
	 "version": "0.1.0",
	 "private": true,
	 "dependencies": {
		 "@testing-library/jest-dom": "^5.11.4",
		 "@testing-library/react": "^11.1.0",
		 "@testing-library/user-event": "^12.1.10",
		 "react": "^17.0.2",
		 "react-dom": "^17.0.2",
		 "react-scripts": "4.0.3",
		 "web-vitals": "^1.0.1"
	 },
```

2つ目にCI/CDパイプライン用のファイルを設定します。
このパイプラインを設定することで、

   1. ローカルからGitHubにPush、またはブランチのマージなどを行う
   2. 自動的に更新内容がGitLabにPushされる
   3. GitLab上でパイプラインが働き、自動的にアプリのビルドされGitLab Pagesにデプロイされる

という流れを一連で自動化する事ができます。

設定としては`.gitlab-ci.yml`という新規ファイルを作成し、`package.json`と同じ階層に追加します。
以下の内容を書き込んで下さい。

```.gitlab-ci.yml
image: node:16.13.0 # nodeバージョンを選択

cache:
  paths:
    - node_modules/ # キャッシュすることで次回以降ビルドをスピードアップ

stages:
  - deploy # パイプラインで使用するステージ名

pages:
  stage: deploy
  script:
    - rm -rf build # 既存のbuildディレクトリを削除
    - yarn install # dependenciesをインストール
    - yarn run build # プロジェクトのビルド
    - mv public .public # 既存のpublicディレクトリ中のデータを.publicに移動
    - cp build/index.html build/404.html # 必須ではない
    - mv build public # build中のデータをpublicに移動
  artifacts:
    paths:
      - public # publicディレクトリのみにアクセスを許可
  only:
    refs:
      - main # GitLab pagesのコンテンツはmainブランチにデプロイ
```

設定が完了したら作ったアプリをGitHubのリモートリポジトリにプッシュします。

```zsh
git add .
git commit -m "init"
git push origin main
```

## GitHubとGitLabを連携させる

GitLabにログインし、New Project/Repositoryに進み、Run CI/CD for external repositoryを選択します。



![create new project with ci.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/32601343-c259-fdae-d99c-9d3b6d1815ee.png)

Connect repositories fromでGitHubのボタンをクリックします。

![Run CI:CD pipeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/ca413a0c-7bf8-9dd8-0818-53c59b7aae7e.png)

[Personal Access Token](https://github.com/settings/tokens)からGitHubに移り、トークンを作る。
repoを選択し、Generate tokenを実行。
先程のGitLabページに戻り、生成したトークンをGitLabに登録します。

### 連携されなかったときの追加設定

![Authenticate with GitHub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/23d50ea2-60fe-3615-3613-a100ae254171.png)

私の場合はGitHubリポジトリをGitLab上にミラーすることは出来ましたが、その後GitHubへのPushがGitLabに自動で反映されない状態でした。
そのため追加で次のような設定を行います。

まずはGitHubのリポジトリからSSH用のURLをコピーしてきます。
![Copy repository url.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/d0288305-e5f5-5985-39d8-9821a6809262.png)

なお次の作業に入る前に1点注意点があります。
GitLabでの作業でコピーしたURLを使用しますが、そのまま使用するとエラーになります。
貼り付けた後に以下のように編集することになるので注意して下さい。

![Wrong url.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/a02ae37b-9dad-0b91-16c7-7e480f58de96.png)
![Correct url.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/e29017e4-9dea-9d9b-0485-7f3d1b91eed4.png)

GitLabで先程作成したプロジェクトのSettings > RepositoryからMirroring repositoriesの設定を行います。
先程の作業で1つMirrored repositoriesが作成されていると思いますので、こちらは削除します。


GitHubでコピーしたURLを貼付/編集の上、以下の選択を行いDetect host keysをクリックしてください。

- Mirror direction: Pull
- Authentication method: SSH public key  

下のチェック項目はお好みで選択してください。
Mirror repositoryをクリックし確定します。

![Mirroring ripository.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/a8e240e4-59d9-07e1-5e30-7725eee43374.png)

作成されたMirrored repositoryからSSH public keyをコピーします。

![Copy SSH key.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/d935bc8d-6a3f-eb7d-9028-c067b66826ed.png)

GitHubに移動し、MirrorするリポジトリでSettings > Deploy keysを選択し、新しいDeploy keyを追加します。
Keyの部分には先程コピーしたSSH public keyを貼り付けてください。

![Set deploy key.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/7dcba688-ef6d-d056-d4e2-fa8f2ab7c769.png)


これで連携は完了です。
先程のMirror repositoryの場所に戻り、連携が行われているか確認しましょう。
なおパイプラインを有効にするためにはクレジットカードの登録が必要ですのでご注意下さい。

![Gitlab main protected error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/811158/2f3d998f-77bd-907a-9c1f-cce74c79fc92.png)
上のようにプロテクトの関係でGitHubからのPushが反映されずエラーになる場合は、
Settings > Repository > Protected branches
に移動し、Unprotectで対象ブランチのプロテクトを外しましょう。
これで連携は完了です。



## 実際にページを確認する
Settings > PagesからページのURLを確認できます。
実際に見れるか確認してみましょう。