Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXLVIII · Conditional ExecutionTriggers

Tag and environment conditions — controlling which events touch which environments

Intermediate⏱ ~20 mingit

What you'll learn

  • Write `tags:` filters in GitHub Actions and the equivalent in GitLab CI
  • Use `environment:` to bind a job to a named deployment target
  • Distinguish tag-triggered deploys from branch-triggered deploys in an audit log
  • Recognise why production deploys must be tag-driven, not push-to-main-driven

Prerequisites

Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x

Not yet marked complete on this device.

A tag is a permanent name attached to a specific commit. A push event is a transient notification. The difference matters: a deploy triggered by a tag points at a specific, recoverable, signable artifact; a deploy triggered by a push to main points at whatever main happens to be at that moment.

Tag triggers

on:
  push:
    tags:
      - 'v*.*.*'
    branches-ignore:
      - '**'

The pattern v*.*.* matches v1.0.0 and v2.3.7-rc1. Combining tags: with branches-ignore: ['**'] says only deploy on tag - the trigger fires for tag pushes only, never for branch pushes.

flowchart LR
    A[git push] --> B{Is this a tag?}
    B -- "yes" --> D[Tag trigger fires]
    B -- "no" --> C[Branch trigger fires]
    D --> E[Build signed artifact]
    E --> F[Deploy to named environment]
    C --> G[Build but do not deploy]

The same pattern in GitLab CI uses $CI_COMMIT_TAG:

deploy-prod:
  stage: deploy
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
  environment: production
  script:
    - deploy.sh

Environment conditions

An environment is a named deployment target. The name is metadata with consequences: protection rules, secrets, approval gates, and deployment history are attached to the name.

jobs:
  deploy-prod:
    environment:
      name: production
    runs-on: ubuntu-latest
    steps:
      - run: deploy.sh production

The environment: block is what attaches a job to a named environment. Protection rules on that environment are evaluated when the job tries to start. A job that targets production without the required approvals is held. A job that targets a string called prod is not targeting production; it is targeting a string - the platform does not bind protection rules, secrets, or audit history to a string variable.

Production discipline

  1. The deploy job’s trigger is the release act. A deploy on push-to-main is a side effect, not a release. Move the trigger to tags: or workflow_dispatch:.
  2. The environment name is the audit unit. Use the platform’s environment mechanism; do not reimplement it with a string variable.
  3. The tag is signed. git tag -s or -u for hardware roots of trust.

Cross-course references

  • Linux for Production Sysadmins - Part XXX covers signed packages, the package-management analogue.
  • Terraform for Production Sysadmins - Part XXVIII covers promoting an immutable artifact across environments.

Quiz

Knowledge check · 4 questions

  1. Q1. A team writes a deploy job that fires on every push to `main` and targets an environment literal `prod` instead of declaring `environment: production`. What is the production-discipline concern?

  2. Q2. A deploy triggered by `push` to `main` is functionally equivalent to a deploy triggered by a tag, provided the workflow file is the same.

  3. Q3. Name the three identifiers that must agree before a production deploy should be permitted to run.

  4. Q4. Reconstruct the audit trail after a force-push to `main` triggered an unintended production deploy.

    A developer force-pushed to `main` to clean up a sloppy commit history. The push triggered the deploy job. The `production` environment had no required-reviewers rule. The deploy succeeded. Two hours later, the cleanup commit was discovered to have removed a security header.

Passing score: 75%. Answers are checked in this browser.