GitLab CIでXServerに自動でデプロイ

# やりたいこと

- GitLabのmasterブランチにpushすると、自動的にXServerの特定のフォルダにアップロードする
- GitLabのmaster以外のブランチにpushすると、XServerにフォルダを作ってアップロードする

# やりかた

GitLab CIを使ってpushしたときにスクリプトを実行する。
XServerにgitは入れず、rsyncでファイル同期を行う。

## 下準備

### SSH接続用の鍵を生成する

```
$ ssh-keygen -C ""
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/[ユーザー名]/.ssh/id_rsa): /Users/[ユーザー名]/Decktop/gitlab
  (Windowsなら「C:/Users/[ユーザー名]/Desktop/gitlab」)(保存場所、ファイル名を指定する。他の場所を指定しても良い。)
Enter passphrase (empty for no passphrase): (何も入力せずにEnter)
Enter same passphrase again: (何も入力せずにEnter)
Your identification has been saved in /Users/[ユーザー名]/.ssh/gitlab.
Your public key has been saved in /Users/[ユーザー名]/.ssh/gitlab.pub.
The key fingerprint is:
SHA256:*******************************************
The key's randomart image is:
+---[RSA 3072]----+
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
+----[SHA256]-----+
```

### 公開鍵を登録

![xsrver1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/13577c0b-ace7-0d2c-cb25-2d55e8c678bd.png)
![xserver2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/e618586e-a29b-5650-4cbb-52a2cc096c9e.png)
↑すでにONになっている場合は、飛ばしてOK
![xserver3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/90cd2d78-0a52-3d70-a9dc-c1c2ec743d7a.png)
![xserver4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/1a851f82-d25d-b7a5-c4cb-97737a1fd2d5.png)
![xserver5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/85868d8a-062f-b9bc-2916-9b725c24df48.png)
生成した公開鍵(gitlab.pub)を開き、中身をコピペ。
すでに登録されている場合は、改行区切りで追記する。
![xserver6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/fa957224-4101-b104-aa30-56618da8ea0f.png)

### 秘密鍵を登録

![gitlab1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/9b31c92a-f369-aef0-c533-e173a6b9f746.png)
![gitlab2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/7f410b1f-9c2d-8b77-0a07-480c70f32b35.png)
![gitlab3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/a8f73c50-00d3-b563-bd9e-09fd7f3bb12a.png)
![gitlab4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508581/dbc6b387-ef8b-91f2-b943-9f454a494659.png)

生成した秘密鍵(gitlab)を開き、中身をコピペ。
青で示したところは、特定のブランチなどのみで秘密鍵が使えるようにするときはON、全てのブランチで使用できるようにするときはOFFにする。
ONにしたときは、対象のブランチを「保護ブランチ」として指定する。詳しくは[こちら](https://docs.gitlab.com/ee/user/project/protected_branches.html)

## GitLab CI の設定

gitリポジトリのルートに「git-ci.yml」をおいて、以下を記述。

```gitlab-ci.yml
variables:
  STAGE_HOST: [サーバーID].xsrv.jp
  STAGE_USER: [サーバーID]
  STAGE_PATH: /home/[サーバーID]/[ドメイン名]/public_html/[アップロード先のフォルダまでのパス]
  STAGE_REVIEW_PATH: $STAGE_PATH-$CI_COMMIT_REF_SLUG
    # $CI_COMMIT_REF_SLUGはブランチの名前を表す
    # ここでは、本番用のディレクトリと同じ場所にレビュー用のディレクトリを作成する
    # $STAGE_PATH/$CI_COMMIT_REF_SLUGにすれば、本番用のディレクトリの中にレビュー用のディレクトリが作成される

stages:
  - review
  - deploy

review_start:
  stage: review
  before_script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - touch ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - ssh-keyscan -p 10022 $STAGE_HOST >> ~/.ssh/known_hosts
      # ここまで、SSH接続用の鍵の取得。
      # 秘密鍵を設定したときに「Protect Variable」をONにした場合、
          ブランチが保護ブランチでないと秘密鍵が取得できないので注意
    - apt-get update
    - apt-get install -y rsync
  script:
    - ssh -p 10022 $STAGE_USER@$STAGE_HOST mkdir -p "$STAGE_REVIEW_PATH"
      # アップロード先のディレクトリを作成する
    - time rsync -av --exclude='.gitlab-ci.yml' --exclude='.git/' -e "ssh -p 10022" --delete --checksum ./ $STAGE_USER@$STAGE_HOST:"$STAGE_REVIEW_PATH"
      # 実際にファイルを転送するコマンド
      # 「.gitlab-ci.yml」と「.git/」は除外する
      # timeはかかった時間を出力するコマンド。不要ならrsyncから書く。
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    url: https://[ドメイン名]/[アップロード先のフォルダまでのパス]-$CI_COMMIT_REF_SLUG
      # XServerへアクセスするURL
      # httpsに対応していないのであればhttp
    on_stop: review_stop
  rules:
    - if: '$SSH_PRIVATE_KEY && $CI_COMMIT_REF_NAME!="master" && $CI_COMMIT_MESSAGE=~/^review.*/'
      # 秘密鍵が取得可能で、ブランチの名前が「master」でなく、コメントが「review」から始まる場合に実行する

review_stop:
  stage: review
  variables:
    GIT_STRATEGY: none
  before_script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - touch ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - ssh-keyscan -p 10022 $STAGE_HOST >> ~/.ssh/known_hosts
  script:
    - ssh -p 10022 $STAGE_USER@$STAGE_HOST rm -r "$STAGE_REVIEW_PATH"
      # アップロード先のディレクトリを削除する
  interruptible: true
    # 中断可能(このジョブが実行される前に他のpushがあったら中断)
  environment:
    name: review/$CI_COMMIT_REF_SLUG
    action: stop
  rules:
    - if: '$SSH_PRIVATE_KEY && $CI_COMMIT_REF_NAME!="master" && $CI_COMMIT_MESSAGE=~/^review.*/' 
      when: manual
        # 手動(自動では実行しない)

deploy:
  stage: deploy
  before_script:
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - touch ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - ssh-keyscan -p 10022 $STAGE_HOST >> ~/.ssh/known_hosts
    - apt-get update
    - apt-get install -y rsync
  script:
    - time rsync -av --exclude='.gitlab-ci.yml' --exclude='.git/' -e "ssh -p 10022" --delete --checksum ./ $STAGE_USER@$STAGE_HOST:$STAGE_PATH
  rules:
    - if: '$SSH_PRIVATE_KEY && $CI_COMMIT_REF_NAME=="master"'
```

## カスタマイズ

以下の記事を参考にしてください。
[GitLab CI/CDパイプライン設定リファレンス(日本語訳:GitLab CI/CD Pipeline Configuration Reference)](https://qiita.com/ynott/items/1ff698868ef85e50f5a1)
[GitLab CI/CD Variables を翻訳しました。](https://qiita.com/ynott/items/4c5085b4cd6221bb71c5)

# 参考にした記事

[.gitlab-ci.yml ジョブ事例集](https://qiita.com/pekeq/items/5ee6303d53fa7f163e40)
[【Review Apps】GitLab CI を使って、自動デプロイ・自動レビュー環境作成を実現する](https://bashalog.c-brains.jp/17/05/11-090000.php)
[GitLab CI/CDパイプライン設定リファレンス(日本語訳:GitLab CI/CD Pipeline Configuration Reference)](https://qiita.com/ynott/items/1ff698868ef85e50f5a1)
[GitLab CI/CD Variables を翻訳しました。](https://qiita.com/ynott/items/4c5085b4cd6221bb71c5)