From afa821252f8fa90076e39cd6fec178b55aa4cd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Borbo=C3=ABn?= <ponsfrilus@gmail.com> Date: Mon, 2 Sep 2019 11:05:16 +0200 Subject: [PATCH] Finishing * clean up and comment the deploy.sh script * improve the README.md with more steps and the project's pipeline image * remove extra except in the gitlab-ci.yml file --- .gitignore | 1 + .gitlab-ci.yml | 6 ---- README.md | 79 +++++++++++++++++++++++++++++++++++++++---- deploy.sh | 46 +++++++++++-------------- project_pipeline.jpg | Bin 0 -> 16583 bytes 5 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 project_pipeline.jpg diff --git a/.gitignore b/.gitignore index 8d1ef24..e1790ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ priv +README.pdf diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 520208c..39d6c2e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,6 @@ test: stage: test except: - tags - - pdf@idevelop/howtogitlabrunner script: - whoami - pwd @@ -26,12 +25,10 @@ test: - echo $MY_TOKEN - echo $(date '+%Y%m%d') - build: stage: build except: - tags - - pdf@idevelop/howtogitlabrunner script: - echo "pandocify README" - pandoc README.md -o README.pdf @@ -44,7 +41,6 @@ create_release: stage: deploy except: - tags - - pdf@idevelop/howtogitlabrunner script: - ls -al * - sh deploy.sh @@ -53,7 +49,6 @@ deploy_to_production: stage: deploy except: - tags - - pdf@idevelop/howtogitlabrunner when: manual allow_failure: false script: echo "Manual input needed to deploy to production..." @@ -62,5 +57,4 @@ control: stage: control except: - tags - - pdf@idevelop/howtogitlabrunner script: echo "control, cleanup and finish the pipeline" diff --git a/README.md b/README.md index 6166352..34b20a4 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,41 @@ The goal of this project is to demonstrate how to use [GitLab-Runner](https://docs.gitlab.com/runner) with a very simple use case: transform this README.md into README.pdf with [pandoc](https://pandoc.org/). -## Setup the runner + +## Preamble + +Doing this demo taught us some important lesson: we first tried to build the +PDF and push it into this repo. While it seems feasible, it not a good idea. +First of all, you want to keep your commits atomic, meaning that you don't want +a commit creates a new commit automatically. Secondly, the CI/CD mechanisms is +here to build and/or deploy a specific commit to other systems/environment, not +modifying the current one. Finally, we choose to do it the following way: + + 1. One modify the README.md file; + 1. It trigger the build to create the README.pdf file; + 1. Then, a release is created, association the README.pdf file to it. + +That way, the things are kept as close as possible to a standard dev/stage/prod +environment. + +Before diving into it, you may want to check the +[documentation](https://docs.gitlab.com/ce/ci/README.html) and some [real world +examples](https://docs.gitlab.com/ce/ci/examples/README.html). + + +## Setup the "shell" runner First of all, you need a runner on a machine. The installation process is described here: https://docs.gitlab.com/runner/install/ -It create the gitlab-runner user on your system. On some Linux, you may want to +It creates the gitlab-runner user on your system. On some Linux, you may want to remove the `~/.bash_logout` file to avoid the issue [4092](https://gitlab.com/gitlab-org/gitlab-runner/issues/4092) — thanks me later. -The last step explain how to register the runner with `sudo gitlab-runner -register`. Answer the questions (the runner key is found on your project > -settings > CI/CD > runner). You may want to check the runner options (for +The last step explains how to register the runner with `sudo gitlab-runner +register`. Answer the questions (the runner key is found on your Project > +Settings > CI/CD > Runner). You may want to check the runner options (for example "Run untagged jobs"). It will create the `/etc/gitlab-runner/config.toml` file which looks like that: @@ -38,4 +60,49 @@ check_interval = 0 [runners.cache.gcs] ``` -## next +## Check and edit the `.gitlab-ci.yaml` + +The whole pipeline/stage/job configuration stands in the `.gitlab-ci.yaml` file. +A lot of documentation is provided by GitLab: https://docs.gitlab.com/ce/ci/. + +In short, this file defines the pipeline process, which contain stages. A stage +can contains jobs, which describes what to do. Jobs can be parallel and manual. + +Note that if you want to keep "produced" files between jobs, you have to use the +artifacts system. + +The file of this project define 4 stages: + - test → just prints out some env variables. + - build → creates the README.pdf. + - deploy → creates the release via the `deploy.sh` script. Need a variable. + - control → final steps, does nothing but echoing a string. + + + + + +## Set the variable + +To be able to use the API to create a release, you need to have a [Personal +Access +Tokens](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) +which can be created in your [personal +account](https://gitlab.epfl.ch/profile/personal_access_tokens). In the +`deploy.sh` script, it's used as the `MY_TOKEN` variable. Once you get the +token, there are 2 ways to use it: + + - define it into the project (Project > Settings > CI/CD > Variables), which + means that other persons of the project will use it. + - define it when manually launching the pipeline: only you will be able to + use it but automatic trigger will fail. + +This is OK for this demo or small projects, but a better way to do it is to +create a "bot" user to whom you give some right on your repository and create +the personal access token from it. + +## Observe + +Now it time that you try out the pipeline. Either change something to this file +(I'm sure there's a lot of errors and missing/incorrect information) and push to +the repo, or go into your project CI/CD pipelines and use the "Run pipeline" +button. diff --git a/deploy.sh b/deploy.sh index 7aadf6f..5be38d3 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,49 +1,42 @@ #!/usr/bin/env bash -PROJECT_ID=1298 +# +# This script create a release containg the README.pdf file from the +# https://gitlab.epfl.ch/idevelop/howtogitlabrunner/ project. +# set -e -x -env +PROJECT_ID=1298 -echo "Hello from deploy.sh" +# Print out the environment +# env +echo "Hello from deploy.sh" echo "My token is $MY_TOKEN" +# Should display "gitlab-runner" whoami -# -# -# eval `ssh-agent` -# -# ssh-add ~/.ssh/id_rsa -# -# ssh-add -l -# ssh-add -L - -# git clone git@gitlab.epfl.ch:idevelop/howtogitlabrunner.wiki.git wiki -# cp README.pdf wiki/public/ -# echo \n$(date '+%Y-%m-%d %H:%M:%S') >> wiki/home.md -# cd wiki -# git config user.email "gitlab@epfl.ch" -# git config user.name "gitlab-runner" -# git commit -am "New PDF file $(date '+%Y-%m-%d %H:%M:%S')" -# git push --push-option=ci.skip origin master -# https://docs.gitlab.com/ee/ci/yaml/README.html#skipping-jobs + +# Create a release name RELEASE_V=v$(date '+%s') echo "Creating release: ${RELEASE_V}" + +# Tagging the version git tag -f -a -m "My sweet release $(date '+%Y-%m-%d %H:%M:%S')" ${RELEASE_V} -echo "END OF TAGGING" + +# Be sure to have a remote to push to git remote set-url origin "git@gitlab.epfl.ch:idevelop/howtogitlabrunner.git" +# And actually push the tags git push -f --tags -echo "END OF PUSH" +# @domq's trick to be able to see the curl response in stderr tee_stderr="perl -pe STDERR->print(\$_);warn(\$!)if(\$!)" -echo "BEGIN UPLOAD" +# Uploading the README.pdf file UPLOADED_FILE_URL="$(curl --request POST --header "Private-Token: ${MY_TOKEN}" --form "file=@README.pdf" "https://gitlab.epfl.ch/api/v4/projects/$PROJECT_ID/uploads" | $tee_stderr | jq -r .url)" -echo "END UPLOAD" -echo "BEGIN POST RELEASE" +# Create the release until it finally work... for retry in $(seq 1 5); do if curl --request POST \ --header 'Content-Type: application/json' \ @@ -57,4 +50,3 @@ for retry in $(seq 1 5); do sleep 5 fi done -echo "END POST RELEASE" diff --git a/project_pipeline.jpg b/project_pipeline.jpg new file mode 100644 index 0000000000000000000000000000000000000000..95fa2a39695a6c06a19dfccfcb0d408d10d16421 GIT binary patch literal 16583 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 L0MAA8|Gx<U{405z literal 0 HcmV?d00001 -- GitLab