Skip to main content

GitHub Actions Beginner to Advanced

 

GitHub Actions: From Beginner to Advanced

📅 Published: June 2026
⏱️ Estimated Reading Time: 28 minutes
🏷️ Tags: GitHub Actions, CI/CD, Automation, DevOps, Workflows


Introduction: What is GitHub Actions?

GitHub Actions is a CI/CD platform built directly into GitHub. It allows you to automate your software development workflows right in your repository. You can build, test, and deploy code without leaving GitHub.

Think of GitHub Actions as a powerful automation engine that responds to events in your repository. When someone pushes code, opens a pull request, or creates a release, GitHub Actions can run any command you specify—from running tests to deploying to production.

Why GitHub Actions matters:

  • No separate CI/CD tool to manage

  • Tight integration with GitHub (PR comments, status checks, secrets)

  • Free for public repositories (generous free tier for private)

  • Huge marketplace of pre-built actions

  • Runs on Linux, Windows, and macOS


Part 1: Core Concepts

The Building Blocks

text
Event → Workflow → Job → Step → Action/Run
ConceptDescriptionExample
EventWhat triggers the workflowpush, pull_request, schedule
WorkflowThe entire automation processA YAML file in .github/workflows/
JobA set of steps that run on the same runnertest, build, deploy
StepA single task within a jobuses: actions/checkout@v4
ActionReusable unit of codeactions/setup-node@v4
RunnerThe server that executes jobsubuntu-latest, windows-latest

Your First Workflow

Create .github/workflows/ci.yml:

yaml
name: CI

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run a one-liner
        run: echo "Hello, GitHub Actions!"

When you push this file, GitHub Actions runs the workflow. You can see it in the Actions tab of your repository.


Part 2: Events (Triggers)

Push and Pull Request Events

yaml
# Run on every push to any branch
on: [push]

# Run on push to specific branches
on:
  push:
    branches: [ main, develop ]

# Run on pull request to main
on:
  pull_request:
    branches: [ main ]

# Run on both push and pull request
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

Path Filtering

yaml
on:
  push:
    paths:
      - 'src/**'           # Any file in src directory
      - '**.js'            # Any JavaScript file
      - '!docs/**'         # Exclude docs directory

Scheduled Events (Cron)

yaml
on:
  schedule:
    # Runs at 2 AM UTC every day
    - cron: '0 2 * * *'
    
    # Runs at 9 AM and 5 PM Monday-Friday
    - cron: '0 9,17 * * 1-5'

Manual Trigger (Workflow Dispatch)

yaml
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deployment environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - dev
          - staging
          - prod

Users can then manually trigger the workflow from GitHub UI with selected inputs.

Other Useful Events

yaml
on:
  release:
    types: [published]    # When a release is published
  
  workflow_call:           # Called by another workflow
    secrets:
      API_KEY:
        required: true
  
  repository_dispatch:     # Triggered by external API
    types: [webhook]

Part 3: Jobs and Runners

Basic Job Configuration

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running tests"

Multiple Jobs (Parallel by Default)

yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: npm run lint
  
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm test
  
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build

Job Dependencies (needs)

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm test
  
  build:
    runs-on: ubuntu-latest
    needs: test          # Waits for test to complete
    steps:
      - run: npm run build
  
  deploy:
    runs-on: ubuntu-latest
    needs: [test, build] # Waits for both
    steps:
      - run: npm run deploy

Matrix Strategy (Testing Multiple Configurations)

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
        os: [ubuntu-latest, windows-latest]
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm test

This creates 6 parallel jobs (3 Node versions × 2 operating systems).

Runner Types

RunnerSpecsBest For
ubuntu-latest2-core, 7GB RAMMost Linux/Node.js/Python apps
windows-latest2-core, 7GB RAM.NET, Windows-specific
macos-latest3-core, 14GB RAMiOS/macOS builds
self-hostedYour own hardwareSpecial requirements, cost control

Part 4: Steps and Actions

Using Actions vs Running Commands

yaml
steps:
  # Using an action (reusable)
  - uses: actions/checkout@v4
  
  # Running a command
  - name: Install dependencies
    run: npm install
  
  # Multiple commands
  - name: Build and test
    run: |
      npm run build
      npm test

Popular Built-in Actions

ActionPurpose
actions/checkout@v4Check out repository code
actions/setup-node@v4Install Node.js
actions/setup-python@v5Install Python
actions/upload-artifact@v4Save build artifacts
actions/download-artifact@v4Download saved artifacts
actions/cache@v3Cache dependencies
actions/github-script@v7Run JavaScript using GitHub API

Example: Complete Node.js CI

yaml
name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'
      
      - run: npm ci
      
      - run: npm run lint
      
      - run: npm test
      
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: test-results.xml

Part 5: Environment Variables and Secrets

Setting Environment Variables

yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    
    # Job-level environment variables
    env:
      NODE_ENV: production
    
    steps:
      # Step-level environment variables
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: ./deploy.sh

Using GitHub Context Variables

yaml
- name: Show context
  run: |
    echo "Repository: ${{ github.repository }}"
    echo "Branch: ${{ github.ref_name }}"
    echo "Commit: ${{ github.sha }}"
    echo "Actor: ${{ github.actor }}"
    echo "Event: ${{ github.event_name }}"

Secrets

Store secrets in: Repository → Settings → Secrets and variables → Actions

yaml
steps:
  - name: Deploy to AWS
    env:
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    run: aws s3 sync dist/ s3://my-bucket

Environment-Specific Secrets

yaml
jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}  # From staging environment
        run: ./deploy.sh

  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}  # From production environment
        run: ./deploy.sh

Part 6: Conditional Execution

Using if Conditions

yaml
steps:
  - name: Only on main branch
    if: github.ref == 'refs/heads/main'
    run: echo "Deploying to production"
  
  - name: Only on pull requests
    if: github.event_name == 'pull_request'
    run: echo "Running PR checks"
  
  - name: Only on schedule
    if: github.event_name == 'schedule'
    run: echo "Running scheduled job"
  
  - name: Only on success
    if: success()
    run: echo "Previous steps succeeded"
  
  - name: Only on failure
    if: failure()
    run: echo "Something failed"
  
  - name: Always run
    if: always()
    run: echo "Runs even if previous steps failed"

Job-level Conditions

yaml
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - run: ./deploy.sh

Part 7: Caching Dependencies

Caching npm Dependencies

yaml
- uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

Caching pip Dependencies (Python)

yaml
- uses: actions/cache@v3
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

Caching Docker Layers

yaml
- uses: actions/cache@v3
  with:
    path: /tmp/.buildx-cache
    key: ${{ runner.os }}-buildx-${{ github.sha }}
    restore-keys: |
      ${{ runner.os }}-buildx-

Setup Node with Built-in Caching (Simpler)

yaml
- uses: actions/setup-node@v4
  with:
    node-version: '18'
    cache: 'npm'  # Automatically caches node_modules

Part 8: Artifacts

Uploading Build Artifacts

yaml
- name: Build
  run: npm run build

- name: Upload build artifacts
  uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/
    retention-days: 7

Downloading Artifacts

yaml
- name: Download build artifacts
  uses: actions/download-artifact@v4
  with:
    name: build-output
    path: ./downloaded-build

Multiple Artifacts

yaml
- name: Upload multiple artifacts
  uses: actions/upload-artifact@v4
  with:
    name: all-builds
    path: |
      dist/
      coverage/
      logs/

Part 9: Matrix Builds (Advanced)

Testing Across Multiple Versions

yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [16, 18, 20]
        os: [ubuntu-latest, windows-latest]
        exclude:
          - os: windows-latest
            node: 16
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm test

Dynamic Matrix from JSON

yaml
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - id: set-matrix
        run: |
          echo 'matrix={"node":["16","18","20"]}' >> $GITHUB_OUTPUT
  
  test:
    needs: setup
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJson(needs.setup.outputs.matrix) }}
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm test

Part 10: Reusable Workflows

Creating a Reusable Workflow

Save as .github/workflows/test.yml:

yaml
name: Reusable Test Workflow

on:
  workflow_call:
    inputs:
      node-version:
        description: 'Node.js version'
        required: true
        type: string
      environment:
        description: 'Test environment'
        required: false
        type: string
        default: 'development'
    secrets:
      API_KEY:
        required: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm test
        env:
          NODE_ENV: ${{ inputs.environment }}
          API_KEY: ${{ secrets.API_KEY }}

Using a Reusable Workflow

yaml
name: CI

on: [push]

jobs:
  call-test:
    uses: ./.github/workflows/test.yml
    with:
      node-version: '18'
      environment: 'staging'
    secrets:
      API_KEY: ${{ secrets.API_KEY }}

Calling Workflows from Other Repositories

yaml
jobs:
  call-test:
    uses: my-org/shared-workflows/.github/workflows/test.yml@main
    with:
      node-version: '18'
    secrets:
      API_KEY: ${{ secrets.API_KEY }}

Part 11: Deployment Examples

Deploy to AWS S3

yaml
- name: Deploy to S3
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
    aws-region: us-west-2

- name: Sync to S3
  run: aws s3 sync dist/ s3://my-bucket --delete

Deploy to AWS ECS

yaml
- name: Build Docker image
  run: docker build -t myapp:${{ github.sha }} .

- name: Push to ECR
  run: |
    aws ecr get-login-password | docker login --username AWS --password-stdin ${{ secrets.ECR_REGISTRY }}
    docker push ${{ secrets.ECR_REGISTRY }}/myapp:${{ github.sha }}

- name: Deploy to ECS
  run: |
    aws ecs update-service --cluster production --service myapp --force-new-deployment

Deploy to GitHub Pages

yaml
- name: Build
  run: npm run build

- name: Deploy to GitHub Pages
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./build

Deploy to Vercel

yaml
- name: Deploy to Vercel
  uses: amondnet/vercel-action@v20
  with:
    vercel-token: ${{ secrets.VERCEL_TOKEN }}
    vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
    vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
    vercel-args: '--prod'

Part 12: Complete Production Pipeline Example

yaml
name: Complete CI/CD Pipeline

on:
  pull_request:
    branches: [ main ]
  push:
    branches: [ main ]
  release:
    types: [published]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
      - run: npm ci
      - run: npm run lint

  test:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
      - run: npm ci
      - run: npm test -- --coverage
      - uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - name: Build Docker image
        run: docker build -t ${{ env.IMAGE_NAME }}:${{ github.sha }} .
      - name: Push to registry
        if: github.ref == 'refs/heads/main' || github.event_name == 'release'
        run: |
          echo ${{ secrets.GITHUB_TOKEN }} | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin
          docker push ${{ env.IMAGE_NAME }}:${{ github.sha }}
          docker tag ${{ env.IMAGE_NAME }}:${{ github.sha }} ${{ env.IMAGE_NAME }}:latest
          docker push ${{ env.IMAGE_NAME }}:latest

  deploy-staging:
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'
    environment: staging
    steps:
      - name: Deploy to staging
        run: |
          kubectl set image deployment/myapp myapp=${{ env.IMAGE_NAME }}:${{ github.sha }} -n staging
          kubectl rollout status deployment/myapp -n staging

  deploy-production:
    runs-on: ubuntu-latest
    needs: deploy-staging
    if: github.event_name == 'release'
    environment: 
      name: production
      url: https://example.com
    steps:
      - name: Deploy to production
        run: |
          kubectl set image deployment/myapp myapp=${{ env.IMAGE_NAME }}:${{ github.sha }} -n production
          kubectl rollout status deployment/myapp -n production

  create-release:
    runs-on: ubuntu-latest
    needs: build
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          tag_name: v${{ github.run_number }}
          name: Release v${{ github.run_number }}
          generate_release_notes: true

Part 13: Advanced Patterns

OIDC Authentication (No Long-lived Secrets)

yaml
- name: Configure AWS credentials with OIDC
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
    aws-region: us-west-2

Self-Hosted Runners

yaml
jobs:
  build:
    runs-on: self-hosted
    steps:
      - run: echo "Running on my own server"

Concurrency Control

yaml
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Timeouts

yaml
jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - run: npm run build

GitHub Actions Commands Cheat Sheet

bash
# Workflow syntax
name: workflow-name
on: [push, pull_request]
jobs:
  job-name:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Step name
        run: command

# Conditionals
if: github.ref == 'refs/heads/main'
if: success()
if: failure()
if: always()

# Contexts
${{ github.repository }}
${{ github.ref_name }}
${{ github.sha }}
${{ secrets.MY_SECRET }}
${{ env.MY_VAR }}
${{ vars.MY_VARIABLE }}

Common Interview Questions

Q: What is the difference between uses and run?

A: uses calls a pre-built reusable action (like actions/checkout@v4). run executes shell commands directly. Use uses for common tasks, run for your specific commands.

Q: How do you pass data between jobs?

A: Use artifacts for files, outputs for values:

yaml
# Output from first job
- name: Set output
  id: set
  run: echo "value=hello" >> $GITHUB_OUTPUT

# Use in second job
${{ needs.first-job.outputs.value }}

Q: How do you limit workflow execution to specific file paths?

A: Use paths and paths-ignore in the on section:

yaml
on:
  push:
    paths:
      - 'src/**'
      - '!docs/**'

Summary

ConceptKey Points
EventsTriggers: push, pull_request, schedule, workflow_dispatch
JobsRun in parallel by default, use needs for ordering
Runnersubuntu, windows, macos, self-hosted
StepsActions (uses) or commands (run)
MatrixTest multiple configurations in parallel
ArtifactsShare files between jobs
SecretsStore sensitive data, never expose in logs
CachingSpeed up workflows by reusing dependencies
EnvironmentsSeparate settings for dev, staging, prod

Learn More

Practice GitHub Actions with hands-on exercises in our interactive labs:
https://devops.trainwithsky.com/

Comments

Popular posts from this blog

🌐 Holographic Communications & 6G: The Future of Immersive Connectivity

  🌐 Holographic Communications & 6G: The Future of Immersive Connectivity 🚀 Introduction As the world moves towards 6G , a revolutionary technology is set to redefine digital interactions: Holographic Communications . Imagine real-time, 3D holographic video calls, immersive remote collaboration, and lifelike virtual experiences —all powered by ultra-fast, ultra-low-latency 6G networks . This topic explores Holographic Communications , its impact on various industries, key enabling technologies, and how 6G will bring this futuristic concept to reality . Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🔍 1. What is Holographic Communication? Hologr...

📊 Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd

  Monitoring & Logging in Kubernetes – Tools like Prometheus, Grafana, and Fluentd Monitoring and logging are essential for maintaining a healthy and well-performing Kubernetes cluster. In this guide, we’ll cover why monitoring is important, key monitoring tools like Prometheus and Grafana, and logging tools like Fluentd to help you gain visibility into your cluster’s performance and logs. Shape Your Future with AI & Infinite Knowledge...!! Want to Generate Text-to-Voice, Images & Videos? http://www.ai.skyinfinitetech.com Read In-Depth Tech & Self-Improvement Blogs http://www.skyinfinitetech.com Watch Life-Changing Videos on YouTube https://www.youtube.com/@SkyInfinite-Learning Transform Your Skills, Business & Productivity – Join Us Today! 🚀 Introduction In today’s fast-paced cloud-native environment, Kubernetes has emerged as the de-facto container orchestration platform. But deploying and managing applications in Kubernetes is just half the ba...

How to Use SKY TTS: The Complete, Step-by-Step Guide for 2025

 What is SKY TTS? SKY TTS  is a free, next-generation  AI audio creation platform  that brings together high-quality  Text-to-Speech ,  Speech-to-Text , and a full suite of professional  audio editing tools  in one seamless experience. Our vision is simple — to make advanced audio technology  free, accessible, and effortless  for everyone. From creators and educators to podcasters, developers, and businesses, SKY TTS helps users produce  studio-grade voice content  without expensive software or technical skills. With support for  70+ languages, natural voices, audio enhancement, waveform generation, and batch automation , SKY TTS has become a trusted all-in-one toolkit for modern digital audio workflows. Why Choose SKY TTS? Instant Conversion:  Enjoy rapid text-to-speech generation, even with large documents. Advanced Voice Settings:   Adjust speed, pitch, and style for a personalized listening experience. Multi-...