Master GitHub Actions fundamentals including workflow syntax, runners, actions, and automation patterns to build powerful CI/CD pipelines.
Master GitHub Actions fundamentals including workflow syntax, runners, actions, and automation patterns to build powerful CI/CD pipelines.
GitHub Actions is a CI/CD platform integrated directly into GitHub repositories. Understanding its architecture is essential for building effective automation.
Core Components:
.github/workflows/ directoryWorkflow Execution Flow:
1Event Triggered (push, PR, schedule)
2 ↓
3Workflow File Read (.github/workflows/*.yml)
4 ↓
5Jobs Queued (parallel or sequential based on dependencies)
6 ↓
7Runner Assigned (GitHub-hosted or self-hosted)
8 ↓
9Environment Prepared (checkout code, setup tools)
10 ↓
11Steps Execute (commands and actions run)
12 ↓
13Artifacts/Results Stored
14 ↓
15Workflow Complete (success/failure status)Key Concepts:
GitHub Actions uses YAML for workflow configuration. Understanding the syntax is crucial for building reliable automation.
Workflow Structure:
1name: Workflow Name # Required: Workflow display name
2on: [push, pull_request] # Required: Trigger events
3env: # Optional: Workflow-level env vars
4 KEY: value
5jobs: # Required: At least one job
6 job_id: # Required: Unique job identifier
7 name: Job Display Name # Optional: Human-readable name
8 runs-on: ubuntu-latest # Required: Runner type
9 steps: # Required: At least one step
10 - name: Step Name # Optional but recommended
11 run: command # Command or action requiredEvent Triggers:
1on:
2 push:
3 branches:
4 - main
5 - 'releases/**'
6 branches-ignore:
7 - 'experimental/**'
8 tags:
9 - 'v*'
10 paths:
11 - 'src/**'
12 - 'package.json'
13 paths-ignore:
14 - 'docs/**'1on:
2 pull_request:
3 types: [opened, synchronize, reopened]
4 branches: [main]
5 pull_request_target: # For fork PRs
6 types: [opened]1on:
2 schedule:
3 - cron: '30 5 * * 1-5' # 5:30 AM UTC, weekdays
4 - cron: '0 0 * * 0' # Midnight Sunday1on:
2 workflow_dispatch:
3 inputs:
4 environment:
5 description: 'Target environment'
6 required: true
7 default: 'staging'
8 type: choice
9 options: [staging, production]
10 debug:
11 description: 'Enable debug mode'
12 required: false
13 type: booleanJob Configuration:
1jobs:
2 job_id:
3 name: Display Name
4 runs-on: ubuntu-latest
5
6 # Conditional execution
7 if: github.event_name == 'push'
8
9 # Job dependencies
10 needs: [build, test]
11
12 # Timeout (default: 360 minutes)
13 timeout-minutes: 30
14
15 # Run in container
16 container:
17 image: node:18
18 options: --cpus 2
19
20 # Service containers
21 services:
22 postgres:
23 image: postgres:15
24 env:
25 POSTGRES_PASSWORD: postgres
26 ports:
27 - 5432:5432
28
29 # Environment
30 environment:
31 name: production
32 url: https://example.com
33
34 # Permissions (for GITHUB_TOKEN)
35 permissions:
36 contents: read
37 packages: write
38
39 # Concurrency control
40 concurrency:
41 group: ${{ github.workflow }}-${{ github.ref }}
42 cancel-in-progress: true
43
44 # Output variables
45 outputs:
46 version: ${{ steps.version.outputs.value }}
47
48 # Strategy for matrix builds
49 strategy:
50 matrix:
51 node-version: [16, 18, 20]
52 os: [ubuntu-latest, windows-latest]
53 fail-fast: false
54 max-parallel: 3
55
56 steps:
57 - run: echo "Step commands"Step Configuration:
1steps:
2 # Run shell command
3 - name: Run command
4 run: npm test
5 shell: bash
6 working-directory: ./src
7
8 # Use action
9 - name: Checkout
10 uses: actions/checkout@v4
11 with:
12 fetch-depth: 0
13
14 # Conditional step
15 - name: Deploy
16 if: github.ref == 'refs/heads/main'
17 run: npm run deploy
18
19 # Continue on error
20 - name: Optional step
21 run: npm audit
22 continue-on-error: true
23
24 # Timeout for step
25 - name: Long operation
26 run: npm run build
27 timeout-minutes: 10
28
29 # Multi-line command
30 - name: Complex script
31 run: |
32 npm ci
33 npm run build
34 npm run testEnvironment Variables and Secrets:
1env:
2 # Workflow level
3 GLOBAL_VAR: value
4
5jobs:
6 job:
7 env:
8 # Job level
9 JOB_VAR: value
10
11 steps:
12 - name: Use variables
13 env:
14 # Step level
15 STEP_VAR: value
16 # From secrets
17 API_KEY: ${{ secrets.API_KEY }}
18 # From context
19 BRANCH: ${{ github.ref_name }}
20 run: |
21 echo "Global: $GLOBAL_VAR"
22 echo "Job: $JOB_VAR"
23 echo "Step: $STEP_VAR"
24 echo "Branch: $BRANCH"Contexts (Built-in Variables):
github.* - Workflow and event infoenv.* - Environment variablessecrets.* - Encrypted secretsvars.* - Configuration variablesjob.* - Current job infosteps.* - Step outputsrunner.* - Runner infomatrix.* - Matrix parametersActions are the building blocks of GitHub Actions workflows. They encapsulate common tasks and can be reused across workflows and repositories.
Types of Actions:
Using Marketplace Actions:
1steps:
2 # Basic usage
3 - uses: actions/checkout@v4
4
5 # With inputs
6 - uses: actions/setup-node@v4
7 with:
8 node-version: '18'
9 cache: 'npm'
10
11 # Specific version (recommended)
12 - uses: actions/[email protected]
13 with:
14 path: ~/.npm
15 key: npm-${{ hashFiles('**/package-lock.json') }}
16
17 # From private repo (requires token)
18 - uses: my-org/my-action@v1
19 with:
20 token: ${{ secrets.ACCESS_TOKEN }}
21
22 # From local path
23 - uses: ./.github/actions/custom-actionVersion Pinning Strategies:
1# Major version (gets updates)
2- uses: actions/checkout@v4
3
4# Minor version (more stable)
5- uses: actions/[email protected]
6
7# Patch version (most stable)
8- uses: actions/[email protected]
9
10# Commit SHA (most secure)
11- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
12
13# Branch (risky, not recommended)
14- uses: actions/checkout@mainPopular Marketplace Actions:
1# Checkout repository
2- uses: actions/checkout@v4
3 with:
4 fetch-depth: 0 # Full history
5 submodules: recursive # Include submodules
6 lfs: true # Git LFS support
7
8# Upload artifact
9- uses: actions/upload-artifact@v4
10 with:
11 name: my-artifact
12 path: dist/
13 retention-days: 7
14 if-no-files-found: error
15
16# Download artifact
17- uses: actions/download-artifact@v4
18 with:
19 name: my-artifact
20 path: dist/1# Node.js
2- uses: actions/setup-node@v4
3 with:
4 node-version: '18'
5 cache: 'npm'
6
7# Python
8- uses: actions/setup-python@v5
9 with:
10 python-version: '3.11'
11 cache: 'pip'
12
13# Java
14- uses: actions/setup-java@v4
15 with:
16 distribution: 'temurin'
17 java-version: '17'
18 cache: 'maven'
19
20# Go
21- uses: actions/setup-go@v5
22 with:
23 go-version: '1.21'
24 cache: true1# Docker login
2- uses: docker/login-action@v3
3 with:
4 registry: ghcr.io
5 username: ${{ github.actor }}
6 password: ${{ secrets.GITHUB_TOKEN }}
7
8# Build and push
9- uses: docker/build-push-action@v5
10 with:
11 context: .
12 push: true
13 tags: myorg/myimage:latest
14 cache-from: type=gha
15 cache-to: type=gha,mode=max1# Deploy to Azure
2- uses: azure/webapps-deploy@v2
3 with:
4 app-name: my-app
5 publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }}
6
7# Deploy to AWS
8- uses: aws-actions/configure-aws-credentials@v4
9 with:
10 role-to-assume: arn:aws:iam::123456789012:role/my-role
11 aws-region: us-east-1Creating Composite Actions:
Composite actions live in .github/actions/custom-action/action.yml:
1name: 'Setup Application'
2description: 'Install dependencies and build application'
3
4inputs:
5 node-version:
6 description: 'Node.js version'
7 required: false
8 default: '18'
9
10 build-command:
11 description: 'Build command to run'
12 required: false
13 default: 'npm run build'
14
15outputs:
16 version:
17 description: 'Application version'
18 value: ${{ steps.package.outputs.version }}
19
20runs:
21 using: 'composite'
22 steps:
23 - name: Setup Node.js
24 uses: actions/setup-node@v4
25 with:
26 node-version: ${{ inputs.node-version }}
27 cache: 'npm'
28
29 - name: Install dependencies
30 run: npm ci
31 shell: bash
32
33 - name: Get version
34 id: package
35 run: echo "version=$(node -p \"require('./package.json').version\")" >> $GITHUB_OUTPUT
36 shell: bash
37
38 - name: Build
39 run: ${{ inputs.build-command }}
40 shell: bashUse it in workflow:
1steps:
2 - uses: actions/checkout@v4
3
4 - name: Setup and build
5 uses: ./.github/actions/custom-action
6 with:
7 node-version: '20'
8 build-command: 'npm run build:prod'Action Security Best Practices:
Runners are the compute resources where GitHub Actions workflows execute. Understanding runners is essential for optimizing performance and cost.
GitHub-Hosted Runners:
Available Operating Systems:
1runs-on: ubuntu-latest # Ubuntu 22.04 (most common)
2runs-on: ubuntu-22.04 # Specific Ubuntu version
3runs-on: ubuntu-20.04 # Older Ubuntu version
4
5runs-on: windows-latest # Windows Server 2022
6runs-on: windows-2019 # Windows Server 2019
7
8runs-on: macos-latest # macOS 13 (Ventura)
9runs-on: macos-13 # macOS 13
10runs-on: macos-12 # macOS 12 (Monterey)Runner Specifications:
| Runner | vCPU | RAM | Storage | Notes |
|--------|------|-----|---------|-------|
| ubuntu-latest | 2 | 7 GB | 14 GB SSD | Most cost-effective |
| windows-latest | 2 | 7 GB | 14 GB SSD | 2x billing minutes |
| macos-latest | 3 | 14 GB | 14 GB SSD | 10x billing minutes |
Pre-installed Software:
All GitHub-hosted runners come with:
Full list: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#preinstalled-software
Runner Groups (Enterprise/Organization):
1runs-on: [self-hosted, linux, x64, production]
2runs-on: [self-hosted, macOS, ARM64]Container Jobs:
Run jobs inside Docker containers for consistent environments:
1jobs:
2 test:
3 runs-on: ubuntu-latest
4 container:
5 image: node:18-alpine
6 env:
7 NODE_ENV: test
8 volumes:
9 - my_docker_volume:/volume_mount
10 options: --cpus 2 --memory 4g
11
12 steps:
13 - run: node --versionService Containers:
Run services like databases alongside your job:
1jobs:
2 integration-test:
3 runs-on: ubuntu-latest
4
5 services:
6 postgres:
7 image: postgres:15
8 env:
9 POSTGRES_PASSWORD: postgres
10 POSTGRES_DB: testdb
11 options: >-
12 --health-cmd pg_isready
13 --health-interval 10s
14 --health-timeout 5s
15 --health-retries 5
16 ports:
17 - 5432:5432
18
19 redis:
20 image: redis:7-alpine
21 options: >-
22 --health-cmd "redis-cli ping"
23 --health-interval 10s
24 --health-timeout 5s
25 --health-retries 5
26 ports:
27 - 6379:6379
28
29 steps:
30 - name: Test database connection
31 env:
32 DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
33 REDIS_URL: redis://localhost:6379
34 run: |
35 npm run test:integrationSelf-Hosted Runners:
When to use:
Setting up self-hosted runner:
1# Example for Linux
2mkdir actions-runner && cd actions-runner
3curl -o actions-runner-linux-x64-2.311.0.tar.gz -L \
4 https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
5tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz
6
7# Configure
8./config.sh --url https://github.com/YOUR-ORG/YOUR-REPO --token YOUR-TOKEN
9
10# Run as service
11sudo ./svc.sh install
12sudo ./svc.sh startUse in workflow:
1jobs:
2 build:
3 runs-on: self-hosted
4 # Or with labels
5 runs-on: [self-hosted, linux, x64, gpu]Security Considerations for Self-Hosted:
⚠️ Never use self-hosted runners with public repositories!
For public repos:
Runner Context Variables:
1steps:
2 - name: Check runner info
3 run: |
4 echo "OS: ${{ runner.os }}" # Linux, Windows, macOS
5 echo "Arch: ${{ runner.arch }}" # X64, ARM, ARM64
6 echo "Name: ${{ runner.name }}" # Runner name
7 echo "Temp: ${{ runner.temp }}" # Temp directory
8 echo "Tool cache: ${{ runner.tool_cache }}"Resource Optimization:
1jobs:
2 test:
3 strategy:
4 matrix:
5 node: [16, 18, 20]
6 runs-on: ubuntu-latest
7 # All 3 run in parallel1concurrency:
2 group: ${{ github.workflow }}-${{ github.ref }}
3 cancel-in-progress: true # Cancel old runs1jobs:
2 build:
3 timeout-minutes: 30 # Don't run foreverProper secrets and variables management is crucial for security and maintainability of CI/CD pipelines.
Types of Configuration:
Secrets Management:
Creating Secrets:
Repository level:
Organization level:
Environment level:
Using Secrets in Workflows:
1steps:
2 - name: Use secret in command
3 env:
4 API_KEY: ${{ secrets.API_KEY }}
5 run: |
6 # Secret is available as env var
7 curl -H "Authorization: Bearer $API_KEY" https://api.example.com
8
9 - name: Pass secret to action
10 uses: some-action@v1
11 with:
12 api-key: ${{ secrets.API_KEY }}
13
14 # ❌ NEVER log secrets
15 - name: Wrong - Logs secret
16 run: echo "API key is ${{ secrets.API_KEY }}"
17
18 # ✅ Secrets are automatically redacted
19 - name: Safe - Secret redacted in logs
20 env:
21 API_KEY: ${{ secrets.API_KEY }}
22 run: echo "API key is set"Secret Security Best Practices:
1# ❌ Bad
2- run: echo $SECRET_VALUE
3
4# ✅ Good
5- run: echo "Secret is configured"1jobs:
2 deploy-staging:
3 environment: staging
4 steps:
5 - run: deploy
6 env:
7 API_KEY: ${{ secrets.STAGING_API_KEY }}
8
9 deploy-production:
10 environment: production
11 steps:
12 - run: deploy
13 env:
14 API_KEY: ${{ secrets.PRODUCTION_API_KEY }}Configuration Variables:
Variables are for non-sensitive configuration:
1jobs:
2 build:
3 runs-on: ubuntu-latest
4 steps:
5 - name: Use variables
6 env:
7 # Configuration variables
8 API_URL: ${{ vars.API_URL }}
9 FEATURE_FLAG: ${{ vars.FEATURE_FLAG }}
10 MAX_RETRIES: ${{ vars.MAX_RETRIES }}
11 run: |
12 echo "API URL: $API_URL"
13 echo "Feature enabled: $FEATURE_FLAG"Environment Variables:
Three levels of environment variables:
1# Workflow level
2env:
3 GLOBAL_VAR: 'available to all jobs'
4
5jobs:
6 job1:
7 # Job level
8 env:
9 JOB_VAR: 'available to all steps in job'
10
11 steps:
12 # Step level
13 - name: Step with env
14 env:
15 STEP_VAR: 'only in this step'
16 run: |
17 echo $GLOBAL_VAR
18 echo $JOB_VAR
19 echo $STEP_VARDefault Environment Variables:
GitHub provides many default environment variables:
1steps:
2 - name: Use default variables
3 run: |
4 echo "Repository: $GITHUB_REPOSITORY"
5 echo "Ref: $GITHUB_REF"
6 echo "SHA: $GITHUB_SHA"
7 echo "Actor: $GITHUB_ACTOR"
8 echo "Workflow: $GITHUB_WORKFLOW"
9 echo "Run ID: $GITHUB_RUN_ID"
10 echo "Run Number: $GITHUB_RUN_NUMBER"Full list: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
Working with GITHUB_TOKEN:
Automatic token with limited permissions:
1jobs:
2 build:
3 runs-on: ubuntu-latest
4 # Configure token permissions
5 permissions:
6 contents: read
7 packages: write
8 pull-requests: write
9
10 steps:
11 - name: Create release
12 env:
13 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
14 run: gh release create v1.0.0
15
16 - name: Comment on PR
17 uses: actions/github-script@v7
18 with:
19 github-token: ${{ secrets.GITHUB_TOKEN }}
20 script: |
21 github.rest.issues.createComment({
22 issue_number: context.issue.number,
23 owner: context.repo.owner,
24 repo: context.repo.repo,
25 body: 'Build completed!'
26 })Token Permissions:
1permissions:
2 contents: read # Repository contents
3 issues: write # Issues
4 pull-requests: write # Pull requests
5 packages: write # GitHub Packages
6 deployments: write # Deployments
7 id-token: write # OIDC tokenAccessing Secrets in Specific Scenarios:
1- name: Build with secrets
2 uses: docker/build-push-action@v5
3 with:
4 context: .
5 secrets: |
6 "npm_token=${{ secrets.NPM_TOKEN }}"
7 "api_key=${{ secrets.API_KEY }}"1- name: Use multi-line secret (SSH key)
2 env:
3 SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
4 run: |
5 echo "$SSH_KEY" > ~/.ssh/id_rsa
6 chmod 600 ~/.ssh/id_rsa1- name: Parse JSON secret
2 env:
3 SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT_JSON }}
4 run: |
5 echo "$SERVICE_ACCOUNT" > sa.json
6 gcloud auth activate-service-account --key-file=sa.jsonSecret Patterns:
API_KEY_PRODUCTION, API_KEY_STAGINGGITHUB_PAT, NPM_TOKEN, DOCKER_TOKENAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYSSL_CERTIFICATE, SIGNING_CERTIFICATESSH_PRIVATE_KEY, DEPLOY_KEYLearn proven workflow patterns used in production environments to build robust, maintainable CI/CD pipelines.
Pattern 1: Pull Request Validation
Comprehensive PR checks before merging:
1on:
2 pull_request:
3 types: [opened, synchronize, reopened]
4
5jobs:
6 validate:
7 runs-on: ubuntu-latest
8 steps:
9 - uses: actions/checkout@v4
10 - name: Lint
11 run: npm run lint
12 - name: Type check
13 run: npm run type-check
14 - name: Tests
15 run: npm test -- --coverage
16 - name: Security scan
17 run: npm audit
18 - name: Build
19 run: npm run buildPattern 2: Continuous Deployment
Automated deployment on successful merge:
1on:
2 push:
3 branches: [main]
4
5jobs:
6 deploy:
7 runs-on: ubuntu-latest
8 environment: production
9 steps:
10 - name: Build and deploy
11 run: |
12 npm run build
13 npm run deployPattern 3: Scheduled Maintenance
Regular tasks on a schedule:
1on:
2 schedule:
3 - cron: '0 2 * * *' # 2 AM daily
4
5jobs:
6 maintenance:
7 runs-on: ubuntu-latest
8 steps:
9 - name: Dependency updates
10 run: npm update
11 - name: Cleanup old artifacts
12 run: cleanup-script.sh
13 - name: Generate reports
14 run: npm run reportsPattern 4: Manual Deployment Workflow
Controlled deployment with manual trigger:
1on:
2 workflow_dispatch:
3 inputs:
4 environment:
5 type: choice
6 options: [staging, production]
7 version:
8 description: 'Version to deploy'
9 required: true
10
11jobs:
12 deploy:
13 runs-on: ubuntu-latest
14 environment: ${{ github.event.inputs.environment }}
15 steps:
16 - name: Deploy version ${{ github.event.inputs.version }}
17 run: deploy.shPattern 5: Conditional Workflows
Run different jobs based on conditions:
1jobs:
2 check-changes:
3 outputs:
4 frontend: ${{ steps.filter.outputs.frontend }}
5 backend: ${{ steps.filter.outputs.backend }}
6 steps:
7 - uses: dorny/paths-filter@v2
8 id: filter
9 with:
10 filters: |
11 frontend:
12 - 'frontend/**'
13 backend:
14 - 'backend/**'
15
16 build-frontend:
17 needs: check-changes
18 if: needs.check-changes.outputs.frontend == 'true'
19 runs-on: ubuntu-latest
20 steps:
21 - run: npm run build:frontend
22
23 build-backend:
24 needs: check-changes
25 if: needs.check-changes.outputs.backend == 'true'
26 runs-on: ubuntu-latest
27 steps:
28 - run: npm run build:backendPattern 6: Reusable Workflows
Create DRY workflows:
.github/workflows/reusable-build.yml:
1on:
2 workflow_call:
3 inputs:
4 environment:
5 required: true
6 type: string
7 secrets:
8 api-key:
9 required: true
10
11jobs:
12 build:
13 runs-on: ubuntu-latest
14 steps:
15 - name: Build for ${{ inputs.environment }}
16 env:
17 API_KEY: ${{ secrets.api-key }}
18 run: npm run buildCall it:
1jobs:
2 staging:
3 uses: ./.github/workflows/reusable-build.yml
4 with:
5 environment: staging
6 secrets:
7 api-key: ${{ secrets.STAGING_API_KEY }}Pattern 7: Monorepo Workflows
Handle multiple packages efficiently:
1jobs:
2 changes:
3 outputs:
4 packages: ${{ steps.filter.outputs.changes }}
5 steps:
6 - uses: dorny/paths-filter@v2
7 id: filter
8 with:
9 filters: |
10 package-a:
11 - 'packages/a/**'
12 package-b:
13 - 'packages/b/**'
14
15 build:
16 needs: changes
17 strategy:
18 matrix:
19 package: ${{ fromJSON(needs.changes.outputs.packages) }}
20 runs-on: ubuntu-latest
21 steps:
22 - run: npm run build --workspace=packages/${{ matrix.package }}Pattern 8: Progressive Deployment
Canary and blue-green deployments:
1jobs:
2 deploy-canary:
3 runs-on: ubuntu-latest
4 steps:
5 - name: Deploy 10% traffic
6 run: deploy --canary 10
7
8 - name: Run smoke tests
9 run: npm run test:smoke
10
11 - name: Deploy 50% traffic
12 run: deploy --canary 50
13
14 - name: Monitor metrics
15 run: check-metrics.sh
16
17 - name: Full deployment
18 run: deploy --canary 100Best Practices:
A production-ready workflow demonstrating all GitHub Actions concepts including matrix builds, caching, artifacts, secrets, environments, and deployment strategies.
1name: Complete Production Pipeline
2
3on:
4 push:
5 branches: [main, develop]
6 tags: ['v*']
7 pull_request:
8 branches: [main]
9 schedule:
10 - cron: '0 2 * * 1' # Weekly Monday 2 AM
11 workflow_dispatch:
12 inputs:
13 environment:
14 description: 'Target environment'
15 type: choice
16 options: [staging, production]
17 required: true
18 dry_run:
19 description: 'Dry run (no actual deployment)'
20 type: boolean
21 default: false
22
23env:
24 NODE_VERSION: '18'
25 REGISTRY: ghcr.io
26 IMAGE_NAME: ${{ github.repository }}
27
28permissions:
29 contents: read
30 packages: write
31 pull-requests: write
32
33concurrency:
34 group: ${{ github.workflow }}-${{ github.ref }}
35 cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
36
37jobs:
38 # Detect what changed
39 changes:
40 runs-on: ubuntu-latest
41 outputs:
42 frontend: ${{ steps.filter.outputs.frontend }}
43 backend: ${{ steps.filter.outputs.backend }}
44 docs: ${{ steps.filter.outputs.docs }}
45 steps:
46 - uses: actions/checkout@v4
47 - uses: dorny/paths-filter@v2
48 id: filter
49 with:
50 filters: |
51 frontend:
52 - 'frontend/**'
53 backend:
54 - 'backend/**'
55 docs:
56 - 'docs/**'
57
58 # Code quality checks
59 lint:
60 runs-on: ubuntu-latest
61 needs: changes
62 if: needs.changes.outputs.frontend == 'true' || needs.changes.outputs.backend == 'true'
63 steps:
64 - uses: actions/checkout@v4
65 - uses: actions/setup-node@v4
66 with:
67 node-version: ${{ env.NODE_VERSION }}
68 cache: 'npm'
69 - run: npm ci
70 - run: npm run lint
71 - run: npm run type-check
72
73 # Test matrix across platforms
74 test:
75 runs-on: ${{ matrix.os }}
76 needs: lint
77 strategy:
78 fail-fast: false
79 matrix:
80 os: [ubuntu-latest, windows-latest]
81 node: [18, 20]
82 steps:
83 - uses: actions/checkout@v4
84 - uses: actions/setup-node@v4
85 with:
86 node-version: ${{ matrix.node }}
87 cache: 'npm'
88 - run: npm ci
89 - run: npm test -- --coverage
90 - if: matrix.os == 'ubuntu-latest' && matrix.node == '20'
91 uses: codecov/codecov-action@v4
92
93 # Build frontend and backend
94 build:
95 runs-on: ubuntu-latest
96 needs: test
97 outputs:
98 version: ${{ steps.version.outputs.value }}
99 steps:
100 - uses: actions/checkout@v4
101 - uses: actions/setup-node@v4
102 with:
103 node-version: ${{ env.NODE_VERSION }}
104 cache: 'npm'
105 - run: npm ci
106 - id: version
107 run: echo "value=$(node -p \"require('./package.json').version\")" >> $GITHUB_OUTPUT
108 - run: npm run build
109 - uses: actions/upload-artifact@v4
110 with:
111 name: build-${{ steps.version.outputs.value }}
112 path: dist/
113
114 # Docker build and push
115 docker:
116 runs-on: ubuntu-latest
117 needs: build
118 permissions:
119 contents: read
120 packages: write
121 steps:
122 - uses: actions/checkout@v4
123 - uses: docker/setup-buildx-action@v3
124 - uses: docker/login-action@v3
125 with:
126 registry: ${{ env.REGISTRY }}
127 username: ${{ github.actor }}
128 password: ${{ secrets.GITHUB_TOKEN }}
129 - uses: docker/metadata-action@v5
130 id: meta
131 with:
132 images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
133 tags: |
134 type=ref,event=branch
135 type=semver,pattern={{version}}
136 type=sha
137 - uses: docker/build-push-action@v5
138 with:
139 push: true
140 tags: ${{ steps.meta.outputs.tags }}
141 cache-from: type=gha
142 cache-to: type=gha,mode=max
143
144 # Deploy to staging
145 deploy-staging:
146 runs-on: ubuntu-latest
147 needs: [build, docker]
148 if: github.ref == 'refs/heads/develop' || github.event.inputs.environment == 'staging'
149 environment:
150 name: staging
151 url: https://staging.example.com
152 steps:
153 - uses: actions/download-artifact@v4
154 - run: echo "Deploying to staging"
155
156 # Deploy to production
157 deploy-production:
158 runs-on: ubuntu-latest
159 needs: [build, docker]
160 if: github.ref == 'refs/heads/main' || github.event.inputs.environment == 'production'
161 environment:
162 name: production
163 url: https://example.com
164 steps:
165 - uses: actions/download-artifact@v4
166 - run: echo "Deploying to production"Your progress is saved in this browser, on this device only.