{"id":3473,"date":"2017-04-14T22:17:13","date_gmt":"2017-04-14T20:17:13","guid":{"rendered":"https:\/\/florianbrinkmann.com\/en\/?p=3473"},"modified":"2020-02-09T10:59:53","modified_gmt":"2020-02-09T09:59:53","slug":"deployment-gitlab-ci","status":"publish","type":"post","link":"https:\/\/florianbrinkmann.com\/en\/deployment-gitlab-ci-3473\/","title":{"rendered":"Auto deployment to staging and production server with GitLab CI"},"content":{"rendered":"\n

I recently started to deal with Continuous Integration (CI). With that, you can automatically run tasks after pushing to a version control repo, like code testing or deployment. This post shows you how a deployment process to staging and production server can look with GitLab.<\/p>\n\n\n\n\n\n\n\n

In contrast to GitHub, GitLab<\/a> comes with an integrated CI, which also can be used in the free plan. My website\u2019s theme is versioned with git. There is a local version, one on a staging server and one on this server. With GitLab CI I can automatically push the theme to the staging or production server after pushing a commit \u2014 depending on whether the push goes to the staging<\/code> or master<\/code> branch. So I do not need to upload files to different servers via SFTP but can handle that via Git.<\/p>\n\n\n\n

To use the CI in the free GitLab account, you just have to create a .gitlab-ci.yml<\/code> at the top level of your repo (in a self-hosted GitLab instance, you may have to create a runner first). This file stores the information, what the CI should do after a commit. We want to realize the following:<\/p>\n\n\n\n

After pushing to the staging<\/code> branch:<\/strong><\/p>\n\n\n\n

  1. Connect to the staging server.<\/li>
  2. Create a temporary theme folder.<\/li>
  3. Copy the theme files from the git repo to the temp directory on the staging server.<\/li>
  4. Move the now-outdated theme to another temp folder.<\/li>
  5. Move the new state of the theme to the theme folder.<\/li>
  6. Remove the temp folder with the old theme files.<\/li><\/ol>\n\n\n\n

    The way with the temporary directories is taken from the GitLab documentation<\/a> and ensures that there is no short timeframe, where no working theme is on the server.<\/p>\n\n\n\n

    After pushing to the master<\/code> branch:<\/strong><\/p>\n\n\n\n

    This process looks almost exactly like the one for the staging. The only difference is the connection to the production server.<\/p>\n\n\n\n

    Creating the .gitlab-ci.yml<\/code> for deployment<\/h2>\n\n\n\n

    Like already said, the .gitlab-ci.yml<\/code> stores the information for the CI jobs. The tasks can be grouped into jobs which can run in different stages. We only need a deploy<\/code> stage and the jobs deploy_staging<\/code> and deploy_production<\/code>. The beginning of the file looks like that:<\/p>\n\n\n

    stages:\n - deploy\n\ndeploy_staging:\n stage: deploy\n image: tetraweb\/php:7.1<\/span>\n before_script:\n - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'<\/span>\n - mkdir -p ~\/.ssh\n - eval<\/span> $(ssh-agent -s)\n - '[[ -f \/.dockerenv ]] && echo -e \"Host *\\n\\tStrictHostKeyChecking no\\n\\n\" > ~\/.ssh\/config'<\/span>\n - ssh-add <(echo<\/span> \"$STAGING_PRIVATE_KEY\"<\/span>)\n - apt-get install rsync\n script:\n - ssh -p22 user@staging.example.com \"mkdir -p \/html\/wp-content\/themes\/_tmp\"<\/span>\n - rsync -rav -e ssh --exclude='.git\/'<\/span> --exclude='.gitlab-ci.yml'<\/span> --delete-excluded .\/ user@staging.example.com:\/html\/wp-content\/themes\/_tmp\n - ssh -p22 user@staging.example.com \"mv \/html\/wp-content\/themes\/fbn \/html\/wp-content\/themes\/_old && mv \/html\/wp-content\/themes\/_tmp \/html\/wp-content\/themes\/fbn\"<\/span>\n - ssh -p22 user@staging.example.com \"rm -rf \/html\/wp-content\/themes\/_old\"<\/span>\n only:\n - staging<\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

    The script<\/code> part of the job connects to the staging server via SSH and creates the _tmp<\/code> directory in the theme folder if it not already exists. After that, we move the files from the git repo to this folder via rsync<\/code>. Then the folder moving is done. With only<\/code> we can specify a branch for which the job should be done \u2014 otherwise, the job would be run after each push because the file is in the master<\/code> branch, too.<\/p>\n\n\n\n

    The deployment job looks much like the above one (just insert the code after the code of the staging job):<\/p>\n\n\n

    deploy_production:\n stage: deploy\n image: tetraweb\/php:7.1<\/span>\n before_script:\n - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'<\/span>\n - mkdir -p ~\/.ssh\n - eval<\/span> $(ssh-agent -s)\n - '[[ -f \/.dockerenv ]] && echo -e \"Host *\\n\\tStrictHostKeyChecking no\\n\\n\" > ~\/.ssh\/config'<\/span>\n - ssh-add <(echo<\/span> \"$STAGING_PRIVATE_KEY\"<\/span>)\n - apt-get install rsync\n script:\n - ssh -p22 user@example.com \"mkdir -p \/html\/wp-content\/themes\/_tmp\"<\/span>\n - rsync -rav -e ssh --exclude='.git\/'<\/span> --exclude='.gitlab-ci.yml'<\/span> --delete-excluded .\/ user@example.com:\/html\/wp-content\/themes\/_tmp\n - ssh -p22 user@example.com \"mv \/html\/wp-content\/themes\/fbn \/html\/wp-content\/themes\/_old && mv \/html\/wp-content\/themes\/_tmp \/html\/wp-content\/themes\/fbn\"<\/span>\n - ssh -p22 user@example.com \"rm -rf \/html\/wp-content\/themes\/_old\"<\/span>\n only:\n - master<\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

    Because the before_script<\/code> part is equal to both jobs, we could also move it outside the jobs at the level of stages<\/code>.<\/p>\n\n\n\n

    And that is it.<\/p>\n","protected":false},"excerpt":{"rendered":"

    I recently started to deal with Continuous Integration (CI). With that, you can automatically run tasks after pushing to a version control repo, like code testing or deployment. This post shows you how a deployment process to staging and production server can look with GitLab.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","lazy_load_responsive_images_disabled":false},"categories":[3],"tags":[],"wp-worthy-pixel":{"ignored":false,"public":"690f08bf21104781b4529c3012c5d752","server":"vg07.met.vgwort.de","url":"https:\/\/vg07.met.vgwort.de\/na\/690f08bf21104781b4529c3012c5d752"},"wp-worthy-type":"normal","_links":{"self":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/3473"}],"collection":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/comments?post=3473"}],"version-history":[{"count":4,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/3473\/revisions"}],"predecessor-version":[{"id":5899,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/3473\/revisions\/5899"}],"wp:attachment":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/media?parent=3473"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/categories?post=3473"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/tags?post=3473"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}

    At first, we define the deploy<\/code> stage and the job deploy_staging<\/code>, who is responsible for pushing changes to the staging server. This job is part of the deploy<\/code> stage and uses the docker image tetraweb\/php:7.1<\/code>. With before_script<\/code>, we can run tasks before the actual script \u2014 in our case the preparation for the SSH connection and install the rsync<\/code> package. The SSH steps are again from the GitLab docs<\/a>. To get it working, you have to set a variable STAGING_PRIVATE_KEY<\/code> in GitLab which stores the private SSH key.<\/p>\n\n\n\n