Using Concourse CI/CD to publish Helm charts to ChartMuseum and report results to Slack (part #2 — reusable tasks)

In the previous post we discussed how one can build Concourse CI/CD pipeline to build and publish Helm charts to ChartMuseum with Slack notifications. Check part #1 if you haven’t seen it yet.

Now, let’s see how we can extend this pipeline, leveraging external tasks in Concourse 5.0 to create reusable units of work across your CI/CD pipelines.

Yay! External tasks in your declarative CI/CD.

Adding Helm chart validation to our pipeline

It might be a good idea to run Helm linter prior to publishing the chart, just to ensure that chart passes basic validation before it gets published to ChartMuseum.

Let’s add another job called validate-chart (which runs helm lint ./consul) and make publish-chart run only on git commits which pass validate-chart job.

The complete pipeline can be found at and can be imported using:

$ git clone
$ cd concourse-pipelines/consul-chart-blog
$ fly -t target set-pipeline -c 03_with_validation.yaml -p publish-consul-chart -l params.yaml
$ fly -t target unpause-pipeline -p publish-consul-chart

After that you will see two jobs instead of one in Concourse UI:

And you can see that Helm linter is called successfully:

Introducing reusable tasks

So far so good. But if you keep adding more jobs and tasks to the pipeline, pipeline config will quickly become very repetitive. It will be hard to read and understand for your development teams too.

The situation may get even worse once you start pulling images from your company’s private DockerHub registry. It will force you to pass way more parameters to every task (repository, username, password, etc) — see discussion in #454 for more background.

At this point it’s a good idea to introduce reusable tasks, which can be:

  1. stored in git and retrieved when a pipeline runs
  2. iterated on and modified without changing the pipeline
  3. tested via fly before pushing changes to git
  4. reused between different pipelines

To demonstrate how this works, we will create an external task YAML for chart validation, add two parameters into it — ((image)) and ((chart-name)) along with some statements to print debug information:

Testing external tasks

Now, let’s test this task by telling Concourse to run this task as a one-off build, taking Helm chart git repo from ‘publish-consul-chart’ pipeline and variable values from the command line:

$ fly -t target execute \
-c tasks/validate_helm_chart.yaml \
-v chart-name=consul \
-v image=dtzar/helm-kubectl \


Important Note: ability to specify task template variables for interpolation via fly is a new feature in Concourse 5.0. This will not work in prior versions as fly execute doesn’t support -v and a few other essential flags. See

You can see that the task, which we just extracted into a separate file, works just fine on its own:

Simplifying the pipeline

Now it’s time to simplify the main pipeline by calling our external task from it (instead of embedding task definition into the pipeline itself, like we’ve done before).

For that, we need to introduce a git resource, so that our pipeline can retrieve repository with tasks:

And then change the corresponding part of the job to call our external task, passing ((image)) and ((chart-name)) from the main pipeline down to the task using vars:

The complete pipeline is available at and you can test it by running:

$ fly -t target set-pipeline -c 04_external_task.yaml -p publish-consul-chart -l params.yaml

Important Note: ability to pass template variables from pipeline down to the task is is a new feature in Concourse 5.0. This will not work in prior versions.

After you trigger the job, you can see that it runs successfully and makes a call to an external task retrieved from the corresponding git repo:

External tasks do work with credential managers as well. If there is a task variable which is not interpolated through pipeline vars, it will be tried to be interpolated through a credential manager configured in Concourse.

Exercise for the reader

Can you improve the pipeline even further and create another reusable task, which will produce colored Slack messages for success and failure cases?


  • Use Slack attachments
  • Task should generate Slack message/attachment and store them in files as its output
  • Use text_file and attachments_file parameters of Slack notification resource to read them back from files
  • Use Concourse do step if you want to execute multiple steps in on_failure (i.e. generate message, then send it)


This is a very simple example, but it gives you an idea that Concourse 5.0 (Q1 2019 release date) has better support for reusable tasks.

Having all your steps embedded into a CI/CD pipeline may quickly lead to the 500+ lines of YAML code which will be difficult to navigate.

With external tasks and template variables, you can:

  • reuse the same task from several CI/CD pipelines
  • change task implementation while keeping your pipeline intact
  • test task changes without breaking existing pipelines
  • debug tasks locally with different parameters and even different container images


If you have any questions, please don’t hesitate to comment below or reach out to me directly via