You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

168 lines
6.8 KiB

pipeline {
agent any
environment {
REGISTRY = '192.168.1.11:5000'
IMAGE_NAME = 'cammonitor'
// Tag with the short git commit SHA; also push 'latest' on the main branch.
IMAGE_TAG = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
// TRIVY_SEVERITY controls which finding levels fail the build.
// Set to 'CRITICAL' to only fail on critical CVEs, or remove the
// --exit-code flag entirely to make the scan advisory-only.
TRIVY_SEVERITY = 'HIGH,CRITICAL'
}
options {
// Keep only the last 10 builds to save disk space on the Atom server.
buildDiscarder(logRotator(numToKeepStr: '10'))
timestamps()
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
// Builds the image defined in Dockerfile using the same flags
// that docker compose would use, but targets the registry tag directly.
sh """
docker build \
--tag ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} \
--tag ${REGISTRY}/${IMAGE_NAME}:latest \
.
"""
}
}
stage('Test') {
// Spin up a throw-away container and hit /health to confirm the binary
// starts correctly before we push anything.
steps {
sh """
docker run --rm -d \
--name cammonitor-ci-${BUILD_NUMBER} \
-p 19080:8080 \
-e FOOTAGE_ROOT=/tmp \
-e DB_PATH=/tmp/cammonitor-ci.db \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
# Give the process a moment to bind the port, then check health.
sleep 3
curl --fail --silent --show-error http://127.0.0.1:19080/health
"""
}
post {
always {
sh 'docker stop cammonitor-ci-${BUILD_NUMBER} || true'
}
}
}
stage('Scan') {
// Trivy scans the locally built image for OS and library CVEs before
// anything is pushed to the registry.
//
// Trivy must be installed on the Jenkins agent:
// https://aquasecurity.github.io/trivy/latest/getting-started/installation/
// e.g. apt install trivy or brew install trivy
//
// The vulnerability DB is cached at ~/.cache/trivy between builds.
// On air-gapped networks set TRIVY_NO_PROGRESS=true and pre-download
// the DB: https://aquasecurity.github.io/trivy/latest/docs/advanced/air-gap/
steps {
// --exit-code 1 makes the stage (and build) fail when findings at
// or above TRIVY_SEVERITY are detected. Remove it to make the scan
// informational only.
sh """
trivy image \
--severity ${TRIVY_SEVERITY} \
--exit-code 1 \
--no-progress \
--ignore-unfixed \
--format table \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
"""
// --ignore-unfixed suppresses findings where Debian maintainers have
// marked the CVE as will_not_fix or fix_deferred with no fixed version
// available in the repo (libexpat1, ncurses, perl-base, zlib1g).
// These cannot be upgraded away — blocking on them produces no action.
}
post {
always {
// Also emit a machine-readable SARIF report so results can be
// ingested by Jenkins Warnings NG or uploaded to GitHub Advanced
// Security if you ever connect this repo there.
sh """
trivy image \
--severity ${TRIVY_SEVERITY} \
--exit-code 0 \
--no-progress \
--ignore-unfixed \
--format sarif \
--output trivy-report.sarif \
${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
"""
archiveArtifacts artifacts: 'trivy-report.sarif', allowEmptyArchive: true
}
}
}
stage('Push') {
// NOTE: 192.168.1.11:5000 is an HTTP registry (no TLS).
// The Docker daemon on the Jenkins agent must have it listed under
// "insecure-registries" in /etc/docker/daemon.json:
//
// { "insecure-registries": ["192.168.1.11:5000"] }
//
// If the registry requires credentials, replace the plain push below
// with the block commented out underneath it, and add a 'registry-creds'
// username/password credential in Jenkins > Manage Credentials.
steps {
sh "docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
// Only move the 'latest' pointer when building the main branch.
script {
if (env.BRANCH_NAME == 'main') {
sh "docker push ${REGISTRY}/${IMAGE_NAME}:latest"
}
}
/* --- With registry credentials (uncomment if needed) ---
withCredentials([usernamePassword(
credentialsId: 'registry-creds',
usernameVariable: 'REG_USER',
passwordVariable: 'REG_PASS'
)]) {
sh """
docker login ${REGISTRY} -u \$REG_USER -p \$REG_PASS
docker push ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}
docker push ${REGISTRY}/${IMAGE_NAME}:latest
docker logout ${REGISTRY}
"""
}
-------------------------------------------------------- */
}
}
}
post {
always {
// Remove local copies so the build agent does not fill up.
sh """
docker rmi ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} || true
docker rmi ${REGISTRY}/${IMAGE_NAME}:latest || true
"""
}
success {
echo "Deployed: ${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
}
failure {
echo "Build failed — image was NOT pushed or deployed."
}
}
}