lotr-sut/Jenkinsfile
2026-04-07 00:37:05 +00:00

169 lines
5.8 KiB
Groovy

pipeline {
agent any
environment {
PYTHON_VERSION = '3.11'
NODE_VERSION = '18'
SUT_URL = 'http://localhost'
}
options {
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '10'))
disableConcurrentBuilds()
}
stages {
stage('Checkout') {
steps {
echo 'One does not simply skip the checkout stage...'
checkout scm
}
}
stage('Setup Environment') {
steps {
echo 'Preparing the Fellowship environment...'
sh '''
# Create a Python virtual environment (PEP 668 compliant)
python3 -m venv venv
. venv/bin/activate
python3 -m pip install --upgrade pip setuptools wheel
echo "✓ Virtual environment ready"
'''
}
}
stage('Install Dependencies') {
parallel {
stage('Backend Deps') {
steps {
echo 'Installing backend dependencies for the Fellowship...'
sh '''
. venv/bin/activate
pip install -r sut/backend/requirements.txt
pip install -r tests/requirements.txt
'''
}
}
stage('Frontend Deps') {
steps {
echo 'Installing frontend dependencies...'
sh '''
cd sut/frontend
npm ci --prefer-offline || npm install
'''
}
}
}
}
stage('Lint') {
steps {
echo 'The Eye of Sauron watches for code style violations...'
sh '''
. venv/bin/activate
pip install flake8 --quiet
# E9/F63/F7/F82 = syntax & fatal errors — these must fail the build.
# Warnings (E1xx, W) are informational only for this tutorial pipeline.
flake8 sut/backend tests/*.py \
--count --select=E9,F63,F7,F82 \
--show-source --statistics
'''
}
}
stage('Unit Tests') {
steps {
echo 'Running unit tests across the Fellowship...'
sh '''
. venv/bin/activate
mkdir -p reports
PYTHONPATH=sut/backend python -m pytest \
tests/backend/services/test_bargaining_algorithm.py \
tests/backend/services/test_bargaining_config.py \
tests/backend/services/test_shop_service.py \
tests/backend/services/test_negotiation_logger.py \
-v --tb=short \
--junitxml=reports/unit-tests.xml \
--no-header \
--override-ini="addopts=--strict-markers --tb=short -v"
'''
}
post {
always {
junit allowEmptyResults: true, testResults: 'reports/unit-tests.xml'
}
}
}
stage('Build Frontend') {
steps {
echo 'Forging the frontend in the fires of Mount Doom...'
sh '''
cd sut/frontend
npm run build
'''
}
}
stage('Integration Tests') {
steps {
echo 'The quest continues — running integration tests...'
sh '''
. venv/bin/activate
mkdir -p reports
PYTHONPATH=sut/backend python -m pytest \
tests/backend/services/test_bargaining_algorithm.py \
tests/backend/services/test_bargaining_config.py \
tests/backend/services/test_shop_service.py \
tests/backend/services/test_negotiation_logger.py \
-v --tb=short \
--junitxml=reports/integration-tests.xml \
--no-header \
--override-ini="addopts=--strict-markers --tb=short -v" \
-m "not realstack"
'''
}
post {
always {
junit allowEmptyResults: true, testResults: 'reports/integration-tests.xml'
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
echo 'Deploying the Fellowship SUT to the land of Docker...'
sh '''
cp caddy/Caddyfile.local caddy/Caddyfile
docker compose down --remove-orphans || true
docker compose up -d --build
'''
}
}
}
post {
always {
echo 'The journey is complete — collecting artifacts...'
archiveArtifacts artifacts: 'reports/**/*.xml', allowEmptyArchive: true
}
success {
echo 'The Fellowship has succeeded! The quest is complete.'
}
failure {
echo 'The Fellowship has failed! Sauron grows stronger...'
mail to: 'fellowship@middle-earth.local',
subject: "Build Failed: ${env.JOB_NAME} [${env.BUILD_NUMBER}]",
body: "The Ring has been lost. Build ${env.BUILD_URL} has failed."
}
unstable {
echo 'The quest is unstable — some tests did not pass.'
}
}
}