Filters
How to search
  • Plain words find names, texts and examples.
  • "two words" finds the exact phrase.
  • --name or -x finds an option.
  • #word looks only inside commands.
  • @grep finds lessons and labs that use grep.
  • Filters: cat:network · in:labs · in:notes · level:rhcsa · ch:8 · root:yes. Put ! before a word to leave it out. OR between words means one of them.
  • ↑ ↓ to move · Enter to open · Esc to close · / to search

Chapter 6Name in the course: Continuous integration

Build a CI pipeline that tests and scans

Continuous integration checks every change automatically: lint, test, build, scan. A pull request can merge only when all checks are green. This chapter builds a full CI workflow for a PHP or Python app with a database service and a container image.

  • 5 Lessons
  • 0 Commands

$ run as a normal user# run as root (or with sudo)

6.1

Lint and test the code

Fast checks first (lint), then tests. Fail early, fail cheap.

.github/workflows/ci.yml
name: CI
on:
  pull_request:
  push:
    branches: [main]
permissions:
  contents: read
jobs:
  lint-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: pip
      - run: pip install -r requirements.txt -r requirements-dev.txt
      - run: ruff check .
      - run: pytest -q --junitxml=report.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-report
          path: report.xml

6.2

Test with a real database

Service containers start a database next to the job, for integration tests.

.github/workflows/ci.yml
jobs:
  integration:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: shop
        ports: ["5432:5432"]
        options: >-
          --health-cmd "pg_isready -U postgres"
          --health-interval 5s --health-retries 10
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/integration-test.sh
        env:
          DATABASE_URL: postgres://postgres:test@localhost:5432/shop

6.3

Build and scan the container image

Build the image, scan it for known vulnerabilities, and only push it when the scan passes.

.github/workflows/image.yml
jobs:
  image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/build-push-action@v6
        with:
          context: .
          load: true
          tags: shop:${{ github.sha }}
      - uses: aquasecurity/trivy-action@0.28.0
        with:
          image-ref: shop:${{ github.sha }}
          severity: CRITICAL,HIGH
          exit-code: "1"
          ignore-unfixed: true
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: ${{ github.ref == 'refs/heads/main' }}
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}

6.4

Scan code and dependencies

Security scans belong in CI: code (SAST), dependencies (SCA), infrastructure code (IaC scan) and secrets.

ScanTool examples
Code (SAST)CodeQL, Semgrep
Dependencies (SCA)Dependabot alerts, pip-audit, npm audit, Trivy fs
IaCTrivy config, Checkov, tfsec rules
SecretsGitleaks, GitHub secret scanning
Container imageTrivy, Grype
.github/workflows/codeql.yml
on:
  push: { branches: [main] }
  pull_request:
  schedule: [{ cron: "0 3 * * 1" }]
permissions:
  contents: read
  security-events: write
jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v3
        with: { languages: python }
      - uses: github/codeql-action/analyze@v3

6.5

Make checks required

A check that is not required can be ignored. Add the important jobs to the branch ruleset of main (see Protect the main branch).

Remember

Name jobs clearly (lint-test, image, codeql) — the required checks are chosen by job name.