The pipelines in this repository follow opinionated best practices. They are documented here for reference and easier debugging.
And there's a ca. 20 minute video walkthrough explaining the pipelines and why I organize them this way:
youtube.com/watch?v=e0bF1LlclEs
The main pipelines sit in this azure-pipelines
directory and use subfolders for templating.
├── README.md
├── production.yaml
├── dev.yaml
├── jobs
│ ├── app-service.yaml
│ ├── asset-pipeline.yaml
│ ├── docker.yaml
│ └── tests.yaml
├── steps
│ └── debug-vars.yaml
└── vars
├── dev.yaml
├── global.yaml
└── prod.yaml
The jobs and steps are intended for local use only and thus do not require parameters:
definitions at each job scope. Therefore any varable referenced in a job, e.g. variables.isProduction
can be found at a higher scope.
Due to the sheer amount of variables set, the conditionals and global defaults are set in vars/global.yaml
.
# Global scope
trigger:
- main
variables:
- template: vars/global.yaml
Note: this is no longer used since switching to environment specific pipelines, but kept here for reference.
Some parameters, e.g. app name are dependent on the deployment target environment. Using conditionals at stages:
scope, the defaults are overwritten.
- stage: StageName
variables:
- ${{ if eq(variables.isMain, 'True') }}:
- template: vars/dev.yaml
- ${{ if eq(variables.isProduction, 'True') }}:
- template: vars/prod.yaml
Important: Environment specific variables must be set in a non-root, e.g. downstream scope.
Please also see Docker Images section, which describes the git tag trigger.
Pipeline | Branch Triggers | Pull Request Triggers | Deployment |
---|---|---|---|
dev.yaml |
• main • feat/* • fix/* |
main |
Dev |
production.yaml |
• production |
(none) | Production |
Pull Requests only runs tests and does not build any images. The YAML pedanticly excludes forks, pull requests and scheduled runs. In this manner only git merge
events, which requires human intervention will trigger deployments. This is configured using branch production configurations.
See vars/global.yaml
for details:
# Excerpt
variables:
isFork: ${{ eq(variables['System.PullRequest.IsFork'], 'True') }}
isPR: ${{ eq(variables['Build.Reason'], 'PullRequest') }}
isScheduled: ${{ eq(variables['Build.Reason'], 'Schedule') }}
isTrustedCI: ${{ and( eq(variables.isFork,'False'), eq(variables.isPR,'False'), eq(variables.isScheduled,'False') ) }}
-
Image Tag
Refers to version of particular image, e.g.0.1.0
orlatest
-
Docker Tag
Refers to full image name that includes private registry hostname, for example:
nodejsdemo.azurecr.io/azure-nodejs-demo:0.1.0
- Docker Images are only built in the
dev.yaml
CI pipeline. - Production-ready images are locked (Azure Container Registry specific feature) and thus immutable.
Triggers | Image Tag | Immutable |
---|---|---|
main branch |
dev-$(git rev-parse --short HEAD) |
- |
v* tag |
e.g. 0.1.0 |
True |
Deployment pipeline only deploys existing images, which have passed previous stages and are thus assumed valid and secure.
Branch | Image Tag |
---|---|
main |
dev |
production |
Version per package.json |