PipelineLib/Tutorial/TestingHelloNode using multiple stages
Welcome to Testing Hello Node (with multiple stages), a slightly more advanced PipelineLib tutorial than the previous Testing Hello Node tutorial.
Prerequisites
- An understanding of the basic PipelineLib concepts introduced in Testing Hello Node as well as the prerequisites mentioned there.
Learning objectives
At the end of this tutorial, you wll understand:
- How to define more re-usable Blubber variants.
- How to split up discrete CI build, test, and lint tasks into multiple pipeline stages that execute serially.
- How to pass arguments to image variants.
- How to utilize output from one pipeline stage in subsequent stages.
Tutorial
The basic steps to this tutorial will be to:
- Set up the example repo.
- Modify the repo's existing Blubber configuration to provide a more generic test runner variant.
- Define a pipeline stage that builds the test-runner variant.
- Define additional pipeline stages that execute the test-runner's
lint
andtest
scripts. - Submit your change to Gerrit.
- Observe how Jenkins executes the project's multi-stage
test
pipeline. - Clean up.
Set up the example repo
dev@laptop:~$ git clone ssh://gerrit.wikimedia.org:29418/blubber-doc/example/helloworldoid blubber-doc/example/helloworldoid
dev@laptop:~$ cd blubber-doc/example/helloworldoid
dev@laptop:helloworldoid$ curl -o .git/hooks/commit-msg https://gerrit.wikimedia.org/r/tools/hooks/commit-msg
dev@laptop:helloworldoid$ chmod +x .git/hooks/commit-msg
Modify the existing test variant to run linters
In the previous tutorial, we instructed our CI system to execute the project's test variant. Let's look again at that variant in .pipeline/blubber.yaml
.
dev@laptop:helloworldoid$ grep -A 2 ' test:' .pipeline/blubber.yaml
test:
includes: [build]
entrypoint: [npm, test]
Again, it's a really simple variant that simply runs npm test
, which in NPM speak is really just a shortcut for npm run-script test
.
Does the project perhaps have another script defined in package.json
for running its linters?
dev@laptop:helloworldoid$ grep -A 4 scripts package.json
"scripts": {
"lint": "eslint --ignore-path .gitignore .",
"systemtest": "mocha ./test/system.js",
"test": "mocha ./test/unit.js"
},
Indeed it does, and the script is creatively named lint
.
At this point, we have options. One would be to copy/paste the existing test variant and change [npm, test]
to [npm, run-script, lint]
. But what if there are other scripts to run in the future—like that temptatious systemtest
one? We'd eventually end up with a bunch of strikingly similar image variants for doing almost the same thing aside from differences between a single argument.
Let's instead modify the existing test variant to be able to run any project script. In .pipeline/blubber.yaml
, change this.
test:
includes: [build]
entrypoint: [npm, test]
To this.
script:
includes: [build]
entrypoint: [npm, run-script]
A very generic variant called script
for running any script defined in package.json
. Wee.
Define a stage to build the script runner
On to CI orchestration! It's what we're really here for after all.
Similar to how we defined our single build-and-run-tests stage in the previous tutorial, we're going to open up .pipeline/config.yaml
and define a single stage for now.
dev@laptop:helloworldoid$ vim .pipeline/config.yaml # or emacs; flame on
pipelines:
test:
blubberfile: blubber.yaml
stages:
- name: build
build: script
Line by line, we are:
- Defining a new pipeline named
test
. - Telling CI where our image variants are defined.
- Defining a new stage in our pipeline called
build
. - Specifying that we'd like that stage to build our generic
script
image variant.
run: true
below line 6. We don't want to execute the script-runner variant in this stage, just build it.Define additional test and lint stages
Now that we have a stage that will build our generic script-runner image, let's create two more stages for the dual purposes of running our unit tests and running our linter using that image variant.
pipelines:
test:
blubberfile: blubber.yaml
stages:
- name: build
build: script
- name: test
run:
image: '${build.imageID}'
arguments: [test]
- name: lint
run:
image: '${build.imageID}'
arguments: [lint]
Line by line, we are:
- Defining a new stage in our pipeline called
test
. - Specifying that this stage should run the image built in the
build
stage (script
) and pass it the argumenttest
. - Defining a new stage in our pipeline called
lint
. - Specifying that this stage should run the image built in the
build
stage (script
) and pass it the argumentlint
.
${build.imageID}
placeholders. You can read a lot more about those in PiplineLib concepts, but essentially they are replaced with the ID of the image built in the build
stage.Submit our change and watch our pipeline execute
Now that we have a .pipeline/config.yaml
that will tell our CI system to (serially) execute both our project's unit-test suite and linter using a common script-runner image, let's push up our change up to Gerrit and watch Jenkins run our three-stage test pipeline.
dev@laptop:helloworldoid$ git add .pipeline/{blubber,config}.yaml
dev@laptop:helloworldoid$ git commit -m 'tutorial2: Configure CI to run tests and linters'
dev@laptop:helloworldoid$ git push origin HEAD:refs/for/master
Head over to the helloworldoid-pipeline-test job to see its progress!
Clean up and move on
Help us to keep Gerrit tidy by abandoning your change!
If you're ready to learn more about PipelineLib, move on to the next tutorial, Testing Hello Node (using parallel_execution). Otherwise, you can reset your local working copy to origin/master
or commit your changes in a branch—whatever you like!