GitHub Actions Guide — CI/CD Workflows for Modern Development
GitHub Actions is a CI/CD platform that lets you automate build, test, and deployment workflows directly from your GitHub repository — triggered by pushes, pull requests, issue comments, or scheduled intervals.
What You’ll Learn
- Writing workflow files with jobs, steps, and triggers
- Using matrix builds to test across multiple versions and platforms
- Leveraging the Actions Marketplace for reusable actions
- Managing secrets and environment variables securely
- Setting up self-hosted runners for custom environments
- Caching dependencies to speed up workflows
Why GitHub Actions Matters
Separate CI/CD tools (Jenkins, CircleCI, TravisCI) require configuration outside your repository, separate UIs, and additional maintenance. GitHub Actions lives inside your repository — workflow files are version-controlled alongside your code, triggers are defined in YAML, and results appear on pull requests automatically. DodaZIP uses GitHub Actions to run linting, type checking, and tests on every pull request — preventing broken code from merging into the main branch and deploying releases automatically when tags are pushed.
flowchart LR
A[JavaScript & Bash Basics] --> B[GitHub Actions]
B --> C[Workflow Triggers]
B --> D[Jobs & Steps]
B --> E[Matrix Builds]
B --> F[Secrets & Caching]
C --> G[Push / PR / Schedule]
D --> H[Parallel Execution]
E --> I[Multi-OS Testing]
style B fill:#2088ff,color:#fff
Core Concepts
Workflow File Structure
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: # Manual trigger from GitHub UI
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
needs: lint # Wait for lint to pass
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./distOutput: The workflow runs on every push to main and on every pull request. The lint job runs first. test waits for lint. deploy waits for both and only runs on pushes to main.
Matrix Builds
Test across multiple Node versions and operating systems:
jobs:
test:
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false # Continue other matrix jobs if one fails
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v3
if: matrix.os == 'ubuntu-latest' && matrix.node-version == 20Output: GitHub Actions runs tests across all 9 combinations (3 Node versions × 3 OSes) in parallel. The fail-fast: false flag continues running even if one combination fails. Coverage is uploaded only for the ubuntu/Node 20 combination.
Marketplace Actions
jobs:
deploy:
steps:
# Official GitHub actions
- uses: actions/checkout@v4 # Check out code
- uses: actions/setup-node@v4 # Set up Node.js
- uses: actions/cache@v3 # Cache dependencies
- uses: actions/upload-artifact@v3 # Upload build artifacts
# Community actions
- uses: docker/login-action@v3 # Docker Hub login
- uses: aws-actions/configure-aws-credentials@v4 # AWS auth
- uses: peaceiris/actions-gh-pages@v3 # Deploy to GitHub Pages
- uses: superfly/flyctl-actions@master # Deploy to Fly.io
- uses: slackapi/slack-github-action@v1 # Send Slack notificationsSecrets and Environment Variables
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Optional: require approval
steps:
- uses: actions/checkout@v4
# Secrets set in GitHub repo Settings > Secrets and variables
- name: Deploy to server
env:
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
NODE_ENV: production
run: |
echo "$SSH_KEY" > ssh_key
chmod 600 ssh_key
ssh -i ssh_key $SSH_USER@$SSH_HOST "cd /app && git pull && npm ci && npm run build"Output: Secrets are encrypted at rest in GitHub, masked in logs, and never shown in plain text. Environment variables without ${{ secrets }} are visible in logs.
Caching Dependencies
jobs:
build:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Cache npm dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- name: Cache build output
uses: actions/cache@v3
with:
path: dist/
key: ${{ runner.os }}-build-${{ github.sha }}Output: The first run installs dependencies and saves them to the cache. Subsequent runs restore from cache — install drops from 60 seconds to 2 seconds. The cache key changes when package-lock.json changes.
Self-Hosted Runners
jobs:
test:
runs-on: self-hosted # Your own machine
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp .
- run: docker run myapp npm test# Install a self-hosted runner on your machine
# Go to GitHub repo > Settings > Actions > Runners > Add runner
# Follow the platform-specific instructions
# Run the runner
./run.sh
# Output:
# √ Connected to GitHub
# √ Listening for JobsSelf-hosted runners are useful for: Accessing private networks, using specific hardware (GPU), running long jobs without timeout limits, and reducing costs compared to GitHub-hosted runners.
Common Mistakes
Not using
actions/checkout@v4as the first step: The checkout action pulls your code. Without it, subsequent steps have nothing to work with.Storing secrets in YAML files: Secrets should be set in GitHub’s Secrets UI, not hardcoded. Use
${{ secrets.NAME }}to reference them.Running all tests on every push: Use path filters (
paths:orpaths-ignore:) to skip workflows when only documentation changes.Not caching dependencies: Without caching, every CI run downloads all dependencies from scratch. Caching reduces install time by 90%+.
Ignoring workflow concurrency: Without
concurrencysettings, pushing multiple commits quickly creates multiple simultaneous runs. Addconcurrency: group: ${{ github.workflow }}-${{ github.ref }}.
Practice Questions
What is the difference between
on: pushandon: pull_request? Answer:pushtriggers when code is pushed to a branch.pull_requesttriggers when a PR is opened or updated. Both are typically combined for complete CI coverage.How do matrix builds speed up testing? Answer: Matrix builds run the same test suite across multiple OS/version combinations in parallel, completing all variations in the time of one sequential run.
What is the purpose of the
needskeyword? Answer:needsestablishes job dependencies. The job waits for all listed jobs to complete successfully before starting.When should you use self-hosted runners? Answer: When you need access to private networks, specific hardware (GPU, custom CPUs), or want to avoid GitHub-hosted runner costs and timeouts.
Challenge
Build a complete CI/CD pipeline: create a workflow that lints on push, runs matrix tests (Node 18/20/22 on ubuntu/windows), caches npm dependencies, deploys to GitHub Pages on main branch merges, sends a Slack notification on failure, and requires manual approval for production deployment via environments.
FAQ
Try It Yourself
# Create a workflow in any GitHub repo
mkdir -p .github/workflows
cat > .github/workflows/hello.yml << 'EOF'
name: Hello World
on: [push]
jobs:
hello:
runs-on: ubuntu-latest
steps:
- run: echo "Hello from GitHub Actions!"
- run: echo "Triggered by ${{ github.event_name }} on ${{ runner.os }}"
- run: |
echo "Workflow: ${{ github.workflow }}"
echo "Branch: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
EOF
git add .github/workflows/hello.yml
git commit -m "Add hello workflow"
git pushThen go to the Actions tab in your GitHub repo to see the workflow run.
What’s Next
| Topic | Description |
|---|---|
| Version control for CI/CD workflows | |
| Containerized testing with Actions |
Related topics: JavaScript, Bash, Docker, Git
What’s Next
Congratulations on completing this GitHub Actions tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro