{"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<p>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<!--more-->\n\n\n\n<p>In contrast to GitHub, <a href=\"https:\/\/gitlab.com\/\">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 <code class=\"lang-markup\">staging<\/code> or <code class=\"lang-markup\">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<p>To use the CI in the free GitLab account, you just have to create a <code class=\"lang-markup\">.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<p><strong>After pushing to the <code class=\"lang-markup\">staging<\/code> branch:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Connect to the staging server.<\/li><li>Create a temporary theme folder.<\/li><li>Copy the theme files from the git repo to the temp directory on the staging server.<\/li><li>Move the now-outdated theme to another temp folder.<\/li><li>Move the new state of the theme to the theme folder.<\/li><li>Remove the temp folder with the old theme files.<\/li><\/ol>\n\n\n\n<p>The way with the temporary directories <a href=\"https:\/\/docs.gitlab.com\/ce\/ci\/examples\/deployment\/composer-npm-deploy.html#how-to-deploy-things\">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<p><strong>After pushing to the <code class=\"lang-markup\">master<\/code> branch:<\/strong><\/p>\n\n\n\n<p>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<h2 class=\"wp-block-heading\">Creating the <code class=\"lang-markup\">.gitlab-ci.yml<\/code> for deployment<\/h2>\n\n\n\n<p>Like already said, the <code class=\"lang-markup\">.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 <code class=\"lang-markup\">deploy<\/code> stage and the jobs <code class=\"lang-markup\">deploy_staging<\/code> and <code class=\"lang-markup\">deploy_production<\/code>. The beginning of the file looks like that:<\/p>\n\n\n<pre class=\"wp-block-code lang-yaml\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">stages:\n  - deploy\n\ndeploy_staging:\n  stage: deploy\n  image: tetraweb\/php:<span class=\"hljs-number\">7.1<\/span>\n  before_script:\n    - <span class=\"hljs-string\">'which ssh-agent || ( apt-get update -y &amp;&amp; apt-get install openssh-client -y )'<\/span>\n    - mkdir -p ~\/.ssh\n    - <span class=\"hljs-keyword\">eval<\/span> $(ssh-agent -s)\n    - <span class=\"hljs-string\">'&#91;&#91; -f \/.dockerenv ]] &amp;&amp; echo -e \"Host *\\n\\tStrictHostKeyChecking no\\n\\n\" &gt; ~\/.ssh\/config'<\/span>\n    - ssh-add &lt;(<span class=\"hljs-keyword\">echo<\/span> <span class=\"hljs-string\">\"$STAGING_PRIVATE_KEY\"<\/span>)\n    - apt-get install rsync\n  script:\n    - ssh -p22 user@staging.example.com <span class=\"hljs-string\">\"mkdir -p \/html\/wp-content\/themes\/_tmp\"<\/span>\n    - rsync -rav -e ssh --exclude=<span class=\"hljs-string\">'.git\/'<\/span> --exclude=<span class=\"hljs-string\">'.gitlab-ci.yml'<\/span> --delete-excluded .\/ user@staging.example.com:\/html\/wp-content\/themes\/_tmp\n    - ssh -p22 user@staging.example.com <span class=\"hljs-string\">\"mv \/html\/wp-content\/themes\/fbn \/html\/wp-content\/themes\/_old &amp;&amp; mv \/html\/wp-content\/themes\/_tmp \/html\/wp-content\/themes\/fbn\"<\/span>\n    - ssh -p22 user@staging.example.com <span class=\"hljs-string\">\"rm -rf \/html\/wp-content\/themes\/_old\"<\/span>\n  only:\n    - staging<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>At first, we define the <code class=\"lang-markup\">deploy<\/code> stage and the job <code class=\"lang-markup\">deploy_staging<\/code>, who is responsible for pushing changes to the staging server. This job is part of the <code class=\"lang-markup\">deploy<\/code> stage and uses the docker image <code class=\"lang-markup\">tetraweb\/php:7.1<\/code>. With <code class=\"lang-markup\">before_script<\/code>, we can run tasks before the actual script \u2014 in our case the preparation for the SSH connection and install the <code class=\"lang-markup\">rsync<\/code> package. <a href=\"https:\/\/docs.gitlab.com\/ce\/ci\/examples\/deployment\/composer-npm-deploy.html#how-to-transfer-files-to-a-live-server\">The SSH steps are again from the GitLab docs<\/a>. To get it working, you have to set a variable <code class=\"lang-markup\">STAGING_PRIVATE_KEY<\/code> in GitLab which stores the private SSH key.<\/p>\n\n\n\n<p>The <code class=\"lang-markup\">script<\/code> part of the job connects to the staging server via SSH and creates the <code class=\"lang-markup\">_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 <code class=\"lang-markup\">rsync<\/code>. Then the folder moving is done. With <code class=\"lang-markup\">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 <code class=\"lang-markup\">master<\/code> branch, too.<\/p>\n\n\n\n<p>The deployment job looks much like the above one (just insert the code after the code of the staging job):<\/p>\n\n\n<pre class=\"wp-block-code lang-yaml\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">deploy_production:\n  stage: deploy\n  image: tetraweb\/php:<span class=\"hljs-number\">7.1<\/span>\n  before_script:\n    - <span class=\"hljs-string\">'which ssh-agent || ( apt-get update -y &amp;&amp; apt-get install openssh-client -y )'<\/span>\n    - mkdir -p ~\/.ssh\n    - <span class=\"hljs-keyword\">eval<\/span> $(ssh-agent -s)\n    - <span class=\"hljs-string\">'&#91;&#91; -f \/.dockerenv ]] &amp;&amp; echo -e \"Host *\\n\\tStrictHostKeyChecking no\\n\\n\" &gt; ~\/.ssh\/config'<\/span>\n    - ssh-add &lt;(<span class=\"hljs-keyword\">echo<\/span> <span class=\"hljs-string\">\"$STAGING_PRIVATE_KEY\"<\/span>)\n    - apt-get install rsync\n  script:\n    - ssh -p22 user@example.com <span class=\"hljs-string\">\"mkdir -p \/html\/wp-content\/themes\/_tmp\"<\/span>\n    - rsync -rav -e ssh --exclude=<span class=\"hljs-string\">'.git\/'<\/span> --exclude=<span class=\"hljs-string\">'.gitlab-ci.yml'<\/span> --delete-excluded .\/ user@example.com:\/html\/wp-content\/themes\/_tmp\n    - ssh -p22 user@example.com <span class=\"hljs-string\">\"mv \/html\/wp-content\/themes\/fbn \/html\/wp-content\/themes\/_old &amp;&amp; mv \/html\/wp-content\/themes\/_tmp \/html\/wp-content\/themes\/fbn\"<\/span>\n    - ssh -p22 user@example.com <span class=\"hljs-string\">\"rm -rf \/html\/wp-content\/themes\/_old\"<\/span>\n  only:\n    - master<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Because the <code class=\"lang-markup\">before_script<\/code> part is equal to both jobs, we could also move it outside the jobs at the level of <code class=\"lang-markup\">stages<\/code>.<\/p>\n\n\n\n<p>And that is it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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":{"wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","webmentions_disabled_pings":false,"webmentions_disabled":false,"lazy_load_responsive_images_disabled":false,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-3473","post","type-post","status-publish","format-standard","hentry","category-tips"],"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","targetHints":{"allow":["GET"]}}],"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}]}}