build(ci): stop .claude-only changes from triggering full builds - #1846
Merged
Conversation
Editing an agent skill rebuilt the whole project on GitHub Actions and Jenkins. No code changes, so every one of those runs was wasted. GitHub Actions, non-required workflows (codeql, owasp, sonar): plain paths-ignore on both push and pull_request. Nothing they report is required in .asf.yaml, so a run that never happens blocks nothing. GitHub Actions, maven.yml: paths-ignore on push only. It is deliberately NOT applied to pull_request, because "Build and Test (JDK 17)" is a required check and GitHub documents that a workflow skipped by path filtering never reports - the check stays Pending and the pull request can never be merged. Instead a small `changes` job inspects the PR's file list and the build job is skipped by condition. A job skipped that way does report, as "skipped", and required checks accept "successful, skipped, or neutral". Jenkins polls SCM, so the trigger cannot be filtered; the two JDK stages are guarded instead. Detection fails open - no previous successful commit, an unreachable commit, or any git error reports true and the build runs exactly as before. The filter tests for a non-empty list of files outside .claude/ rather than using `grep -qv`: the local ugrep 7.5.0 returns 1 from `-qv` on input where `-cv` counts 1 and `-v` prints the line, which silently inverts the decision. Testing emptiness behaves the same everywhere. Exercised against six inputs, including the mixed .claude/ + code case that must still build, and .claudefoo/ which must not be treated as .claude/. Only the 7.x line is changed here; support/struts-6-x-x needs the same edit on its own branch. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
lukaszlenart
added a commit
that referenced
this pull request
Aug 14, 2026
#1846 gated the maven.yml build job with a job-level `if:` on the `changes` output, on the reasoning that a job skipped that way still reports its check as "skipped", which required status checks accept. That holds for a plain job, but not for a matrix one. A matrix job whose condition is false is skipped before the matrix expands, so it emits a single check run named after the raw name template rather than one per matrix entry. On #1848 the reported name was literally Build and Test (JDK ${{ matrix.java }})${{ ... }} while .asf.yaml requires the context "Build and Test (JDK 17)". That context never appeared, so it stayed Pending and the pull request could not be merged - exactly the failure mode #1846 set out to avoid. Drop the job-level condition and gate the four steps instead. The matrix expands, all five checks report success under their expanded names, and no Maven build runs: a .claude-only pull request costs five idle runners for a few seconds instead of five full builds. Jenkins is unaffected - stage-level `when` has no matrix to expand. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
lukaszlenart
added a commit
that referenced
this pull request
Aug 14, 2026
…ch (#1850) #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 b633817... 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Editing an agent skill under
.claude/rebuilt the whole project on GitHub Actions and Jenkins. No code changed, so every one of those runs was wasted.The trap this has to avoid
.asf.yamlmakesBuild and Test (JDK 17)a required status check onmain(andBuild and Test (8)onsupport/struts-6-x-x). GitHub documents that a workflow skipped by path filtering never reports its checks — they stay Pending, and "a pull request that requires those checks to be successful will be blocked from merging."So a plain
paths-ignoreonmaven.yml'spull_requesttrigger would make every docs-only PR permanently unmergeable. A job skipped by anif:condition is different: it does report, asskipped, and required checks accept "successful,skipped, orneutral".What changed
codeql.yml,owasp.yml,sonar.ymlpaths-ignoreon bothpushandpull_request— none of their checks are required, so a run that never happens blocks nothingmaven.ymlpaths-ignoreonpushonly; onpull_requesta smallchangesjob reads the PR's file list and the build job carriesif: needs.changes.outputs.code == 'true'JenkinsfilepollSCMcan't be path-filtered, so the two JDK stages are guarded by aDetect changesstage insteadJenkins detection fails open: no previous successful commit, an unreachable commit, or any git error reports
trueand the build runs exactly as before.One thing worth knowing
The filter tests for a non-empty list of files outside
.claude/rather than usinggrep -qv. While testing I hit ugrep 7.5.0 (installed locally asgrep) returning 1 from-qvon input where-cvcounts 1 and-vprints the line — which silently inverts the decision and would have skipped builds for real code changes. GNU grep on the runners wouldn't do that, but testing emptiness behaves the same everywhere.Exercised against six inputs:
.claude/-only (skip), mixed.claude/+ code (build), empty (skip),.claudefoo/(build — must not be treated as.claude/), code-only (build), nested.claude/.../scripts/*.sh(skip).Not verified yet
This PR itself touches
.github/andJenkinsfile, so it exercises the build path, not the skip path. The skip path is first proven by the next.claude/-only PR — #1844 is one, once this merges. If the required check somehow fails to report there, the fallback is to drop theif:frommaven.ymland keep thepushfilter.Only the 7.x line is changed here;
support/struts-6-x-xneeds the same edit on its own branch.🤖 Generated with Claude Code