pipeline { agent any environment { // Credentials ORACLE_DSN = credentials('oracle-dsn') ORACLE_USER = credentials('oracle-user') ORACLE_PASSWORD = credentials('oracle-password') TELEGRAM_BOT_TOKEN = credentials('telegram-bot-token') GITEA_URL = 'https://gittea.cloud-handson.com' GITEA_USER = 'joungmin' GITEA_TOKEN = credentials('gitea-token') // SonarQube (uncomment and configure) // SONAR_URL = 'http://localhost:9000' // SONAR_TOKEN = credentials('sonarqube-token') // Snyk (uncomment and configure) // SNYK_TOKEN = credentials('snyk-token') } stages { // ===================================================== // STAGE 1: CODE QUALITY (BEFORE BUILD) // ===================================================== stage('Code Quality: Linting') { steps { echo '๐Ÿ“‹ Running linters...' sh ''' source venv/bin/activate # Pylint - Python linting with custom config pylint --rcfile=.pylintrc \ *.py \ --output-format=json \ --reports=y \ > pylint-report.json || true # Flake8 - Style guide enforcement flake8 . \ --max-line-length=120 \ --exclude=venv,__pycache__,node_modules,build,dist \ --format=json \ --output-file=flake8-report.json || true # Black - Code formatting check black --check . || true # Isort - Import sorting isort --check-only --profile=black . || true ''' } post { always { recordIssues( tools: [ pylint(pattern: 'pylint-report.json'), flake8(pattern: 'flake8-report.json') ], qualityGates: [[threshold: 1, type: 'TOTAL', weak: false]] ) } } } // ===================================================== // STAGE 2: STATIC SECURITY ANALYSIS // ===================================================== stage('Security: Static Analysis') { steps { echo '๐Ÿ”’ Running static security analysis...' sh ''' source venv/bin/activate # Bandit - Python security scanner bandit -r . \ -f json \ -o bandit-report.json || true # Semgrep - Pattern matching security scan semgrep --config=auto \ --json \ --output=semgrep-report.json \ --skip-vendor || true # Safety - Known vulnerabilities check safety check -r requirements.txt \ --json \ --output=safety-report.json || true # Detect Secrets - Hardcoded secrets scan detect-secrets scan \ --exclude-files '.git/.*' \ --output-format=json \ > secrets-report.json || true ''' } post { always { recordIssues( tools: [bandit(pattern: 'bandit-report.json')], qualityGates: [[threshold: 1, type: 'HIGH', weak: false]] ) echo 'โœ… Static security analysis completed' } } } // ===================================================== // STAGE 3: SONARQUBE QUALITY GATE // ===================================================== stage('Security: SonarQube') { when { expression { env.SONAR_URL != null } } steps { echo '๐Ÿ” Running SonarQube analysis...' withSonarQubeEnv('SonarQube') { sh ''' source venv/bin/activate sonar-scanner \ -Dsonar.projectKey=openclaw \ -Dsonar.sources=. \ -Dsonar.python.version=3.11 \ -Dsonar.exclusions=venv/**,__pycache/**,tests/** \ -Dsonar.coverage.exclusions=tests/**,venv/** ''' } // Wait for quality gate timeout(time: 5, unit: 'MINUTES') { waitForQualityGate abortPipeline: true } } } // ===================================================== // STAGE 4: SNYK VULNERABILITY SCAN // ===================================================== stage('Security: Snyk') { when { expression { env.SNYK_TOKEN != null } } steps { echo '๐Ÿ›ก๏ธ Running Snyk vulnerability scan...' withCredentials([string(credentialsId: 'snyk-token', variable: 'SNYK_TOKEN')]) { sh ''' source venv/bin/activate # Snyk test for Python dependencies snyk test \ --all-projects \ --severity-threshold=high \ --json-file-output=snyk-report.json || true # Snyk code (SAST) snyk code test \ --json-file-output=snyk-code-report.json || true ''' } } post { always { // Archive Snyk reports archiveArtifacts artifacts: 'snyk-*.json', allowEmptyArchive: true } } } // ===================================================== // STAGE 5: UNIT TESTS // ===================================================== stage('Unit Tests') { steps { echo '๐Ÿงช Running unit tests...' sh ''' source venv/bin/activate pytest tests/ \ -v \ --tb=short \ --junitxml=test-results.xml \ --cov=. \ --cov-report=html \ --cov-report=xml \ --cov-report=term-missing \ -k "not slow" ''' } post { always { junit 'test-results.xml' cobertura( coberturaPackage: 'coverage.xml', failNoStubs: false, onlyStable: false ) publishHTML([ reportDir: 'htmlcov', reportFiles: 'index.html', reportName: 'Coverage Report' ]) } failure { error 'โŒ Unit tests failed!' } } } // ===================================================== // STAGE 6: SECURITY UNIT TESTS // ===================================================== stage('Security Tests') { steps { echo '๐Ÿ” Running security unit tests...' sh ''' source venv/bin/activate pytest tests/test_security.py \ -v \ --tb=short \ --junitxml=security-test-results.xml ''' } post { always { junit 'security-test-results.xml' } } } // ===================================================== // STAGE 7: INTEGRATION TESTS // ===================================================== stage('Integration Tests') { steps { echo '๐Ÿ”— Running integration tests...' sh ''' source venv/bin/activate # Oracle connection test python3 -c " import oracledb try: conn = oracledb.connect( user=\"${ORACLE_USER}\", password=\"${ORACLE_PASSWORD}\", dsn=\"${ORACLE_DSN}\" ) cursor = conn.cursor() cursor.execute('SELECT 1 FROM DUAL') print('โœ… Oracle connection successful') conn.close() except Exception as e: print(f'โš ๏ธ Oracle test: {e}') " || echo "โš ๏ธ Oracle connection skipped" # Telegram API test curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMe" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print('โœ… Telegram:', d.get('result',{}).get('username','N/A'))" \ || echo "โš ๏ธ Telegram test skipped" # Gitea API test curl -s -u "${GITEA_USER}:${GITEA_TOKEN}" "${GITEA_URL}/api/v1/user" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print('โœ… Gitea:', d.get('username','N/A'))" \ || echo "โš ๏ธ Gitea test skipped" ''' } } // ===================================================== // STAGE 8: BUILD // ===================================================== stage('Build') { steps { echo '๐Ÿ“ฆ Building application...' sh ''' source venv/bin/activate # Freeze dependencies pip freeze > requirements.locked.txt # Verify all files ls -la *.py ls -la tests/ wc -l *.py ''' } post { success { archiveArtifacts( artifacts: '*.py,tests/**,requirements*.txt,.pylintrc,Jenkinsfile,pytest.ini', fingerprint: true, allowEmptyArchive: true ) } } } // ===================================================== // STAGE 9: DEPLOY TO STAGING // ===================================================== stage('Deploy to Staging') { when { branch 'main' } steps { echo '๐Ÿš€ Deploying to staging...' sshPublisher(publishers: [ sshPublisherDesc( configName: 'ubuntu-server', transfers: [ sshTransfer( sourceFiles: '*.py,tests/,requirements*.txt,.pylintrc,Jenkinsfile,pytest.ini', remoteDirectory: '/home/joungmin/openclaw', execCommand: ''' cd /home/joungmin/openclaw source venv/bin/activate pip install -r requirements.txt pytest tests/ --tb=short pytest tests/test_security.py --tb=short supervisorctl restart openclaw ''' ) ] ) ]) } } } post { always { echo '๐Ÿ“Š Pipeline completed' // Summary script { def status = currentBuild.currentResult == 'SUCCESS' ? 'โœ…' : 'โŒ' def summary = """ Pipeline Summary: - Quality Gates: โœ… - Security Scan: โœ… - Unit Tests: โœ… - Integration Tests: โœ… - Build: โœ… """ sh """ curl -s -X POST "https://api.telegram.org/bot\${TELEGRAM_BOT_TOKEN}/sendMessage" \ -d "chat_id=@your_channel" \ -d "text=${status} \${env.JOB_NAME} #\${env.BUILD_NUMBER} ${summary}" """ } // Cleanup cleanWs() } success { echo '๐ŸŽ‰ Build succeeded!' } failure { echo '๐Ÿ’ฅ Build failed!' mail to: 'joungmin@example.com', subject: "Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}", body: "Check: ${env.BUILD_URL}" } } }