CI/CD
Git Actions & CI/CD Basics
An introduction to using Git with GitHub Actions for CI/CD pipelines, including trigger strategies, checkout action, Git context, and secure token management.
- Developers using Git in CI/CD pipelines
- Readers who want to understand Git operation boundaries in automation
- Basic understanding of branch, commit, and push
- Basic CI/CD concepts
- Misusing GITHUB_TOKEN causing security issues
- Not understanding the trade-off between shallow and partial clone
One-Sentence Understanding
Modern CI/CD pipelines use Git not just for source control, but as a data source for triggers, versioning, artifacts, and release management. Understanding this integration helps you build more reliable pipelines.
Core Concepts
CI/CD Lifecycle with Git
Source Push → Trigger → Checkout → Build → Test → Release → Tag
↓
(Git operations)
GitHub Actions is deeply integrated with Git. Every workflow run has access to Git history, commit information, and the ability to perform Git operations.
Trigger Strategies
Workflows can be triggered by various Git events:
name: CI
on:
push:
branches: [main, develop]
paths:
- 'src/**'
- '!src/docs/**'
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
release:
types: [published]
The Checkout Action
The actions/checkout action makes the repository available in the runner:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history (for tags, branches)
fetch-tags: true # Fetch tags
ref: ${{ github.ref }} # Checkout the branch/ref that triggered
Fetch Depth Options
fetch-depth | Behavior | Use Case |
|---|---|---|
1 (default) | Shallow clone | Simple build/test |
0 | All history | Linting, versioning |
50 | Recent history | Balanced approach |
2 | Base + HEAD | PR checks |
Using Git in CI
Accessing Git Information
GitHub Actions provides a rich context object with Git data:
- name: Display Git info
run: |
echo "Commit: ${{ github.sha }}"
echo "Branch: ${{ github.ref_name }}"
echo "Tag: ${{ github.ref_type == 'tag' && github.ref_name || 'no tag' }}"
echo "Actor: ${{ github.actor }}"
echo "Commit message: ${{ github.event.head_commit.message }}"
Running Git Commands
- name: Git operations in CI
run: |
git fetch --tags origin
git log --oneline -5
# Create a version from tags
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "VERSION=$VERSION" >> $GITHUB_ENV
Conditional Checks
- name: Check if specific files changed
uses: dorny/paths-filter@v2
id: filter
with:
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
- name: Frontend checks
if: steps.filter.outputs.frontend == 'true'
run: cd frontend && npm run lint
Managing Tokens
Automatic Token (GITHUB_TOKEN)
Every workflow automatically receives a token:
- name: Push with GITHUB_TOKEN
run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git add package.json
git commit -m "Bump version [skip ci]"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Limitations of GITHUB_TOKEN
- Cannot trigger other workflows
- Has repository-level scope only
- May need a PAT for cross-repo operations
Using Deploy Keys
- name: Git operations with deploy key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_PRIVATE_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
GIT_SSH_COMMAND="ssh -i ~/.ssh/deploy_key" git push
CI Security Best Practices
Verify Commits
- name: Verify signed commits
run: |
git verify-commit HEAD
git log --show-signature -1
Use Immutable References
# Use SHA instead of version tags for Actions
- uses: actions/checkout@v4 # Okay
- uses: actions/checkout@a81bbbf8298c0fa # Better (immutable SHA)
Continue Learning
gitlab/gitlab-ci-and-runners— GitLab CI setupsecurity/ssh-key-management— SSH keys & deploy keysbest-practices/security-with-git— Git security practices