AB
The second part of our GitHub Actions guide covers building practical workflows, customizing with environment variables and secrets, and implementing advanced features.
Welcome to the second part of our GitHub Actions guide! In Part 1, we covered the fundamentals of GitHub Actions, including key concepts and setting up your first workflow. Now, let’s dive deeper into building more sophisticated workflows and customizing them to meet your specific project needs.
In this section, we’ll create and understand more practical GitHub Actions workflows. We’ll build upon the basic concepts and explore how to construct workflows that can run jobs both sequentially and in parallel.
Let’s automate a simple “Hello World” script. We’ll create two examples: one where jobs run sequentially and another where jobs run in parallel.
name: Sequential Workflow
on:
push:
branches:
- main
jobs:
setup:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Environment
run: echo "Setting up environment..."
execute:
needs: setup
runs-on: ubuntu-latest
steps:
- name: Run Hello World
run: echo "Hello, World!"
Explanation:
on: push
: The workflow triggers when you push code to the main
branch.jobs
:setup
:execute
:setup
job (needs: setup
) and runs only after it completes.Outcome:
setup
job completes first, then the execute
job runs.name: Parallel Workflow
on:
push:
branches:
- main
jobs:
job1:
runs-on: ubuntu-latest
steps:
- name: Run First Task
run: echo "Running job 1..."
job2:
runs-on: ubuntu-latest
steps:
- name: Run Second Task
run: echo "Running job 2..."
Explanation:
job1
and job2
run independently and simultaneously.Outcome:
Triggering the Workflow:
main
branch to start the workflow.git add .
git commit -m "Add basic workflow"
git push origin main
Reviewing the Output:
Outcome: You’ll see logs like:
Running job 1...
Running job 2...
This confirms that the jobs ran successfully.
GitHub Actions allows you to use pre-built actions to simplify workflows. These actions are reusable tasks provided by GitHub or the community.
The GitHub Marketplace is a repository of pre-built actions. These actions can automate tasks like:
Use When:
Avoid When:
Let’s use the github/super-linter
action to lint code.
name: Lint Code
on: push
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Run Linter
uses: github/super-linter@v4
env:
VALIDATE_ALL_CODEBASE: true
Explanation:
uses: actions/checkout@v3
:uses: github/super-linter@v4
:VALIDATE_ALL_CODEBASE: true
: Ensures the linter checks all files in your codebase.Outcome:
Suppose you’re testing a Node.js application. You can use the actions/setup-node
action.
name: Test Node.js App
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Dependencies
run: npm install
- name: Run Tests
run: npm test
Explanation:
actions/setup-node@v3
:npm install
:npm test
:Outcome:
Customizing workflows is about tailoring them to your project’s needs, whether it’s by reusing values, handling sensitive information securely, or running tasks across different configurations.
Environment variables are reusable values that you can define once and use throughout your workflow. This is helpful for storing common configurations, like API URLs or project names, that might change depending on the environment (e.g., development or production).
Here’s an example of using env
to define variables directly in a workflow:
name: Use Environment Variables
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
env:
API_URL: https://api.example.com
PROJECT_NAME: MyCoolProject
steps:
- name: Print Environment Variables
run: |
echo "The API URL is $API_URL"
echo "The project name is $PROJECT_NAME"
Explanation:
env
Block:API_URL
and PROJECT_NAME
) that are available to all steps in the job.run
Command:echo
.Outcome:
The API URL is https://api.example.com
The project name is MyCoolProject
Secrets are used to store sensitive information like API keys, tokens, or passwords securely. Unlike environment variables, secrets are encrypted and cannot be accessed directly in plain text.
API_TOKEN = abc123xyz
).name: Use Secrets
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Use Secret API Token
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
echo "Using secret API token to make requests..."
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com
Explanation:
secrets
:API_TOKEN
) defined in repository settings.curl
Command:Outcome:
Matrix builds allow you to test or run workflows across multiple configurations, like different operating systems, Node.js versions, or Python versions. This ensures compatibility across various environments.
name: Matrix Build Example
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12, 14, 16]
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Checking the node version
run: node --version
- name: Install Dependencies
run: npm install
- name: Run Tests
run: npm test
Explanation:
matrix.node-version
:12
, 14
, 16
) to test against.actions/setup-node
:Outcome:
Testing with Node.js 12
Testing with Node.js 14
Testing with Node.js 16
Imagine you’re a chef testing a recipe with three different ingredients (e.g., sugar, honey, and maple syrup). Instead of making one dish and swapping the ingredients manually, you have three chefs who each make the dish with one ingredient at the same time. This way, you can see which one tastes best, faster!
Environment Variables:
Secrets:
Matrix Builds:
This section explores powerful GitHub Actions features to enhance workflow efficiency and flexibility.
Reusable workflows save time by centralizing common tasks. Instead of duplicating code across multiple workflows, you can call a pre-defined workflow.
workflow_call
Reusable workflows are stored in a repository and can be invoked from other workflows. To make a workflow reusable, you define on: workflow_call
.
Example: Creating a Shared Deployment Workflow
In ./.github/workflows/deploy.yml
(Reusable Workflow):
name: Reusable Deployment Workflow
on:
workflow_call:
inputs:
environment:
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Deploy to ${{ inputs.environment }}
run: echo "Deploying to ${{ inputs.environment }}"
In another workflow that calls this reusable one:
name: Main Workflow
on:
push:
branches:
- main
jobs:
invoke-deploy:
uses: org/repo/.github/workflows/deploy.yml@main
with:
environment: production
Explanation:
deploy.yml
):environment
to specify the deployment environment.production
).Outcome:
Custom actions let you define unique tasks that aren’t covered by existing pre-built actions. You can create these actions using JavaScript or Docker.
Create a Repository: For your custom action.
Add an action.yml
File:
name: My Custom Action
description: Prints a greeting message.
inputs:
name:
description: "Name to greet"
required: true
runs:
using: "node16"
main: "index.js"
Write the JavaScript Logic:
In index.js
:
const core = require("@actions/core");
try {
const name = core.getInput("name");
console.log(`Hello, ${name}!`);
} catch (error) {
core.setFailed(error.message);
}
Publish to GitHub Marketplace:
Outcome:
Caching improves build speeds by reusing previously downloaded dependencies.
name: Cache npm Dependencies
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Cache npm
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 install
Explanation:
actions/cache
:~/.npm
directory to reuse dependencies between builds.key
:package-lock.json
file changes.Outcome:
A self-hosted runner is a machine that you manage yourself to run GitHub Actions. It allows for more customization, such as using specific hardware or software configurations.
Install Runner Software:
Configure the Runner:
./config.sh --url https://github.com/your-repo --token YOUR_TOKEN
Start the Runner:
./run.sh
Use the Runner in a Workflow:
name: Use Self-hosted Runner
on:
push:
branches:
- main
jobs:
build:
runs-on: self-hosted
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Run a Script
run: echo "Running on a self-hosted runner!"
When to Use Self-hosted Runners:
Outcome:
Reusable Workflows:
Custom Actions:
Caching:
Self-hosted Runners:
Adhering to best practices ensures that your GitHub Actions workflows are efficient, maintainable, and easy to debug.
Optimizing workflows can save time and reduce costs by ensuring jobs run only when needed and making efficient use of resources.
GitHub Actions workflows consume resources, and unnecessary executions can be costly. You can optimize them using conditionals and caching.
Example: Running Jobs Only When Necessary
name: Conditional Workflow
on:
push:
branches:
- main
jobs:
test:
if: github.event_name == 'push' && github.event.head_commit.message != 'skip-ci'
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Run Tests
run: npm test
Explanation:
if
Condition:push
and the commit message doesn’t include skip-ci
.skip-ci
, saving execution time and resources.Well-organized workflows improve readability, maintainability, and collaboration.
build-and-deploy.yml
instead of generic ones like main.yml
.# This workflow builds and deploys the application
name: Build and Deploy
on:
push:
branches:
- main
jobs:
build:
# Build the application
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
Break down workflows into smaller, modular files for specific tasks. For example:
build.yml
for building the app.deploy.yml
for deployment.You can call reusable workflows to combine these smaller files when needed.
Outcome:
Efficient debugging and monitoring of workflows ensure smooth execution and quick resolution of issues.
GitHub Actions provide logs for each step in a workflow. Use them to identify and fix issues.
Example: Debugging with debug
Logs
name: Debugging Example
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Debugging Step
run: echo "Current branch: ${{ github.ref }}"
- name: Run Tests
run: npm test
env:
CI: true
Explanation:
echo
Statement:env: CI
:Error: Workflow not triggering.
on
event configuration.push
, pull_request
) and branch filters.Error: Missing or invalid secrets.
Error: Job fails due to missing dependencies.
npm install
or pip install
.In this second part of our GitHub Actions guide, we’ve covered:
By now, you should have a solid understanding of how to create sophisticated GitHub Actions workflows tailored to your project’s needs. These automated workflows can significantly improve your development process, ensuring code quality and streamlining repetitive tasks.
In Part 3, we’ll explore real-world use cases for GitHub Actions, including deploying applications, running CI/CD pipelines, automating documentation, and scheduling periodic tasks. We’ll also cover integrations with other tools and troubleshooting techniques to help you address common issues.
Continue to Part 3: Real-world Applications and Troubleshooting or go back to Part 1: Introduction and Setup