From cb76f85353233b5f06cf54f7705dfc49674d099c Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 02:00:39 -0500 Subject: [PATCH 1/8] Discussion point JENKINS-67725 --- .../java/jenkins/branch/MultiBranchProject.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/jenkins/branch/MultiBranchProject.java b/src/main/java/jenkins/branch/MultiBranchProject.java index 596accfe..52395278 100644 --- a/src/main/java/jenkins/branch/MultiBranchProject.java +++ b/src/main/java/jenkins/branch/MultiBranchProject.java @@ -2018,10 +2018,26 @@ private void observeExisting(@NonNull SCMHead head, @NonNull SCMRevision revisio listener.getLogger() .format("Changes detected: %s (%s → %s)%n", rawName, scmLastBuiltRevision, revision); + needSave = true; // get the previous seen revision SCMRevision scmLastSeenRevision = lastSeenRevisionOrDefault(project, scmLastBuiltRevision); doAutomaticBuilds(head, revision, rawName, project, revisionActions, scmLastBuiltRevision, scmLastSeenRevision); + + //We need to save the revision here so we dont get in a loop + //Imagine a buildstrategy said dont build - but changes detected always is true - it puts jenkins into a loop + //This probably wont be merged - but we need a discussion why this isnt valid. + //Either it was chosen not to be built or it did build - either way we want to gaurantee it doesnt build the same thing again right? + //At least I feel I can say the above comment with this specific code path. + try { + _factory.setRevisionHash(project, revision); + listener.getLogger() + .format("Setting job as built - either buildstrategies or actually built - either way we shouldnt build the same commit again"); + + } catch (IOException e) { + printStackTrace(e, listener.error("Could not update last revision hash observeExisting")); + } + } else { listener.getLogger().format("No changes detected: %s (still at %s)%n", rawName, revision); @@ -2104,6 +2120,7 @@ private boolean changesDetected(@NonNull SCMRevision revision, @NonNull P projec } } } + return changesDetected; } From 7b58e9c519a282017f4aaccdd027d594a1582baa Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 03:23:27 -0500 Subject: [PATCH 2/8] Initial version - slightly extended API. --- .../jenkins/branch/BranchBuildStrategy.java | 27 ++++++++ .../jenkins/branch/MultiBranchProject.java | 65 ++++++++++++++----- 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/main/java/jenkins/branch/BranchBuildStrategy.java b/src/main/java/jenkins/branch/BranchBuildStrategy.java index f32ddd5d..5a9b633b 100644 --- a/src/main/java/jenkins/branch/BranchBuildStrategy.java +++ b/src/main/java/jenkins/branch/BranchBuildStrategy.java @@ -274,6 +274,33 @@ public final boolean automaticBuild(@NonNull SCMSource source, return isAutomaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener); } + + + + + /** + * API:Should we update last built revision if we did not do a build? + * + * @return {@code true} if and only if we should consider whatever commit we received as built. + */ + @SuppressWarnings("deprecation") + public final boolean updatingLastBuiltRevisionWithNoBuild() { + return isUpdatingLastBuiltRevisionWithNoBuild(new LogTaskListener(Logger.getLogger(getClass().getName()), Level.INFO)); + } + + /** + * SPI: Should we update last built revision if we did not do a build? + * + * @param listener the TaskListener to be used + * @return {@code true} if and only if we should consider whatever commit we received as built. + */ + @Restricted(ProtectedExternally.class) + public boolean isUpdatingLastBuiltRevisionWithNoBuild(@NonNull TaskListener listener) { + return false; + } + + + /** * {@inheritDoc} */ diff --git a/src/main/java/jenkins/branch/MultiBranchProject.java b/src/main/java/jenkins/branch/MultiBranchProject.java index 52395278..4a96f149 100644 --- a/src/main/java/jenkins/branch/MultiBranchProject.java +++ b/src/main/java/jenkins/branch/MultiBranchProject.java @@ -2023,21 +2023,6 @@ private void observeExisting(@NonNull SCMHead head, @NonNull SCMRevision revisio // get the previous seen revision SCMRevision scmLastSeenRevision = lastSeenRevisionOrDefault(project, scmLastBuiltRevision); doAutomaticBuilds(head, revision, rawName, project, revisionActions, scmLastBuiltRevision, scmLastSeenRevision); - - //We need to save the revision here so we dont get in a loop - //Imagine a buildstrategy said dont build - but changes detected always is true - it puts jenkins into a loop - //This probably wont be merged - but we need a discussion why this isnt valid. - //Either it was chosen not to be built or it did build - either way we want to gaurantee it doesnt build the same thing again right? - //At least I feel I can say the above comment with this specific code path. - try { - _factory.setRevisionHash(project, revision); - listener.getLogger() - .format("Setting job as built - either buildstrategies or actually built - either way we shouldnt build the same commit again"); - - } catch (IOException e) { - printStackTrace(e, listener.error("Could not update last revision hash observeExisting")); - } - } else { listener.getLogger().format("No changes detected: %s (still at %s)%n", rawName, revision); @@ -2224,6 +2209,23 @@ private void doAutomaticBuilds(@NonNull SCMHead head, @NonNull SCMRevision revis ); } else { listener.getLogger().format("No automatic build triggered for %s%n", rawName); + + //Should we update the last Build revision with no build performed? + if(isUpdatingLastBuiltRevisionWithNoBuild()){ + //We need to save the revision here so we dont get in a loop + //Imagine a buildstrategy said dont build - but changes detected always is true - it puts jenkins into a loop + //Either it was chosen not to be built or it did build - either way we want to gaurantee it doesnt build the same thing again right? + //At least let a plugin owner extend and change behavior even if the default is do not update the last built revision. + try { + listener.getLogger().format("Updating revision hash so it will not be built again %n"); + _factory.setRevisionHash(project, revision); + listener.getLogger() + .format("Setting job as built - either buildstrategies or actually built - either way we shouldnt build the same commit again"); + + } catch (IOException e) { + printStackTrace(e, listener.error("Could not update last revision hash observeExisting")); + } + } } try { _factory.setLastSeenRevisionHash(project, revision); @@ -2232,6 +2234,39 @@ private void doAutomaticBuilds(@NonNull SCMHead head, @NonNull SCMRevision revis } } + + /** + * Tests if the specified buildStrategies say to update the last built revision. + * @return {@code true} if we are updating using setRevisionHash. + */ + private boolean isUpdatingLastBuiltRevisionWithNoBuild() { + BranchSource branchSource = null; + for (BranchSource s: MultiBranchProject.this.sources) { + if (s.getSource().getId().equals(source.getId())) { + branchSource = s; + break; + } + } + if (branchSource == null) { + // no match, means default to False + return false; + } + List buildStrategies = branchSource.getBuildStrategies(); + if (buildStrategies.isEmpty()) { + // we will use default behaviour, return False + return false; + } else { + for (BranchBuildStrategy s: buildStrategies) { + if (s.updatingLastBuiltRevisionWithNoBuild()) { + //IF it is ever true, return + return true; + } + } + return false; + } + } + + /** * Tests if the specified {@link SCMHead} should be automatically built when discovered / modified. * @param head the head. From 975123f679c5517251d9190ed7c14cbbfb186fbd Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 03:37:58 -0500 Subject: [PATCH 3/8] Added Tests --- src/test/java/integration/EventsTest.java | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/test/java/integration/EventsTest.java b/src/test/java/integration/EventsTest.java index 7f9e09c6..dccd70f0 100644 --- a/src/test/java/integration/EventsTest.java +++ b/src/test/java/integration/EventsTest.java @@ -2907,6 +2907,46 @@ public void given_multibranch_when__build_is_skipped_then_lastBuiltRevision_is_n } } + @Test + public void given_multibranch_when__build_is_skipped_then_lastBuiltRevision_is_updated() throws Exception { + try (MockSCMController c = MockSCMController.create()) { + c.createRepository("foo"); + BasicMultiBranchProject prj = r.jenkins.createProject(BasicMultiBranchProject.class, "my-project"); + prj.setCriteria(null); + BranchSource source = new BranchSource(new MockSCMSource(c, "foo", new MockSCMDiscoverBranches())); + source.setBuildStrategies(Collections.singletonList(new SkipAndUpdateRevisionHashBuildStrategyImpl())); + prj.getSourcesList().add(source); + fire(new MockSCMHeadEvent(SCMEvent.Type.CREATED, c, "foo", "master", c.getRevision("foo", "master"))); + FreeStyleProject master = prj.getItem("master"); + r.waitUntilNoActivity(); + assertThat("The master branch was built", master.getLastBuild(), nullValue()); + SCMRevision scmLastSeenRevision1 = prj.getProjectFactory().getLastSeenRevision(master); + assertNotNull(scmLastSeenRevision1); + assertNotNull(prj.getProjectFactory().getRevision(master)); + } + } + + public static class SkipAndUpdateRevisionHashBuildStrategyImpl extends BranchBuildStrategy { + @Override + public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, + @NonNull SCMRevision currRevision, + SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, + TaskListener listener) { + return false; + } + + @Override + public boolean isUpdatingLastBuiltRevisionWithNoBuild(TaskListener listener){ + return true; + } + + @TestExtension( + "given_multibranch_when__build_is_skipped_then_lastBuiltRevision_is_updated") + public static class DescriptorImpl extends BranchBuildStrategyDescriptor { + + } + } + public static class SkipIAllBuildStrategyImpl extends BranchBuildStrategy { @Override public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, From 8e323857771a5a0373eef10c68642152c292512a Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 03:50:54 -0500 Subject: [PATCH 4/8] Remove whitespace --- src/main/java/jenkins/branch/BranchBuildStrategy.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/jenkins/branch/BranchBuildStrategy.java b/src/main/java/jenkins/branch/BranchBuildStrategy.java index 5a9b633b..36950636 100644 --- a/src/main/java/jenkins/branch/BranchBuildStrategy.java +++ b/src/main/java/jenkins/branch/BranchBuildStrategy.java @@ -274,10 +274,6 @@ public final boolean automaticBuild(@NonNull SCMSource source, return isAutomaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener); } - - - - /** * API:Should we update last built revision if we did not do a build? * @@ -299,8 +295,6 @@ public boolean isUpdatingLastBuiltRevisionWithNoBuild(@NonNull TaskListener list return false; } - - /** * {@inheritDoc} */ From 2e784f3196f59cb257dfcfaf6874a884e288220d Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 03:52:47 -0500 Subject: [PATCH 5/8] More whitespace and minor edits --- src/main/java/jenkins/branch/MultiBranchProject.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/jenkins/branch/MultiBranchProject.java b/src/main/java/jenkins/branch/MultiBranchProject.java index 4a96f149..03b673da 100644 --- a/src/main/java/jenkins/branch/MultiBranchProject.java +++ b/src/main/java/jenkins/branch/MultiBranchProject.java @@ -2018,7 +2018,6 @@ private void observeExisting(@NonNull SCMHead head, @NonNull SCMRevision revisio listener.getLogger() .format("Changes detected: %s (%s → %s)%n", rawName, scmLastBuiltRevision, revision); - needSave = true; // get the previous seen revision SCMRevision scmLastSeenRevision = lastSeenRevisionOrDefault(project, scmLastBuiltRevision); @@ -2105,7 +2104,6 @@ private boolean changesDetected(@NonNull SCMRevision revision, @NonNull P projec } } } - return changesDetected; } @@ -2223,7 +2221,7 @@ private void doAutomaticBuilds(@NonNull SCMHead head, @NonNull SCMRevision revis .format("Setting job as built - either buildstrategies or actually built - either way we shouldnt build the same commit again"); } catch (IOException e) { - printStackTrace(e, listener.error("Could not update last revision hash observeExisting")); + printStackTrace(e, listener.error("Could not update last revision hash doAutomaticBuilds")); } } } @@ -2234,7 +2232,6 @@ private void doAutomaticBuilds(@NonNull SCMHead head, @NonNull SCMRevision revis } } - /** * Tests if the specified buildStrategies say to update the last built revision. * @return {@code true} if we are updating using setRevisionHash. @@ -2266,7 +2263,6 @@ private boolean isUpdatingLastBuiltRevisionWithNoBuild() { } } - /** * Tests if the specified {@link SCMHead} should be automatically built when discovered / modified. * @param head the head. From 4955abf7fb5549dfb1438c2bcfa03ca5d70c690d Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 04:05:59 -0500 Subject: [PATCH 6/8] More cleanup --- src/main/java/jenkins/branch/MultiBranchProject.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/jenkins/branch/MultiBranchProject.java b/src/main/java/jenkins/branch/MultiBranchProject.java index 03b673da..b91c0a53 100644 --- a/src/main/java/jenkins/branch/MultiBranchProject.java +++ b/src/main/java/jenkins/branch/MultiBranchProject.java @@ -2217,8 +2217,6 @@ private void doAutomaticBuilds(@NonNull SCMHead head, @NonNull SCMRevision revis try { listener.getLogger().format("Updating revision hash so it will not be built again %n"); _factory.setRevisionHash(project, revision); - listener.getLogger() - .format("Setting job as built - either buildstrategies or actually built - either way we shouldnt build the same commit again"); } catch (IOException e) { printStackTrace(e, listener.error("Could not update last revision hash doAutomaticBuilds")); From 47e54a0a734125decdd4964b006d80a834b32421 Mon Sep 17 00:00:00 2001 From: Nicholas Carpenter Date: Thu, 3 Feb 2022 20:30:42 -0500 Subject: [PATCH 7/8] Add SCMEvent to BranchBuildStrategy Extension. --- .../jenkins/branch/BranchBuildStrategy.java | 76 ++++++++++++++++++- .../jenkins/branch/MultiBranchProject.java | 2 +- src/test/java/integration/EventsTest.java | 55 +++++++++++--- 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/src/main/java/jenkins/branch/BranchBuildStrategy.java b/src/main/java/jenkins/branch/BranchBuildStrategy.java index 36950636..11e99b5a 100644 --- a/src/main/java/jenkins/branch/BranchBuildStrategy.java +++ b/src/main/java/jenkins/branch/BranchBuildStrategy.java @@ -37,6 +37,7 @@ import jenkins.scm.api.SCMSource; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.ProtectedExternally; +import jenkins.scm.api.SCMEvent; /** * An extension point that allows controlling whether a specific {@link SCMHead} should be automatically built when @@ -160,13 +161,46 @@ public boolean isAutomaticBuild(@NonNull SCMSource source, * {@link SCMHead} has been detected as created / modified. * @since 2.4.2 */ + @Deprecated + @SuppressWarnings("deprecation") + @Restricted(ProtectedExternally.class) + public boolean isAutomaticBuild(@NonNull SCMSource source, + @NonNull SCMHead head, + @NonNull SCMRevision currRevision, + @CheckForNull SCMRevision lastBuiltRevision, + @CheckForNull SCMRevision lastSeenRevision, + @NonNull TaskListener listener){ + throw new UnsupportedOperationException("Modern implementation accessed using legacy API method"); + } + + /** + * SPI: Should the specified {@link SCMRevision} of the {@link SCMHead} for the specified {@link SCMSource} be + * triggered when the {@link SCMHead} has been detected as created / modified? + * + * @param source the {@link SCMSource} + * @param head the {@link SCMHead} + * @param currRevision the {@link SCMRevision} that the build head is now at + * @param lastBuiltRevision the {@link SCMRevision} that the build head was last seen at or {@code null} if this is a newly + * discovered head. It replaces prevRevision from the previous SPI version. Care should be taken to consider + * the case of non {@link SCMRevision#isDeterministic()} previous revisions as polling for changes will have + * confirmed that there is a change between this and {@code currRevision} even if the two are equal. + * @param lastSeenRevision the {@link SCMRevision} that the head was last seen + * @param listener the {@link TaskListener} that can be used for outputting any rational for the decision + * @return {@code true} if and only if the {@link SCMRevision} should be automatically built when the + * {@link SCMHead} has been detected as created / modified. + * @param scmEvent the {@link SCMEvent} that started the build if it exists. + * @since 2.4.2 + */ @Restricted(ProtectedExternally.class) public abstract boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, @CheckForNull SCMRevision lastBuiltRevision, @CheckForNull SCMRevision lastSeenRevision, - @NonNull TaskListener listener); + @NonNull TaskListener listener, + @CheckForNull SCMEvent scmEvent); + + /** * API: Should the specified {@link SCMRevision} of the {@link SCMHead} for the specified {@link SCMSource} be @@ -243,10 +277,46 @@ public final boolean automaticBuild(@NonNull SCMSource source, @NonNull SCMRevision currRevision, @CheckForNull SCMRevision lastBuiltRevision, @CheckForNull SCMRevision lastSeenRevision, - @NonNull TaskListener listener) { + @NonNull TaskListener listener ) { + return automaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener, null); + } + + + /** + * API: Should the specified {@link SCMRevision} of the {@link SCMHead} for the specified {@link SCMSource} be + * triggered when the {@link SCMHead} has been detected as created / modified? + * + * @param source the {@link SCMSource} + * @param head the {@link SCMHead} + * @param currRevision the {@link SCMRevision} that the head is now at + * @param lastBuiltRevision the {@link SCMRevision} that the build head was last seen at or {@code null} if this is a newly + * discovered head. Care should be taken to consider the case of non + * {@link SCMRevision#isDeterministic()} previous revisions as polling for changes will have + * confirmed that there is a change between this and {@code currRevision} even if the two + * are equal. + * @param lastSeenRevision the {@link SCMRevision} that the head was last seen + * @param listener the TaskListener to be used + * @return {@code true} if and only if the {@link SCMRevision} should be automatically built when the + * {@link SCMHead} has been detected as created / modified. + * @since 2.4.2+ + */ + @SuppressWarnings("deprecation") + public final boolean automaticBuild(@NonNull SCMSource source, + @NonNull SCMHead head, + @NonNull SCMRevision currRevision, + @CheckForNull SCMRevision lastBuiltRevision, + @CheckForNull SCMRevision lastSeenRevision, + @NonNull TaskListener listener, + @CheckForNull SCMEvent scmEvent ) { + + if (Util.isOverridden(BranchBuildStrategy.class, getClass(), "isAutomaticBuild", SCMSource.class, + SCMHead.class, SCMRevision.class, SCMRevision.class, SCMRevision.class, TaskListener.class, SCMEvent.class)) { + // modern implementation written to the 2.4.2+ + return isAutomaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener, scmEvent); + } if (Util.isOverridden(BranchBuildStrategy.class, getClass(), "isAutomaticBuild", SCMSource.class, SCMHead.class, SCMRevision.class, SCMRevision.class, SCMRevision.class, TaskListener.class)) { - // modern implementation written to the 2.4.2+ spec + // modern implementation written to the 2.4.2 spec return isAutomaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener); } if (Util.isOverridden(BranchBuildStrategy.class, getClass(), "isAutomaticBuild", SCMSource.class, diff --git a/src/main/java/jenkins/branch/MultiBranchProject.java b/src/main/java/jenkins/branch/MultiBranchProject.java index b91c0a53..8e1010b6 100644 --- a/src/main/java/jenkins/branch/MultiBranchProject.java +++ b/src/main/java/jenkins/branch/MultiBranchProject.java @@ -2290,7 +2290,7 @@ private boolean isAutomaticBuild(@NonNull SCMHead head, return !(head instanceof TagSCMHead); } else { for (BranchBuildStrategy s: buildStrategies) { - if (s.automaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener)) { + if (s.automaticBuild(source, head, currRevision, lastBuiltRevision, lastSeenRevision, listener, event)) { return true; } } diff --git a/src/test/java/integration/EventsTest.java b/src/test/java/integration/EventsTest.java index dccd70f0..8a474eed 100644 --- a/src/test/java/integration/EventsTest.java +++ b/src/test/java/integration/EventsTest.java @@ -649,7 +649,7 @@ public static class BuildEverythingStrategyImpl extends BranchBuildStrategy { public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - TaskListener listener) { + TaskListener listener, @CheckForNull SCMEvent scmEvent) { return true; } @@ -700,7 +700,7 @@ public static class BuildChangeRequestsStrategyImpl extends BranchBuildStrategy public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - @NonNull TaskListener listener) { + @NonNull TaskListener listener, @CheckForNull SCMEvent scmEvent) { return head instanceof ChangeRequestSCMHead; } @@ -716,7 +716,7 @@ public static class BuildTrustedChangeRequestsStrategyImpl extends BranchBuildSt public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - @NonNull TaskListener listener) { + @NonNull TaskListener listener, @CheckForNull SCMEvent scmEvent) { if (head instanceof ChangeRequestSCMHead) { try { return currRevision.equals(source.getTrustedRevision(currRevision, listener)); @@ -1083,7 +1083,7 @@ public BuildRevisionStrategyImpl(String... approved) { public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - @NonNull TaskListener listener) { + @NonNull TaskListener listener, @CheckForNull SCMEvent scmEvent) { return currRevision instanceof MockSCMRevision && approved.contains(((MockSCMRevision) currRevision).getHash()); } @@ -1149,7 +1149,7 @@ public IgnoreTargetChangesStrategyImpl() { public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - @NonNull TaskListener listener) { + @NonNull TaskListener listener, @CheckForNull SCMEvent scmEvent) { if (currRevision instanceof ChangeRequestSCMRevision) { ChangeRequestSCMRevision currCR = (ChangeRequestSCMRevision) currRevision; if (lastBuiltRevision instanceof ChangeRequestSCMRevision) { @@ -2866,7 +2866,7 @@ public static class SkipInitialBuildStrategyImpl extends BranchBuildStrategy { public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - TaskListener listener) { + TaskListener listener, @CheckForNull SCMEvent scmEvent) { if (lastSeenRevision != null) { return true; } @@ -2907,7 +2907,7 @@ public void given_multibranch_when__build_is_skipped_then_lastBuiltRevision_is_n } } - @Test + @Test public void given_multibranch_when__build_is_skipped_then_lastBuiltRevision_is_updated() throws Exception { try (MockSCMController c = MockSCMController.create()) { c.createRepository("foo"); @@ -2926,12 +2926,49 @@ public void given_multibranch_when__build_is_skipped_then_lastBuiltRevision_is_u } } + @Test + public void given_multibranch_when_build_is_skipped_due_to_use_of_scmevent() throws Exception { + try (MockSCMController c = MockSCMController.create()) { + c.createRepository("foo"); + BasicMultiBranchProject prj = r.jenkins.createProject(BasicMultiBranchProject.class, "my-project"); + prj.setCriteria(null); + BranchSource source = new BranchSource(new MockSCMSource(c, "foo", new MockSCMDiscoverBranches())); + source.setBuildStrategies(Collections.singletonList(new UseSCMEventBuildStrategy())); + prj.getSourcesList().add(source); + c.addFile("foo", "master", "adding file", "file", new byte[0]); + fire(new MockSCMHeadEvent(SCMEvent.Type.UPDATED, c, "foo", "master", c.getRevision("foo", "master"))); + FreeStyleProject master = prj.getItem("master"); + r.waitUntilNoActivity(); + assertThat("The master branch was not built", master.getLastBuild(), notNullValue()); + assertNotNull(prj.getProjectFactory().getRevision(master)); + } + } + + public static class UseSCMEventBuildStrategy extends BranchBuildStrategy { + @Override + public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, + @NonNull SCMRevision currRevision, + SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, + TaskListener listener, @CheckForNull SCMEvent scmEvent) { + //Assume not null + //Returns effective true + return scmEvent.getTimestamp() >= 0; + } + + + @TestExtension( + "given_multibranch_when_build_is_skipped_due_to_use_of_scmevent") + public static class DescriptorImpl extends BranchBuildStrategyDescriptor { + + } + } + public static class SkipAndUpdateRevisionHashBuildStrategyImpl extends BranchBuildStrategy { @Override public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - TaskListener listener) { + TaskListener listener, @CheckForNull SCMEvent scmEvent) { return false; } @@ -2952,7 +2989,7 @@ public static class SkipIAllBuildStrategyImpl extends BranchBuildStrategy { public boolean isAutomaticBuild(@NonNull SCMSource source, @NonNull SCMHead head, @NonNull SCMRevision currRevision, SCMRevision lastBuiltRevision, SCMRevision lastSeenRevision, - TaskListener listener) { + TaskListener listener, @CheckForNull SCMEvent scmEvent) { return false; } From 8659b96967eca0c1f14b33ec4751a9ec2bad6e31 Mon Sep 17 00:00:00 2001 From: Nick Carpenter Date: Tue, 1 Mar 2022 09:25:50 -0500 Subject: [PATCH 8/8] Update src/main/java/jenkins/branch/MultiBranchProject.java Co-authored-by: James Nord --- src/main/java/jenkins/branch/MultiBranchProject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jenkins/branch/MultiBranchProject.java b/src/main/java/jenkins/branch/MultiBranchProject.java index 8d4ee1bf..9a0f5ced 100644 --- a/src/main/java/jenkins/branch/MultiBranchProject.java +++ b/src/main/java/jenkins/branch/MultiBranchProject.java @@ -2209,7 +2209,7 @@ private void doAutomaticBuilds(@NonNull SCMHead head, @NonNull SCMRevision revis listener.getLogger().format("No automatic build triggered for %s%n", rawName); //Should we update the last Build revision with no build performed? - if(isUpdatingLastBuiltRevisionWithNoBuild()){ + if (isUpdatingLastBuiltRevisionWithNoBuild()) { //We need to save the revision here so we dont get in a loop //Imagine a buildstrategy said dont build - but changes detected always is true - it puts jenkins into a loop //Either it was chosen not to be built or it did build - either way we want to gaurantee it doesnt build the same thing again right?