// Jenkins pipeline for the PLATFORM itself (this repo): every push to `main` // lints the tree and updates the production paas server through // `paas-self-deploy` (infra/paas-self-deploy) over SSH. Job `paas-platform` // on the LAN Jenkins is "Pipeline script from SCM" pointing at this file — // see ci/jenkins-setup.sh (creates job + credential) and // docs/ci-platform-deploy.md. // // Jenkins is not reachable from the internet (by design, see // infra/opnsense-notes.md), so instead of a GitHub webhook the job polls the // repo every two minutes; a push lands on the server within ~3 minutes. // // Credentials on Jenkins: // PAAS_SSH_CRED SSH private key whose public half was authorized on the // server with infra/setup-ci-deploy.sh (forced command — // it can only run paas-self-deploy) pipeline { agent any options { disableConcurrentBuilds() buildDiscarder(logRotator(numToKeepStr: '30')) } triggers { pollSCM('H/2 * * * *') } environment { PAAS_HOST = 'deploy@192.168.13.10' // production paas VM PAAS_SSH_CRED = 'paas-platform-deploy' // Jenkins credential id DEPLOY_BRANCH = 'main' } stages { stage('Checkout') { steps { checkout scm script { env.COMMIT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim() env.COMMIT_MSG = sh(script: 'git log -1 --pretty=%s', returnStdout: true).trim() currentBuild.description = "${env.COMMIT} ${env.COMMIT_MSG}".take(80) } } } stage('Lint') { steps { // Same checks as CLAUDE.md asks for before a commit. Tools that // are missing on the agent are skipped here — the server-side // paas-self-deploy repeats bash -n + node --check before it // touches anything, so nothing broken can go live. sh ''' set -e for f in bootstrap.sh bin/* infra/*.sh infra/paas-self-deploy infra/paas-ci-ssh; do [ -f "$f" ] && head -1 "$f" | grep -q bash && bash -n "$f" done if command -v shellcheck >/dev/null; then shellcheck -S warning bootstrap.sh bin/paas bin/paas-init infra/*.sh infra/paas-self-deploy infra/paas-ci-ssh else echo "shellcheck not installed on this agent — skipped" fi if command -v node >/dev/null; then for f in panel/*.js panel/lib/*.js panel/bin/*.js; do [ -f "$f" ] && node --check "$f"; done else echo "node not installed on this agent — skipped (checked again on the server)" fi ''' } } stage('Deploy') { when { expression { (env.GIT_BRANCH ?: "origin/${env.DEPLOY_BRANCH}").tokenize('/').last() == env.DEPLOY_BRANCH } } steps { // credentials-binding ships with Pipeline (no SSH Agent plugin needed) withCredentials([sshUserPrivateKey(credentialsId: env.PAAS_SSH_CRED, keyFileVariable: 'SSH_KEY')]) { // The whole checkout goes over as a tarball on stdin; the // forced command on the server turns `deploy ` // into `sudo paas-self-deploy deploy …` (lint → backup → // rsync → bootstrap.sh → verify). Its exit code is ours. sh '''#!/bin/bash set -eo pipefail tar --exclude=.git --exclude=.idea --exclude=.claude --exclude=node_modules \ -czf - . \ | ssh -i "$SSH_KEY" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o BatchMode=yes \ "$PAAS_HOST" deploy "$COMMIT" "$COMMIT_MSG" ''' } } } } post { failure { echo "Platform deploy FAILED for ${env.COMMIT}. On the server: ssh younes@192.168.13.10 'sudo paas-self-deploy status' / 'sudo paas-self-deploy rollback'" } success { echo "Platform at ${env.COMMIT} is live on ${env.PAAS_HOST}" } } }