Unit 5.4 · Tutorial 1

CI Pipelines (GitHub Actions / GitLab CI)

Chapter 15 · System Design & Deployment
~2.5 hours Intermediate CI · GitHub Actions · GitLab CI · Jenkins

Overview

Continuous Integration (CI) automates the building, testing, and validation of code changes. This tutorial covers the most popular CI tools: GitHub Actions, GitLab CI, and Jenkins. You'll learn to write CI pipelines that catch bugs early, enforce code quality, and accelerate development cycles.

Why this matters: CI catches issues before they reach production, reduces manual work, and gives developers fast feedback. It's the foundation of modern software delivery.

1. Introduction to CI

Continuous Integration is the practice of automatically building and testing every code change that is pushed to the repository.

Key benefits

  • Early bug detection: Issues are caught before they reach production.
  • Faster feedback: Developers know within minutes if their changes break anything.
  • Reduced manual work: Automated builds and tests replace manual steps.
  • Consistent builds: Every build runs in a clean environment.

CI Pipeline Stages

  • Checkout: Clone the repository.
  • Install: Install dependencies.
  • Lint: Check code style and quality.
  • Test: Run unit and integration tests.
  • Build: Compile or package the application.
  • Deploy: (Optional, often part of CD).
// Sample CI pipeline stages Stage 1: Checkout code Stage 2: Install dependencies Stage 3: Lint (ESLint, Prettier) Stage 4: Run tests (Jest, Vitest) Stage 5: Build application Stage 6: Upload artifacts

2. GitHub Actions

GitHub Actions is a CI/CD platform integrated with GitHub repositories.

# .github/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: 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: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Lint run: npm run lint - name: Run tests run: npm run test - name: Build run: npm run build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build path: dist/

Key concepts

  • Workflow: A YAML file defining the CI pipeline.
  • Job: A set of steps that run on a runner.
  • Step: A single action or shell command.
  • Action: Reusable pieces of code (official or community).
  • Runner: The machine where jobs run (GitHub‑hosted or self‑hosted).

3. GitLab CI

GitLab CI is integrated with GitLab repositories and offers a powerful pipeline system.

# .gitlab-ci.yml stages: - test - build - deploy variables: NODE_VERSION: 18 test: stage: test image: node:${NODE_VERSION} script: - npm ci - npm run lint - npm run test artifacts: reports: junit: reports/junit.xml build: stage: build image: node:${NODE_VERSION} script: - npm ci - npm run build artifacts: paths: - dist/ deploy: stage: deploy image: node:${NODE_VERSION} script: - npm run deploy only: - main

Key concepts

  • Stages: Define the order of pipeline execution.
  • Jobs: Tasks that run within stages.
  • Runners: Agents that execute jobs.
  • Artifacts: Files passed between jobs.
  • Environment variables: Configure jobs dynamically.

4. Jenkins

Jenkins is a self‑hosted CI/CD server with extensive plugin support.

// Jenkinsfile (Declarative Pipeline) pipeline { agent any environment { NODE_VERSION = '18' } stages { stage('Checkout') { steps { checkout scm } } stage('Install') { steps { sh 'npm ci' } } stage('Test') { steps { sh 'npm run test' } } stage('Build') { steps { sh 'npm run build' } } stage('Deploy') { when { branch 'main' } steps { sh 'npm run deploy' } } } post { failure { emailext ( subject: "Build failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}", body: "Please check the build logs.", to: 'team@example.com' ) } } }
Comparison:
  • GitHub Actions: Integrated with GitHub, easy to use, extensive marketplace.
  • GitLab CI: Integrated with GitLab, powerful, self‑hosted option.
  • Jenkins: Self‑hosted, highly customisable, mature ecosystem.

5. CI Best Practices

  • Keep pipelines fast: Optimise build times (caching, parallel jobs).
  • Fail fast: Run linting and quick tests before expensive builds.
  • Use caching: Cache dependencies to speed up builds.
  • Run on pull requests: Test every PR before merging.
  • Secure secrets: Use environment variables or secret managers.
  • Monitor builds: Set up notifications for failures.
# Caching dependencies (GitHub Actions) - name: Cache npm dependencies uses: actions/cache@v3 with: path: node_modules key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} restore-keys: | ${{ runner.os }}-node- # Parallel jobs test: strategy: matrix: node-version: [16, 18, 20]

Quiz

Question 1

What is the primary purpose of Continuous Integration?

  • To deploy to production automatically
  • To automatically build and test every code change
  • To monitor application performance
  • To manage team permissions
Show answer
B. To automatically build and test every code change.

Question 2

Which file defines a GitHub Actions workflow?

  • .github/workflows/ci.yml
  • .gitlab-ci.yml
  • Jenkinsfile
  • docker-compose.yml
Show answer
A. .github/workflows/ci.yml.

Question 3

What is a Jenkinsfile used for?

  • Defining a Docker container
  • Defining a Jenkins pipeline as code
  • Configuring GitHub Actions
  • Writing tests
Show answer
B. Defining a Jenkins pipeline as code.

Exercises

Exercise 1

Write a GitHub Actions workflow that lints, tests, and builds a React application on every push to main.

Sample answer
name: CI on: push: branches: [main] jobs: build: 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 run test - run: npm run build

Exercise 2

Convert the GitHub Actions workflow above to GitLab CI format.

Sample answer
stages: - test - build variables: NODE_VERSION: 18 test: stage: test image: node:${NODE_VERSION} script: - npm ci - npm run lint - npm run test build: stage: build image: node:${NODE_VERSION} script: - npm ci - npm run build artifacts: paths: - dist/

Homework

Homework 1

Set up a CI pipeline for your capstone project using GitHub Actions or GitLab CI. Include linting, testing, and building. Document the workflow file and any challenges you encountered.

Sample outline
  • Tool: GitHub Actions.
  • Stages: Checkout, Install, Lint, Test, Build.
  • Challenges: Environment variables, caching, test coverage.

Mini‑Project

CI Pipeline for Full‑Stack App

Build a comprehensive CI pipeline for a full‑stack application that:

  • Runs linting and tests for both frontend and backend
  • Builds both applications
  • Runs integration tests
  • Checks test coverage and fails if below threshold
  • Uploads build artifacts for later use
  • Runs on both push and pull request
Sample outline
  • Frontend: React app with Jest tests.
  • Backend: Node.js API with Jest tests.
  • Pipeline: Install → Lint → Test → Build → Upload artifacts.
  • Coverage: Set threshold of 80%.

Tutorial Summary

You learned the fundamentals of Continuous Integration using GitHub Actions, GitLab CI, and Jenkins. You can now write CI pipelines that automatically lint, test, and build your code, giving you fast feedback and ensuring code quality.

Key takeaway: CI is the first step towards automated software delivery. It reduces bugs, improves code quality, and frees developers from manual tasks.