Skip to content

CI/CD Pipeline

CI/CD Pipeline #48

name: CI/CD Pipeline
on:
schedule:
- cron: '0 1 * * *' # Daily security scans
push:
tags:
- "[1-9]+.[0-9]+.[0-9]+"
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Top-level permissions used as default for all jobs
permissions:
contents: write
packages: write
issues: write
pull-requests: write
security-events: write # Required for uploading Trivy scan results
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
security-scan:
runs-on: ubuntu-latest
# Job-specific permissions override - this job only needs security-events
permissions:
security-events: write # Required for github/codeql-action/upload-sarif
contents: read # Required for actions/checkout
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
lint:
runs-on: ubuntu-latest
permissions:
contents: read # Only needs read access for checkout
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run hadolint
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
- name: Run shellcheck
uses: ludeeus/action-shellcheck@master
with:
scandir: './scripts'
build-and-push:
needs: [security-scan, lint]
runs-on: ubuntu-latest
permissions:
contents: write # Changed from 'read' to 'write' for changelog commits
packages: write # For pushing to GHCR
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
docker.io/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=sha,format=long
type=ref,event=branch
type=ref,event=pr
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Generate changelog
if: github.event_name != 'pull_request'
uses: orhun/git-cliff-action@v4
id: git-cliff
with:
config: cliff.toml
args: --verbose --tag ${{ steps.meta.outputs.version }}
env:
OUTPUT: CHANGELOG.md
- name: Commit and push changelog
if: github.event_name != 'pull_request'
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'docs: update changelog for ${{ steps.meta.outputs.version }}'
file_pattern: CHANGELOG.md
branch: main
push_options: '--force'
notify:
needs: build-and-push
runs-on: ubuntu-latest
if: always()
permissions:
issues: write # Required for creating issue comments
pull-requests: write # Required for creating PR comments
steps:
- name: Notify status
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { repo, owner } = context.repo;
const run_id = context.runId;
const run_url = `https://github.com/${owner}/${repo}/actions/runs/${run_id}`;
const status = '${{ needs.build-and-push.result }}' === 'success' ? '✅' : '❌';
if (context.issue.number) {
await github.rest.issues.createComment({
owner,
repo,
issue_number: context.issue.number,
body: `Build status: ${status}\nDetails: ${run_url}`
});
}