GitLab・さくらのレンタルサーバでCD環境

# はじめに

需要ないかなぁと思ったんだけど、貧乏プロジェクトで経験したお話。さくらのレンタルサーバでPHPのサイトを作るというプロジェクトがありました。

最初、WinSCPでドラッグ＆ドロップでリリースしてたんだけど、確認待ちとかの待ち時間に、このプロジェクトで技術的な何か身につけたいなぁと思い、CD環境構築してみることにしました。その時のお話。

# CD

GitLab CIを使ってさくらインターネットにlftpでデプロイ。

developブランチに変化があったときは、dev環境に自動デプロイ。
すごく時間がかかってるので、WinSCPで良いかなと思うけど。
CSS、JSのクランチとかも入れるとちょっとはかっこがつくかな？

```yml
stages:
  - deploy

deploy-job:
  image: buildpack-deps:jessie-scm
  stage: deploy
  environment: dev
  script:
    - apt-get update && apt-get install -y --force-yes lftp sed
    - for FILE in `git diff-tree --no-commit-id --name-only -r $CI_COMMIT_SHA` ; do
    -   echo $FILE
    -   if [ $FILE = 'html/func/db/dao.php' ]; then
    -     sed -i -e "s/HOST = 'mysql'/HOST = '$DEV_DB_HOST'/g" html/func/db/dao.php
    -     sed -i -e "s/DB_NAME = 'hoge'/DB_NAME = '$DEV_DB_NAME'/g" html/func/db/dao.php
    -     sed -i -e "s/USER = 'hoge'/USER = '$DEV_DB_USER'/g" html/func/db/dao.php
    -     sed -i -e "s/PASSWORD = 'hoge'/PASSWORD = '$DEV_DB_PASSWORD'/g" html/func/db/dao.php
    -   fi
    -   TARGET=`echo $FILE | sed -e "s/\/[^/]\+$/\//" | sed -e "s/html//"`
    -   echo $TARGET
    -   SOURCE="html${TARGET}"
    -   echo $SOURCE
    -   if [ $TARGET = '/' ]; then
    -     TARGET=''
    -   fi
    -   DEST="/home/dev$TARGET"
    -   echo $DEST
    - lftp -e "set ftp:ssl-auth TLS; set ftp:ssl-force true; set ftp:ssl-allow yes; set ftp:ssl-protect-list yes; set ftp:ssl-protect-data on; set ftp:ssl-protect-fxp yes; set cmd:fail-exit yes; set net:reconnect-interval-base 4; set ftp:use-mdtm no; set ftp:timezone -9; open $DEV_FTP_SERVER -u $DEV_FTP_USERNAME,$DEV_FTP_PASSWORD; mirror -v -R --no-recursion --delete -X .htaccess $SOURCE $DEST; quit;"
    - done
  only:
    changes:
      - html/**/*
    refs:
      - develop

```

## セキュリティ情報

repositoryにパスワードとか載せないのが一般的なのでGitLabのvariablesに定義

settings → CI/CD → Variables に設定する。

.yml内では $○○で参照する

## LFTP

さくらインターネットでは、ftpしか使えないので、LFTPを利用します。


```shell
open $DEV_FTP_SERVER -u $DEV_FTP_USERNAME,$DEV_FTP_PASSWORD; mirror -v -R html/ /home/dev;
```

**user名とパスワードはカンマ区切りで、スペースを入れないことに注意する。**

mirror -R でソース（repository)からdestination(さくらインターネット）にコピーという意味。

`--only-newer` をつけて新しいファイルだけアップロードします。

`-X .desktop.ini`　-Xで除外ファイルを設定できます。

ファイルのタイムスタンプを維持できない（送信した日になってしまう）ので毎回全アップロードになってしまい、時間がかかる。

とりあえず、全ファイルアップロードしないように、なるべく、変更のあったディレクトリ（ファイルではないことに注意）だけをアップロードするようにした。

```yml
script:
    - apt-get update && apt-get install -y --force-yes lftp sed
    - for FILE in `git diff-tree --no-commit-id --name-only -r $CI_COMMIT_SHA` ; do
    -   echo $FILE
    -   if [ $FILE = '.gitlab-ci.yml' ]; then
    -     continue
    -   fi
    -   if [ $FILE = '/html/func/db/dao.php' ]; then
    -     sed -i -e "s/HOST = 'mysql'/HOST = '$DEV_DB_HOST'/g" html/func/db/dao.php
    -     sed -i -e "s/DB_NAME = 'hoge'/DB_NAME = '$DEV_DB_NAME'/g" html/func/db/dao.php
    -     sed -i -e "s/USER = 'hoge'/USER = '$DEV_DB_USER'/g" html/func/db/dao.php
    -     sed -i -e "s/PASSWORD = 'hoge'/PASSWORD = '$DEV_DB_PASSWORD'/g" html/func/db/dao.php
    -   fi
    -   TARGET=`echo $FILE | sed -e "s/\/[^/]\+$/\//" | sed -e "s/html//"`
    -   echo $TARGET
    -   SOURCE="html${TARGET}"
    -   echo $SOURCE
    -   if [ $TARGET = '/' ]; then
    -     TARGET=''
    -   fi
    -   DEST="/home/dev$TARGET"
    -   echo $DEST
    - lftp -e "set ftp:ssl-auth TLS; set ftp:ssl-force true; set ftp:ssl-allow yes; set ftp:ssl-protect-list yes; set ftp:ssl-protect-data on; set ftp:ssl-protect-fxp yes; set cmd:fail-exit yes; set net:reconnect-interval-base 4; set ftp:use-mdtm no; set ftp:timezone -9; open $DEV_FTP_SERVER -u $DEV_FTP_USERNAME,$DEV_FTP_PASSWORD; mirror -v -R --no-recursion --delete -X .htaccess $SOURCE $DEST; quit;"
    - done
```

ざっくり説明すると、今回の変更のあったファイルをgit diff-tree --name-only で取得

dao.phpにはDB接続情報が書かれていて、repositoryに上げたくないので、gitlabのセキュリティ情報から取得し、文字列置換。

mirrorコマンドでftpを実行するのだが、ファイル名指定はできないので、ディレクトリ名が必要。

ディレクトリ名をsedで取得して、--no-recursionをつけることでそのディレクトリだけが影響を受けるように設定。

同一ディレクトリ内の複数ファイルがあった場合、複数回実行されるが気にしない。

sourceもdestも/で終わってると、意図した動作しないので注意。

# メモ

どうしてこんな複雑なことをしたかというと、lftpでファイルを送るとファイルを送った時間にタイムスタンプが書き換わってしまう。repositoryのタイムスタンプと比較して常に古くなってしまうので、全ファイルがアップされてしまう。（ファイルサイズでアップするかどうかもできるが、ファイルサイズが変わらない変更の場合、アップされないことが起きてしまう。）

泣く泣く、git diff-treeで変更のあったファイルを取得してそれをアップしようとした。

しかしながら、新規ディレクトリが必要な場合putではディレクトリ作成はしてくれないということなので、mirrorを使うしかなく、mirrorにするとファイル名まで指定するとエラーになりディレクトリーを指定しなければならない。