Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discard by Unique Display Names #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .factorypath

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -65,6 +62,9 @@ public class DiscardBuildPublisher extends Recorder {
* If true, will keep the last builds.
*/
private final boolean keepLastBuilds;
private final int displayNameToKeep;
private final String displayNameRegex;

/**
* Regular expression.
*/
Expand All @@ -84,13 +84,22 @@ public DiscardBuildPublisher(
String minLogFileSize,
String maxLogFileSize,
String regexp,
boolean keepLastBuilds
boolean keepLastBuilds,
String displayNameToKeep,
String displayNameRegex
) {

this.daysToKeep = parse(daysToKeep);
this.intervalDaysToKeep = parse(intervalDaysToKeep);
this.numToKeep = parse(numToKeep);
this.intervalNumToKeep = parse(intervalNumToKeep);
this.displayNameToKeep = parse(displayNameToKeep);

if (displayNameRegex == null || displayNameRegex.length() == 0){
this.displayNameRegex = "(.*)";
} else {
this.displayNameRegex = displayNameRegex;
}

resultsToDiscard = new HashSet<Result>();
if (discardSuccess) {
Expand Down Expand Up @@ -312,7 +321,7 @@ private void deleteOldBuildsByNum(AbstractBuild<?, ?> build, BuildListener liste

private void deleteOldBuildsByIntervalNum(AbstractBuild<?, ?> build, BuildListener listener, int intervalNumToKeep) {
ArrayList<Run<?, ?>> list = updateBuildsList(build, listener);
if (intervalNumToKeep == -1) return;
if (intervalNumToKeep <= -1) return;
int index = 0;
try {
if (intervalNumToKeep == 1) intervalNumToKeep = 2;
Expand All @@ -338,6 +347,38 @@ private void deleteOldBuildsByStatus(AbstractBuild<?, ?> build, BuildListener li
}
}

private void deleteOldBuildsByDisplayName(AbstractBuild<?, ?> build, BuildListener listener, int displayNameToKeep,
String displayNameRegex) {
ArrayList<Run<?, ?>> list = updateBuildsList(build, listener);
Map<String, Integer> displayNameCounts = new HashMap<String, Integer>();

try {
if (displayNameToKeep > 0) {
Run<?, ?> r;
Pattern pattern = Pattern.compile(displayNameRegex);
for (int index = 0; index < list.size(); index++) {
r = list.get(index);
String displayName = r.getDisplayName();
Matcher matcher = pattern.matcher(displayName);
if (matcher.find()) {
String displayMatch = matcher.group(1);
if (displayNameCounts.containsKey(displayMatch)) {
int displayNameCount = displayNameCounts.get(displayMatch) + 1;
if (displayNameCount > displayNameToKeep) {
discardBuild(r, "display name limit", listener);
}
displayNameCounts.put(displayMatch, displayNameCount);
} else {
displayNameCounts.put(displayMatch, 1);
}
}
}
}
} catch (IOException e) {
e.printStackTrace(listener.error(""));
}
}

private ArrayList<Run<?, ?>> updateBuildsList(AbstractBuild<?, ?> build, BuildListener listener) {
RunList<Run<?, ?>> builds = new RunList<Run<?, ?>>();
ArrayList<Run<?, ?>> list = new ArrayList<Run<?, ?>>();
Expand All @@ -357,6 +398,7 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen

// priority influence discard results, TODO: dynamic adjust priority on UI
deleteOldBuildsByDays(build, listener, daysToKeep);
deleteOldBuildsByDisplayName(build, listener, displayNameToKeep, displayNameRegex);
deleteOldBuildsByNum(build, listener, numToKeep);
deleteOldBuildsByIntervalDays(build, listener, intervalDaysToKeep);
deleteOldBuildsByIntervalNum(build, listener, intervalNumToKeep);
Expand Down Expand Up @@ -418,6 +460,14 @@ public String getIntervalDaysToKeep() {
return intToString(intervalDaysToKeep);
}

public String getDisplayNameToKeep() {
return intToString(displayNameToKeep);
}

public String getDisplayNameRegex() {
return displayNameRegex;
}

public String getRegexp() {
return regexp;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
<f:textbox />
</f:entry>

<f:entry title="${%DisplayNameToKeep}" field="displayNameToKeep">
<f:textbox />
</f:entry>

<f:entry title="${%DisplayNameRegex}" field="displayNameRegex">
<f:textbox default="(.*)" />
</f:entry>

<f:block>
<table>
<tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ DaysToKeep=Days to keep builds
NumToKeep=Max # of builds to keep
IntervalDaysToKeep=Interval to keep old builds (by days)
IntervalNumToKeep=Interval to keep old builds (by # of builds)
DisplayNameToKeep=Distinct number of builds per Display Name to keep
DisplayNameRegex=Regular expression used to determine display name grouping
StatusToDiscard=Status to discard
MinLogFileSize=Min Logfile size in bytes
MaxLogFileSize=Max Logfile size in bytes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div>
<p>
The regular expression is used to determine what part of the display name is used to group. The first regular expression
group (designated by parenthesis) is used. The default is the whole display name (.*).
</p>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div>
<p>
The maximum number of builds to keep based on the display name. To group by a portion of the display name,
specify a Display Name Regex.
</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,27 @@ public class DiscardBuildPublisherTest extends TestCase {

public void setUp() throws Exception {
// setUp build histories
buildList.add(createBuild(job, Result.SUCCESS, "20130120", true)); // #20
buildList.add(createBuild(job, Result.SUCCESS, "20130120")); // #20
buildList.add(createBuild(job, Result.FAILURE, "20130119")); // #19
buildList.add(createBuild(job, Result.SUCCESS, "20130118")); // #18
buildList.add(createBuild(job, Result.SUCCESS, "20130117")); // #17 // for daysToKeep
buildList.add(createBuild(job, Result.FAILURE, "20130117")); // #16 // for daysToKeep
buildList.add(createBuild(job, Result.SUCCESS, "20130117")); // #15
buildList.add(createBuild(job, Result.SUCCESS, "20130114")); // #14
buildList.add(createBuild(job, Result.SUCCESS, "20130113")); // #13
buildList.add(createBuild(job, Result.SUCCESS, "20130112")); // #12
buildList.add(createBuild(job, Result.FAILURE, "20130111")); // #11
buildList.add(createBuild(job, Result.FAILURE, "20130110")); // #10
buildList.add(createBuild(job, Result.SUCCESS, "20130109")); // #9
buildList.add(createBuild(job, Result.SUCCESS, "20130108")); // #8
buildList.add(createBuild(job, Result.SUCCESS, "20130107")); // #7
buildList.add(createBuild(job, Result.SUCCESS, "20130106")); // #6
buildList.add(createBuild(job, Result.SUCCESS, "20130105")); // #5
buildList.add(createBuild(job, Result.FAILURE, "20130104")); // #4
buildList.add(createBuild(job, Result.ABORTED, "20130103")); // #3
buildList.add(createBuild(job, Result.UNSTABLE, "20130102")); // #2
buildList.add(createBuild(job, Result.NOT_BUILT, "20130101")); // #1
buildList.add(createBuild(job, Result.SUCCESS, "20130120", true, "My Build C")); // #20
buildList.add(createBuild(job, Result.SUCCESS, "20130120", "G Build A")); // #20
buildList.add(createBuild(job, Result.FAILURE, "20130119", "My Build C")); // #19
buildList.add(createBuild(job, Result.SUCCESS, "20130118", "My Build A")); // #18
buildList.add(createBuild(job, Result.SUCCESS, "20130117", "My Build A")); // #17 // for daysToKeep
buildList.add(createBuild(job, Result.FAILURE, "20130117", "My Build A")); // #16 // for daysToKeep
buildList.add(createBuild(job, Result.SUCCESS, "20130117", "Y Build B")); // #15
buildList.add(createBuild(job, Result.SUCCESS, "20130114", "My Build B")); // #14
buildList.add(createBuild(job, Result.SUCCESS, "20130113", "My Build A")); // #13
buildList.add(createBuild(job, Result.SUCCESS, "20130112", "My Build C")); // #12
buildList.add(createBuild(job, Result.FAILURE, "20130111", "My Build A")); // #11
buildList.add(createBuild(job, Result.FAILURE, "20130110", "Z Build A")); // #10
buildList.add(createBuild(job, Result.SUCCESS, "20130109", "My Build B")); // #9
buildList.add(createBuild(job, Result.SUCCESS, "20130108", "My Build A")); // #8
buildList.add(createBuild(job, Result.SUCCESS, "20130107", "My Build A")); // #7
buildList.add(createBuild(job, Result.SUCCESS, "20130106", "My Build B")); // #6
buildList.add(createBuild(job, Result.SUCCESS, "20130105", "My Build C")); // #5
buildList.add(createBuild(job, Result.FAILURE, "20130104", "My Build A")); // #4
buildList.add(createBuild(job, Result.ABORTED, "20130103", "X Build A")); // #3
buildList.add(createBuild(job, Result.UNSTABLE, "20130102", "My Build A")); // #2
buildList.add(createBuild(job, Result.NOT_BUILT, "20130101", "My Build D")); // #1

when(listener.getLogger()).thenReturn(logger);
when(job.getBuilds()).thenReturn(RunList.fromRuns(buildList));
Expand All @@ -69,7 +69,8 @@ public void testPerformNoCondition() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "", "", "",
false, false, false, false, false,
"", "", "", true));
"", "", "", true, "",
""));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);
for (int i = 0; i < 21; i++) {
Expand All @@ -81,7 +82,8 @@ public void testPerformDaysToKeep() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"3", "", "", "",
false, false, false, false, false,
"", "", "", true));
"", "", "", true, "",
""));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);
for (int i = 0; i < 7; i++) {
Expand All @@ -96,7 +98,8 @@ public void testPerformNumToKeep() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "","5", "",
false, false, false, false, false,
"", "", "", true));
"", "", "", true, "",
""));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);
for (int i = 1; i < 6; i++) {
Expand All @@ -111,7 +114,8 @@ public void testPerformStatusToDiscard() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "","", "",
false, true, false, true, true, // unstable, not built, aborted
"", "", "", true));
"", "", "", true, "",
""));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);
for (int i = 1; i < 18; i++) {
Expand All @@ -126,7 +130,8 @@ public void testPerformNumAndStatus() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "","5","",
false, false, true, false, false, // failure
"", "", "", true)); // failure
"", "", "", true, "",
"")); // failure

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);

Expand Down Expand Up @@ -157,7 +162,8 @@ public void testPerformIntervalDaysToKeep() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "3", "", "",
false, false, false, false, false,
"", "", "", true));
"", "", "", true, "",
""));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);

Expand Down Expand Up @@ -188,7 +194,8 @@ public void testPerformIntervalNumToKeep() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "", "", "3",
false, false, false, false, false,
"", "", "", true));
"", "", "", true, "",
""));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);

Expand All @@ -215,13 +222,50 @@ public void testPerformIntervalNumToKeep() throws Exception {
verify(buildList.get(20), times(1)).delete(); // to keep
}

public void testPerformDisplayNameToKeep() throws Exception {
DiscardBuildPublisher publisher = getPublisher(new DiscardBuildPublisher(
"", "", "", "",
false, false, false, false, false,
"", "", "", true, "2",
"(Build [A-Z]+)"));

publisher.perform((AbstractBuild<?, ?>) build, launcher, listener);

verify(buildList.get(0), never()).delete(); // building
verify(buildList.get(1), never()).delete(); // new build
verify(buildList.get(2), never()).delete(); // new build
verify(buildList.get(3), never()).delete(); // new build
verify(buildList.get(4), times(1)).delete(); // new build
verify(buildList.get(5), times(1)).delete(); // new build
verify(buildList.get(6), never()).delete();
verify(buildList.get(7), never()).delete();
verify(buildList.get(8), times(1)).delete(); // to keep
verify(buildList.get(9), never()).delete();
verify(buildList.get(10), times(1)).delete();
verify(buildList.get(11), times(1)).delete(); // to keep
verify(buildList.get(12), times(1)).delete();
verify(buildList.get(13), times(1)).delete();
verify(buildList.get(14), times(1)).delete(); // to keep
verify(buildList.get(15), times(1)).delete();
verify(buildList.get(16), times(1)).delete();
verify(buildList.get(17), times(1)).delete(); // to keep
verify(buildList.get(18), times(1)).delete();
verify(buildList.get(19), times(1)).delete();
verify(buildList.get(20), never()).delete(); // to keep
}

private FreeStyleBuild createBuild(FreeStyleProject project, Result result, String yyyymmdd) throws Exception {
return createBuild(project, result, yyyymmdd, false);
return createBuild(project, result, yyyymmdd, false, "My Build");
}

private FreeStyleBuild createBuild(FreeStyleProject project, Result result, String yyyymmdd, boolean building) throws Exception {
private FreeStyleBuild createBuild(FreeStyleProject project, Result result, String yyyymmdd, String displayName) throws Exception {
return createBuild(project, result, yyyymmdd, false, displayName);
}

private FreeStyleBuild createBuild(FreeStyleProject project, Result result, String yyyymmdd, boolean building, String displayName) throws Exception {
FreeStyleBuild build = spy(new FreeStyleBuild(project));

when(build.getDisplayName()).thenReturn(displayName);
when(build.getResult()).thenReturn(result);
when(build.isBuilding()).thenReturn(building);
Calendar cal = Calendar.getInstance();
Expand Down