Strip executable() conditions from consumer POMs - #12874
Conversation
The executable() function evaluates against the local system PATH, making profile activation environment-dependent. When such conditions survive into published consumer POMs, downstream consumers silently evaluate them against their own PATH, producing non-reproducible builds. Strip the entire condition string when it contains an executable() call during DefaultConsumerPomBuilder processing. This applies to all three transform paths: transformNonPom and transformBom (via prune()), and transformPom (directly). When no other activation triggers remain after stripping, the activation is removed entirely. Closes #12570 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gnodet
left a comment
There was a problem hiding this comment.
Correct and well-motivated change that prevents environment-dependent executable() conditions from leaking into published consumer POMs. The implementation covers all three transform paths and the design decision to remove the entire condition string (rather than surgically extracting executable() from compound expressions) is sound.
The condition.contains("executable(") heuristic correctly catches negated forms like not(executable(...)) and compound expressions. All activation fields are covered by isActivationEmpty.
Findings:
-
Test assertion pattern —
testTransformNonPomStripsExecutableConditionguards theassertNullcheck withif (!result.getProfiles().isEmpty()), which silently passes if the profile is unexpectedly removed during pruning. ThetestTransformPomStripsExecutableConditiontest correctly usesassertFalse(result.getProfiles().isEmpty(), ...)before checking the activation — the same pattern should be used here:assertFalse(result.getProfiles().isEmpty(), "Profile with dependencies should survive pruning"); assertNull(result.getProfiles().get(0).getActivation(), "executable() condition should be stripped");
-
Missing BOM test (minor) — No test covers the
transformBompath with executable() conditions. It shares the sameprune()code path, but a dedicated test would confirm end-to-end. -
Empty profile survival (minor) — In
transformPom,stripExecutableConditionsis applied but there's noisEmptyfilter afterwards (unlikeprune()which does filter). A profile whose sole content was anexecutable()activation would become id-only after stripping. This is consistent with existingtransformPombehavior and the practical impact is negligible.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
Claude Code on behalf of Guillaume Nodet
Summary
executable()conditions from profile activations during consumer POM building, preventing environment-dependent PATH checks from polluting published artifactstransformNonPom,transformBom(viaprune()), andtransformPom(directly)Details
The
executable()function (added in #12332 / MNG-8768) evaluates against the local systemPATH. When such conditions survive into published consumer POMs, downstream consumers silently evaluate them against their ownPATH, producing non-reproducible builds.This PR removes the entire
conditionstring when it contains anexecutable(call duringDefaultConsumerPomBuilderprocessing, rather than attempting partial expression surgery which could change boolean semantics in unexpected ways.Test plan
ConsumerPomBuilderTestcovering:executable()activation → activation removedexecutable()+ other triggers (OS, property) → condition stripped, other triggers preservedexecutable()→ unchangedexecutable()(not(executable(...))) → strippedtransformNonPompath strips executable conditionstransformPompath strips executable conditionsConsumerPomBuilderTesttests pass (9 existing + 8 new)Closes #12570
🤖 Generated with Claude Code