Skip to content

Commit 6ec8c23

Browse files
committed
Introduce conditional forceBuildModules
Resolves #421
1 parent 36a32a8 commit 6ec8c23

File tree

6 files changed

+148
-11
lines changed

6 files changed

+148
-11
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,22 @@ Each of these modules is subject to `argsForUpstreamModules` and `skipTestsForUp
620620

621621
This property has no effect in case `buildAll` is enabled.
622622

623+
Additionally (since 3.14.4), forced modules can be defined _conditionally_, e.g.:
624+
625+
```
626+
changed-module=unchanged-module-1|unchanged-module-2
627+
```
628+
629+
will build `unchanged-module-1` _and_ `unchanged-module-2` only if `changed-module` was changed (or depends on changed modules).
630+
631+
There can be multiple such key=value pairs, fully supporting regular expressions and you can mix both flavors, e.g.:
632+
633+
```
634+
another-module, changed-module=unchanged-module-1|unchanged-module-2, .*-core-module=lib-.*-module, test-.*
635+
```
636+
637+
Note: `,` and `=` are reserved delimiters and can therefore _not_ be used in artifact id strings/patterns.
638+
623639
### gib.excludeDownstreamModulesPackagedAs
624640

625641
Defines the packaging (e.g. `jar`) of modules that depend on changed modules but shall not be built.

src/main/java/com/vackosar/gitflowincrementalbuild/boundary/Configuration.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.Map.Entry;
1515
import java.util.Optional;
1616
import java.util.Properties;
17+
import java.util.function.Function;
1718
import java.util.function.Predicate;
1819
import java.util.regex.Pattern;
1920
import java.util.regex.PatternSyntaxException;
@@ -63,6 +64,7 @@ public class Configuration {
6364
public final boolean skipTestsForUpstreamModules;
6465
public final Map<String, String> argsForUpstreamModules;
6566
public final List<Pattern> forceBuildModules;
67+
public final Map<Pattern, Pattern> forceBuildModulesConditionally;
6668
public final List<String> excludeDownstreamModulesPackagedAs;
6769
public final boolean disableSelectedProjectsHandling;
6870

@@ -112,6 +114,7 @@ public Configuration(MavenSession session) {
112114
argsForUpstreamModules = null;
113115

114116
forceBuildModules = null;
117+
forceBuildModulesConditionally = null;
115118

116119
excludeDownstreamModulesPackagedAs = null;
117120

@@ -159,9 +162,20 @@ public Configuration(MavenSession session) {
159162
.map(Configuration::keyValueStringToEntry)
160163
.collect(collectingAndThen(toLinkedMap(), Collections::unmodifiableMap));
161164

162-
forceBuildModules = parseDelimited(Property.forceBuildModules.getValue(pluginProperties, projectProperties), ",")
163-
.map(str -> compilePattern(str, Property.forceBuildModules))
165+
Map<String, String> forceBuildModulesMap = parseDelimited(Property.forceBuildModules.getValue(pluginProperties, projectProperties), ",")
166+
.map(Configuration::keyValueStringToEntry)
167+
.collect(toLinkedMap());
168+
forceBuildModules = forceBuildModulesMap.entrySet().stream()
169+
.filter(entry -> entry.getValue().isEmpty())
170+
.map(entry -> compilePattern(entry.getKey(), Property.forceBuildModules))
164171
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
172+
forceBuildModulesConditionally = forceBuildModulesMap.entrySet().stream()
173+
.filter(entry -> !entry.getValue().isEmpty())
174+
.collect(collectingAndThen(
175+
toLinkedMap(
176+
entry -> compilePattern(entry.getKey(), Property.forceBuildModules),
177+
entry -> compilePattern(entry.getValue(), Property.forceBuildModules)),
178+
Collections::unmodifiableMap));
165179

166180
excludeDownstreamModulesPackagedAs = parseDelimited(Property.excludeDownstreamModulesPackagedAs.getValue(pluginProperties, projectProperties), ",")
167181
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
@@ -267,7 +281,12 @@ private static Map.Entry<String, String> keyValueStringToEntry(String pair) {
267281
}
268282

269283
private static Collector<Entry<String, String>, ?, LinkedHashMap<String, String>> toLinkedMap() {
270-
return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new);
284+
return toLinkedMap(Map.Entry::getKey, Map.Entry::getValue);
285+
}
286+
287+
private static <IN, OUT> Collector<Entry<IN, IN>, ?, LinkedHashMap<OUT, OUT>> toLinkedMap(
288+
Function<Entry<IN, IN>, OUT> keyMapper, Function<Entry<IN, IN>, OUT> valueMapper) {
289+
return Collectors.toMap(keyMapper, valueMapper, (a, b) -> a, LinkedHashMap::new);
271290
}
272291

273292
private static Pattern compilePattern(String patternString, Property property) {

src/main/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemover.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.LinkedHashSet;
1515
import java.util.List;
1616
import java.util.Map;
17+
import java.util.Map.Entry;
1718
import java.util.Objects;
1819
import java.util.Optional;
1920
import java.util.Properties;
@@ -211,10 +212,18 @@ private void modifyProjectList(Set<MavenProject> selected, Set<MavenProject> cha
211212
if (rebuild.isEmpty()) {
212213
handleNoChangesDetected(selected, projectComparator, config);
213214
} else {
214-
if (!config.forceBuildModules.isEmpty()) {
215+
if (!config.forceBuildModules.isEmpty() || !config.forceBuildModulesConditionally.isEmpty()) {
216+
217+
List<Pattern> conditionalPatterns = config.forceBuildModulesConditionally.entrySet().stream()
218+
.filter(entry -> impacted.stream()
219+
.anyMatch(proj -> entry.getKey().matcher(proj.getArtifactId()).matches()))
220+
.map(Entry::getValue)
221+
.collect(Collectors.toList());
222+
215223
Set<MavenProject> forceBuildModules = config.mavenSession.getProjects().stream()
216224
.filter(proj -> !rebuild.contains(proj))
217-
.filter(proj -> matchesAny(proj.getArtifactId(), config.forceBuildModules))
225+
.filter(proj -> matchesAny(proj.getArtifactId(), config.forceBuildModules)
226+
|| matchesAny(proj.getArtifactId(), conditionalPatterns))
218227
.map(proj -> applyUpstreamModuleArgs(proj, config))
219228
.collect(Collectors.toCollection(LinkedHashSet::new));
220229
rebuild.addAll(forceBuildModules);
@@ -373,7 +382,7 @@ private Stream<MavenProject> streamUpstreamProjects(MavenProject project, MavenS
373382
}
374383

375384
private boolean matchesAny(final String str, Collection<Pattern> patterns) {
376-
return patterns.stream().anyMatch(pattern -> pattern.matcher(str).matches());
385+
return !patterns.isEmpty() && patterns.stream().anyMatch(pattern -> pattern.matcher(str).matches());
377386
}
378387

379388
private static class ProjectSelectionUtil {

src/main/java/com/vackosar/gitflowincrementalbuild/control/Property.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public Optional<String> deprecatedName() {
129129
*/
130130
skipTestsForUpstreamModules("false", "stfum", true),
131131
/**
132-
* This property allows adding arbitrary arguments/properties for upstream modules to futher reduce overhead.
132+
* This property allows adding arbitrary arguments/properties for upstream modules to further reduce overhead.
133133
*/
134134
argsForUpstreamModules("", "afum"),
135135
/**

src/test/java/com/vackosar/gitflowincrementalbuild/boundary/ConfigurationTest.java

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Arrays;
1313
import java.util.LinkedHashMap;
1414
import java.util.Map;
15+
import java.util.Optional;
1516
import java.util.Properties;
1617
import java.util.regex.Pattern;
1718
import java.util.regex.PatternSyntaxException;
@@ -168,11 +169,13 @@ public void forceBuildModules_pattern() {
168169

169170
Configuration configuration = new Configuration(mavenSessionMock);
170171

171-
assertThat(configuration.forceBuildModules).as("Field forceBuildModules is null").isNotNull();
172-
assertThat(configuration.forceBuildModules).as("Unexpected number of Patterns in forceBuildModules").hasSize(1);
172+
assertThat(configuration.forceBuildModules).as("Field forceBuildModules").isNotNull();
173+
assertThat(configuration.forceBuildModules).as("Number of Patterns in forceBuildModules").hasSize(1);
173174
Pattern pattern = configuration.forceBuildModules.get(0);
174-
assertThat(pattern).as("Pattern form forceBuildModules is null").isNotNull();
175-
assertThat(pattern.pattern()).as("Unexpected pattern string of Pattern from forceBuildModules").isEqualTo(expectedPatternString);
175+
assertThat(pattern).as("Pattern from forceBuildModules").isNotNull();
176+
assertThat(pattern.pattern()).as("String of Pattern from forceBuildModules").isEqualTo(expectedPatternString);
177+
178+
assertThat(configuration.forceBuildModulesConditionally).isEmpty();
176179
}
177180

178181
@Test
@@ -184,6 +187,48 @@ public void forceBuildModules_patternInvalid() {
184187
.withCauseExactlyInstanceOf(PatternSyntaxException.class);
185188
}
186189

190+
@Test
191+
public void forceBuildModules_withConditionals() {
192+
System.setProperty(Property.forceBuildModules.prefixedName(), "A, X=B|C, D, .*M, E.*=F|G.*");
193+
194+
Configuration configuration = new Configuration(mavenSessionMock);
195+
196+
assertThat(configuration.forceBuildModules).as("Field forceBuildModules").isNotNull();
197+
assertThat(configuration.forceBuildModules)
198+
.as("Pattern strings of forceBuildModules")
199+
.extracting(Pattern::pattern)
200+
.containsExactly("A", "D", ".*M");
201+
202+
assertThat(configuration.forceBuildModulesConditionally).as("Field forceBuildModulesConditionally").isNotNull();
203+
assertThat(configuration.forceBuildModulesConditionally)
204+
.as("LHS pattern strings of forceBuildModulesConditionally")
205+
.extractingFromEntries(e -> Optional.ofNullable(e.getKey()).map(Pattern::pattern).orElse(null))
206+
.containsExactly("X", "E.*");
207+
assertThat(configuration.forceBuildModulesConditionally)
208+
.as("RHS pattern strings of forceBuildModulesConditionally")
209+
.extractingFromEntries(e -> Optional.ofNullable(e.getValue()).map(Pattern::pattern).orElse(null))
210+
.containsExactly("B|C", "F|G.*");
211+
}
212+
213+
@Test
214+
public void forceBuildModules_onlyConditionals() {
215+
System.setProperty(Property.forceBuildModules.prefixedName(), "X=B|C");
216+
217+
Configuration configuration = new Configuration(mavenSessionMock);
218+
219+
assertThat(configuration.forceBuildModules).isEmpty();
220+
221+
assertThat(configuration.forceBuildModulesConditionally).as("Field forceBuildModulesConditionally").isNotNull();
222+
assertThat(configuration.forceBuildModulesConditionally)
223+
.as("LHS pattern strings of forceBuildModulesConditionally")
224+
.extractingFromEntries(e -> Optional.ofNullable(e.getKey()).map(Pattern::pattern).orElse(null))
225+
.containsExactly("X");
226+
assertThat(configuration.forceBuildModulesConditionally)
227+
.as("RHS pattern strings of forceBuildModulesConditionally")
228+
.extractingFromEntries(e -> Optional.ofNullable(e.getValue()).map(Pattern::pattern).orElse(null))
229+
.containsExactly("B|C");
230+
}
231+
187232
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
188233
// tests for configuration.buildUpstreamMode (which is calculated from two(!) properties: buildUpstream and buildUpstreamMode)
189234

src/test/java/com/vackosar/gitflowincrementalbuild/boundary/UnchangedProjectsRemoverTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,28 @@ public void singleChanged_forceBuildModules() throws GitAPIException, IOExceptio
526526
verify(mavenSessionMock).setProjects(Arrays.asList(moduleA, changedModuleMock));
527527
}
528528

529+
@Test
530+
public void singleChanged_forceBuildModules_conditionalMatches() throws GitAPIException, IOException {
531+
MavenProject changedModuleMock = addModuleMock(AID_MODULE_B, true);
532+
533+
addGibProperty(Property.forceBuildModules, changedModuleMock.getArtifactId() + "=" + moduleA.getArtifactId());
534+
535+
underTest.act(config());
536+
537+
verify(mavenSessionMock).setProjects(Arrays.asList(moduleA, changedModuleMock));
538+
}
539+
540+
@Test
541+
public void singleChanged_forceBuildModules_conditionalMatchesNot() throws GitAPIException, IOException {
542+
MavenProject changedModuleMock = addModuleMock(AID_MODULE_B, true);
543+
544+
addGibProperty(Property.forceBuildModules, changedModuleMock.getArtifactId() + "=X");
545+
546+
underTest.act(config());
547+
548+
verify(mavenSessionMock).setProjects(Arrays.asList(changedModuleMock));
549+
}
550+
529551
@Test
530552
public void singleChanged_forceBuildModules_two() throws GitAPIException, IOException {
531553
MavenProject changedModuleMock = addModuleMock(AID_MODULE_B, true);
@@ -565,6 +587,32 @@ public void singleChanged_forceBuildModules_twoWildcards() throws GitAPIExceptio
565587
Arrays.asList(moduleA, changedModuleMock, unchangedModuleMock));
566588
}
567589

590+
@Test
591+
public void singleChanged_forceBuildModules_twoWildcards_secondMatchesNot() throws GitAPIException, IOException {
592+
MavenProject changedModuleMock = addModuleMock(AID_MODULE_B, true);
593+
addModuleMock("module-C", false);
594+
595+
addGibProperty(Property.forceBuildModules, AID_MODULE_A + ".*,.*-X");
596+
597+
underTest.act(config());
598+
599+
verify(mavenSessionMock).setProjects(
600+
Arrays.asList(moduleA, changedModuleMock));
601+
}
602+
603+
@Test
604+
public void singleChanged_forceBuildModules_mixWithConditional() throws GitAPIException, IOException {
605+
MavenProject changedModuleMock = addModuleMock(AID_MODULE_B, true);
606+
MavenProject unchangedModuleMock = addModuleMock("module-C", false);
607+
608+
addGibProperty(Property.forceBuildModules, AID_MODULE_A + ".*,.*B=.*-C");
609+
610+
underTest.act(config());
611+
612+
verify(mavenSessionMock).setProjects(
613+
Arrays.asList(moduleA, changedModuleMock, unchangedModuleMock));
614+
}
615+
568616
@Test
569617
public void singleChanged_excludeDownstreamModulesPackagedAs_oneTransitive_oneExcluded() throws GitAPIException, IOException {
570618
MavenProject changedProjectMock = addModuleMock(AID_MODULE_B, true);

0 commit comments

Comments
 (0)