GitHub Actions

CI/CDGitHubYAMLAutomationDevOpsWorkflowCloudMarketplace

CI/CD Tool

GitHub Actions

Overview

GitHub Actions is a GitHub-native CI/CD platform. With YAML-based workflow configuration, rich marketplace, and seamless GitHub integration, it provides comprehensive automation from development to deployment.

Details

GitHub Actions is a GitHub platform-integrated CI/CD (Continuous Integration/Continuous Delivery) service provided by Microsoft. Since its release in 2019, it has played a central role in automating software development lifecycles. By defining workflows in YAML files within the .github/workflows/ directory of repositories, you can automate code building, testing, and deployment. GitHub Marketplace offers over 11,000 reusable actions, supporting a wide range of languages and frameworks including Node.js, Python, Java, and .NET. It provides Linux, macOS, Windows, ARM, and GPU runners, enabling efficient CI/CD pipeline construction through matrix builds and parallel execution. It also supports self-hosted runners, enabling flexible operation in enterprise environments. Since 2024, security features have been enhanced, providing secure development processes through Artifact Attestations, encrypted secrets, and dependency scanning.

Pros and Cons

Pros

  • Complete GitHub Integration: Seamless connectivity with repositories, Issues, and Pull Requests
  • Rich Marketplace: Over 11,000 reusable actions available
  • Diverse Execution Environments: Support for Linux, macOS, Windows, ARM, and GPU runners
  • Free Tier: Unlimited for public repositories, 2,000 minutes monthly for private repositories
  • Scalability: High-speed processing through matrix builds and parallel execution
  • Security: Support for encrypted secrets and Artifact Attestations
  • Flexibility: Self-hosted runners and custom action creation
  • Version-Controlled Configuration: Workflow settings managed through YAML files

Cons

  • GitHub Lock-in: Cannot be used on platforms other than GitHub
  • Cost: High cost for large-scale use with private repositories
  • Learning Curve: Need to understand YAML syntax and workflow concepts
  • Complex Debugging: Difficult to identify causes of workflow failures
  • Execution Time Limits: Maximum 6-hour limit per job
  • Storage Limitations: Limited retention period for artifacts and cache
  • Network Constraints: Restrictions and rate limits on external API access

Key Links

Code Examples

Basic Node.js Workflow

# .github/workflows/node.yml
name: Node.js CI

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

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x, 20.x, 22.x]

    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test

TypeScript Project CI/CD

# .github/workflows/typescript.yml
name: TypeScript CI/CD

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

env:
  NODE_VERSION: '20.x'

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Type check
      run: npm run type-check
      
    - name: Lint
      run: npm run lint
      
    - name: Run tests
      run: npm run test:coverage
      
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage/lcov.info

  build:
    needs: lint-and-test
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ env.NODE_VERSION }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v4
      with:
        name: build-files
        path: dist/
        retention-days: 30

Docker Image Build and Deploy

# .github/workflows/docker.yml
name: Docker Build and Deploy

on:
  push:
    branches: [main]
    tags: ['v*']

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

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      
    - name: Log in to Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
        
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
          
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Deploy to production
      uses: appleboy/[email protected]
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.SSH_KEY }}
        script: |
          docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
          docker stop myapp || true
          docker rm myapp || true
          docker run -d --name myapp -p 80:3000 ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main

Multi-Platform Support

# .github/workflows/multi-platform.yml
name: Multi-Platform CI

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

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.9', '3.10', '3.11', '3.12']
        exclude:
          - os: macos-latest
            python-version: '3.9'
          - os: windows-latest
            python-version: '3.12'
            
    runs-on: ${{ matrix.os }}
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest
        
    - name: Run tests
      run: pytest tests/ -v
      
    - name: Platform-specific commands
      shell: bash
      run: |
        if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
          echo "Running Ubuntu-specific commands"
          sudo apt-get update
        elif [[ "${{ matrix.os }}" == "windows-latest" ]]; then
          echo "Running Windows-specific commands"
          choco --version
        elif [[ "${{ matrix.os }}" == "macos-latest" ]]; then
          echo "Running macOS-specific commands"
          brew --version
        fi

Conditional Workflows and Release Automation

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
        
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '20.x'
        registry-url: 'https://registry.npmjs.org'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Build
      run: npm run build
      
    - name: Run tests
      run: npm test
      
    - name: Generate changelog
      id: changelog
      run: |
        # Generate changelog from git commits
        echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
        git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:"- %s (%h)" >> $GITHUB_OUTPUT
        echo "EOF" >> $GITHUB_OUTPUT
        
    - name: Create GitHub Release
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        tag_name: ${{ github.ref_name }}
        release_name: Release ${{ github.ref_name }}
        body: |
          ## Changes in this release
          ${{ steps.changelog.outputs.CHANGELOG }}
        draft: false
        prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }}
        
    - name: Publish to NPM
      if: ${{ !contains(github.ref_name, 'beta') && !contains(github.ref_name, 'alpha') }}
      run: npm publish
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Security Scanning and Code Quality

# .github/workflows/security.yml
name: Security and Quality

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 1' # Every Monday

jobs:
  security:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Run dependency vulnerability scan
      uses: actions/dependency-review-action@v3
      if: github.event_name == 'pull_request'
      
    - name: Run CodeQL Analysis
      uses: github/codeql-action/init@v2
      with:
        languages: javascript
        
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2
      
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
      
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
        
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: 'trivy-results.sarif'