GitHub Actions exam Cheat Sheet -(part2)
GitHub Actions Workflows components:
- Actions: Reusable tasks that perform specific jobs within a workflow.
- Workflows: Automated processes in your repo that run one or more jobs, triggered by events or on a schedule.
- Jobs: Groups of steps that execute on the same runner, typically running in parallel unless configured otherwise.
- Steps: Individual tasks within a job that run commands or actions sequentially.
- Runs: Instances of workflow execution triggered by events, representing the complete run-through of a workflow.
- Runners: Servers that execute workflow jobs, either GitHub-hosted or self-hosted.
- Marketplace: A hub for sharing reusable actions, enhancing workflow abilities with community-developed tools.
GitHub Actions Usage Limits
- Max Job Execution Time: 6 hours
- Max Workflow Run Time: 35 days
- Max Job waiting for approval Time: 30 days
- Max API Requests: 1000 requests
- Max jobs generated by a matrix : 256 jobs (per workflow)
- Max artifact retention: 90 days
- Max Workflow time before deletion” More than two weeks ol
- Max unique reusable workflows call from a single workflow file: 20Job
Total Job limits

Structure
Name: Workflow name
on: Events that trigger a workflow (push on the branch git_actions for any change in paths section)
env: Variables definition (hardcoded or imported from the environment such as secrets, vars)
Permissions: To allow your actions to use the token_id
Jobs section: JobName, runner OS (runs-on) , environment, default shell and working directory
Steps section: checkout your code repo + other steps(run tests, build artifacts..)
Same for the next job…
Workflow Triggers:
Basic Event Triggers:
Trigger Type | Description |
---|---|
push |
Triggers when code is pushed to a repo. Can be filtered by branches or tags. |
pull_request |
Triggers when a pull request is opened, updated, or synchronized. Used for pull request validations and reviews. |
schedule |
Triggers workflows on a cron basis. Used for periodic tasks. |
workflow_dispatch |
Manually triggered from the GitHub Actions UI. Useful for on-demand tasks. |
repository_dispatch |
You can use the GitHub API to trigger a webhook event called repository_dispatch. |
workflow_call |
Allows the workflow to be called from another workflow. Used to create reusable workflows. |
release |
Triggers when a new release is published. Used for release-related tasks. |
Events Types:
Event Type | Details |
---|---|
Manual Events |
|
Scheduled Events (CRON) |
|
Repository Dispatch Events |
|
Trigger Filtering & combinations
Category | Filter | Comment |
---|---|---|
Branch/Tag Filters | branches : [ main, develop ] (or branches-ignore ) |
tags : [ ‘v*’ ] (or tags-ignore ) |
Path Filters | paths : [ ‘docs/**’, ‘*.md’ ] |
(or paths-ignore ) |
Activity Types (PR) | types : [ opened, synchronize ] |
(with pull_request) |
Activity Types (Release) | types : [ published, created ] |
(with release) |
Webhooks | types issues, issue_comment , create , delete |
Trigger examples
// 1. PUSH PULL REQUEST
on:
push:
branches: [main]
pull_request:
branches: [main]
// 1. Scheduled tasks
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight
// 3. Release
on:
release:
types: [published]
// 3.2 run when a push is made to any branch or when a branch or tag is created.
on: [push, create]
// 4. Manual trigger wit filters
on:
workflow_dispatch:
inputs:
logLevel:
description: 'Log level'
required: true
default: 'warning'
tags:
description: 'Test scenario tags'
// 4.2 use the GH API to trigger a webhook repository_dispatch event.
##
on:
repository_dispatch:
types: [opened, deleted]
### call the run
curl \
-X POST \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/{owner}/{repo}/dispatches \
-d '{"event_type":"event_type"}'
#####################################################################
// 5. Github actions check WebHook
# triggering a workflow to run on webhook events related to check_run actions
on:
check_run:
types: [created, completed]
// 6. Reusable workflow:
on:
workflow_call:
inputs:
environment:
required: true
type: string
// File change
on:
push:
paths:
- 'docs/**'
- '*.md'
# This triggers the workflow when any file in the `docs` directory or any Markdown file (`.md`) is changed.
// push with paths-ignore
on:
push:
paths-ignore:
- 'README.md'
- 'build/**'
- '!build/docs/**'
# * This triggers the workflow on any push *except* when only the `README.md` file or files in the `build` directory are changed.
# When Issue comment is added
on:
issue_comment:
types: [created]
# IF condition to check if the github.ref (the branch or tag ref that triggered the workflow run) matches refs/heads/main.
name: CI
on: push
jobs:
prod-check:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
...
# Run when the workflow named Build runs on branch releases/:
on:
workflow_run:
workflows: ["Build"]
types: [requested]
branches:
- 'releases/**'
branches-ignore:
- "canary"
Configure workflows to run for scheduled events

Cron expression Example:
# run a workflow every 15 minutes on jan 31s if it's Saturday
on:
schedule:
- cron: '*/15 * 31 JAN SAT'
# run a workflow every Sunday at 3:00am
on:
schedule:
- cron: '0 3 * * SUN'
Delete Vs. Disable Workflow
Disabling a Workflow | Deleting a Workflow |
---|---|
Temporarily stop triggering | Permanently remove |
Easily reversible | Not directly reversible (requires recreation or restore) |
Use cases: Updates, maintenance, frequent triggers | No longer needed, repository cleanup |
cmd:gh workflow disable my_wrkf
|
In GitHub UI |
N/A |
|
Skip Workflow
You can Skip workflow runs by adding a keyword to the commit message or PR title. Use any of the following to the commit message in a push, or HEAD commit of a pull request:
[skip ci] [ci skip] [no ci] [skip actions] [actions skip]
Reusable workflows
workflow_call
Outputs from a reusable workflow or job need to be explicitly defined and passed using the correct syntax.
They are not automatically passed. Options to pass the files location (or other outputs) from a called workflow to the caller workflow are:
- Writing the output into
$GITHUB_OUTPUT
- Defining an output at the workflow level
- Defining an output at the job level.
1. Called Workflow
# .github/workflows/build.yml
name: Build
on:
workflow_call:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create a zip file
run: |
echo "This is a zip file content" > output/sample.txt
zip -r output/sample.zip output/
- name: Set output for zip file location
run: echo "zip_file=output/sample.zip" >> $GITHUB_OUTPUT
2. Caller Workflow
# .github/workflows/caller.yml
name: Caller Workflow
on:
push:
branches:
- main
jobs:
call-build:
uses: ./.github/workflows/build.yml
with:
# Pass input parameters if needed
use-output:
runs-on: ubuntu-latest
needs: call-build
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Access zip file location
run: echo "The zip file location is: ${{ needs.call-build.outputs.zip_file }}"
Example:
- If a caller workflow passes
contents: read
permission, - Called workflow can restrict it to
contents: none
but cant elevate it tocontents: write
.
# 1. # Caller workflow
name: Caller Workflow
on:
push:
branches: [main]
permissions:
contents: read # Setting read permission for contents
issues: write
jobs:
call-reusable-workflow:
uses: ./.github/workflows/reusable-workflow.yml
...
# 2. # Reusable workflow (./.github/workflows/reusable-workflow.yml)
name: Reusable Workflow
on:
workflow_call:
# This is valid - downgrading the permissions
permissions:
contents: none # Downgrading from read to none
issues: read # Downgrading from write to read
Workflow Concurrency
By default GHA allows multiple jobs or workflow runs to run concurrently. The concurrency
keyword allows you to control the concurrency of workflow runs.
Features:
- You can use
jobs.<job_id>.concurrency
to ensure that only a single job or workflow using the same concurrency group will run at a time. - You can also specify
concurrency
at the workflow level. - A concurrency group can be any string or expression i.e
github
,inputs
,vars
,needs
,strategy
, andmatrix
. - Any existing
pending
job or workflow in the same concurrency group, will be canceled - Specify
cancel-in-progress: true
to cancel currently running job.
Example: Limit workflow runs on a branch using the concurrency
keyword after trigger conditions.
### workflow Level
on:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Concurrency groups
Manage and limit the execution of workflow runs or jobs that share the same concurrency key.
## Job level
on:
push:
branches:
- main
jobs:
job-1:
runs-on: ubuntu-latest
concurrency:
group: staging_environment
cancel-in-progress: true
staging_environment
concurrency group that are already in progress will be cancelled.
In-progress job Cancelation examples:
1. Cancel any in-progress job or run in GitHub Actions
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
2.Cancel in-progress jobs/runs on pull_request
events only; if github.head_ref
is undefined, it will fallback to the run ID
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
Exclude Branch Protection Deletion
on:
branch_protection_rule:
types: [created, edited] # (omit deleted).
Functions in GitHub Actions:
Function | Example |
---|---|
contains(search, item) |
contains('Hello world', 'llo') |
startsWith(searchString, searchValue) |
startsWith('Hello world', 'He') |
endsWith(searchString, searchValue) |
endsWith('Hello world', 'ld') |
format(string, replaceValue0, replaceValue1, ..., replaceValueN) |
format('Hello {0} {1} {2}', 'Mona', 'the', 'Octocat') |
join(array, optionalSeparator) |
join(github.event.issue.labels.*.name, ', ') |
toJSON(value) |
toJSON(job) |
fromJSON(value) |
|
hashFiles(path) |
hashFiles('**/package-lock.json', '**/Gemfile.lock') |
Status Check Functions:
Function | Description |
---|---|
success() |
Runs if all previous steps succeeded: if: ${{ success() }} |
always() |
Runs regardless of outcome: if: ${{ always() }} |
cancelled() |
Runs if the workflow was canceled: if: ${{ cancelled() }} |
failure() |
Runs if any previous step fails: if: ${{ failure() }} |
Example (failure check):
- The
failure()
function ensures the step only runs if any previous step has failed - The
steps.run-tests.outcome == 'failure'
condition specifically checks that the “Run Tests
” step failed
- name: Run Tests
id: run-tests
run: npm run test
- name: Upload Failure test report
if: failure() && steps.run-tests.outcome == 'failure' <----
uses: actions/upload-artifact@v3
with:
name: test-report
path: test-reports.html
GitHub actions Expression
contains('hello world', 'hello')
→ Returnstrue
startsWith('hello world', 'hello')
→ Returnstrue
endsWith('hello world', 'world')
→ Returnstrue
format('Hello {0} {1} {2}', 'Mona', 'the', 'Octocat')
join(github.event.issue.labels.*.name, ', ')
toJSON(obj), fromJSON(value)
hashFiles('/Gemfile.lock', '/package-lock.json')
What does the if: ${{ always() }}
key-value do in a step?
- Forces the step to run, even if a step earlier in the job has failed.
Checks API endpoint
GitHub Actions use Checks API to output statuses, results, and logs for a workflow.
GitHub uses webhooks to notify Actions of check
events (i.e check_run
), triggering workflows.
- Repo/org webhooks for
check_run
only receivecreated
andcompleted
event payloads- Other events/activity types :
rerequested
|requested_action
- Other events/activity types :
- When checks are set up, pull requests have a “Checks” tab for detailed build output and checks.
Example: Triggering a workflow to run on webhook events related to check_run actions
- The
check_suite
event is designed to exclusively trigger workflow runs on the default branch.
# triggering a workflow to run on webhook events related to check_run actions
on:
check_run:
types: [created, completed] # activity types
Caching & Artifacts
1. Artifacts:
Set custom retention periods
uses: actions/upload-artifact@v4
with:
name: my-artifact
path: my_file.txt
retention-days: 5 <----------------
Find the expiration date of a specific artifact
Execute a specific API call
, by checking the expires_at
value returned by the “Actions” API
curl -L -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR-TOKEN>" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/{owner}/{repo}/actions/artifacts/{artifact_id}
---
"expires_at": "2020-01-21T14:59:22Z"
GitHub Packages Supported Artifact Types
The following are supported by GitHub Packages: container images, Ruby gems, NPM modules, Maven plugins, Gradle plugins, and NuGet packages.
- Containers: Docker images
- Java: Maven and Gradle dependencies
- Ruby: Gems
- Node.js: npm packages
- .NET: NuGet packages
- Doesn’t support python libraries.
2. Caching:
Stores dependencies to reduce workflow execution time.
- all branches can restore caches created on the default branch
- Purpose: improve efficiency, especially for feature branches.
Language based Caching in GitHub Actions
- Speed up workflows by caching dependencies and files.
- Use built-in caching mechanisms for various package managers:
npm
,yarn
,pnpm
—actions/
setup-node
@v3
pip
,pipenv
,poetry
—actions/
setup-python
@v4
gradle
,maven
—actions/
setup-java
@v3
rubygems
—actions/
setup-ruby
@v1
go.sum
—actions/
setup-go
@v4
GitHub Packages and Container Registry
Once published (in public or in private) you can use these images from anywhere, including:
- In your local dev environment
- As a base image from your GitHub Codespaces development environment
- As a step to execute into your workflow with GitHub Actions
- On a server or a cloud service
Features
- Package Container url:
ghcr.io
- Example:
docker pull ghcr.io/YOURNAME/publish-packages/game:TAG
- Authentication Requirements:
- GitHub username
- Personal Access Token (Classic) with
read:packages
scope - GitHub Packages endpoint
- Authenticate using a GitHub Actions workflow
- use a
GITHUB_TOKEN
: - For package registries at
PACKAGE-REGISTRY.pkg.github.com
. - For the container registry at
ghcr.io/OWNER/IMAGE-NAME
.
- use a
Authenticate using a GitHub Actions workflow
- use a
GITHUB_TOKEN
: - For package registries at
PACKAGE-REGISTRY.pkg.github.com
. - For the container registry at
ghcr.io/OWNER/IMAGE-NAME
.
GitHub Packages vs GitHub Releases
Packages published with GitHub inherit the visibility and permissions assigned at the repository level.
Feature | GitHub Packages | GitHub Releases |
---|---|---|
Purpose | Publish library releases to package feeds/registries. | Release software bundles with notes & binaries. |
Integration | Leverages package manager clients. | Direct download binaries via URLs, tar/ZIP. |
Tracking | Links to repo & commit. | Links to specific commit. |
Publish GitHub Package using Workflow
1. Simple Package
NPM Package
- Checkout:
actions/checkout@v4
- Setup node:
actions/setup-node
- publish:
run: npm publish
...snipet
uses: ruby/setup-ruby@v1 #Ruby
with:
bundler-cache: true
publish-gpr: #Node
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 12
registry-url: https://npm.pkg.github.com/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} #<--- authenticates to registry using GITHUB_TOKEN to publish the npm package
# npm ci ("CI) to install dependencies directly from the package-lock.json file,
# as well as the tests for the project.
# workflow publishes the package to the registry-url: https://npm.pkg.github.com/
Package endpoint reference
- The endpoint will look like:
https://<PACKAGE_TYPE>.pkg.github.com/OWNER/REPOSITORY
PACKAGE_TYPE
is the type of package ecosystem you’re using.
2. Docker based package
- Checkout:
docker/login-action
- Extract metdata
docker/metadata-action
- Setup Docker buildx (arm):
docker/setup-buildx-action
- publish
docker/build-push-action
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
docker tag IMAGE_ID ghcr.io/OWNER/IMAGE_NAME:latest
docker push ghcr.io/OWNER/IMAGE_NAME:latest
you must Configure deployment steps in the workflow’s YAML file.
Workflow example
name: Create and publish a Docker image
on:
push:
branches: ['release']
env:
REGISTRY: ghcr.io <------- ${{ env.REGISTRY }}
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
To pass input as an environment variable to a Docker container action, use the args
keyword in the action’s metadata file.
Share Docker Custom Actions
Ensure your repository is public, define the action’s inputs, outputs, and environment variables, and publish the action as a Docker container.
- Custom Code: Interact with repo, GitHub APIs, 3rd-party APIs.
- Public Sharing: Public repo required.
- Execution: VMs or Docker containers.
- Configurable: Inputs, outputs, env vars.
- Examples: npm publish, SMS alerts, production deploys.
Publish Component as GitHub Release
This workflow automates creation and publication process of a GitHub release when a new tag (matching the v*
pattern) is pushed.
- Build Step: step to build your project (e.g., compile code). i.e placeholder
echo
command. - Create Release Step: uses the
actions/create-release@v1
action to create a GitHub release. - Upload Release Asset Step: uses the
actions/upload-release-asset@v1
action to upload a binary file (or any other asset) to the release. - Asset Upload: The example shows how to upload a binary file; you would need to adjust the
asset_path
andasset_name
to match your specific file.
name: Release Workflow
on:
push:
tags:
- 'v*' <--------- trigger
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Build project
run: |
echo "Build your project here"
# Example: gcc -o output_binary source_code.c
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload Release Asset
uses: actions/upload-release-asset@v4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./path/to/your/output_binary
asset_name: output_binary_name
asset_content_type: application/octet-stream
Service Containers
SC are Docker containers that provide a simple and portable way to host services needed for testing or operating an application in a workflow.
Features
- Configuration: Configurable per job.
- Lifecycle: A Fresh Docker container is created per service; then destroyed on job completion.
- Communication: Job steps can communicate with all service containers in the same job.
- Limitations: Service container cannot be created/used within composite actions.
- Type of docker environments per runner
- GitHub-hosted runners: Use an Ubuntu runner
- Self-hosted runners: Use a Linux machine with Docker installed
- Docker mode per type of runner
- In a Container: Mode>Docker bridge mode: hostname (e.g.,
redis
) - On the Runner host: Mode>Docker host mode:
localhost:<port>
or127.0.0.1:<port>
- In a Container: Mode>Docker bridge mode: hostname (e.g.,
Containers service container mode
The services
keyword will create service containers that are part of a job in your workflow.
name: Redis container example
on: push
jobs:
container-job:
runs-on: ubuntu-latest
container: node:16-bullseye <-- parent container image
services:
redis:
image: redis < child container image
Notice the container job specifies container.
Host service container mode
The services
keyword will not include container: section just the image and port
name: Redis container example
on: push
jobs:
runner-job:
runs-on: ubuntu-latest
services:
redis:
image: redis <---- host container
ports:
- 6379:6379 <------For a runner job you need to map the ports.
Notice the container job specifies container.
CodeQL Step:
The services
keyword will not include container: section just the iIntegrates CodeQL into a GitHub Actions workflow for automated code analysis.
- Init action :
github/codeql-action/init@v1
- Analyze output:
github/codeql-action/analyze@v1
name: "CodeQL"
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
cron: '0 14 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['java', 'javascript', 'python']
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
Examples
1. Specify credentials
for your service containers to authenticate with an image registry.
jobs:
build:
services:
redis:
# Docker Hub image
image: redis
ports:
- 6379:6379
credentials:
username: ${{ secrets.dockerhub_username }}
password: ${{ secrets.dockerhub_password }}
db:
# Private registry image
image: ghcr.io/octocat/testdb:latest
credentials:
username: ${{ github.repository_owner }}
password: ${{ secrets.ghcr_password }}
2. Postgres container parameter
# Running a Postgres server as a service container
jobs:
container-job:
runs-on: ubuntu-latest
container: node:10.18-jessie
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
# Connecting the postgres service container in a step.
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Connect to PostgreSQL
run: node client.js
env:
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
Runners
Runners: Logical grouping of execution environments
GitHub Actions: Hosted vs Self-Hosted Runners
Feature/Aspect | GitHub-Hosted Runners | Self-Hosted Runners |
---|---|---|
Updates | Auto OS, packages, runner app updates | Manual runner app updates; manual OS/software |
Management | GitHub managed | User managed (cloud/local) |
Instance | Clean per job | Persistent, potentially unclean |
Cost | Free within limits, then per-min rates | Free with Actions; user pays for infrastructure |
Access Control | N/A | Runner groups for org/enterprise access |
Customization | Limited | Fully configurable (hardware, OS, software, security), x64 architecture |
Labels | N/A | User-defined labels |
Proxy | N/A | Configurable via https_proxy , http_proxy env vars |
IP Allowlists | N/A | Must add runner IPs to corporate allowlists |
Troubleshooting | N/A |
Check runner status, _diag folder
(Runner_ & Worker_ ), OS-specific tools
|
Self-Hosted Runners
- Must self-manage OS patching
- Free to use with GitHub Actions
- Shared environment for every job execution
Features:
- Flexible: Run on various hardware (physical, virtual, cloud, etc.).
- Levels: Can be set up at the repository, organization, or enterprise level.
- Enterprise-level runners can be assigned to multiple organizations in an enterprise account.
- Setup: Requires adding a runner and installing the GitHub Actions Runner software.
- Images: Supports macOS, Linux, and Windows operating systems.
- Architecture: Uses x64 architecture.
Restricting Actions
- Install with:
npm install @actions/core @actions/github
- GitHub Enterprise can sync actions with:
actions-sync
- Continuous sync with GH Connect
self hosted runners labels
A self-hosted runner automatically receives certain labels when it is added to GitHub Actions.
1. Default Labels
These are used to indicate its operating system and hardware platform:
self-hosted
: Default label applied to self-hosted runners.linux
,windows
, ormacOS
: Applied depending on operating system.x64
,ARM
, orARM64
: Applied depending on hardware architecture.
runs-on: self-hosted linux ARM64
2. Custom Labels
- Create and assign custom labels to self-hosted runners to target specific types of runners for jobs.
- Cumulative: Runners must have all specified labels (including OS/architecture) to be eligible for a job.
- GPU is the custom label.
runs-on: self-hosted linux x64 gpu
Runner groups
- Organize runners into sets and establish security boundaries.
- Availability: For enterprise, organizations within those accounts, and organizations using GitHub Team.
- Placement: New self-hosted runners are sent to default group and must be moved to the correct group manually.
- Labels and Groups can be combined when routing workflows to the appropriate runner.
Exp: group and labels
name: learn-github-actions
on: push
jobs:
check-bats-version:
runs-on:
group: ubuntu-runners <---- group
labels: ubuntu-20.04-16core <---- label within group
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '14'
- run: npm install -g bats
- run: bats -v
GitHub Runner Tab Statuses
- idle: Connected and ready for jobs.
- active: Currently running a job.
- offline: Not connected to GitHub (machine offline, app not running, or com issue).
Configure Self-hosted Runners for Enterprise
Self-hosted runners for GitHub Enterprise have additional configuration options.
Network requirement for github connectivity
- Permitting outbound connectivity from the runner to GitHub using long polling.
- Self-hosted runners connect to GitHub to get jobs and download updates.
- They use HTTPS long polling (50-second outbound connection, then reconnect).
- ❌ Inbound to hosted runners isn’t required
Proxy Servers
The following environment variables are available:
https_proxy
– URLs for HTTPS traffic to proxyhttp_proxy
– URLs for HTTP traffic to proxyno_proxy
– URLs that should not be used as a proxy
Additional URLs are comma-separated.
IP Allowlists
A GitHub-hosted runner must establish connections to GitHub-owned endpoints to perform essential communication operations.
You must add the IP address or range of your self-hosted runners to the IP allowlist for communication to work between runners and GitHub workflows.
- Windows and Ubuntu runners are hosted in Azure same IP address ranges as Azure datacenters.
- MacOS runners are hosted in GitHub’s own macOS cloud.
- The list of GitHub Actions IP addresses returned by the API is updated once a week.
- Use larger runners with a static IP address range, or self-hosted runners.
Large Runners & IP Allowlists
- Large runners with static IPs ranges simplify
allowlist
management. - Increased flexibility & isolation.
- Balances security & efficiency.
Monitor and troubleshoot self-hosted runners
log files:
Runner_*
files in_diag
folder: Runner activity/updates.Worker_*
files in_diag
folder: Job execution status.
Self-hosted connectivity Troubleshooting:
- Tool: Use
--check
command. - Parameters: Runner URL and authentication token.
- URL: to your GitHub repository, organization, or enterprise.
- The log files are located in the
_diag
directory
- Purpose: Diagnose and resolve network connectivity issues with GitHub services.
# Networking check Example
./config.sh --check --url <URL> --pat ghp_abcd1234
# URL example https://github.com/octo-org/octo-repo
Control access to self-hosted runners at the ORG and enterprise level:
- Enterprise Account: Go to
Policies > Actions
. - Runner Groups Tab: Select “New runner group”.
- Configure: Set group name and organization access policy.
- Organization level Go To
Settings>Actions >
Select “New runner group”.

Ephemeral runners in containers can lead to repeated software updates when a new runner version is released.
Solution: Turn-off automatic updates allows you to update the runner version on the container image directly on your own schedule.
GitHub Actions commands, contexts & Variables
You can have repository, environment, as well as workflow, job, and step variable level.
1. Environment variables

2. Default Environment Variables
Default env variables that GitHub sets are available to every step in a workflow. Example: $GITHUB_RUN_ID
. most of them are similar to context variables (GITHUB_REF=github.ref)
- Specify
$
followed by the environment variable’s name. - Are all uppercase.
- Most but not all have the prefix “GITHUB_”
- Available to every step in a workflow
- Are set by GitHub and not defined in a workflow
- ❌not accessible through the env context
GITHUB_ACTIONS
- it always evaluates to
true
when GitHub Actions is running in the workflow
Example
GITHUB_RUN_ID
: Unique, unchanging ID on re-run for a specific workflow run within a repository.1658821493
.GITHUB_RUN_NUMBER
: A sequential number that increments with each new workflow run. For example third run,3
.
- run: echo "Deploying to production server on branch $GITHUB_REF"
Workflow Defaults
Valid use cases for using defaults?
- Using defaults.run on job level to set default
working-directory
for all steps in a single job - Using defaults.run on workflow level to set
default shell
(e.g bash) for an entire workflow
Custom Variables:
# Add variable value for the current repository in an interactive prompt
$ gh variable set MYVARIABLE
# Read variable value from an environment variable
$ gh variable set MYVARIABLE --body "$ENV_VALUE"
# Read variable value from a file
$ gh variable set MYVARIABLE < myfile.txt
# Set variable for a deployment environment in the current repository
$ gh variable set MYVARIABLE --env myenvironment
# Set organization-level variable visible to both public and private repositories
$ gh variable set MYVARIABLE --org myOrg --visibility all
# Set organization-level variable visible to specific repositories
$ gh variable set MYVARIABLE --org myOrg --repos repo1,repo2,repo3
# Set multiple variables imported from the ".env" file
$ gh variable set -f .env
3. Environment Files
- Intra-job env: You can share custom env-variables with following steps in a workflow job by defining or updating the env-variable into the
GITHUB_ENV
environment file.echo "{environment_variable_name}={value}" >> "$GITHUB_ENV"
- Between jobs/workflows you will need
GITHUB_OUTPUT
Environment variables files Examples
# Set Env Vars
steps:
- name: Set environment variable
run: echo "ACTION_ENV=production" >> $GITHUB_ENV
# Adding custom path to System Path
steps:
- name: Add directory to PATH
run: echo "/path/to/dir" >> $GITHUB_PATH
# Setting Output Parameters
steps:
- name: Set output
id: example_step
run: echo "result=output_value" >> $GITHUB_OUTPUT
- name: Use output
run: echo "The output was ${{ steps.example_step.outputs.result }}"
# Creating Debugging Messages
steps:
- name: Create debug message
run: echo "::debug::This is a debug message"

4. Contexts
Context is a collection of variables describing workflow runs, runner environments, jobs, steps, secrets & much more.
- Provide a way to access information about various aspects of your workflow execution.
- Context environment variables are prefixed with
"context.variable": i.e
${{ github.sha }}
Available Contexts:
github
: Information about the workflow run & events that triggered the run. i.e github.repositoryenv
: Workflow, job, or step variables. .i.e${{ env.MY_VARIABLE }}
vars
: Repository, organization, or environment variables${{ vars.MY_VAR }}
.job
: Information about the current job.jobs
: Outputs from reusable workflows.steps
: Information about steps in the current job.runner
: Information about the runner environment.secrets
: Access secret names and values.strategy
: Matrix execution strategy information.matrix
: Matrix properties for the current job.needs
: Outputs of dependent jobs.inputs
: Inputs of reusable or manually triggered workflows.- There’s a similar collection of variables called default variables within a runner (i.e
GITHUB_RUN_ID <=> github.run_id
).
Default Env. vs. Context Vars:
- Default Environment vars : runner scope
- Context vars: workflow scope.
Exp: contexts in workflows
# if statement with context
jobs:
prod-check:
if: github.ref == 'refs/heads/main' # check if triggered on the main branch
runs-on: ubuntu-latest
steps:
- run: echo "Deploying to production server on branch $GITHUB_REF"
Using secret context
# Example using secrets context
steps:
- name: Use Secret as Env Var
run: echo "Secret Value: $MY_SECRET"
env:
MY_SECRET: ${{ secrets.MY_SECRET }}
My_TOK: ${{ secrets.GITHUB_TOKEN }}
### Interpolating variables is done with
${{ variable }} — the spaces are mandatory.
Runner env variables are always interpolated on the runner vm.
However, parts of a workflow are processed by GitHub Actions and are not sent to the runner.
GitHub Actions Security (Secrets)
- GitHubActions secrets store sensitive info passwords, API keys, tokens etc.
- maximum secret size 48 KB
- There are organization, repository and environment level secrets.
- In case of conflict, organization is overridden by repository–and repo is overridden by env values.
- Secret names are not case-sensitive, & must be unique within the same level.
- Secret names only contain alphanum characters and
'_'
and can’t contain spaces - Secret names must not begin with the prefix
GITHUB_.
& must not start with numbers
secrets.GITHUB_TOKEN
is a temporary token automatically generated for each workflow run.
GitHub Actions Commands
Customize runner environment
Commands prefixed with the ::
can customize the runner environment, i.e set env variables or modify the working directory
Commands Examples
jobs:
example:
runs-on: ubuntu-latest
steps:
- name: Set Environment Variable
run: echo "::set-env name=MY_VAR::my_value"
- name: Change Working Directory
run: |
echo "::cd::$GITHUB_WORKSPACE/my_directory" # Change to the new directory
Job conditional execution
if condition:
my-job:
if: ${{ vars.MY_VAR == 'MY_VALUE' }}
Execute scripts in a workflow
The defaults: run: working-directory: ./scripts
section sets the default working directory for the run steps.
Make sure you granting execute permissions to the script file on the runner.
jobs:
example-job:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./scripts
steps:
- name: Check out the repository to the runner
uses: actions/checkout@v4
- name: Run a script
run: ./my-script.sh <---------- run script
- name: Run another script
run: ./my-other-script.sh <-------- runs script
GitHub Actions Workflow commands
workflow commands provided by the actions/toolkit
allow to send instructions to runner.
- Can Create error annotations, set
env vars
, print debugmessages
. - No workflow code modification needed.
Logging commands
##############################
# 1.Setting Output Parameters#
##############################
steps:
- name: Set output
id: example_step
run: echo "result=output_value" >> $GITHUB_OUTPUT
- name: Use output
run: echo "The output was ${{ steps.example_step.outputs.result }}"
# Creating Debugging Messages
steps:
- name: Create debug message
run: echo "::debug::This is a debug message"
##############################
# 2. Grouping Log Messages #
##############################
steps:
- name: Group log messages
run: |
echo "::group:: My Grouped Messages"
echo "Message 1"
echo "Message 2"
echo "::endgroup::"
# Masking Values in Logs
steps:
- name: Masking value
run: echo "::add-mask::${{ secrets.SECRET_VALUE }}"
# Stopping and Failing Actions
steps:
- name: Fail workflow
run: echo "::error:: This is an error message"
Logging Levels
- Debug:
echo "::debug::This is a debug message"
- Info:
echo "This is an info message"
- Error:
echo "::error:: This is an error message"
- Warning:
echo "::warning::This is a warning message"
- Group logs: Use
::group::title
to create collapsible log sections.
Debugging Workflow
- Access workflow logs via REST API:
GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs
- Information needed : Owner, repository, and
run_id
are required
GitHub Actions runner and steps Debug Logging
- Runner Logs:
ACTIONS_RUNNER_DEBUG
=true
. - Step Logs:
ACTIONS_STEP_DEBUG
=true
.
Jobs
Jobs have something called dependence that allows the to run in a certain order.
Sequential jobs
jobs:
job1:
# ... job1 steps ...
job2:
needs: job1
# ... job2 steps ...
job3:
needs: [job1, job2]
# ... job3 steps ...
Conditional job with Always Run
Example: job3
will run after job1 & job2 have been completed, regardless of whether they were successful.
jobs:
job1:
# ... job1 steps ...
job2:
needs: job1
# ... job2 steps ...
job3:
if: ${{ always() }} <--- runs no matter what
needs: [job1, job2]
# ... job3 steps ...
Matrix strategy
jobs:
example_matrix:
strategy:
matrix:
version: [10, 12, 14]
os: [ubuntu-latest, windows-latest]
## total jobes 3 x 2 = 6
The include
matrix option: adds a new combination rather than replacing an existing one.
jobs:
matrix-job:
runs-on: ubuntu-latest
strategy:
matrix:
pet: [cat, dog]
color: [pink, brown]
include: <---- addition not replacement
- color: white
pet: dog
steps:
- run: echo "Hello ${{ matrix.color }} ${{ matrix.pet }}"
total jobs = 5 jobs:
cat + pink
cat + brown
dog + pink
dog + brown
dog + white
Public GitHub Actions
How to vet a public action:
- Check if the action is in the GitHub Marketplace.
- Check if the action is verified in the GitHub Marketplace.
- Review the action’s action.yml file to make sure the code does what it says it does.
Publish an action to the GitHub Marketplace
Below requirements apply to both Docker container actions and JavaScript-based actions:
Publication Requirements
- Public Repository: Action must reside in a public GitHub repository.
- Single Action: One action per repository.
- Metadata file
action.yml
located in root.- Metadata file
category
must match an existing GitHub Marketplace category. - Metadata file
name
must be unique.
- Metadata file
- Unique
name
- Can’t match existing user or organization.
- Can’t match an existing Marketplace category or GitHub feature.
- Add to GitHub Marketplace: by tagging it as a new release and then publishing it.
Note: Actions require a metadata file to define the inputs, outputs and main entrypoint for your action.
Multiple custom actions in one repository
Recommended location to store the action files is .github/actions
with separate subdirectories for each action
Actions Versioning
General Recommendations
- Release patch versions for critical fixes and security patches, maintaining compatibility.
- Release new major versions for changes affecting compatibility.
- Advise users to specify a major version, directing them to specific versions only if necessary.
- Adhere to the
MAJOR.MINOR.PATCH
format for- Clear communication of backward-incompatible changes,
- Feature enhancements, and bug fixes.
TAGGING Recommendations Tagging Recommendations
- Release Branch: Test validate a release on a
release/*
branches. - Semantic Versioning: Follow
major.minor.patch
. (e.g.,v1.0.2
orv1)
- Major Version Tags: Update
v1
,v2
to point to the Git ref of the current release. - Breaking Changes: Introduce a new major version tag (
v2
) for breaking changes. - Introduce a new major version tag
v2.0.0
on the main branch and update the action’s metadata accordingly. - Beta Releases: Use
-beta
tags (v2-beta
) initially. - Referencing: Use major version tags (e.g.,
@v1
) in workflows. Avoid specific versions unless necessary.
Badges:
You can display a status badge in your repository to indicate the status of your workflows.

Show badge status of a specific event (i.e push
): add ?event=push
to the end of the status badge URL
Benefits of automated release process
- Security Focus: Dependencies committed ONLY on tagged releases.
- Secure Builds: Builds performed ONLY during release.
Version calling
You can include version of the action in several ways by specifying a Git ref, SHA, tag, or branch.
Action Version reference examples
# Reference a specific #SHA commit
steps:
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
# Reference the major version of a release
steps:
- uses: actions/checkout@v4 # Major version
# Reference a specific version
steps:
- uses: actions/checkout@v4.2.0 # Specific version
# Reference a branch
steps:
- uses: actions/checkout@main # Branch
If a version is not specified then the workflow will error:
- uses: actions/checkout
Error: .github#L1 the `uses' attribute must be a path, a Docker image, or owner/repo@ref
GitHub Action Types
Types of Actions | Linux | macOS | Windows |
---|---|---|---|
Docker container | Yes | No | No |
JavaScript | Yes | Yes | Yes |
Composite Actions | Yes | Yes | Yes |
1. JavaScript Actions
- Runs directly on the runner machine (Host OS)
- Separates action code from the environment
- Supports Windows, macOS, and Linux
- JavaScript action runs as a single Node.js process in the runner.
JavaScript actions vs Node.js projects (dev and distribution)
- Bundled Dependencies: Include committing all dependent packages
node_modules
& code. - Tagged Releases Published directly to Marketplace.
- API Integration: Requires thorough end-to-end testing.
- Integration with various APIs
- You can speed up development by using the GitHub Actions toolkit
- Javascript actions can run on Linux, Windows, or macOS runners
- Javascript actions run directly on the runner using existing binaries (don’t require additional setup or install of dependencies.)
- the packaged JavaScript code you write should be pure JavaScript and not rely on other binaries.
JavaScript Action Structure
JavaScript action elements
/your-action-name
: Root directory of your action./node_modules
: Directory for project dependencies.action.yml
: Action’s metadata file.index.js
: contains Main JavaScript code.package.json
: Dependency and script management (npm
). (exam question)README.md
: Documentation for your action.
JavaScript Metadata file: action.yml
name: 'My JavaScript Action'
description: 'A description of your action'
inputs:
my-input:
description: 'Input to use in the action'
required: true
default: 'default input value'
outputs:
my-output:
description: 'Output from the action'
runs:
using: 'node12' <------- Executor
main: 'index.js' <------------- main Action code file.
Index.js file:
const core = require('@actions/core');
const github = require('@actions/github');
try {
// `who-to-greet` input defined in action metadata file
const nameToGreet = core.getInput('who-to-greet');
console.log(`Hello ${nameToGreet}!`);
const time = (new Date()).toTimeString();
core.setOutput("time", time);
// Get the JSON webhook payload for the event that triggered the workflow
const payload = JSON.stringify(github.context.payload, undefined, 2)
console.log(`The event payload: ${payload}`);
} catch (error) {
core.setFailed(error.message);
}
core.setFailed(error.message);
uses the actions toolkit@actions/core
package to log a message and set a failing exit code.- Checking in your
node_modules
directory can cause problems. As an alternative, you can use a tool called@vercel/ncc
to compile your code and modules into one file used for distribution. See Doc
npm i -g @vercel/ncc
ncc build index.js --license licenses.txt
# You'll see a new dist/index.js file with your code and the compiled modules.
Change the main keyword in your action.yml file to use the new dist/index.js file.
main: 'dist/index.js'
2. Docker container Actions
- Runs your action code in a Docker container. Best suited for an Action that requires a specific Linux OS and custom tools to run.
- Debug: for detailed information about the execution of the Docker container action, check the GitHub Actions logs.
- Must use Linux runners and have Docker installed
Docker Action Structure
Docker action elements
/your-action-name
: Root directory of your action.Dockerfile
: Defines the Docker image. (Case sensitive)action.yml
: Action’s metadata file.entrypoint.sh
: Shell script to execute.README.md
: Documentation for your action.
Docker Metadata file: action.yml
name: 'My Docker Action'
description: 'A description of your action'
inputs:
my-input:
description: 'Input to use in the action'
required: true
default: 'default input value'
outputs:
my-output:
description: 'Output from the action'
runs:
using: 'docker'
image: 'Dockerfile'
entrypoint: 'entrypoint.sh'
post: 'cleanup.sh' <---- cleanup script after the main task has finished.
entrypoint.sh
#!/bin/sh
echo "My input: $INPUT_MYINPUT"
# ... your code here ...
echo "::set-output name=my-output::my-output-value"
Modifying the entrypoint.sh
script to explicitly set executable permissions before running will resolve the permission denied error. `chmod +x entrypoint.sh
`
Docker Environment variables
name: Verify Docker Action Env
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Docker Container Action and Verify Env
uses: docker://my-dockerhub-user/my-action:latest <-------
env:
MY_ACTION_VAR: "Action Special Value"
with:
entrypoint: /bin/bash # Override entrypoint to run a shell command
args: -c "echo '--- Environment Variables in Action Container ---';\
env; echo '--- End ---'"
Manage GitHub Actions in the Enterprise
Synchronizing GH entreprise with Github Actions
1. GitHub Connect (Recommended)
- Allows GitHub Enterprise Server for automatic syncing from actions on GitHub.com,
- Connects your enterprise instance to GitHub Enterprise Cloud.
- No inbound connections from GitHub.com are required.
2. Actions-sync tool
- To manually sync individual action repositories from GitHub.com to your enterprise.
- Stricter control over which actions are allowed in your enterprise
Enterprise GitHub Actions standards
Document the following in a GitHub wiki (README) in a repo accessible to all within an organization:
- Repositories for storage (different workflow components)
- Files/folders naming conventions
- Location of shared components
- Plans for ongoing maintenance
- Contribution guidelines
Policies
The following options should appear:

Granular selection within Enterprise repo:

You can also manually download and sync actions into your enterprise instance using the actions-sync tool.
Internal/Private Action Access
- GitHub provides a scoped, auto-expiring (1 hour) installation token with read access to download actions from internal or private repositories.
Enterprise GitHub Secrets
Organization Secrets:
Organization Settings > Secrets and variables > Actions > New organization secret
.- Name, value, repository access policy.

Repo Secrets
Repo Settings > Secrets and variables > Actions > New repository secret
.- Name, value.
Accessing Secrets in Actions:
- Declare as
input
inaction.yml
:
inputs:
super_secret:
description: 'My secret token'
required: true
Workflow Templates
Is an Enterprise feature that allows to create reusable templates that other enterprises members can use.
- Store workflow and metadata files in
.github/workflow-templates
workflow-templates
should be within a repository named.github
- Use
$default-branch
placeholder for flexibility - Metadata file format:
<WF_FILE_NAME>.properties.json
- Users with write access to the
enterprise.github
repo can use them. - Files must have the same name (e.g.,
my-workflow.yml
andmy-workflow.json
).
Best Practice :
Create a dedicated repository to store and manage all reusable workflows.
- YAML Workflow File:
- Define workflow logic.
- Use
$default-branch
placeholder.
- JSON Metadata File:
- Same filename as YAML,
.properties.json
extension.
- Same filename as YAML,
- Place both files in
.github/workflow-templates
in a public repo.
Example
Workflow YAML File:(octo-organization-ci.yml
)
name: Octo Organization CI
on:
push:
branches: ${default-branch}
pull_request:
branches: ${default-branch}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run a one-line script
run: echo Hello from Octo Organization
Metadata (octo-organization-ci.properties.json
)
name
: Template name (required).description
: Template description (required).iconName
: SVG icon (optional).categories
: Language category (optional).filePatterns
: Regex file matching (optional).
{
"name": "Octo Organization Workflow",
"description": "Octo Organization CI workflow template.",
"iconName": "example-icon",
"categories": [
"Go"
],
"filePatterns": [
"package.json$",
"^Dockerfile",
".*\\.md$"
]
}
Template Access & Usage:
Actions > New workflow > Workflows created by [org name]
.- Select from listed Workflows by clicking on >
Set up this workflow
Starter Workflow
Starter Workflow is a pre-defined, customizable GitHub Actions workflow template provided by GitHub or organizations to help users quickly set up CI/CD automation for various languages, tools, and use cases.
Features
- GitHub provides and maintains starter workflows for different categories, languages and tooling
- They allow users to leverage ready-to-use (or requiring minimal changes) workflow templates
- Your organization can create custom starter workflows for users in your organization
- Starter workflows can be modified and enhanced,
- Starter workflows can call reusable workflows
Click
"Configure"
to customize and set up a starter workflow..
GitHub Actions UI
When looking at workflow runs in the GitHub Actions GUI, you’ll typically find these status filters:
Workflow Status filters
- Success: The workflow run completed successfully.
- Failure: The workflow run encountered an error and failed.
- Cancelled: The workflow run was manually cancelled.
- In progress: The workflow run is currently running.
- Queued: The workflow run is waiting to be executed.
- Skipped: The workflow run was skipped due to conditional logic.
- Neutral: The workflow run had neutral results. (less common)
- Timed out: The workflow timed out.
- Completed: The workflow run finished, regardless of success or failure.
"Set up job"
: Beginning. Info contained: runner_image, OS, GITHUB_TOKEN permissions"Complete job":
end cleanup.How to share a workflow log with the team:
click on the step’s line number to get a link to the specific line and share the link with a colleague

on: pull request
, where can you see the workflow run status?
- Pull Request (Before Merge): Visible directly on the pull request page.
- Pull Request (Checks Tab): Detailed status within the “Checks” tab of the pull request.
- Repository (Actions Tab): Comprehensive view within the “Actions” tab of the repository.
Static Code Analysis
Feature | Description |
---|---|
Any Checks Run on Raw Code | Static analysis includes tests on code itself, not build artifacts. Includes unit tests. |
Check for Style, Version, and More | Detects style/syntax issues, version conflicts, and security vulnerabilities in dependencies. |
Fail Fast | Runs before time-consuming builds, quickly identifying errors. |
Feature | Static | Functional |
---|---|---|
Tests | Raw code | Built or compiled code |
Detects | Unit tests: Issues in isolation Linters: Syntax and formatting errors |
Deviations from expected outputs |
Resources | Checks execute on runner | Needs non-prod/testing environment |