| #!groovy | |
| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| pipeline { | |
| agent none | |
| options { | |
| buildDiscarder logRotator(daysToKeepStr: '14', numToKeepStr: '10') | |
| timeout(80) | |
| disableConcurrentBuilds() | |
| skipStagesAfterUnstable() | |
| quietPeriod(30) | |
| } | |
| triggers { | |
| pollSCM 'H/15 * * * *' | |
| } | |
| stages { | |
| stage('JDK 17') { | |
| agent { | |
| label 'ubuntu' | |
| } | |
| tools { | |
| jdk 'jdk_17_latest' | |
| maven 'maven_3_latest' | |
| } | |
| environment { | |
| MAVEN_OPTS = "-Xmx2048m" | |
| } | |
| stages { | |
| stage('Clean up') { | |
| steps { | |
| cleanWs deleteDirs: true, patterns: [[pattern: '**/target/**', type: 'INCLUDE']] | |
| } | |
| } | |
| stage('Detect changes') { | |
| steps { | |
| script { | |
| // Skip the build when a change only touched .claude/ or the root | |
| // process docs - agent instructions and policy, not code. The | |
| // pattern mirrors .github/actions/detect-changes; keep the two in | |
| // step. Fails open: anything unexpected (no baseline, an | |
| // unreachable commit, a git error) reports true and the build | |
| // runs as before. | |
| // | |
| // On a pull request the baseline is the merge base with the | |
| // target branch, NOT GIT_PREVIOUS_SUCCESSFUL_COMMIT. That pointer | |
| // is the previous head of this same PR, so once the PR is rebased | |
| // (or the target is merged into it) everything the target branch | |
| // absorbed in the meantime looks like a change of the PR's own. | |
| // The multibranch checkout already fetches the target branch, so | |
| // origin/$CHANGE_TARGET resolves here. On a branch build there is | |
| // no target and the previous successful commit is the only | |
| // baseline available. | |
| env.CODE_CHANGED = sh(returnStdout: true, script: ''' | |
| set -u | |
| target="${CHANGE_TARGET:-}" | |
| if [ -n "$target" ]; then | |
| base=$(git merge-base "origin/${target}" HEAD 2>/dev/null || true) | |
| else | |
| base="${GIT_PREVIOUS_SUCCESSFUL_COMMIT:-}" | |
| fi | |
| if [ -z "$base" ] || ! git cat-file -e "${base}^{commit}" 2>/dev/null; then | |
| echo true | |
| exit 0 | |
| fi | |
| outside=$(git diff --name-only "$base" HEAD | grep -vE '^(\\.claude/|CLAUDE\\.md$|AGENTS\\.md$|SECURITY\\.md$|THREAT_MODEL\\.md$|$)' || true) | |
| if [ -n "$outside" ]; then | |
| echo true | |
| else | |
| echo false | |
| fi | |
| ''').trim() | |
| echo "Changes outside .claude/ and the process docs: ${env.CODE_CHANGED}" | |
| } | |
| } | |
| } | |
| // Tests run once, on the baseline JDK. The other supported JDKs and | |
| // the Jakarta EE 11 profile are covered by .github/workflows/maven.yml; | |
| // Jenkins exists for what GitHub Actions cannot do: the snapshot deploy | |
| // and the nightlies upload. | |
| stage('Test') { | |
| when { | |
| expression { env.CODE_CHANGED != 'false' } | |
| } | |
| steps { | |
| sh './mvnw -B -DskipAssembly verify --no-transfer-progress' | |
| } | |
| post { | |
| always { | |
| junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true) | |
| junit(testResults: '**/failsafe-reports/*.xml', allowEmptyResults: true) | |
| } | |
| } | |
| } | |
| // -DskipITs as well as -DskipTests: failsafe does not know skipTests, | |
| // so without it the showcase integration tests boot Jetty again here. | |
| stage('Deploy Snapshot') { | |
| when { | |
| allOf { | |
| expression { env.CODE_CHANGED != 'false' } | |
| anyOf { | |
| branch 'main' | |
| branch 'support/struts-6-x-x' | |
| } | |
| } | |
| } | |
| steps { | |
| withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) { | |
| sh './mvnw -B -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipITs -DskipAssembly --no-transfer-progress' | |
| } | |
| } | |
| } | |
| // The deploy above installed every module, so only the assembly | |
| // module needs building; its descriptors read the sibling modules' | |
| // target/ directories from this same workspace. | |
| stage('Upload nightlies') { | |
| when { | |
| allOf { | |
| expression { env.CODE_CHANGED != 'false' } | |
| anyOf { | |
| branch 'main' | |
| branch 'support/struts-6-x-x' | |
| } | |
| } | |
| } | |
| steps { | |
| sh './mvnw -B package -DskipTests -DskipITs -pl assembly --no-transfer-progress' | |
| sshPublisher(publishers: [ | |
| sshPublisherDesc( | |
| configName: 'Nightlies', | |
| transfers: [ | |
| sshTransfer( | |
| remoteDirectory: '/struts/snapshot', | |
| removePrefix: 'assembly/target/assembly/out', | |
| sourceFiles: 'assembly/target/assembly/out/struts-*.zip' | |
| ) | |
| ], | |
| verbose: true | |
| ) | |
| ]) | |
| } | |
| } | |
| } | |
| post { | |
| always { | |
| cleanWs deleteDirs: true, patterns: [[pattern: '**/target/**', type: 'INCLUDE']] | |
| } | |
| } | |
| } | |
| } | |
| post { | |
| // If this build failed, send an email to the list. | |
| failure { | |
| script { | |
| emailext( | |
| to: "notifications@struts.apache.org", | |
| recipientProviders: [[$class: 'DevelopersRecipientProvider']], | |
| from: "Mr. Jenkins <jenkins@builds.apache.org>", | |
| subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} failed", | |
| body: """ | |
| There is a build failure in ${env.JOB_NAME}. | |
| Build: ${env.BUILD_URL} | |
| Logs: ${env.BUILD_URL}console | |
| Changes: ${env.BUILD_URL}changes | |
| -- | |
| Mr. Jenkins | |
| Director of Continuous Integration | |
| """ | |
| ) | |
| } | |
| } | |
| // If this build didn't fail, but there were failing tests, send an email to the list. | |
| unstable { | |
| script { | |
| emailext( | |
| to: "notifications@struts.apache.org", | |
| recipientProviders: [[$class: 'DevelopersRecipientProvider']], | |
| from: "Mr. Jenkins <jenkins@builds.apache.org>", | |
| subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} unstable", | |
| body: """ | |
| Some tests have failed in ${env.JOB_NAME}. | |
| Build: ${env.BUILD_URL} | |
| Logs: ${env.BUILD_URL}console | |
| Changes: ${env.BUILD_URL}changes | |
| -- | |
| Mr. Jenkins | |
| Director of Continuous Integration | |
| """ | |
| ) | |
| } | |
| } | |
| // Send an email, if the last build was not successful and this one is. | |
| fixed { | |
| script { | |
| emailext( | |
| to: "notifications@struts.apache.org", | |
| recipientProviders: [[$class: 'DevelopersRecipientProvider']], | |
| from: 'Mr. Jenkins <jenkins@builds.apache.org>', | |
| subject: "Jenkins job ${env.JOB_NAME}#${env.BUILD_NUMBER} back to normal", | |
| body: """ | |
| The build for ${env.JOB_NAME} completed successfully and is back to normal. | |
| Build: ${env.BUILD_URL} | |
| Logs: ${env.BUILD_URL}console | |
| Changes: ${env.BUILD_URL}changes | |
| -- | |
| Mr. Jenkins | |
| Director of Continuous Integration | |
| """ | |
| ) | |
| } | |
| } | |
| } | |
| } |