Navigating Environment-Specific Deployments with GitHub
Locking Down Tag-Based Releases from Environments
As a DevOps engineer, I’ve always found tag-based releases to be a powerful tool for managing deployments across multiple environments. They offer a level of precision and control that’s hard to match. But the challenge lies in ensuring that only the right branches - those that are production-ready - get deployed in a CI/CD pipeline. Let’s dive into how GitHub’s release feature can help us streamline this process. With GitHub, you can create a “release” which creates a tag that can be automatically deployed through the environments. But what if you want to restrict certain branches from being deployed to specific environments, like production?
graph TB
A[GitHub] -->|Create Release| B[Base tag off feature/* branch]
B -->|Automatic Deployment| C[Development Environment]
C --> D[Staging Environment]
D -->|Restrict all branches except head branch| E[Production Environment]
Let’s say you have a feature branch that you want to test in a staging environment, but you don’t want it to go to production. This is where you can leverage GitHub Actions to control the flow of your deployments.
Option 1: Navigating the PreRelease and Release Waters
GitHub’s “prerelease” and “release” options can seem like a handy way to steer deployments. Marking a release as “prerelease” can help differentiate production-ready versions from others.
But this method does have a catch: It’s too easy for someone to accidentally mark a feature or test branch as a “release”, leading to an unintended production deployment.
So, while “prereleases” can be a tool in your deployment strategy, they’re not a failsafe for controlling deployments to specific environments.
graph LR
A[GitHub] -->|Accidental Mark as Release| B[Feature/Test Branch]
B --> C[Unintended Production Deployment]
A -->|Create Release| D[Production-Ready Version]
D --> E[Controlled Production Deployment]
style A fill:#f9d0c4,stroke:#333,stroke-width:4px
style B fill:#e2a4a4,stroke:#333,stroke-width:4px
style C fill:#e66767,stroke:#333,stroke-width:4px
style D fill:#9ddfd3,stroke:#333,stroke-width:4px
style E fill:#59c9a5,stroke:#333,stroke-width:4px
Option 2: Using Git to Determine Tag and Branch Relationship
The git branch --contains tags/$tag
command can identify branches associated with a specific tag. However, it has limitations. If a branch that contains the tag is merged into the master branch, the tag will also be part of the master branch. This is because when you merge a branch, you’re merging its commit history, which includes any tags associated with those commits.
This could result in deploying code that doesn’t match the current master branch state, especially if additional commits were added post-merge. Conversely, this can be useful for rollbacks to a previous code state.
Using git commands is not foolproof for ensuring a tag matches a specific branch’s current state. Use it cautiously in deployment processes.
gitGraph
commit id: "Initial commit"
branch develop
checkout develop
commit id: "develop work"
branch feature
checkout feature
commit id: "feature work"
checkout develop
merge feature tag: "v1.1"
checkout main
merge develop tag: "v1.2"
Option 3: Using GitHub Action’s Event Trigger to Identify the Original Branch of a Tag
You can use GitHub Actions to trigger events based on repository activity, such as a release being published. By accessing the github.event.release.target_commitish
and github.event.repository.default_branch
variables, you can determine the original branch of a tag.
The github.event.release.target_commitish
specifies the commitish (branch) value that determines where the Git tag is created from. The github.event.repository.default_branch
` is the default branch of the repository and can be used to compare with the tag’s original branch.
This method allows you to restrict deployments to production based on the original branch of a tag. Specifically, you can check if the tag’s original branch matches the repository’s default branch before deploying to production.
So in your deployment job, you’d want to include an if
statement like,
1
if: $
Here’s a sample release workflow that demonstrates this approach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: Release
on:
release:
types:
- published
jobs:
Build:
runs-on: ubuntu-latest
steps:
- name: Build
run: |
# Build Commands
echo "Building app..."
Deploy-To-Staging:
runs-on: ubuntu-latest
needs: [Build]
environment:
name: Staging
steps:
- name: Get variables
run: |
echo $
echo $
Deploy-To-Production:
runs-on: ubuntu-latest
needs: [Build, Deploy-To-Staging]
if: $
environment:
name: Production
steps:
- name: Get Event
run: cat $GITHUB_EVENT_PATH