はじめに
需要ないかなぁと思ったんだけど、貧乏プロジェクトで経験したお話。さくらのレンタルサーバでPHPのサイトを作るというプロジェクトがありました。
最初、WinSCPでドラッグ&ドロップでリリースしてたんだけど、確認待ちとかの待ち時間に、このプロジェクトで技術的な何か身につけたいなぁと思い、CD環境構築してみることにしました。その時のお話。
CD
GitLab CIを使ってさくらインターネットにlftpでデプロイ。
developブランチに変化があったときは、dev環境に自動デプロイ。
すごく時間がかかってるので、WinSCPで良いかなと思うけど。
CSS、JSのクランチとかも入れるとちょっとはかっこがつくかな?
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を利用します。
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で除外ファイルを設定できます。
ファイルのタイムスタンプを維持できない(送信した日になってしまう)ので毎回全アップロードになってしまい、時間がかかる。
とりあえず、全ファイルアップロードしないように、なるべく、変更のあったディレクトリ(ファイルではないことに注意)だけをアップロードするようにした。
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にするとファイル名まで指定するとエラーになりディレクトリーを指定しなければならない。