Table of contents
Open Table of contents
Introduction
GitHub Actions has revolutionized how we automate, build, and deploy our projects directly from GitHub. While many are familiar with the basics of setting up workflows, there are advanced features that can significantly enhance your CI/CD pipelines. In this article, we’ll delve into:
- Using Service Containers to mimic production environments.
- Scheduling workflows with cron syntax.
- Creating Composite Actions to avoid code duplication.
- Implementing Environment Protection Rules with manual approvals.
- Speeding up workflows using Caching.
Let’s explore each of these features with practical examples and code snippets.
1. Using Service Containers in GitHub Actions
What Are Service Containers?
Service containers are Docker containers that run alongside your job in a GitHub Actions workflow. They allow you to create test environments that mimic your application’s production environment, including databases, APIs, and other services your application interacts with.
Why Use Service Containers?
- Consistency: Ensure that your tests run in an environment similar to production.
- Isolation: Avoid conflicts by isolating services in containers.
- Simplicity: Easily spin up and tear down services without manual setup.
Configuring a Service Container
Let’s walk through setting up a PostgreSQL database as a service container in a workflow.
Workflow File: .github/workflows/ci.yml
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest # Must be a Linux runner
services:
postgres:
image: postgres:13
env:
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
POSTGRES_DB: mydatabase
ports:
- 5432:5432 # Maps container port 5432 to runner port 5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_HOST: localhost
DATABASE_PORT: 5432
DATABASE_USER: myuser
DATABASE_PASSWORD: mypassword
DATABASE_NAME: mydatabase
steps:
- uses: actions/checkout@v3
- name: Install Dependencies
run: |
pip install -r requirements.txt
- name: Run Database Migration
run: |
python manage.py migrate
- name: Run Tests
run: |
python manage.py test
Key Points
- Linux Runner: Service containers require a Linux runner (ubuntu-latest).
- Ports Mapping: Map container ports to the runner’s ports so your application can communicate with the service.
- Environment Variables: Set environment variables for both the service and your application.
- Accessing Services: Use localhost and the mapped port to access the service within your steps.
2. Scheduling Workflows with Cron Syntax
Why Schedule Workflows?
Scheduled workflows allow you to run tasks at specific intervals, such as nightly builds, weekly reports, or periodic maintenance tasks.
Configuring a Scheduled Workflow
Workflow File: .github/workflows/schedule.yml
# Run at minute 0 past every 6th hour (UTC)
on:
schedule:
- cron: "0 */6 * * *"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Build Script
run: |
./build.sh
Understanding Cron Syntax
The cron expression 0 */6 * * *
breaks down as:
- Minute: 0 (at minute 0)
- Hour: */6 (every 6 hours)
- Day of Month: * (every day)
- Month: * (every month)
- Day of Week: * (every day of the week)
Important Notes
- UTC Time: Cron schedules use Coordinated Universal Time (UTC). Adjust accordingly.
- Minimum Interval: The shortest interval is every 5 minutes.
- Quoting: Always enclose cron expressions in single quotes to prevent YAML parsing errors.
3. Creating Composite Actions
What Are Composite Actions?
Composite actions allow you to bundle multiple steps into a single action. This promotes reusability and simplifies your workflows by avoiding code duplication.
Creating a Composite Action
Directory Structure
my-action/
├── action.yml
└── scripts/
└── my_script.sh
Action Definition: my-action/action.yml
name: My Composite Action
description: Runs multiple steps as a single action
inputs:
greet:
description: "Who to greet"
default: "World"
runs:
using: "composite"
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Run a Script
run: |
echo "Hello, ${{ inputs.greet }}!"
Using the Composite Action in a Workflow
Workflow File: .github/workflows/use-composite.yml
name: Use Composite Action
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Use My Composite Action
uses: ./my-action
with:
greet: "GitHub Actions"
Benefits
- Maintainability: Update code in one place.
- Reusability: Use across multiple workflows or repositories.
- Simplification: Cleaner and more readable workflows.
4. Environment Protection Rules and Manual Approvals
Why Use Environment Protection?
In multi-environment deployments (e.g., development, staging, production), you might want manual approvals before deploying to sensitive environments like production.
Setting Up Environments in GitHub
- Navigate to Settings: Go to your repository’s settings.
- Select Environments: Click on Environments.
- Create Environment: Add environments like development and production.
Configuring Protection Rules
- Select Environment: Click on your environment (e.g., production).
- Configure Protection Rules:
- Required Reviewers: Add users or teams who can approve deployments.
- Wait Timer: (Optional) Add a delay before deployment proceeds.
- Environment Secrets: Add secrets specific to the environment.
Modifying Your Workflow
Workflow File: .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://your-production-url.com
steps:
- uses: actions/checkout@v3
- name: Deploy to Production
run: |
echo "Deploying to production..."
Manual Approval Process
When the workflow reaches the deployment job:
- Pending Approval: The job pauses and waits for approval.
- Notification: Approvers receive an email and a GitHub notification.
- Approval: An approver can approve or reject the deployment.
- Deployment Proceeds: Upon approval, the job continues.
Notes
- Permissions: Approvers need at least read access to the repository.
- Limitations: As of this writing, environments and protection rules are available only in public repositories or with GitHub Enterprise.
5. Caching Dependencies to Speed Up Workflows
The Need for Caching
Dependencies often take time to download and install. Caching these can significantly speed up your workflows, saving time and resources.
Built-in Caching with Setup Actions
For certain languages, the setup actions support caching out of the box.
Node.js: actions/setup-node@v3
- uses: actions/setup-node@v3
with:
node-version: "16"
cache: "npm"
Python: actions/setup-python@v4
- uses: actions/setup-python@v4
with:
python-version: "3.9"
cache: "pip"
Java: actions/setup-java@v3
- uses: actions/setup-java@v3
with:
java-version: "17"
distribution: "adopt"
cache: "maven"
Ruby: actions/setup-ruby@v1
- uses: actions/setup-ruby@v1
with:
ruby-version: "3.0"
bundler-cache: true
Using the Cache Action Directly
For custom caching needs, use actions/cache@v3.
Workflow File: .github/workflows/cache.yml
name: Cache Example
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache Dependencies
id: cache-deps
uses: actions/cache@v3
with:
path: |
~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install Dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: |
pip install -r requirements.txt --cache-dir ~/.cache/pip
- name: Run Build
run: |
python setup.py build
Explanation
- key: Unique identifier for the cache. Changing requirements.txt changes the hash, leading to a cache miss.
- restore-keys: Fallback keys if an exact match isn’t found.
- Conditional Installation: Only install dependencies if the cache was a miss.
Cache Limitations
- Scope: Caches are per-branch but default branch caches are available to all branches.
- Size Limit: Total cache size per repository is 10 GB.
- Retention: Unaccessed caches are cleared after 7 days.
Conclusion
Mastering these advanced features of GitHub Actions can greatly enhance your CI/CD workflows:
- Service Containers: Help you simulate production environments for testing.
- Scheduled Workflows: Automate tasks that need to run at specific times.
- Composite Actions: Promote code reuse and cleaner workflows.
- Environment Protection Rules: Add a layer of security with manual approvals.
- Caching: Significantly speeds up builds by avoiding redundant installations.
By integrating these features, you can create more efficient, maintainable, and secure pipelines that save time and reduce complexity.