Updated Jenkinsfile
This commit is contained in:
parent
b52d3c8b3b
commit
80f56245dd
69
expenses_manager/Jenkinsfile
vendored
69
expenses_manager/Jenkinsfile
vendored
@ -2,18 +2,30 @@ pipeline {
|
|||||||
agent any
|
agent any
|
||||||
|
|
||||||
environment {
|
environment {
|
||||||
// settings.py demands SECRET_KEY unless DEBUG is exactly "True".
|
// DEBUG=True keeps settings.py from enabling SECURE_SSL_REDIRECT, which
|
||||||
// A throwaway value lets CI run with DEBUG unset — that is, False, as
|
// makes Django's test client receive a 301 on every request.
|
||||||
// in production — instead of falling back to the insecure dev key.
|
|
||||||
DEBUG = 'True'
|
DEBUG = 'True'
|
||||||
SECRET_KEY = 'ci-only-value-never-used-in-production'
|
SECRET_KEY = 'ci-only-value-never-used-in-production'
|
||||||
DJANGO_SETTINGS_MODULE = 'expenses_manager.settings'
|
DJANGO_SETTINGS_MODULE = 'expenses_manager.settings'
|
||||||
PYTHONUNBUFFERED = '1'
|
PYTHONUNBUFFERED = '1'
|
||||||
|
|
||||||
|
// Jenkins has no Docker daemon, so images are built on the NAS over SSH.
|
||||||
|
// Absolute paths are required: the remote non-interactive PATH is
|
||||||
|
// /usr/bin:/bin:/usr/sbin:/sbin and does not include /usr/local/bin.
|
||||||
|
NAS_HOST = '192.168.1.140'
|
||||||
|
NAS_USER = 'jenkins-ci'
|
||||||
|
NAS_DOCKER = '/usr/local/bin/docker'
|
||||||
|
CI_DIR = '/volume1/docker/ci/expenses_manager'
|
||||||
|
SSH_OPTS = '-o StrictHostKeyChecking=accept-new'
|
||||||
|
|
||||||
|
// Deliberately not "finanzas-web": that name belongs to the image
|
||||||
|
// docker-compose builds for production, and CI must never overwrite it.
|
||||||
|
IMAGE = 'expenses-manager-ci'
|
||||||
}
|
}
|
||||||
|
|
||||||
options {
|
options {
|
||||||
buildDiscarder(logRotator(numToKeepStr: '30'))
|
buildDiscarder(logRotator(numToKeepStr: '30'))
|
||||||
timeout(time: 20, unit: 'MINUTES')
|
timeout(time: 30, unit: 'MINUTES')
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
@ -48,13 +60,60 @@ pipeline {
|
|||||||
'''
|
'''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stage('Build image') {
|
||||||
|
steps {
|
||||||
|
sshagent(credentials: ['nas-ssh']) {
|
||||||
|
sh '''
|
||||||
|
SHA=$(echo "${GIT_COMMIT}" | cut -c1-7)
|
||||||
|
TAG=$(echo "${BRANCH_NAME}" | tr '/' '-')
|
||||||
|
|
||||||
|
# Ship the workspace that was just tested, rather than
|
||||||
|
# cloning again on the NAS: what gets built is then
|
||||||
|
# provably the same code the tests ran against.
|
||||||
|
ssh ${SSH_OPTS} ${NAS_USER}@${NAS_HOST} \
|
||||||
|
"rm -rf ${CI_DIR} && mkdir -p ${CI_DIR}"
|
||||||
|
|
||||||
|
tar -czf - --exclude=./expenses_manager/venv \
|
||||||
|
--exclude=./.git . \
|
||||||
|
| ssh ${SSH_OPTS} ${NAS_USER}@${NAS_HOST} \
|
||||||
|
"tar -xzf - -C ${CI_DIR}"
|
||||||
|
|
||||||
|
ssh ${SSH_OPTS} ${NAS_USER}@${NAS_HOST} \
|
||||||
|
"cd ${CI_DIR} && sudo ${NAS_DOCKER} build \
|
||||||
|
-t ${IMAGE}:${SHA} -t ${IMAGE}:${TAG} ."
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A successful build only proves the Dockerfile is syntactically sound.
|
||||||
|
// Running Django's own checks inside the image proves it can actually
|
||||||
|
// start: settings load, middleware resolves, static files are collected.
|
||||||
|
stage('Smoke test image') {
|
||||||
|
steps {
|
||||||
|
sshagent(credentials: ['nas-ssh']) {
|
||||||
|
sh '''
|
||||||
|
SHA=$(echo "${GIT_COMMIT}" | cut -c1-7)
|
||||||
|
|
||||||
|
ssh ${SSH_OPTS} ${NAS_USER}@${NAS_HOST} \
|
||||||
|
"sudo ${NAS_DOCKER} run --rm \
|
||||||
|
-e SECRET_KEY=smoke-test-only \
|
||||||
|
-e DEBUG=False \
|
||||||
|
-e ALLOWED_HOSTS=localhost \
|
||||||
|
-e DB_ENGINE=sqlite3 \
|
||||||
|
${IMAGE}:${SHA} python manage.py check"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
always {
|
always {
|
||||||
// allowEmptyResults: false is deliberate. A build where pytest died
|
// allowEmptyResults: false is deliberate. A build where pytest died
|
||||||
// before collecting anything produces no XML, and that must fail
|
// before collecting anything produces no XML, and that must fail
|
||||||
// rather than pass quietly — as happened for months in 2026.
|
// rather than pass quietly.
|
||||||
junit allowEmptyResults: false, testResults: 'expenses_manager/junit.xml'
|
junit allowEmptyResults: false, testResults: 'expenses_manager/junit.xml'
|
||||||
}
|
}
|
||||||
failure {
|
failure {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user