Introduction to GitHub Actions
1.1 What is GitHub Actions?
GitHub Actions is a powerful feature within GitHub that allows developers to automate tasks in their software development workflows directly within their GitHub repositories. It provides a seamless way to build, test, and deploy code, making it an integral tool in DevOps pipelines and modern software development.
Overview and Use Cases
GitHub Actions stands out as a Continuous Integration/Continuous Deployment (CI/CD) tool. However, its versatility extends far beyond just CI/CD:
- Automating CI/CD Pipelines: Automate code builds, run tests, and deploy applications seamlessly.
- Managing Repository Workflows: Automatically label issues, assign reviewers, and trigger custom scripts.
- Security Checks: Perform automated security scans and apply fixes using tools like Dependabot.
- Custom Development Tasks: Automate mundane tasks such as merging pull requests, generating release notes, or even interacting with APIs.
Whether you're a solo developer managing side projects or part of a large enterprise, GitHub Actions is a game-changer for increasing productivity and reliability in software projects.
Key Features and Benefits
Integrated with GitHub
GitHub Actions is tightly integrated into the GitHub ecosystem, allowing developers to manage automation alongside their code without relying on external services.Event-Driven
Workflows are triggered by events, such as a new commit, a pull request, or a scheduled time, providing immense flexibility.Pre-Built Marketplace Actions
GitHub Actions has a vast marketplace of reusable actions for tasks like deploying to AWS, running tests, or notifying team members in Slack.Scalability and Customization
Use GitHub-hosted runners or configure self-hosted runners for complete control over your execution environment.Free for Public Repositories
For open-source projects, GitHub Actions offers free unlimited minutes on GitHub-hosted runners, making it an attractive choice for developers.
1.2 Core Concepts
To fully grasp GitHub Actions, understanding its core building blocks is essential:
Workflows
Workflows are the backbone of GitHub Actions. Defined in YAML files and stored in the .github/workflows
directory, they specify a series of steps to automate tasks.
- Example: A workflow might automate the deployment of your application to a server whenever new code is pushed to the main branch.
Jobs
Jobs are a collection of steps executed on a runner. A workflow can contain multiple jobs, which can either run concurrently or have dependencies.
- Example: A CI pipeline might include separate jobs for running tests, building the application, and deploying it.
Steps
Steps are individual tasks executed in a job. Each step can run commands, execute scripts, or invoke prebuilt or custom actions.
- Example: A step might involve checking out the repository's code or running a shell script.
Actions
Actions are reusable units of code that perform specific tasks. Developers can use pre-built actions from the GitHub Marketplace or create custom actions for tailored automation needs.
- Example: The
actions/checkout
action is commonly used to check out a repository’s code in a workflow.
Events and Triggers
Events trigger workflows. GitHub supports a wide range of events, such as push
, pull_request
, or even manual invocations via workflow_dispatch
.
- Example: You might trigger a workflow every time a new pull request is created to ensure the changes pass all tests.
Runners
Runners are the servers that execute the jobs defined in workflows.
- GitHub-Hosted Runners: Provide managed environments for Linux, macOS, and Windows.
- Self-Hosted Runners: Allow complete control over the environment, ideal for specific dependencies or compliance needs.
With a solid understanding of these concepts, you're ready to create workflows that are not only powerful but also scalable and efficient. In the next section, we’ll dive into how to set up your first GitHub Actions workflow.
Workflow Basics
2.1 YAML Syntax Primer
GitHub Actions workflows are defined in YAML (Yet Another Markup Language), a lightweight and human-readable data serialization format. YAML is straightforward to write and understand, but its simplicity can sometimes cause confusion for those unfamiliar with its syntax. This section provides a detailed primer on YAML to help you confidently write GitHub Actions workflows.
Understanding YAML Structure
YAML organizes data into a hierarchy using indentation to define structure. The building blocks of YAML include key-value pairs, lists, mappings, and nested elements. Let's break down these elements step by step:
Key-Value Pairs
The basic structure of YAML revolves around key-value pairs. Keys represent the variable or property name, and values represent their corresponding data.
name: Build Workflow
on: push
Here, name
is the key, and Build Workflow
is its value. Similarly, on
is a key with the value push
.
Lists
Lists are collections of items. In YAML, they are defined with a -
(dash) followed by a space. Lists are commonly used in GitHub Actions to define multiple triggers, steps, or configurations.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Step One
run: echo "Hello, World!"
- name: Step Two
run: echo "Goodbye, World!"
Here, the steps
key contains a list of items, each representing a different step in the workflow.
Mappings (Dictionaries)
Mappings are collections of key-value pairs grouped under a single key. They allow nesting of properties to create a hierarchy.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Run Tests
run: npm test
In this example, jobs
is a mapping with build
as a key. Within build
, there are additional nested key-value pairs like runs-on
and steps
.
Comments
YAML supports comments for documentation and explanations. Comments begin with a #
symbol and are ignored during execution.
# This is a comment
name: Example Workflow # Inline comments are also supported
Common YAML Elements in GitHub Actions Workflows
Now that we understand YAML basics, let's look at the common elements you'll encounter when writing workflows for GitHub Actions:
1. Workflow Name
The name
key specifies the name of the workflow. This name is displayed in the GitHub Actions UI.
name: CI/CD Pipeline
2. Workflow Triggers (on
)
The on
key defines the events that trigger the workflow. It can be a single event, a list of events, or an advanced configuration using types
and branches
.
Single Event:
on: push
Multiple Events:
on:
- push
- pull_request
Advanced Configuration:
on:
push:
branches:
- main
- dev
pull_request:
types:
- opened
- closed
This configuration specifies that the workflow will trigger on push
events to the main
or dev
branches and specific pull_request
events like opened
and closed
.
3. Jobs
The jobs
key contains the definition of all jobs in the workflow. Each job has a unique name and includes keys like runs-on
, steps
, and dependencies.
Basic Job Structure:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
4. Steps
Steps are individual tasks within a job. Each step can either run commands or use pre-built actions.
Command-Based Steps:
steps:
- name: Install Dependencies
run: npm install
Action-Based Steps:
steps:
- name: Checkout Code
uses: actions/checkout@v3
5. Environment Variables
Environment variables are defined at the job or step level. They allow dynamic data to be passed into workflows.
jobs:
build:
env:
NODE_ENV: production
steps:
- name: Print Environment Variable
run: echo "Environment: $NODE_ENV"
6. Outputs
Jobs or steps can define outputs that subsequent jobs can consume.
Defining Outputs:
jobs:
build:
steps:
- name: Set Output
run: echo "::set-output name=version::1.0.0"
Using Outputs:
jobs:
deploy:
needs: build
steps:
- name: Deploy Version
run: echo "Deploying version ${{ needs.build.outputs.version }}"
Best Practices for Writing YAML in GitHub Actions
- Consistency in Indentation: YAML relies heavily on indentation. Always use spaces, not tabs, and maintain consistent levels.
- Comment Extensively: Add comments to explain the purpose of keys and steps for easier collaboration and maintenance.
- Reuse Code: Use reusable workflows and composite actions to avoid duplication.
- Validate Syntax: Use YAML validation tools to catch syntax errors before committing workflows.
With this foundational understanding of YAML and its use in GitHub Actions workflows, you’re ready to begin writing efficient and effective workflows. In the next section, we’ll dive into how to structure jobs and steps for real-world scenarios.
2.2 Workflow Files
GitHub Actions workflows are defined using workflow files, which are written in YAML format and stored in the .github/workflows/
directory of a repository. Understanding the anatomy of a workflow file and how to create and manage these files is crucial to effectively using GitHub Actions.
Anatomy of a Workflow File
A workflow file is a structured YAML document that defines a series of jobs and steps to automate tasks. Here's a breakdown of its components:
1. Workflow Name
The name
key specifies the workflow's name, which appears in the GitHub Actions UI. This is optional, but a meaningful name improves clarity.
name: CI/CD Pipeline
2. Trigger Events (on
)
The on
key defines the events that trigger the workflow. These can be simple events, multiple events, or advanced configurations.
Example of a Push Event Trigger:
on: push
Example of Multiple Events:
on:
- push
- pull_request
Advanced Trigger Configuration:
on:
push:
branches:
- main
- dev
pull_request:
types:
- opened
- reopened
- closed
schedule:
- cron: "0 0 * * *" # Trigger every day at midnight UTC
3. Jobs
The jobs
key is a collection of tasks that define the steps that need to be executed in the workflow. Each job runs in its own virtual environment (runner) and can have dependencies on other jobs.
Each job is defined by a unique name and contains a series of steps.
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
4. Steps
Each job consists of steps. Steps are individual tasks executed sequentially within the job. They can run commands, invoke actions, or perform other tasks necessary for the workflow. Each step can use either a predefined GitHub Action or custom shell commands.
Example of Steps:
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Run Tests
run: npm test
5. Environment Variables
You can define environment variables at the job or step level. These variables are passed into the job and can be accessed by the steps.
Example:
jobs:
build:
env:
NODE_ENV: production
steps:
- name: Print Environment Variable
run: echo "Environment: $NODE_ENV"
6. Dependencies Between Jobs
You can specify dependencies between jobs using the needs
keyword. This ensures that one job runs only after the completion of another job.
Example of Job Dependencies:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to Production
run: echo "Deploying to production..."
In this example, the deploy
job will only run after the build
job has completed successfully.
Creating and Managing Workflow Files
Workflow files are typically stored in the .github/workflows/
directory within your repository. To create and manage workflow files:
Create a New Workflow File
Create a new YAML file in the.github/workflows/
directory. The file should have a.yml
or.yaml
extension. For example,ci.yml
ordeploy.yml
.Define the Workflow in the File
Within the workflow file, define the necessary components such as the workflow name, trigger events, jobs, steps, and any other necessary configurations.Commit the Workflow File
Once the workflow file is created and defined, commit and push the changes to your repository. GitHub will automatically detect the workflow file and run it based on the defined triggers.Manage Workflow Files
Workflow files can be edited and version-controlled just like any other code file. GitHub will trigger workflows based on the latest version of the workflow file, so changes will automatically take effect after pushing new commits.View Workflow Execution
After a workflow is triggered, you can view the execution status on the GitHub Actions tab in your repository. GitHub provides detailed logs for each step of the workflow, allowing you to troubleshoot and monitor progress.
By following these steps, you can effectively create, manage, and monitor workflow files, automating tasks such as testing, deployment, and more.
2.3 Triggering Workflows
Triggering workflows is at the core of GitHub Actions. Workflows are executed in response to specific events, defined in the on
key within the workflow file. Understanding the available triggers and filters allows you to fine-tune when and how workflows run.
Event Triggers
GitHub provides several types of events that can trigger workflows. Some of the most commonly used triggers are:
1. Push Events
The push
event triggers a workflow whenever changes are pushed to a repository. This is ideal for tasks like continuous integration or deployment.
on: push
You can further filter push events by branch, tag, or specific file paths.
Example: Trigger on Push to main
Branch Only
on:
push:
branches:
- main
2. Pull Request Events
The pull_request
event triggers a workflow whenever a pull request is created, updated, merged, or closed. This is useful for reviewing and testing changes before merging them into the main branch.
on: pull_request
You can configure pull_request
triggers to run only for specific actions, branches, or patterns.
Example: Trigger for Specific Actions
on:
pull_request:
types:
- opened
- synchronize
3. Schedule Events
The schedule
event allows workflows to run at predefined times, specified using a cron syntax. This is useful for recurring tasks like backups, performance monitoring, or periodic reporting.
Example: Trigger Daily at Midnight UTC
on:
schedule:
- cron: "0 0 * * *"
4. Manual Trigger (Workflow Dispatch)
The workflow_dispatch
event lets you manually trigger a workflow via the GitHub Actions UI. This is ideal for ad-hoc tasks like running maintenance scripts or deploying hotfixes.
Example: Enabling Manual Trigger
on:
workflow_dispatch:
inputs:
environment:
description: "Select environment"
required: true
default: "production"
type: string
With the workflow_dispatch
input configuration, users can specify custom inputs (like environment
) when triggering the workflow manually.
Filters
Filters allow you to refine workflow triggers, ensuring workflows only run for specific branches, tags, or file paths.
1. Branch Filters
Branch filters let you specify which branches trigger the workflow. You can include or exclude branches using branches
and branches-ignore
.
Example: Include Specific Branches
on:
push:
branches:
- main
- dev
Example: Exclude Specific Branches
on:
push:
branches-ignore:
- staging
2. Tag Filters
Tag filters let you trigger workflows based on Git tags, commonly used for releases.
Example: Trigger for Specific Tags
on:
push:
tags:
- "v1.*" # Trigger for all tags starting with v1.
3. Path Filters
Path filters allow workflows to run only when changes are made to specific files or directories. This is useful for workflows tied to specific parts of a codebase.
Example: Include Specific Paths
on:
push:
paths:
- "src/**" # Trigger for changes in the src directory
- "README.md" # Trigger for changes to README.md
Example: Exclude Specific Paths
on:
push:
paths-ignore:
- "docs/**" # Ignore changes in the docs directory
Combining Triggers and Filters
You can combine multiple triggers and filters to define complex workflows. For example:
Example: Combined Trigger Configuration
on:
push:
branches:
- main
tags:
- "release-*"
paths:
- "src/**"
pull_request:
branches:
- main
schedule:
- cron: "0 0 * * *"
In this configuration:
- The workflow triggers on
push
to themain
branch, but only forrelease-*
tags and changes in thesrc
directory. - It also triggers on
pull_request
events targeting themain
branch. - Finally, it runs every day at midnight UTC.
By mastering event triggers and filters, you can ensure that workflows are efficient, targeted, and responsive to your development needs. This flexibility is one of GitHub Actions' most powerful features.
Jobs, Steps, and Runners
3.1 Understanding Jobs and Steps
Jobs and steps are the building blocks of GitHub Actions workflows. Together, they define the tasks a workflow performs and how those tasks are executed. This section provides a comprehensive guide to defining jobs, configuring their dependencies, and detailing steps within them.
What are Jobs?
In GitHub Actions, jobs are units of work performed by a workflow. Each job is executed on a runner, which is a virtual machine (or container) that processes the defined tasks. Jobs can run independently in parallel, depend on other jobs to create a sequence of execution, and execute specific tasks using steps.
Defining Jobs
Jobs are defined under the jobs
key in a workflow file. Each job has a unique identifier and contains configuration keys, such as runs-on
to specify the environment (runner) where the job will execute and steps
for a list of tasks within the job.
Example of a Basic Job:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
In this example, build
is the job name, runs-on
specifies that the job will execute on an Ubuntu-based runner, and steps
defines the tasks to be performed within the job.
Configuring Job Dependencies
By default, jobs run independently and in parallel. However, you can create dependencies between jobs using the needs
keyword. A job that depends on another will wait for its completion before executing.
Example of Dependent Jobs:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build Application
run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: Run Tests
run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to Production
run: npm run deploy
In this example, the test
job depends on build
, so it will run only after the build
job completes. The deploy
job depends on test
, so it runs after test
finishes. This sequencing ensures tasks are executed in the correct order.
What are Steps?
Steps are the individual tasks that a job performs. Steps are defined inside a job and executed sequentially. Each step can use a predefined action from the GitHub Actions Marketplace or execute a custom command using the run
key.
Configuring Steps within Jobs
Steps are defined as a list under the steps
key in a job. Each step contains a name
for a descriptive label (optional but recommended), uses
to specify a predefined action, and run
to execute a shell command.
Example: Steps in a Job
jobs:
example-job:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Run Tests
run: npm test
In this example, the first step checks out the code using the actions/checkout
action. The second step sets up Node.js with version 14. The third and fourth steps install dependencies and run tests using custom shell commands.
Best Practices for Jobs and Steps
Use descriptive names for each job and step to make workflows easier to understand and debug. Minimize dependencies to reduce wait times and improve workflow efficiency. Leverage predefined actions from the GitHub Marketplace to simplify workflows and save development time. Isolate tasks by breaking down workflows into smaller, reusable steps or jobs to enhance modularity and maintainability. Fail fast by validating prerequisites (e.g., environment setup) in early steps to catch issues early and fail workflows quickly.
By effectively defining jobs, configuring their dependencies, and structuring steps, you can create powerful, efficient workflows that automate complex development and deployment processes. These foundational concepts form the basis for mastering GitHub Actions.
3.2 Job Outputs and Job Artifacts
GitHub Actions allows jobs to pass data and artifacts between them, enabling workflows to share results and facilitate multi-step pipelines. This is achieved using job outputs and job artifacts.
Job Outputs
Job outputs allow you to pass data from one job to another in the same workflow. Outputs are defined in one job and consumed by another using the needs
keyword.
Setting Job Outputs
Job outputs are declared in the outputs
property of a job. Each output is assigned a value by writing to the $GITHUB_OUTPUT
file in one of the job’s steps.
Example: Setting Outputs
jobs:
generate-output:
runs-on: ubuntu-latest
outputs:
result: ${{ steps.step1.outputs.result }}
steps:
- name: Generate an Output
id: step1
run: echo "result=Hello, World!" >> $GITHUB_OUTPUT
use-output:
needs: generate-output
runs-on: ubuntu-latest
steps:
- name: Access Output
run: echo "The output is: ${{ needs.generate-output.outputs.result }}"
In this example:
- The
outputs
property is declared in thegenerate-output
job, referencing the result ofstep1
. - The
result
variable is set by writingresult=Hello, World!
to$GITHUB_OUTPUT
. - The
use-output
job accesses this output vianeeds.generate-output.outputs.result
.
Using Job Outputs
Outputs are especially useful for sharing computed values between jobs and dynamically modifying workflow behavior based on earlier results.
Example: Dynamic Workflow Behavior
jobs:
build:
runs-on: ubuntu-latest
outputs:
deploy_flag: ${{ steps.check.outputs.isDeployRequired }}
steps:
- name: Check if Deployment is Needed
id: check
run: echo "isDeployRequired=true" >> $GITHUB_OUTPUT
deploy:
needs: build
if: ${{ needs.build.outputs.deploy_flag == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Deploy Code
run: echo "Deploying the application..."
In this example:
- The
build
job declares an outputdeploy_flag
, set by thecheck
step. - The
deploy
job runs only if thedeploy_flag
output istrue
.
Job Artifacts
Artifacts are files or directories saved during a workflow and shared between jobs or downloaded for later inspection. They are commonly used for logs, reports, build outputs (e.g., binaries, test results), and debugging purposes.
Saving Artifacts
You can save artifacts using the actions/upload-artifact
action. Artifacts are stored with the workflow and can be accessed later.
Example: Saving Artifacts
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run Tests
run: |
mkdir test-results
echo "Test passed!" > test-results/report.txt
- name: Upload Test Results
uses: actions/upload-artifact@v3
with:
name: test-results
path: test-results/
In this example:
- A directory
test-results
is created to store test reports. - The
actions/upload-artifact
action uploads the directory as an artifact namedtest-results
.
Downloading Artifacts
Artifacts uploaded during a workflow can be downloaded using the actions/download-artifact
action. This is particularly useful when reusing files across jobs.
Example: Downloading Artifacts
jobs:
retrieve:
runs-on: ubuntu-latest
steps:
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
name: test-results
Best Practices for Job Outputs and Artifacts
Use outputs for lightweight data sharing (e.g., variables or strings). Use artifacts for sharing large or complex files (e.g., logs, binaries). Always name your artifacts and outputs descriptively for easy identification. Clean up unnecessary artifacts to save storage and improve workflow performance.
3.3 Environment Variables
Environment variables are a powerful way to pass configuration and data to workflows. They can be used for credentials, application settings, or runtime adjustments.
Setting Environment Variables
Environment variables can be defined globally for a workflow, at the job level, or for individual steps.
Example: Defining Global Environment Variables
env:
NODE_ENV: production
APP_VERSION: 1.2.3
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Print Environment Variables
run: |
echo "Environment: $NODE_ENV"
echo "Version: $APP_VERSION"
Defining Environment Variables for a Job
You can set environment variables at the job level, affecting all steps within the job.
Example: Job-Level Environment Variables
jobs:
test:
runs-on: ubuntu-latest
env:
TEST_MODE: true
steps:
- name: Print Test Mode
run: echo "Test mode is $TEST_MODE"
Defining Environment Variables for Individual Steps
Environment variables can also be defined for specific steps using the env
key inside the step.
Example: Step-Level Environment Variables
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Set Deployment Key
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: echo "Using deployment key $DEPLOY_KEY"
Using Default Environment Variables
GitHub provides a set of built-in environment variables that you can use directly in workflows. Some commonly used variables include:
-
GITHUB_REPOSITORY
: The repository name (e.g.,user/repo
) -
GITHUB_SHA
: The commit SHA that triggered the workflow -
GITHUB_REF
: The branch or tag ref that triggered the workflow -
GITHUB_RUN_ID
: The unique ID of the workflow run
Example: Using Default Variables
jobs:
info:
runs-on: ubuntu-latest
steps:
- name: Print Default Variables
run: |
echo "Repository: $GITHUB_REPOSITORY"
echo "Commit: $GITHUB_SHA"
Combining Environment Variables with Secrets
GitHub secrets allow you to store sensitive data securely and inject it into workflows as environment variables.
Example: Using Secrets as Environment Variables
jobs:
secure:
runs-on: ubuntu-latest
steps:
- name: Access Secret Key
env:
API_KEY: ${{ secrets.API_KEY }}
run: echo "Using API key: $API_KEY"
Best Practices for Environment Variables
Store sensitive data in secrets instead of plain environment variables to ensure security. Use descriptive names for variables to make workflows self-explanatory. Avoid hardcoding values; prefer inputs, outputs, or secrets for flexibility. Leverage default environment variables to dynamically adapt workflows to different contexts.
By mastering job outputs, artifacts, and environment variables, you can build modular, secure, and flexible workflows in GitHub Actions.
Full YAML File for CI/CD Pipeline
Below is the complete YAML file for a CI/CD pipeline in GitHub Actions:
name: CI/CD Pipeline
run-name: Execute CI/CD Pipeline
on:
push:
branches:
- main
tags:
- v*
paths:
- src/**
pull_request:
branches:
- main
types:
- opened
- synchronize
- closed
paths:
- src/**
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
environment:
description: "Deployment environment"
required: true
type: choice
options:
- development
- staging
- production
version:
description: "Version number to deploy"
required: false
type: string
env:
GLOBAL_VAR: GlobalEnvironmentVariableValue
jobs:
build:
name: Build Project
runs-on: ubuntu-latest
env:
BUILD_ENV: BuildJobEnvironmentValue
outputs:
build-output: ${{ steps.save-build-output.outputs.build_status }}
steps:
- id: checkout-code
name: Checkout Code
uses: actions/checkout@v3
- id: install-dependencies
name: Install Dependencies
run: |
echo "Installing dependencies..."
echo "GLOBAL_VAR=$GLOBAL_VAR"
echo "BUILD_ENV=$BUILD_ENV"
npm install
- id: build-project
name: Build Project
run: |
echo "Building Vite React project..."
echo "NODE_ENV=$NODE_ENV"
npm run build
env:
NODE_ENV: production
- id: save-build-output
name: Save Build Output
run: |
echo "build_status=success" >> $GITHUB_OUTPUT
echo "Custom Input Provided: $CUSTOM_ENV"
env:
CUSTOM_ENV: custom-build-value
- id: upload-artifacts
name: Upload Build Artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: ./dist
test:
name: Run Tests
needs: build
runs-on: ubuntu-latest
env:
TEST_ENV: TestJobEnvironmentValue
outputs:
test-output: ${{ steps.save-test-output.outputs.test_status }}
steps:
- id: checkout-code
name: Checkout Code
uses: actions/checkout@v3
- id: run-tests
name: Run Tests
run: |
echo "Running tests..."
echo "TEST_ENV=$TEST_ENV"
echo "GLOBAL_VAR=$GLOBAL_VAR"
echo "Build Output: ${{ needs.build.outputs.build-output }}"
echo "Test Param Provided: $CUSTOM_PARAM"
env:
CUSTOM_PARAM: test-param-value
- id: save-test-output
name: Save Test Output
run: echo "test_status=passed" >> $GITHUB_OUTPUT
- id: download-artifacts
name: Download Build Artifacts
uses: actions/download-artifact@v3
with:
name: build-artifacts
- id: output-artifacts-content
name: Output Artifacts Content
run: ls
deploy:
name: Deploy Application
needs: [build, test]
runs-on: ubuntu-latest
outputs:
deployment-status: ${{ steps.save-deployment-output.outputs.deployment_status }}
steps:
- id: deploy-to-env
name: Deploy to Environment
run: |
echo "Deploying Vite React application..."
echo "DEPLOY_ENV=$DEPLOY_ENV"
echo "GLOBAL_VAR=$GLOBAL_VAR"
echo "Test Output: ${{ needs.test.outputs.test-output }}"
env:
DEPLOY_ENV: DeploymentJobEnvironmentValue
- id: download-artifacts
name: Download Build Artifacts
uses: actions/download-artifact@v3
with:
name: build-artifacts
- id: output-artifacts-content
name: Output Artifacts Content
run: ls
- id: save-deployment-output
name: Save Deployment Output
run: echo "deployment_status=success" >> $GITHUB_OUTPUT
Explanation of Each Section
1. Workflow Metadata
-
name
: This is the name of the workflow visible in the GitHub Actions tab. -
run-name
: Defines the dynamic run name for the workflow execution. Helpful for identifying runs in the Actions log.
2. Triggers (on
)
Defines the events that trigger the workflow. Each trigger type is configured as follows:
-
push
: Triggers the workflow when code is pushed to specific branches, tags, or file paths:-
branches
: List of branches (e.g.,main
) where the workflow should run. -
tags
: Regex-style tag matching (e.g.,v*
). -
paths
: Filters for files or directories (e.g.,src/**
).
-
-
pull_request
: Triggers workflows for PR events:-
branches
: Targets PRs into specific branches (e.g.,main
). -
types
: Specifies PR events (e.g.,opened
,synchronize
,closed
).
-
schedule
: Runs workflows on a cron schedule. In this example, it runs daily at midnight (0 0 * * *
).-
workflow_dispatch
: Enables manual triggering with input parameters:-
environment
: Dropdown choice of environments (development
,staging
,production
). -
version
: Optional text input for version deployment.
-
3. Global Environment Variables (env
)
Sets variables accessible across all jobs in the workflow:
-
GLOBAL_VAR
: A shared value used by all jobs.
4. Jobs
Each job defines tasks executed on a specific runner. The pipeline includes three jobs: build
, test
, and deploy
.
4.1 Build Job
-
runs-on
: Specifies the runner (ubuntu-latest
). -
env
: Job-specific environment variables (e.g.,BUILD_ENV
). -
outputs
: Declares outputs (e.g.,build-output
), derived from specific steps. -
Steps:
-
Checkout code: Uses
actions/checkout@v3
to fetch repository code. -
Install dependencies: Installs project dependencies using
npm install
. -
Build project: Builds a Vite React project using
npm run build
. -
Save output: Sets an output using
$GITHUB_OUTPUT
. -
Upload artifacts: Saves build outputs (
./dist
) for use in other jobs.
-
Checkout code: Uses
4.2 Test Job
-
needs
: Indicates dependency on thebuild
job. -
Steps:
- Repeats checkout to fetch code.
- Runs project tests and outputs results.
- Downloads and verifies build artifacts.
4.3 Deploy Job
-
needs
: Requires successful execution of bothbuild
andtest
jobs. -
Steps:
- Deploys the built project with environment-specific configurations.
- Downloads build artifacts for deployment validation.
Advanced Features Demonstrated
-
Job Dependencies: Ensures
test
waits forbuild
, anddeploy
depends on both. -
Custom Inputs:
workflow_dispatch
parameters allow user-defined workflows. - Reusable Artifacts: Artifacts ensure build files are shared securely across jobs.
- Dynamic Outputs: Job and step outputs enable modular, flexible workflows.
This YAML file represents a robust and production-ready CI/CD pipeline for building, testing, and deploying a Vite React application. Each feature is designed to support scalable and maintainable automation.
Top comments (3)
Detailed introduction! You could also add workflow templates as a further section, to demonstrate how to re-use flows in different jobs - also across repositories.
Well put, Thank you!
Great article! Thank you!