mirror of
https://github.com/apache/struts.git
synced 2026-09-12 17:15:02 +00:00
791d1e3dbd
#1846 guarded the two JDK stages on a diff against GIT_PREVIOUS_SUCCESSFUL_COMMIT. On a branch build that is the right baseline. On a pull request build it is not: the pointer is the previous head of the same PR, so once the PR is rebased - or the target branch is merged into it - everything the target absorbed in between shows up as a change of the PR's own. PR-1848 build #2 is the case. The pull request touches only .claude/skills/releasing-struts/, but it had been rebased across the maven.yml fix, and Jenkins computed: + base=b633817af047afaa80948404e2e6f1eb78e02b7a + git diff --name-only b633817af... HEAD + outside=.github/workflows/maven.yml Changes outside .claude/: true so both JDK stages ran a full Maven round trip for a documentation-only change. Since main almost always carries code, this made the filter useless for any pull request that is ever brought up to date. Use the merge base with the target branch as the baseline when CHANGE_TARGET is set. The multibranch checkout already fetches it - git fetch ... +refs/heads/main:refs/remotes/origin/main - so origin/$CHANGE_TARGET resolves in the workspace. Branch builds have no target and keep the previous-successful-commit baseline. Fail-open is unchanged and still covers the new path: an unresolvable merge base (target branch absent) yields an empty base and reports true. Exercised against the real commits of #1848 either side of its rebase, and against synthetic heads for: code only, .claude only, mixed, a .claudefoo/ near miss, a missing target branch, and the three branch build baselines. All ten behave as intended. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
280 lines
8.3 KiB
Groovy
280 lines
8.3 KiB
Groovy
#!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('Prepare') {
|
|
agent {
|
|
label 'ubuntu'
|
|
}
|
|
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/ - agent
|
|
// instructions, not code. 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/|$)' || true)
|
|
if [ -n "$outside" ]; then
|
|
echo true
|
|
else
|
|
echo false
|
|
fi
|
|
''').trim()
|
|
echo "Changes outside .claude/: ${env.CODE_CHANGED}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
stage('JDK 21') {
|
|
when {
|
|
expression { env.CODE_CHANGED != 'false' }
|
|
}
|
|
agent {
|
|
label 'ubuntu'
|
|
}
|
|
tools {
|
|
jdk 'jdk_21_latest'
|
|
maven 'maven_3_latest'
|
|
}
|
|
environment {
|
|
MAVEN_OPTS = "-Xmx1024m"
|
|
}
|
|
stages {
|
|
stage('Test') {
|
|
steps {
|
|
sh './mvnw -B -DskipAssembly verify'
|
|
}
|
|
post {
|
|
always {
|
|
junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true)
|
|
junit(testResults: '**/failsafe-reports/*.xml', allowEmptyResults: true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
post {
|
|
always {
|
|
cleanWs deleteDirs: true, patterns: [[pattern: '**/target/**', type: 'INCLUDE']]
|
|
}
|
|
}
|
|
}
|
|
stage('JDK 17') {
|
|
when {
|
|
expression { env.CODE_CHANGED != 'false' }
|
|
}
|
|
agent {
|
|
label 'ubuntu'
|
|
}
|
|
tools {
|
|
jdk 'jdk_17_latest'
|
|
maven 'maven_3_latest'
|
|
}
|
|
environment {
|
|
MAVEN_OPTS = "-Xmx2048m"
|
|
}
|
|
stages {
|
|
stage('Install') {
|
|
steps {
|
|
sh './mvnw -B install -DskipTests -DskipAssembly'
|
|
}
|
|
}
|
|
stage('Test') {
|
|
steps {
|
|
sh './mvnw -B verify -Pcoverage -DskipAssembly'
|
|
}
|
|
post {
|
|
always {
|
|
junit(testResults: '**/surefire-reports/*.xml', allowEmptyResults: true)
|
|
junit(testResults: '**/failsafe-reports/*.xml', allowEmptyResults: true)
|
|
}
|
|
}
|
|
}
|
|
stage('Build Source & JavaDoc') {
|
|
when {
|
|
anyOf {
|
|
branch 'main'
|
|
branch 'support/struts-6-x-x'
|
|
}
|
|
}
|
|
steps {
|
|
dir("local-snapshots-dir/") {
|
|
deleteDir()
|
|
}
|
|
sh './mvnw -B source:jar javadoc:jar -DskipTests -DskipAssembly'
|
|
}
|
|
}
|
|
stage('Deploy Snapshot') {
|
|
when {
|
|
anyOf {
|
|
branch 'main'
|
|
branch 'support/struts-6-x-x'
|
|
}
|
|
}
|
|
steps {
|
|
withCredentials([file(credentialsId: 'lukaszlenart-repository-access-token', variable: 'CUSTOM_SETTINGS')]) {
|
|
sh './mvnw -s \${CUSTOM_SETTINGS} deploy -DskipTests -DskipAssembly'
|
|
}
|
|
}
|
|
}
|
|
stage('Upload nightlies') {
|
|
when {
|
|
anyOf {
|
|
branch 'main'
|
|
branch 'support/struts-6-x-x'
|
|
}
|
|
}
|
|
steps {
|
|
sh './mvnw -B package -DskipTests'
|
|
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
|
|
"""
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|