diff --git a/core/src/main/java/hudson/tasks/ArtifactArchiver.java b/core/src/main/java/hudson/tasks/ArtifactArchiver.java index 02137ad369464..edfa7eeb2964d 100644 --- a/core/src/main/java/hudson/tasks/ArtifactArchiver.java +++ b/core/src/main/java/hudson/tasks/ArtifactArchiver.java @@ -389,7 +389,9 @@ public boolean isApplicable(Class jobType) { if (bd instanceof LogRotator) { LogRotator lr = (LogRotator) bd; if (lr.getArtifactNumToKeep() == -1) { - p.setBuildDiscarder(new LogRotator(lr.getDaysToKeep(), lr.getNumToKeep(), lr.getArtifactDaysToKeep(), 1)); + LogRotator newLr = new LogRotator(lr.getDaysToKeep(), lr.getNumToKeep(), lr.getArtifactDaysToKeep(), 1); + newLr.setRemoveLastSuccessfulBuild(lr.isRemoveLastSuccessfulBuild()); + p.setBuildDiscarder(newLr); } else { LOG.log(Level.WARNING, "will not clobber artifactNumToKeep={0} in {1}", new Object[] {lr.getArtifactNumToKeep(), p}); } diff --git a/core/src/main/java/hudson/tasks/LogRotator.java b/core/src/main/java/hudson/tasks/LogRotator.java index 3d5f3975808a6..901429c95b4fb 100644 --- a/core/src/main/java/hudson/tasks/LogRotator.java +++ b/core/src/main/java/hudson/tasks/LogRotator.java @@ -50,6 +50,7 @@ import jenkins.util.io.CompositeIOException; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; /** * Default implementation of {@link BuildDiscarder}. @@ -108,6 +109,12 @@ public CollatedLogRotatorException(String msg, Collection values) { */ private final Integer artifactNumToKeep; + /** + * If enabled also remove last successful build. + * @since 2.464 + */ + private boolean removeLastSuccessfulBuild; + @DataBoundConstructor public LogRotator(String daysToKeepStr, String numToKeepStr, String artifactDaysToKeepStr, String artifactNumToKeepStr) { this (parse(daysToKeepStr), parse(numToKeepStr), @@ -140,6 +147,11 @@ public LogRotator(int daysToKeep, int numToKeep, int artifactDaysToKeep, int art } + @DataBoundSetter + public void setRemoveLastSuccessfulBuild(boolean removeLastSuccessfulBuild) { + this.removeLastSuccessfulBuild = removeLastSuccessfulBuild; + } + @Override @SuppressWarnings("rawtypes") public void perform(Job job) throws IOException, InterruptedException { @@ -148,9 +160,9 @@ public void perform(Job job) throws IOException, InterruptedException { LOGGER.log(FINE, "Running the log rotation for {0} with numToKeep={1} daysToKeep={2} artifactNumToKeep={3} artifactDaysToKeep={4}", new Object[] {job, numToKeep, daysToKeep, artifactNumToKeep, artifactDaysToKeep}); - // always keep the last successful and the last stable builds - Run lsb = job.getLastSuccessfulBuild(); - Run lstb = job.getLastStableBuild(); + // if configured keep the last successful and the last stable builds + Run lsb = removeLastSuccessfulBuild ? null : job.getLastSuccessfulBuild(); + Run lstb = removeLastSuccessfulBuild ? null : job.getLastStableBuild(); if (numToKeep != -1) { // Note that RunList.size is deprecated, and indeed here we are loading all the builds of the job. @@ -270,6 +282,10 @@ public int getArtifactNumToKeep() { return unbox(artifactNumToKeep); } + public boolean isRemoveLastSuccessfulBuild() { + return removeLastSuccessfulBuild; + } + public String getDaysToKeepStr() { return toString(daysToKeep); } diff --git a/core/src/main/resources/hudson/tasks/LogRotator/config.jelly b/core/src/main/resources/hudson/tasks/LogRotator/config.jelly index b071ec3b9108e..c62ea6dfefca7 100644 --- a/core/src/main/resources/hudson/tasks/LogRotator/config.jelly +++ b/core/src/main/resources/hudson/tasks/LogRotator/config.jelly @@ -41,5 +41,9 @@ THE SOFTWARE. description="${%if not empty, only up to this number of builds have their artifacts retained}" field="artifactNumToKeepStr"> + + + diff --git a/test/src/test/java/hudson/tasks/LogRotatorTest.java b/test/src/test/java/hudson/tasks/LogRotatorTest.java index 8a408b758b01b..05c3b854bf243 100644 --- a/test/src/test/java/hudson/tasks/LogRotatorTest.java +++ b/test/src/test/java/hudson/tasks/LogRotatorTest.java @@ -68,7 +68,7 @@ public class LogRotatorTest { @Test public void successVsFailure() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); - project.setLogRotator(new LogRotator(-1, 2, -1, -1)); + project.setBuildDiscarder(new LogRotator(-1, 2, -1, -1)); j.buildAndAssertSuccess(project); // #1 project.getBuildersList().replaceBy(Set.of(new FailureBuilder())); j.buildAndAssertStatus(Result.FAILURE, project); // #2 @@ -82,11 +82,25 @@ public void successVsFailure() throws Exception { assertEquals(3, numberOf(project.getLastFailedBuild())); } + @Test + public void successVsFailureWithRemoveLastSuccess() throws Exception { + FreeStyleProject project = j.createFreeStyleProject(); + LogRotator logRotator = new LogRotator(-1, 1, -1, -1); + logRotator.setRemoveLastSuccessfulBuild(true); + project.setBuildDiscarder(logRotator); + project.getPublishersList().replaceBy(Set.of(new TestsFail())); + j.buildAndAssertStatus(Result.UNSTABLE, project); // #1 + project.getBuildersList().replaceBy(Set.of(new FailureBuilder())); + j.buildAndAssertStatus(Result.FAILURE, project); // #2 + assertNull(project.getBuildByNumber(1)); + assertEquals(2, numberOf(project.getLastFailedBuild())); + } + @Test @Issue("JENKINS-2417") public void stableVsUnstable() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); - project.setLogRotator(new LogRotator(-1, 2, -1, -1)); + project.setBuildDiscarder(new LogRotator(-1, 2, -1, -1)); j.buildAndAssertSuccess(project); // #1 project.getPublishersList().replaceBy(Set.of(new TestsFail())); j.buildAndAssertStatus(Result.UNSTABLE, project); // #2 @@ -98,11 +112,28 @@ public void stableVsUnstable() throws Exception { assertNull(project.getBuildByNumber(2)); } + @Test + public void stableVsUnstableWithRemoveLastStable() throws Exception { + FreeStyleProject project = j.createFreeStyleProject(); + LogRotator logRotator = new LogRotator(-1, 1, -1, -1); + logRotator.setRemoveLastSuccessfulBuild(true); + project.setBuildDiscarder(logRotator); + j.buildAndAssertSuccess(project); // #1 + project.getPublishersList().replaceBy(Set.of(new TestsFail())); + j.buildAndAssertStatus(Result.UNSTABLE, project); // #2 + project.getBuildersList().replaceBy(Set.of(new FailureBuilder())); + j.buildAndAssertStatus(Result.FAILURE, project); // #3 + assertNull(project.getBuildByNumber(1)); + assertNull(project.getBuildByNumber(2)); + assertEquals(-1, numberOf(project.getLastSuccessfulBuild())); + assertEquals(3, numberOf(project.getLastBuild())); + } + @Test @Issue("JENKINS-834") public void artifactDelete() throws Exception { FreeStyleProject project = j.createFreeStyleProject(); - project.setLogRotator(new LogRotator(-1, 6, -1, 2)); + project.setBuildDiscarder(new LogRotator(-1, 6, -1, 2)); project.getPublishersList().replaceBy(Set.of(new ArtifactArchiver("f", "", true, false))); j.buildAndAssertStatus(Result.FAILURE, project); // #1 assertFalse(project.getBuildByNumber(1).getHasArtifacts());