17 Jul 2020
Long time no see!
Let's see how I configured Gitlab CI to be able to deploy this blog on my private server. As already explained, I use deployer for deployments. Under the hood, it performs a simple rsync
thanks to the upload task.
My CI workflow is quite simple. A test
stage allows me to merge the PR, and a deploy
stage is here to release a new version of this blog. The deploy
stage is launched only on master
, when a pull request is merged. It looks basically like this:
stages:
- test
- deploy
test:
stage: test
script:
- //...
deploy:
stage: deploy
only:
refs:
- master
script:
- // ...
This is quite straightforward:
+.install_deployer: &install_deployer
+ - apt-get install -yqq rsync
+ - curl -LO https://deployer.org/deployer.phar
+ - chmod +x deployer.phar
deploy:
stage: deploy
only:
refs:
- master
+ before_script:
+ - *install_deployer
script:
- // ...
This is the trickiest part. To be able to rsync
the files from Gitlab CI to my server, the CI needs to be able to access this server.
For that, I use a dedicated SSH key. Its role is only to deploy on this server, and it has no passphrase. The SSH private key will be added to Gitlab CI's SSH agent.
+.ssh_to_deploy: &ssh_to_deploy
+ - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )'
+ - eval $(ssh-agent -s)
+ - echo "$DEPLOY_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
+ - mkdir -p ~/.ssh
+ - chmod 700 ~/.ssh
+ - ssh-keyscan -p $DEPLOY_PORT $DEPLOY_HOSTNAME >> ~/.ssh/known_hosts
+ - chmod 644 ~/.ssh/known_hosts
deploy:
stage: deploy
only:
refs:
- master
before_script:
- *install_deployer
+ - *ssh_to_deploy
script:
- // ...
The env vars $DEPLOY_SSH_PRIVATE_KEY
, $DEPLOY_PORT
and $DEPLOY_HOSTNAME
must be added to Gitlab CI (section Settings >> CI/CD >> Variables of your project). Whenever they can, those variables must be masked, so that they don't appear in the jobs' logs.
As we want to deploy only on master
, all those variables can be protected. To protect the master
branch, go to the section Settings >> Repository >> Protected Branches of your project. For your first tests, maybe you'll want to deploy from your pull request, to see if everything goes well. In that case, do not protect those variables right now, do it once your deploy workflow is in place.
At the root of my repository, I put a .deployer.yml.dist
file that looks like this:
jjanvier.com:
hostname: _HOSTNAME_
stage: _STAGE_
deploy_path: _PATH_
port: _PORT_
user: _USER_
I use this file as a template for the CI where I'll replace all the _FOO_
patterns by the associated env var that I defined in my Gitlab project. Once done I can launch the deployment via php deployer.phar deploy production
.
deploy:
stage: deploy
only:
refs:
- master
before_script:
- *install_deployer
- *ssh_to_deploy
+ script:
+ - cp .deployer.yml.dist .deployer.yml
+ - sed -i "s~_HOSTNAME_~$DEPLOY_HOSTNAME~g" .deployer.yml
+ - sed -i "s~_STAGE_~$DEPLOY_STAGE~g" .deployer.yml
+ - sed -i "s~_PATH_~$DEPLOY_PATH~g" .deployer.yml
+ - sed -i "s~_PORT_~$DEPLOY_PORT~g" .deployer.yml
+ - sed -i "s~_USER_~$DEPLOY_USER~g" .deployer.yml
+ - php deployer.phar deploy production
The env vars $DEPLOY_STAGE
,$DEPLOY_PATH
and $DEPLOY_USER
are added, masked and protected to Gitlab CI exactly like explained previously for $DEPLOY_SSH_PRIVATE_KEY
, $DEPLOY_PORT
and $DEPLOY_HOSTNAME
.
The last missing part of my deployment pipeline is to install PHP dependencies. On purpose, I have no JS or CSS to build.
+.install_composer: &install_composer
+ - apt-get update -yqq
+ - apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev libzip-dev
+ - docker-php-ext-install zip
+ - curl -sS https://getcomposer.org/installer | php
deploy:
stage: deploy
only:
refs:
- master
before_script:
+ - *install_composer
+ - php composer.phar install --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader
- *install_deployer
- *ssh_to_deploy
script:
- cp .deployer.yml.dist .deployer.yml
- sed -i "s~_HOSTNAME_~$DEPLOY_HOSTNAME~g" .deployer.yml
- sed -i "s~_STAGE_~$DEPLOY_STAGE~g" .deployer.yml
- sed -i "s~_PATH_~$DEPLOY_PATH~g" .deployer.yml
- sed -i "s~_PORT_~$DEPLOY_PORT~g" .deployer.yml
- sed -i "s~_USER_~$DEPLOY_USER~g" .deployer.yml
- php deployer.phar deploy production
My final .gitlab-ci.yml
looks like this:
image: php:7.2
.install_composer: &install_composer
- apt-get update -yqq
- apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev libzip-dev
- docker-php-ext-install zip
- curl -sS https://getcomposer.org/installer | php
.install_deployer: &install_deployer
- apt-get install -yqq rsync
- curl -LO https://deployer.org/deployer.phar
- chmod +x deployer.phar
.ssh_to_deploy: &ssh_to_deploy
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )'
- eval $(ssh-agent -s)
- echo "$DEPLOY_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan -p $DEPLOY_PORT $DEPLOY_HOSTNAME >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
stages:
- test
- deploy
test:
stage: test
before_script:
- *install_composer
- php composer.phar install --no-ansi --no-interaction --no-progress --no-scripts
script:
- ...
deploy:
stage: deploy
only:
refs:
- master
before_script:
- *install_composer
- php composer.phar install --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader
- *install_deployer
- *ssh_to_deploy
script:
- cp .deployer.yml.dist .deployer.yml
- sed -i "s~_HOSTNAME_~$DEPLOY_HOSTNAME~g" .deployer.yml
- sed -i "s~_STAGE_~$DEPLOY_STAGE~g" .deployer.yml
- sed -i "s~_PATH_~$DEPLOY_PATH~g" .deployer.yml
- sed -i "s~_PORT_~$DEPLOY_PORT~g" .deployer.yml
- sed -i "s~_USER_~$DEPLOY_USER~g" .deployer.yml
- php deployer.phar deploy production
It works like a charm. Everytime a pull request is performed, this blog is automatically updated. Maybe this will help me to write more 😛