From c0a28fccf96e7826b0a1da07317c8550ad2d0f0e Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 14 Aug 2026 19:03:05 +0200 Subject: [PATCH] build(ci): baseline the Jenkins .claude/ filter on the PR target branch #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 --- Jenkinsfile | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 105625d5dd..3611fbaae9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -44,13 +44,28 @@ pipeline { stage('Detect changes') { steps { script { - // Skip the build when a push only touched .claude/ - agent + // Skip the build when a change only touched .claude/ - agent // instructions, not code. Fails open: anything unexpected (no - // previous successful build, an unreachable commit, a git error) - // reports true and the build runs as before. + // 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 - base="${GIT_PREVIOUS_SUCCESSFUL_COMMIT:-}" + 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