pipeline { agent none options { timestamps() } environment { NODE_OPTIONS = '--max_old_space_size=2048' } stages { stage('Init') { agent any steps { script { env.COMMIT_AUTHOR = sh( script: "git show -s --format=%an HEAD", returnStdout: true ).trim() env.COMMIT_SHORT = sh( script: "git rev-parse --short HEAD", returnStdout: true ).trim() } echo "CI build for commit ${env.COMMIT_SHORT} by ${env.COMMIT_AUTHOR}" } } /* ========================= BACKEND CI ========================= */ stage('Backend: lint & test') { agent { docker { image 'python:3.11-slim' } } environment { HOME = "${WORKSPACE}" PIP_CACHE_DIR = "${WORKSPACE}/.cache/pip" PYTHONDONTWRITEBYTECODE = 1 } steps { dir('backend') { sh ''' set -e mkdir -p "$PIP_CACHE_DIR" "$WORKSPACE/.cache/pytest" python -m venv .venv . .venv/bin/activate pip install --upgrade pip pip install -r requirements-dev.txt ruff check app tests pytest -o cache_dir="$WORKSPACE/.cache/pytest" --junitxml=pytest.xml ''' } } } /* ========================= FRONTEND CI ========================= */ stage('Frontend: check & build') { agent { docker { image 'node:20-slim' } } environment { HOME = "${WORKSPACE}" NPM_CONFIG_CACHE = "${WORKSPACE}/.cache/npm" } steps { dir('frontend') { sh ''' set -e mkdir -p "$NPM_CONFIG_CACHE" npm install --no-progress --no-audit --prefer-offline npm run check npm test npm run build ''' } } } } post { always { script { node { junit testResults: 'backend/pytest.xml', allowEmptyResults: true if (env.CHANGE_ID) { def giteaBase = 'https://openbokeron.org' def owner = 'OpenBokeron' def repo = 'TallerCiCd' def pr = env.CHANGE_ID def result = currentBuild.currentResult def msg = "" if (result == "SUCCESS") { msg = """✅ Todo en orden, camarada. Build #${env.BUILD_NUMBER} ${env.BUILD_URL} """ } else if (result == "FAILURE") { msg = """❌ ¿Qué clase de crímenes de guerra has metido en la PR? Build #${env.BUILD_NUMBER} ${env.BUILD_URL} """ } else { msg = """⚠️ Dudoso, *arquea una ceja* Build #${env.BUILD_NUMBER} ${env.BUILD_URL} """ } def commentsUrl = "${giteaBase}/gitea/api/v1/repos/${owner}/${repo}/issues/${pr}/comments" withCredentials([usernamePassword(credentialsId: 'jenkins-bot-api', usernameVariable: 'GITEA_USER', passwordVariable: 'GITEA_TOKEN')]) { def body = msg.replace('\\', '\\\\').replace('"','\\"').replace('\n','\\n') // Avoid interpolation of secret variables (https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#string-interpolation) withEnv(["GITEA_COMMENTS_URL=${commentsUrl}", "GITEA_BODY=${body}"]) { sh(label: 'Comentar en PR (Gitea)', shell: '/bin/bash', script: ''' set -euo pipefail curl -sS -X POST \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ --data-binary @- \ "$GITEA_COMMENTS_URL" <