GitHub Actions for CI

Implementing Continuous Integration with GitHub's Built-in CI/CD Platform

Introduction to GitHub Actions

In our previous lecture, we explored the fundamental concepts of CI/CD. Today, we'll dive deeper into GitHub Actions, a powerful CI/CD platform built directly into GitHub repositories. GitHub Actions allows developers to automate workflows directly in their GitHub repositories, making it an excellent choice for implementing continuous integration pipelines.

GitHub Actions has gained significant popularity since its introduction due to its tight integration with GitHub repositories, extensive marketplace of pre-built actions, and flexible configuration options. In this lecture, we'll learn how to use GitHub Actions to create effective CI pipelines for your projects.

GitHub Actions in the CI/CD Ecosystem

flowchart LR A[GitHub Repository] --> B[GitHub Actions] B --> C[Build] B --> D[Test] B --> E[Deploy] B --> F[Other Automations]

GitHub Actions Key Concepts

Before diving into implementation details, let's understand the key components that make up GitHub Actions:

Workflows

A workflow is an automated process that you set up in your repository to build, test, package, release, or deploy your code. Workflows are defined in YAML files stored in the .github/workflows directory of your repository. A repository can have multiple workflows, each handling different aspects of your automation needs.

Events

Events are specific activities that trigger a workflow. For example, a workflow can be triggered when someone pushes code to a repository, opens a pull request, creates an issue, or based on a schedule. You can configure workflows to run for one or multiple events.

Common Trigger Events:
  • push: When commits are pushed to the repository
  • pull_request: When a pull request is opened, updated, or closed
  • schedule: At a scheduled time
  • workflow_dispatch: Manual trigger via GitHub UI
  • repository_dispatch: External event via API call

Jobs

A job is a set of steps that execute on the same runner (a virtual machine). Jobs can run independently of each other or sequentially, depending on how they're configured. You can set up dependencies between jobs, so one job waits for another to complete before it starts.

Steps

A step is an individual task that can run commands or actions. Each job consists of one or more steps that are executed in order. Steps can run commands, setup tasks, or run an action.

Actions

Actions are reusable units of code that can be used in your workflows. Think of them as building blocks that you can combine to create custom workflows. Actions can be created by GitHub, by the community, or you can create your own.

Runners

A runner is a server that runs your workflows when they're triggered. Each runner can run a single job at a time. GitHub provides runners for Linux, Windows, and macOS, or you can host your own runners.

GitHub Actions Components

flowchart TD A[Workflow] --> B[Job 1] A --> C[Job 2] B --> D[Step 1.1] B --> E[Step 1.2] C --> F[Step 2.1] C --> G[Step 2.2] D --> H[Action or Command] E --> I[Action or Command] F --> J[Action or Command] G --> K[Action or Command]

Creating Your First GitHub Actions Workflow

Let's create a simple CI workflow for a JavaScript project using GitHub Actions. This workflow will run whenever code is pushed to the repository or when a pull request is created. It will install dependencies, run linting, and execute tests.

Step 1: Create the Workflow File

In your repository, create a new file at .github/workflows/ci.yml with the following content:


name: CI

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

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linting
      run: npm run lint
    
    - name: Run tests
      run: npm test
                

Breaking Down the Workflow

Let's analyze each part of this workflow:

Workflow Name
name: CI

This sets the name of the workflow, which will be displayed in the GitHub UI.

Triggers

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

This configures the workflow to run on two events: when code is pushed to the main branch, and when a pull request targeting the main branch is created or updated.

Jobs

jobs:
  build:
    runs-on: ubuntu-latest
                

This defines a job named "build" that will run on the latest Ubuntu runner provided by GitHub.

Steps

steps:
  - uses: actions/checkout@v3
                

The first step uses the checkout action to clone the repository code to the runner.


  - name: Set up Node.js
    uses: actions/setup-node@v3
    with:
      node-version: '18'
      cache: 'npm'
                

This step sets up Node.js on the runner, using version 18, and configures caching for npm packages to speed up future runs.


  - name: Install dependencies
    run: npm ci
                

This step installs the project dependencies using npm ci, which is a faster and more reliable way to install dependencies in CI environments than npm install.


  - name: Run linting
    run: npm run lint
                

This step runs the linting command defined in the project's package.json file.


  - name: Run tests
    run: npm test
                

This step executes the project's tests using the test command defined in the package.json file.

Advanced Workflow Configurations

Now that we understand the basics, let's explore some more advanced configurations that can make our CI workflows more powerful and efficient.

Matrix Builds

Matrix builds allow you to test your code against multiple versions of languages, operating systems, or other dependencies. This is useful for ensuring your code works across different environments.


name: CI

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

jobs:
  test:
    runs-on: ${{ matrix.os }}
    
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16.x, 18.x, 20.x]
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
                

This workflow will run tests on 9 different combinations: 3 operating systems × 3 Node.js versions.

Workflow Dependencies

You can create dependencies between jobs to control the execution order. This is useful when one job depends on the output of another.


name: CI

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build
      run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-artifacts
        path: build/
  
  test:
    needs: build
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Download build artifacts
      uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build/
    
    - name: Run tests
      run: npm test
                

In this example, the test job will only run after the build job has successfully completed. The build artifacts are passed between jobs using the upload-artifact and download-artifact actions.

Conditional Execution

You can use conditions to control whether a job or step should run. This is useful for creating workflows that behave differently based on the event that triggered them.


name: CI

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

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test
    
    - name: Build
      run: npm run build
    
    - name: Deploy to production
      if: github.event_name == 'push' && github.ref == 'refs/heads/main'
      run: |
        echo "Deploying to production..."
        # Add deployment commands here
                

In this example, the deployment step will only run when code is pushed to the main branch, not when a pull request is created or updated.

Environment Variables

You can use environment variables to pass data between steps or to configure your workflow.


name: CI

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    env:
      NODE_ENV: production
      API_URL: https://api.example.com
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Set build version
      run: echo "BUILD_VERSION=$(date +'%Y.%m.%d.%H%M%S')" >> $GITHUB_ENV
    
    - name: Build with version
      run: |
        echo "Building version: $BUILD_VERSION"
        npm run build
      env:
        DEBUG: false
                

This workflow sets environment variables at different levels:

Managing Secrets in GitHub Actions

Many CI/CD workflows require access to sensitive information such as API keys, passwords, or tokens. GitHub Actions provides a secure way to store and use these secrets without exposing them in your workflow files.

Adding Secrets to Your Repository

  1. Go to your repository on GitHub
  2. Click on "Settings"
  3. In the left sidebar, click on "Secrets and variables" then "Actions"
  4. Click on "New repository secret"
  5. Enter a name for your secret (e.g., API_KEY) and its value
  6. Click "Add secret"

Using Secrets in Workflows

Once you've added a secret to your repository, you can use it in your workflows using the secrets context:


name: Deploy

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Deploy to server
      uses: some-deploy-action@v1
      with:
        username: ${{ secrets.SERVER_USERNAME }}
        password: ${{ secrets.SERVER_PASSWORD }}
        host: ${{ secrets.SERVER_HOST }}
    
    - name: API authentication
      run: |
        curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" https://api.example.com/deploy
                
⚠️ Security Warning

Always be careful when working with secrets:

  • Never print secrets in logs (even masked, they can be reconstructed)
  • Limit secret access to only the steps that need them
  • Be cautious with third-party actions that might access your secrets
  • Rotate secrets regularly

Leveraging the GitHub Actions Marketplace

One of the biggest advantages of GitHub Actions is the vast ecosystem of pre-built actions available in the GitHub Marketplace. These actions can save you time and effort by providing ready-to-use functionality for common CI/CD tasks.

Finding Actions in the Marketplace

  1. Go to the GitHub Marketplace
  2. Browse or search for actions relevant to your needs
  3. Click on an action to view its documentation and usage examples
  4. Incorporate the action into your workflow using the uses keyword

Popular Actions for CI Workflows

Optimizing CI Workflows

As your projects grow, optimizing your CI workflows becomes increasingly important to maintain fast feedback cycles and efficient resource usage.

Caching Dependencies

Caching dependencies can significantly reduce workflow execution time by reusing previously downloaded packages.


- name: Cache node modules
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
                
💡 Pro Tip

Many setup actions (like actions/setup-node) include built-in caching capabilities. Use these when available for simpler workflows.

Selective Testing

Run tests only when relevant files have changed to save time in large projects.


name: CI

on:
  push:
    paths:
      - 'src/**'
      - 'tests/**'
      - 'package.json'
      - 'package-lock.json'
      - '.github/workflows/**'
                

Parallel Jobs

Split large test suites into parallel jobs to reduce overall execution time.


jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests (shard ${{ matrix.shard }}/4)
      run: npm test -- --shard=${{ matrix.shard }}/4
                

Workflow Concurrency

Limit concurrent workflow runs to prevent resource contention and unnecessary executions.


name: CI

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

# Cancel in-progress runs when a new workflow with the same concurrency group is triggered
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    # ...
                

Advanced CI Patterns with GitHub Actions

Let's explore some advanced CI patterns that can be implemented with GitHub Actions to enhance your development workflow.

PR Checks and Status Reporting

GitHub Actions automatically reports the status of workflow runs on pull requests, but you can enhance this with more detailed checks.


name: PR Checks

on:
  pull_request:
    branches: [ main ]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    - name: Install dependencies
      run: npm ci
    - name: Run linting
      run: npm run lint
  
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    - name: Install dependencies
      run: npm ci
    - name: Run tests
      run: npm test
  
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    - name: Install dependencies
      run: npm ci
    - name: Build
      run: npm run build
                

This workflow creates three separate jobs (lint, test, build) that run in parallel, providing more granular status reporting on pull requests.

Code Coverage Reporting

Integrate code coverage reporting into your CI workflow to track test coverage over time.


name: CI with Coverage

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests with coverage
      run: npm test -- --coverage
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
                

Scheduled Security Scans

Run security scans on a regular schedule to identify vulnerabilities.


name: Security Scan

on:
  schedule:
    # Run at 1:00 AM every day
    - cron: '0 1 * * *'
  workflow_dispatch:

jobs:
  security-scan:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run security audit
      run: npm audit --audit-level=high
    
    - name: Run SAST scan
      uses: github/codeql-action/analyze@v2
      with:
        languages: javascript
                

Automated Dependency Updates

Use GitHub's Dependabot to automatically create pull requests for dependency updates.

.github/dependabot.yml

version: 2
updates:
  # Enable version updates for npm
  - package-ecosystem: "npm"
    # Look for `package.json` and `lock` files in the `root` directory
    directory: "/"
    # Check for updates once a week
    schedule:
      interval: "weekly"
    # Limit the number of open PRs
    open-pull-requests-limit: 10
                

Real-World Example: Complete CI Pipeline

Let's put everything together and create a comprehensive CI pipeline for a modern JavaScript application.

.github/workflows/ci.yml

name: CI Pipeline

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

# Cancel in-progress runs on new pushes
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Lint code
      run: npm run lint
  
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [16.x, 18.x, 20.x]
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests with coverage
      run: npm test -- --coverage
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
  
  security:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run security audit
      run: npm audit --audit-level=high
    
    - name: Run SAST scan
      uses: github/codeql-action/init@v2
      with:
        languages: javascript
    
    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
  
  build:
    needs: [lint, test, security]
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build application
      run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-artifacts
        path: build/
  
  deploy-staging:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    needs: [build]
    runs-on: ubuntu-latest
    environment: staging
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Download build artifacts
      uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build/
    
    - name: Deploy to staging
      run: |
        echo "Deploying to staging environment..."
        # Add deployment commands here
      env:
        DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
                

This comprehensive CI pipeline includes:

Using Self-Hosted Runners

While GitHub-hosted runners are convenient, there are cases where you might want to use self-hosted runners:

Setting Up a Self-Hosted Runner

  1. Go to your repository on GitHub
  2. Click on "Settings"
  3. In the left sidebar, click on "Actions" then "Runners"
  4. Click "New self-hosted runner"
  5. Choose your operating system and architecture
  6. Follow the instructions to download, configure, and run the runner

Using Self-Hosted Runners in Workflows


name: CI on Self-Hosted Runner

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: self-hosted
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Build
      run: ./build.sh
                
⚠️ Security Considerations

When using self-hosted runners, be aware of the following security considerations:

  • Runners have access to their environment, including network, disk, and other resources
  • Public repositories can submit pull requests that execute code on your self-hosted runners
  • Malicious code in a workflow could compromise the runner environment

For these reasons, it's recommended to only use self-hosted runners with private repositories or with careful security controls in place.

Debugging GitHub Actions Workflows

When your workflows don't run as expected, here are some strategies for debugging:

Enabling Debug Logging

You can enable debug logging to get more detailed information about workflow execution:

  1. Set the ACTIONS_RUNNER_DEBUG secret to true in your repository
  2. Set the ACTIONS_STEP_DEBUG secret to true in your repository

Adding Diagnostic Steps

Add diagnostic steps to your workflow to print information about the environment:


- name: Debug info
  run: |
    echo "GitHub ref: ${{ github.ref }}"
    echo "GitHub event name: ${{ github.event_name }}"
    echo "GitHub workspace: ${{ github.workspace }}"
    echo "Job status: ${{ job.status }}"
    ls -la
    env
                

Using the GitHub CLI

The GitHub CLI can be useful for debugging workflows:


# List workflow runs
gh run list

# View details of a specific run
gh run view RUN_ID

# View logs of a specific run
gh run view RUN_ID --log

# Download logs
gh run download RUN_ID
                

Common Issues and Solutions

Issue Possible Causes Solutions
Workflow not triggering
  • Path filters exclude changed files
  • Branch filters don't match
  • Syntax errors in workflow file
  • Check event triggers and filters
  • Validate workflow syntax
  • Check repository settings
Steps failing
  • Missing dependencies
  • Incorrect environment setup
  • Script errors
  • Check step logs
  • Add debugging steps
  • Test commands locally
Slow workflows
  • Missing caching
  • Inefficient testing strategy
  • Resource limitations
  • Implement caching
  • Parallelize jobs
  • Optimize test execution

GitHub Actions Best Practices

To get the most out of GitHub Actions, follow these best practices:

Keep Workflows Simple and Focused

Create multiple workflows for different purposes rather than one large workflow that does everything. This makes maintenance easier and allows for more targeted executions.

Use Reusable Workflows

Extract common job patterns into reusable workflows to reduce duplication and improve maintainability.

.github/workflows/reusable-build.yml

name: Reusable build workflow

on:
  workflow_call:
    inputs:
      node-version:
        required: false
        default: '18'
        type: string

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: ${{ inputs.node-version }}
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build
      run: npm run build
                    
Using the reusable workflow

name: CI

on:
  push:
    branches: [ main ]

jobs:
  call-build-workflow:
    uses: ./.github/workflows/reusable-build.yml
    with:
      node-version: '20'
                    

Pin Action Versions

Always pin actions to a specific version (preferably a SHA) to ensure consistent behavior and prevent unexpected changes.


# Good: Pinned to a specific version
- uses: actions/checkout@v3.5.3

# Better: Pinned to a specific SHA
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608

# Avoid: Using 'latest' or 'master'
- uses: actions/checkout@master  # Not recommended
                    

Minimize Secrets Usage

Limit the use of secrets to only the steps that need them, and use the least privileged tokens possible.

Implement Proper Error Handling

Use conditional steps and error handling to make workflows more robust.


- name: Build
  id: build
  continue-on-error: true
  run: npm run build

- name: Notify on build failure
  if: steps.build.outcome == 'failure'
  run: |
    echo "Build failed, sending notification..."
    # notification logic here
                    

Optimize for Speed

Implement caching, parallelize jobs, and use efficient testing strategies to keep workflows fast.

Document Workflows

Use comments and descriptive names for workflows, jobs, and steps to make them easier to understand and maintain.


name: CI Pipeline

# This workflow handles continuous integration for the project
# It runs linting, testing, and building on pushes to main and pull requests

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

jobs:
  # Run linting to ensure code quality
  lint:
    # ...
                    

Practical Activity: Creating a CI Pipeline with GitHub Actions

Now it's time to apply what you've learned by creating a CI pipeline for a real project using GitHub Actions.

Prerequisites

Step 1: Create a React Application

If you don't already have a React project, create one and push it to GitHub:


# Create a new React app
npx create-react-app my-ci-project

# Initialize Git repository
cd my-ci-project
git init
git add .
git commit -m "Initial commit"

# Create a repository on GitHub and push to it
git remote add origin https://github.com/yourusername/my-ci-project.git
git push -u origin main
                

Step 2: Create a Basic CI Workflow

Create a GitHub Actions workflow file in your repository:


# Create the workflow directory
mkdir -p .github/workflows

# Create the workflow file
touch .github/workflows/ci.yml
                

Add the following content to the workflow file:


name: CI

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

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test -- --watchAll=false
    
    - name: Build
      run: npm run build
                

Step 3: Commit and Push the Workflow


git add .github/workflows/ci.yml
git commit -m "Add CI workflow"
git push
                

Step 4: Check Workflow Execution

  1. Go to your repository on GitHub
  2. Click on the "Actions" tab
  3. You should see your workflow running or completed
  4. Click on the workflow run to see details

Step 5: Enhance the Workflow

Now, enhance your workflow with additional features:


name: CI

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

jobs:
  lint:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run linting
      run: npm run lint
      
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Run tests
      run: npm test -- --watchAll=false --coverage
    
    - name: Upload coverage report
      uses: actions/upload-artifact@v3
      with:
        name: coverage-report
        path: coverage/
  
  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'
    
    - name: Install dependencies
      run: npm ci
    
    - name: Build
      run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build
        path: build/
                

Step 6: Create a Branch and Test Pull Request Integration


# Create a new branch
git checkout -b feature/update-readme

# Make a change
echo "# CI Demo" > README.md

# Commit and push
git add README.md
git commit -m "Update README"
git push -u origin feature/update-readme
                
  1. Go to your repository on GitHub
  2. Create a pull request from your feature branch to main
  3. Observe the CI workflow running on your pull request
  4. Note how the workflow status is displayed on the pull request

Key Takeaways

Further Resources

Homework Assignment

For your homework, you'll create a comprehensive CI pipeline using GitHub Actions for your own project:

Assignment Requirements

  1. Choose a project to implement CI:
    • Your final project application
    • A personal project
    • A sample application provided by the instructor
  2. Create a GitHub Actions workflow that includes:
    • Linting for code quality
    • Unit and integration tests
    • Building the application
    • At least two advanced features (matrix builds, caching, artifacts, environment deployments, etc.)
  3. Create a pull request to demonstrate CI integration:
    • Branch from main/master
    • Make a small change
    • Create a pull request
    • Show the CI workflow running on the pull request
  4. Document your implementation:
    • Explain your workflow design
    • Describe the advanced features you implemented
    • Discuss any challenges you encountered and how you solved them
    • Suggest future improvements to your CI pipeline

Submission Guidelines

Due Date

Submit your completed assignment before our next class session.