|
2 | 2 | title: Multi-Branch Workflows |
3 | 3 | --- |
4 | 4 |
|
| 5 | +Teams may make use of multiple branches for their development. For instance, some teams create feature branches while |
| 6 | +working on new functionality - once this functionality is ready, the branch will be merged into the main branch and the |
| 7 | +feature branch will be deleted. |
| 8 | + |
| 9 | +While a feature is under development, you'll often want to run tests against the feature branch and possibly deploy to a |
| 10 | +staging environment. To model this in Concourse, you'll need to have a pipeline for each active feature branch. Manually |
| 11 | +setting (and eventually archiving) a pipeline for each feature branch would be quite a burden. For this type of |
| 12 | +workflow, Concourse has a few important tools to help you out: the [`set_pipeline` step](../../steps/set-pipeline.md), [ |
| 13 | +`across`](../../steps/modifier-and-hooks/across.md), and [instanced pipelines](../../pipelines/grouping-pipelines.md). |
| 14 | + |
| 15 | +!!! warning "Experimental Feature" |
| 16 | + |
| 17 | + [`across`](../../steps/modifier-and-hooks/across.md) and [instanced |
| 18 | + pipelines](../../pipelines/grouping-pipelines.md) are both experimental features, and must be enabled with the |
| 19 | + feature flags `CONCOURSE_ENABLE_ACROSS_STEP` and `CONCOURSE_ENABLE_PIPELINE_INSTANCES`, respectively. |
| 20 | + |
| 21 | +In this guide, we'll cover: |
| 22 | + |
| 23 | +1. Writing a pipeline to [Test, Build & Deploy](#test-build-deploy) a branch to a staging environment. We'll |
| 24 | + use [Terraform](https://www.terraform.io/) for our deployment |
| 25 | +2. [Tracking Branches](#tracking-branches) in a repository; for each branch, we'll set a pipeline (using the [ |
| 26 | + `set_pipeline` step](../../steps/set-pipeline.md) and [across](../../steps/modifier-and-hooks/across.md)) |
| 27 | +3. Automatically [Cleaning Up Old Workspaces](#cleaning-up-old-workspaces) after branches get merged or deleted |
| 28 | + |
5 | 29 | ## Test, Build & Deploy |
6 | 30 |
|
| 31 | +We'll start out by defining the pipeline that should run for each active branch. For this example, we'll be working with |
| 32 | +the following [sample Go application](https://github.com/concourse/examples/tree/master/apps/golang). |
| 33 | + |
| 34 | +Our pipeline will have three stages: |
| 35 | + |
| 36 | +1. Run unit tests |
| 37 | +2. Build and upload a binary to a blobstore (in our case, we'll |
| 38 | + use [Google Cloud Storage](https://cloud.google.com/storage)) |
| 39 | +3. Trigger a `terraform apply` to deploy our app to a staging environment. |
| 40 | + The [Terraform module](https://github.com/concourse/examples/blob/master/terraform/staging/main.tf) we'll use here |
| 41 | + doesn't actually provision any infrastructure, and is just used as an example |
| 42 | + |
| 43 | +Since the pipeline config is intended to be used as a template for multiple different branches, we can |
| 44 | +use [Vars](../../vars.md) to parameterize the config. In particular, we'll use the vars `((feature))` and `((branch))`, |
| 45 | +which represent the name of the feature and the name of the branch, respectively. |
| 46 | + |
| 47 | +Below is the full pipeline config from |
| 48 | +the [Examples Repo](https://github.com/concourse/examples/blob/master/pipelines/multi-branch/template.yml): |
| 49 | + |
| 50 | +```yaml linenums="1" title="template.yml" |
| 51 | +--8<-- "libs/examples/pipelines/multi-branch/template.yml" |
| 52 | +``` |
| 53 | + |
7 | 54 | ## Tracking Branches |
8 | 55 |
|
9 | | -## Cleaning Up Old Workspaces |
| 56 | +In addition to the branch pipeline template, we'll also need a pipeline to track the list of branches and set a pipeline |
| 57 | +for each one. |
| 58 | + |
| 59 | +To track the list of branches in a repository, we can use [ |
| 60 | +`aoldershaw/git-branches-resource`](https://github.com/aoldershaw/git-branches-resource). This [ |
| 61 | +`resource_type`](../../resource-types/index.md) emits a new [resource version](../../resources/resource-versions.md) |
| 62 | +whenever a branch is created or deleted. It also lets us filter the list of branches by a regular expression. In this |
| 63 | +case, let's assume our feature branches match the regular expression `feature/.*`. |
| 64 | + |
| 65 | +Below is the current pipeline config for this tracker pipeline: |
| 66 | + |
| 67 | +```yaml linenums="1" title="tracker.yml" |
| 68 | +resource_types: |
| 69 | + - name: git-branches |
| 70 | + type: registry-image |
| 71 | + source: |
| 72 | + repository: aoldershaw/git-branches-resource |
| 73 | + |
| 74 | +resources: |
| 75 | + - name: feature-branches |
| 76 | + type: git-branches |
| 77 | + source: |
| 78 | + uri: https://github.com/concourse/examples |
| 79 | + # The "(?P<name>pattern)" syntax defines a named capture group. |
| 80 | + # aoldershaw/git-branches-resource emits the value of each named capture |
| 81 | + # group under the `groups` key. |
| 82 | + # |
| 83 | + # e.g. feature/some-feature ==> {"groups": {"feature": "some-feature"}} |
| 84 | + branch_regex: 'feature/(?P<feature>.*)' |
| 85 | + |
| 86 | + - name: examples |
| 87 | + type: git |
| 88 | + source: |
| 89 | + uri: https://github.com/concourse/examples |
| 90 | + |
| 91 | +jobs: |
| 92 | + - name: set-feature-pipelines |
| 93 | + plan: |
| 94 | + - in_parallel: |
| 95 | + - get: feature-branches |
| 96 | + trigger: true |
| 97 | + - get: examples |
| 98 | + - load_var: branches |
| 99 | + file: feature-branches/branches.json |
| 100 | + - across: |
| 101 | + - var: branch |
| 102 | + values: ((.:branches)) |
| 103 | + set_pipeline: dev |
| 104 | + file: examples/pipelines/multi-branch/template.yml |
| 105 | + instance_vars: { feature: ((.:branch.groups.feature)) } |
| 106 | + vars: { branch: ((.:branch.name)) } |
| 107 | +``` |
| 108 | +
|
| 109 | +We set each pipeline as an [instanced pipeline](../../pipelines/grouping-pipelines.md) - this will result in Concourse |
| 110 | +grouping all the related `dev` pipelines in the UI. |
| 111 | + |
| 112 | +## Cleaning Up Old Workspaces |
| 113 | + |
| 114 | +With the setup described in [Tracking Branches](#tracking-branches), Concourse will automatically archive any pipelines |
| 115 | +for branches that get removed. However, Concourse doesn't know that it should destroy Terraform workspaces when a branch |
| 116 | +is removed. To accomplish this, we can yet again make use of |
| 117 | +the [Terraform resource](https://github.com/ljfranklin/terraform-resource) to destroy these workspaces. We'll add |
| 118 | +another job to |
| 119 | +the [tracker pipeline](https://github.com/concourse/examples/blob/master/pipelines/multi-branch/tracker.yml) that |
| 120 | +figures out which workspaces don't belong to an active branch and destroy them. |
| 121 | + |
| 122 | +```yaml linenums="1" title="template.yml" |
| 123 | +--8<-- "libs/examples/pipelines/multi-branch/tracker.yml" |
| 124 | +``` |
0 commit comments