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

Draft: Introducing Build Position as a Condition - Seeking Guidance and Collaboration #93

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
98bc3f9
Initial commit of 'BuildPositionCondition.java' and 'BuildPositionCon…
Sep 12, 2023
9f33d38
Rename as maxBuildPosition
Sep 14, 2023
ff83631
Add 'resources' for integraiton test
Sep 25, 2023
db02ec5
Add 'BuildPositionConditionIT.java'
Oct 19, 2023
14639e4
Add config.jelly and help file for Build Position feature
Oct 19, 2023
d1c69dd
Add 'buildPosition' variable in 'BuildHistoryManager.java'
Oct 24, 2023
e0d1cd4
Add 'buildPosition' parameter to 'validateConditions' method in Rule.…
Oct 24, 2023
b3b829f
Add 'buildPosition' parameter to 'matches' method in BuildPositionCon…
Oct 24, 2023
dcdedad
Add overloaded 'matches' method with default buildPosition value (set…
Oct 24, 2023
920c5f4
Modify 'matches' method signature to include buildPosition in 'Condit…
Oct 24, 2023
6ee788e
Pass 'buildPosition' to 'validateConditions' method
Oct 24, 2023
f5d1328
Add .vscode/ in .gitignore file
Oct 25, 2023
aecd790
Removed 'logging.properties' and 'configuration.properties'
Oct 25, 2023
7ac5a40
Removed an overloaded version of 'matches' method from all the condit…
Oct 25, 2023
208b950
Updated the tests to call the updated 'matches()' method with a hardc…
Oct 25, 2023
7d5e971
Refactor: Repalce local 'buildPosition = -1' with 'public static int …
Oct 26, 2023
69b8035
Removed redundant comments
Oct 26, 2023
870c77a
Removed redundant codes and comments
Oct 26, 2023
6a4d3b7
Refactor: Removed redundant variables, adjusted method return type, a…
Oct 26, 2023
6f01959
Refactor: minor code cleanup and corrections
Oct 26, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.idea
**/*.iml
target/
.vscode/
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,22 @@ public synchronized void perform(Job<?, ?> job) throws IOException, InterruptedE
}

Run<?, ?> run = job.getLastCompletedBuild();
int buildPosition = 0;
// for each completed build...
while (run != null) {
log(uniquePerformName, "Processing build #" + run.getNumber());
buildPosition++;
log(uniquePerformName, "Processing build #" + run.getNumber() + " at build position " + buildPosition);
if (run.isKeepLog()) {
log(uniquePerformName, "Build #" + run.getNumber() + " is marked as keep forever -> skip processing");
} else {
for (int i = 0; i < rules.size(); i++) {
Rule rule = rules.get(i);
log(uniquePerformName, "Processing rule no " + (i + 1));
if (rule.validateConditions(run)) {
if (rule.validateConditions(run, buildPosition)) {
log(uniquePerformName, "Processing actions for rule no " + (i + 1));
rule.performActions(run);

// if other rules should not be proceed, shift to next build
// if other rules should not proceed, shift to next build
if (!rule.getContinueAfterMatch()) {
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package pl.damianszczepanik.jenkins.buildhistorymanager.descriptors.conditions;

import org.jenkinsci.Symbol;

import hudson.Extension;
import hudson.model.Descriptor;
import pl.damianszczepanik.jenkins.buildhistorymanager.model.conditions.BuildPositionCondition;
import pl.damianszczepanik.jenkins.buildhistorymanager.model.conditions.Condition;
@Extension
@Symbol("BuildPosition")
public class BuildPositionConditionDescriptor extends Descriptor<Condition> {

public BuildPositionConditionDescriptor() {
super(BuildPositionCondition.class);
}

@Override
public String getDisplayName() {
return "Build Position";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ public void initialize(String uniquePerformName) {
* Checks if passed build matches with all conditions from this rule.
*
* @param run build to validate
* @param buildPosition The position of the build in the history
* @return <code>true</code> if all conditions match otherwise <code>false</code>
*/
public boolean validateConditions(Run<?, ?> run) {
public boolean validateConditions(Run<?, ?> run, int buildPosition) {
// stop checking if max number of processed builds is reached
if (matchedTimes == getMatchAtMost()) {
log(uniquePerformName, String.format("Skipping rule because matched %d times", matchedTimes));
Expand All @@ -86,7 +87,7 @@ public boolean validateConditions(Run<?, ?> run) {
// validateConditions condition one by one...
for (Condition condition : conditions) {
log(uniquePerformName, String.format("Processing condition '%s'", condition.getDescriptor().getDisplayName()));
boolean conditionMatched = condition.matches(run, configuration);
boolean conditionMatched = condition.matches(run, configuration, buildPosition);
// stop checking rest conditions when at least condition does not match
if (!conditionMatched) {
log(uniquePerformName, String.format("Condition '%s' does not match", condition.getDescriptor().getDisplayName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void setMaxDaysAge(int maxDaysAge) {
}

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {

Calendar buildTime = Calendar.getInstance();
buildTime.setTimeInMillis(run.getStartTimeInMillis() + run.getDuration());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void setMaxBuildNumber(int maxBuildNumber) {
}

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
return run.getNumber() >= minBuildNumber && run.getNumber() <= maxBuildNumber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package pl.damianszczepanik.jenkins.buildhistorymanager.model.conditions;

import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import hudson.model.Run;
import pl.damianszczepanik.jenkins.buildhistorymanager.model.RuleConfiguration;

public class BuildPositionCondition extends Condition {

private int maxBuildPosition;

@DataBoundConstructor
public BuildPositionCondition() {

Check warning on line 14 in src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java#L14

Added line #L14 was not covered by tests
// Jenkins stapler requires to have public constructor with @DataBoundConstructor
}

Check warning on line 16 in src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java#L16

Added line #L16 was not covered by tests

public int getMaxBuildPosition() {
return maxBuildPosition;

Check warning on line 19 in src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java#L19

Added line #L19 was not covered by tests
}

@DataBoundSetter
public void setMaxBuildPosition(int maxBuildPosition) {
this.maxBuildPosition = maxBuildPosition;
}

Check warning on line 25 in src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/pl/damianszczepanik/jenkins/buildhistorymanager/model/conditions/BuildPositionCondition.java#L24-L25

Added lines #L24 - L25 were not covered by tests

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
return buildPosition <= maxBuildPosition;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void setMatchNotBuilt(boolean matchNotBuilt) {
}

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
Result result = run.getResult();
if (matchSuccess && result == Result.SUCCESS) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void setCauseClass(String causeClass) {
}

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
for (Cause cause : run.getCauses()) {
// use contains() method to avoid problem with class name
// for causes which are often inner class and name contains $ character
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public abstract class Condition extends AbstractDescribableImpl<Condition> {
*
* @param run build which should be evaluated
* @param configuration configuration from the role
* @param buildPosition the position of the build in the build history
* @return <code>true</code> if the build matches given criteria, otherwise <code>false</code>
*/
public abstract boolean matches(Run<?, ?> run, RuleConfiguration configuration);
public abstract boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public MatchEveryBuildCondition() {
}

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public String getValue() {
}

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {

try {
File workspace = run.getRootDir();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">

<f:entry title="Max Build Position in Build History" field="maxBuildPosition">
<f:number default="0"/>
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<p><b>Description</b></p>
<p>TODO</p>

<p><b>Use cases</b></p>
<p>TODO</p>

<p><b>Warning!</b></p>
<p>TODO</p>
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public String getDisplayName() {
public static class PositiveCondition extends AbstractSampleCondition {

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
matchesTimes++;
return true;
}
Expand All @@ -46,7 +46,7 @@ public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public static class NegativeCondition extends AbstractSampleCondition {

@Override
public boolean matches(Run<?, ?> run, RuleConfiguration configuration) {
public boolean matches(Run<?, ?> run, RuleConfiguration configuration, int buildPosition) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public TestRule(boolean validateResult) {
}

@Override
public boolean validateConditions(Run<?, ?> run) {
public boolean validateConditions(Run<?, ?> run, int buildPosition) {
validateConditionsTimes++;
return validateResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
*/
public class RuleTest {

public static final int FIRST_BUILD_POSITION = 0;

@Test
public void getConditions_ReturnsConditions() {

Expand Down Expand Up @@ -101,7 +103,7 @@ public void validateConditions_MatchesAllConditions() {
Run<?, ?> run = mock(Run.class);

// when
rule.validateConditions(run);
rule.validateConditions(run, FIRST_BUILD_POSITION);

// then
for (Condition condition : rule.getConditions()) {
Expand All @@ -117,7 +119,7 @@ public void validateConditions_OnNegativeCondition_DoesNotIncrementMatchedTimes(
Run<?, ?> run = mock(Run.class);

// when
rule.validateConditions(run);
rule.validateConditions(run, FIRST_BUILD_POSITION);

// then
int matchedTimes = Deencapsulation.getField(rule, "matchedTimes");
Expand Down Expand Up @@ -149,8 +151,8 @@ public void validateConditions_PerformsNTimes() throws IOException, InterruptedE
Run<?, ?> run = mock(Run.class);

// when
rule.validateConditions(run);
rule.validateConditions(run);
rule.validateConditions(run, FIRST_BUILD_POSITION);
rule.validateConditions(run, FIRST_BUILD_POSITION);

// then
int matchedTimes = Deencapsulation.getField(rule, "matchedTimes");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void matches_OnTodayBuild_ReturnsTrue() throws IOException {
Run<?, ?> run = new RunStub();

// when
boolean matches = condition.matches(run, null);
boolean matches = condition.matches(run, null, 0);

assertThat(matches).isTrue();
}
Expand All @@ -72,7 +72,7 @@ public void matches_OnBuildTimeBelowMax_ReturnsFalse() throws IOException {
Run<?, ?> run = new RunStub(1, buildTimeMinus3Days);

// when
boolean matches = condition.matches(run, null);
boolean matches = condition.matches(run, null, 0);

assertThat(matches).isFalse();
}
Expand All @@ -90,7 +90,7 @@ public void matches_OnBuildTimeAboveMin_ReturnsFalse() throws IOException {
Run<?, ?> run = new RunStub(1, buildTimeMinus3Days);

// when
boolean matches = condition.matches(run, null);
boolean matches = condition.matches(run, null,0);

assertThat(matches).isFalse();
}
Expand All @@ -107,7 +107,7 @@ public void matches_OnBuildTimeInRange_ReturnsTrue() throws IOException {
Run<?, ?> run = new RunStub(1, buildTimeMinus3Days);

// when
boolean matches = condition.matches(run, null);
boolean matches = condition.matches(run, null, 0);

assertThat(matches).isTrue();
}
Expand All @@ -123,7 +123,7 @@ public void matches_OnInvalidRange_ReturnsFalse() throws IOException {
Run<?, ?> run = new RunStub();

// when
boolean matches = condition.matches(run, null);
boolean matches = condition.matches(run, null, 0);

assertThat(matches).isFalse();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void matches_ForSmallerBuildNumber_ReturnsFalse() {
condition.setMaxBuildNumber(maxBuildNumberRange);

// when
boolean matches = condition.matches(mockRun(minBuildNumberRange - 1), null);
boolean matches = condition.matches(mockRun(minBuildNumberRange - 1), null, 0);

// then
assertThat(matches).isFalse();
Expand All @@ -69,7 +69,7 @@ public void matches_ForGreaterBuildNumber_ReturnsFalse() {
condition.setMaxBuildNumber(maxBuildNumberRange);

// when
boolean matches = condition.matches(mockRun(maxBuildNumberRange + 1), null);
boolean matches = condition.matches(mockRun(maxBuildNumberRange + 1), null, 0);

// then
assertThat(matches).isFalse();
Expand All @@ -84,7 +84,7 @@ public void matches_ForValidBuildNumber_ReturnsTrue() {
condition.setMaxBuildNumber(maxBuildNumberRange);

// when
boolean matches = condition.matches(mockRun((minBuildNumberRange + maxBuildNumberRange) / 2), null);
boolean matches = condition.matches(mockRun((minBuildNumberRange + maxBuildNumberRange) / 2), null, 0);

// then
assertThat(matches).isTrue();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package pl.damianszczepanik.jenkins.buildhistorymanager.model.conditions;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.concurrent.ExecutionException;

import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import hudson.util.RunList;

public class BuildPositionConditionIT {

@Rule
public JenkinsRule jenkinsRule = new JenkinsRule();

private WorkflowJob createPipelineJobFromScriptFile(String fileName) throws IOException {
Path pipelineScriptPath = Paths.get("src/test/resources", fileName);
String pipelineScript = new String(Files.readAllBytes(pipelineScriptPath));

CpsFlowDefinition cpsFlowDefinition = new CpsFlowDefinition(pipelineScript, true);
WorkflowJob pipelineJob = jenkinsRule.jenkins.createProject(WorkflowJob.class, fileName);

pipelineJob.setDefinition(cpsFlowDefinition);
return pipelineJob;
}

private void executeAndWaitForSuccessfulRun(WorkflowJob pipelineJob) throws InterruptedException, ExecutionException {
WorkflowRun run = Objects.requireNonNull(pipelineJob.scheduleBuild2(0)).get();
jenkinsRule.waitForCompletion(run);
}

@Test
public void queueMultipleRuns() throws Exception {
WorkflowJob pipelineJob = createPipelineJobFromScriptFile("buildPositionCondition_KeepLastTenBuildsTest.jf");
for (int i = 1; i <= 15; i++ ) {
executeAndWaitForSuccessfulRun(pipelineJob);
}

RunList<WorkflowRun> runList = pipelineJob.getBuilds();
int numOfRuns = 0;
for (WorkflowRun ignored : runList) {
numOfRuns++;
}
Assert.assertEquals(10, numOfRuns);
}
}
Loading
Loading