diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000..fb8e3cce48 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,200 @@ +pipeline { + agent any + + environment { + // Node.js environment + NODE_VERSION = '20' + PNPM_VERSION = '9' + + // Build paths + WORKSPACE_DIR = "${WORKSPACE}" + BUILD_OUTPUT = "${BUILD_URL}artifact/output" + } + + options { + skipDefaultCheckout() + buildDiscarder(logRotator(numToKeepStr: '10')) + timeout(time: 30, unit: 'MINUTES') + } + + stages { + stage('Checkout') { + steps { + checkout scm + script { + echo "Branch: ${env.BRANCH_NAME}" + echo "Commit: ${GIT_COMMIT.take(8)}" + } + } + } + + stage('Setup Node.js') { + steps { + nvm(nodeVersion: NODE_VERSION) { + sh 'node --version' + sh 'npm install -g pnpm@${PNPM_VERSION}' + sh 'pnpm --version' + } + } + } + + stage('Install Dependencies') { + steps { + sh 'pnpm install --frozen-lockfile' + } + } + + stage('Type Check') { + steps { + sh 'pnpm run typecheck' + } + } + + stage('Lint') { + steps { + sh 'pnpm run lint' + } + post { + always { + recordIssues( + tools: [eslint(pattern: 'node_modules/.cache/eslint/**/*')] + ) + } + } + } + + stage('Test') { + steps { + sh 'pnpm run test' + } + post { + always { + junit 'junit.xml' + publishHTML([ + reportDir: 'coverage', + reportFiles: 'index.html', + reportName: 'Coverage Report' + ]) + } + } + } + + stage('Build') { + steps { + sh 'pnpm run build' + } + archiveArtifacts( + artifacts: 'build/**/*', + fingerprint: true, + allowEmptyArchive: true + ) + } + + stage('Docker Validation') { + steps { + script { + echo 'Validating Docker production build...' + sh 'docker build --target bolt-ai-production . --no-cache --progress=plain' + echo '✅ Production target builds successfully' + } + } + } + + stage('Docker Development Build') { + steps { + script { + echo 'Validating Docker development build...' + sh 'docker build --target development . --no-cache --progress=plain' + echo '✅ Development target builds successfully' + } + } + } + + stage('Docker Compose Validation') { + steps { + script { + echo 'Validating docker-compose configuration...' + sh 'docker compose config --quiet' + echo '✅ docker-compose configuration is valid' + } + } + } + + stage('Electron Build (Optional)') { + when { branch 'main' } + steps { + sh 'pnpm run electron:build:deps || true' + } + } + } + + post { + success { + emailext( + subject: "✅ Build Successful: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + body: """ +
Job: ${env.JOB_NAME}
+Build: #${env.BUILD_NUMBER}
+Branch: ${env.BRANCH_NAME}
+Duration: ${currentBuild.durationString}
+Status: SUCCESS
+Console: View Console
+ """, + mimeType: 'text/html', + recipientProviders: [[$class: 'RequesterRecipientProvider']] + ) + } + + failure { + emailext( + subject: "❌ Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + body: """ +Job: ${env.JOB_NAME}
+Build: #${env.BUILD_NUMBER}
+Branch: ${env.BRANCH_NAME}
+Duration: ${currentBuild.durationString}
+Status: FAILED
+Console: View Console
+Error: ${currentBuild.rawBuild?.description ?: 'See console output'}
+ """, + mimeType: 'text/html', + recipientProviders: [[$class: 'RequesterRecipientProvider']] + ) + } + + unstable { + emailext( + subject: "⚠️ Build Unstable: ${env.JOB_NAME} #${env.BUILD_NUMBER}", + body: """ +Job: ${env.JOB_NAME}
+Build: #${env.BUILD_NUMBER}
+Branch: ${env.BRANCH_NAME}
+Duration: ${currentBuild.durationString}
+Console: View Console
+ """, + mimeType: 'text/html', + recipientProviders: [[$class: 'RequesterRecipientProvider']] + ) + } + + always { + cleanWs( + cleanWhenAborted: true, + cleanWhenFailure: true, + cleanWhenNotBuilt: true, + cleanWhenSuccess: true, + deleteDirs: true, + notFailBuild: true, + patterns: [ + [pattern: '.gitignore', type: 'EXCLUDE'], + [pattern: 'build/**/*', type: 'EXCLUDE'], + [pattern: 'coverage/**/*', type: 'EXCLUDE'] + ] + ) + } + } +} + diff --git a/jenkins-docker-compose.yaml b/jenkins-docker-compose.yaml new file mode 100644 index 0000000000..575b145675 --- /dev/null +++ b/jenkins-docker-compose.yaml @@ -0,0 +1,39 @@ +version: '3.8' + +services: + jenkins: + image: jenkins/jenkins:lts-jdk17 + container_name: gamechanger-jenkins + restart: unless-stopped + ports: + - "8080:8080" + - "50000:50000" + volumes: + - jenkins_home:/var/jenkins_home + - /var/run/docker.sock:/var/run/docker.sock + - ./build:/var/jenkins_home/workspace/build-output + environment: + - JENKINS_OPTS=--prefix=/jenkins + - JAVA_OPTS=-Dhudson.security.csrf.GlobalCrumbIssuerConfiguration=false + networks: + - jenkins_network + + jenkins-agent: + image: jenkins/agent:latest-jdk17 + container_name: gamechanger-jenkins-agent + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - jenkins + networks: + - jenkins_network + +networks: + jenkins_network: + driver: bridge + +volumes: + jenkins_home: + driver: local + diff --git a/jenkins-job-config.xml b/jenkins-job-config.xml new file mode 100644 index 0000000000..4c02c441aa --- /dev/null +++ b/jenkins-job-config.xml @@ -0,0 +1,184 @@ + +