CICD GitHub Actions
Introduction
GitHub Actions is a powerful CI/CD (Continuous Integration and Continuous Deployment) platform that enables you to automate your software development workflows directly within your GitHub repository. With GitHub Actions, you can build, test, and deploy your code right from GitHub, making it easier to integrate continuous integration and continuous delivery into your development process.
In this guide, we'll explore how GitHub Actions works, how to set up your first workflow, and how to implement common CI/CD patterns to improve your development process.
What is CI/CD?
Before diving into GitHub Actions, let's briefly understand what CI/CD is:
-
Continuous Integration (CI): The practice of frequently merging code changes into a shared repository, followed by automated builds and tests to detect issues early.
-
Continuous Deployment (CD): The practice of automatically deploying code changes to production after they pass all tests in the CI pipeline.
The CI/CD pipeline automates the software delivery process, from code changes to deployment, improving software quality and reducing time to market.
GitHub Actions Core Concepts
Workflows
A workflow is an automated process that you set up in your repository to build, test, package, release, or deploy your project. Workflows are defined in YAML files stored in the .github/workflows
directory of your repository.
Events
Events are specific activities that trigger a workflow to run. For example:
- Push to a repository
- Pull request creation or update
- Scheduled events (cron)
- Manual triggers (workflow_dispatch)
- External events (webhook)
Jobs
Jobs are a set of steps that execute on the same runner. By default, jobs run in parallel, but you can configure them to run sequentially.
Steps
Steps are individual tasks that run commands or actions. Steps within a job execute sequentially.
Actions
Actions are reusable units of code that perform common tasks, like checking out code, setting up programming languages, or deploying to cloud services.
Runners
Runners are servers that run your workflows. GitHub provides runners for Linux, Windows, and macOS, or you can host your own.
Setting Up Your First GitHub Actions Workflow
Let's create a simple workflow that runs tests whenever you push code to your repository:
- In your repository, create a directory structure:
.github/workflows/
- Create a file named
ci.yml
in this directory
Basic Workflow Example
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
Understanding the Workflow
Let's break down this example:
name
: The name of the workflow that appears in the GitHub Actions tabon
: Defines the events that trigger the workflow (pushes and PRs to main)jobs
: Contains the build job that will run on an Ubuntu runnersteps
: The sequence of tasks to perform:- Checkout the code
- Set up Node.js environment
- Install dependencies
- Run tests
Workflow Visualization
Here's a visualization of a typical GitHub Actions workflow:
Common Use Cases for GitHub Actions
1. Building and Testing Applications
name: Build and Test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Test
run: npm test
2. Deploying to Production
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Netlify
uses: netlify/actions/cli@master
with:
args: deploy --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
3. Running on Multiple Operating Systems
name: Cross-Platform Tests
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [14, 16, 18]
steps:
- uses: actions/checkout@v3
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm ci
- name: Test
run: npm test
GitHub Actions Environment Variables and Secrets
Environment Variables
GitHub Actions provides several default environment variables like GITHUB_REPOSITORY
, GITHUB_SHA
, and GITHUB_REF
. You can also set custom environment variables:
jobs:
example-job:
runs-on: ubuntu-latest
env:
MY_ENV_VAR: "custom value"
steps:
- name: Print environment variable
run: echo $MY_ENV_VAR
Secrets
For sensitive data like API tokens, you should use GitHub Secrets:
- In your repository, go to Settings > Secrets > Actions
- Add a new secret with a name and value
- Reference it in your workflow file:
steps:
- name: Deploy with API key
run: ./deploy.sh
env:
API_KEY: ${{ secrets.API_KEY }}
Advanced GitHub Actions Features
1. Caching Dependencies
Caching can significantly speed up your workflows by reusing dependency files:
steps:
- uses: actions/checkout@v3
- name: Cache Node modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
2. Conditional Execution
You can make steps run conditionally:
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
3. Manual Workflow Triggers
Allow workflows to be triggered manually:
name: Manual workflow
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'staging'
type: choice
options:
- staging
- production
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to ${{ github.event.inputs.environment }}
run: echo "Deploying to ${{ github.event.inputs.environment }}"
Practical Example: Full CI/CD Pipeline for a Web Application
Let's create a complete CI/CD pipeline for a JavaScript web application:
name: Web App CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Lint code
run: npm run lint
- name: Run unit tests
run: npm run test
- name: Run e2e tests
run: npm run test:e2e
build:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build
path: build/
deploy-staging:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build
path: build/
- name: Deploy to staging
run: |
echo "Deploying to staging environment"
# Add your deployment commands here
env:
DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
deploy-production:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build
path: build/
- name: Deploy to production
run: |
echo "Deploying to production environment"
# Add your deployment commands here
env:
DEPLOY_TOKEN: ${{ secrets.PRODUCTION_DEPLOY_TOKEN }}
This workflow:
- Runs tests on all pushes and pull requests
- Builds the application if tests pass and it's a push event
- Deploys to staging if it's a push to the develop branch
- Deploys to production if it's a push to the main branch
Best Practices for GitHub Actions
-
Keep Workflows Focused: Create separate workflows for different purposes (e.g., testing, deployment, documentation).
-
Use Reusable Actions: Utilize the marketplace actions instead of writing custom scripts.
-
Optimize for Speed: Use caching, matrix builds, and conditional steps to optimize workflow speed.
-
Secure Secrets: Never hardcode sensitive information; use GitHub Secrets.
-
Test Workflows Locally: Use tools like act to test workflows locally before pushing.
-
Monitor Workflow Usage: Keep an eye on workflow minutes to avoid unexpected charges.
-
Properly Set Permissions: Use the principle of least privilege for workflow permissions.
Troubleshooting GitHub Actions
Common Issues and Solutions
-
Workflow Syntax Errors
- Check your YAML indentation
- Validate your workflow file with online YAML linters
-
Action Not Running
- Verify the event trigger is correctly defined
- Check branch names and paths
-
Failed Dependencies
- Ensure your dependency versions are compatible
- Consider using dependency caching
-
Debugging Workflows
- Enable debug logging by setting the secret
ACTIONS_RUNNER_DEBUG
totrue
- Use the
actions/upload-artifact
action to save logs or build artifacts
- Enable debug logging by setting the secret
Summary
GitHub Actions provides a powerful, flexible platform for implementing CI/CD workflows directly within your GitHub repository. By automating your build, test, and deployment processes, you can deliver code changes more quickly, with higher quality, and with less manual effort.
In this guide, we've covered:
- Core concepts of GitHub Actions
- Setting up basic and advanced workflows
- Creating comprehensive CI/CD pipelines
- Best practices and troubleshooting tips
With GitHub Actions, you can streamline your development process and focus more on writing great code and less on manual deployment tasks.
Additional Resources
Exercises
- Create a simple GitHub Actions workflow that runs tests for your favorite programming language.
- Modify your workflow to run on multiple operating systems using a matrix strategy.
- Set up a workflow that automatically deploys your application to GitHub Pages.
- Create a workflow that labels and comments on issues based on their content.
- Implement a workflow that notifies you (via email or Slack) when a build fails.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)