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 and test 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.
== 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 argument test.
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 argument lint.
== 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!