From 5d56f142511114373f80ca280af9ae17a091249a Mon Sep 17 00:00:00 2001 From: Rechi Date: Sat, 26 Jan 2019 13:59:25 +0100 Subject: [PATCH] convert to unix line endings --- .gitignore | 16 +- README.md | 22 +- RetryFailedSubjobs.md | 88 +- hudson-pom.xml | 82 +- .../plugins/multijob/MultiJobBuild.java | 696 ++--- .../plugins/multijob/MultiJobBuilder.java | 2486 ++++++++--------- .../multijob/MultiJobChangeLogSet.java | 62 +- .../plugins/multijob/PhaseJobsConfig.java | 1148 ++++---- .../multijob/views/AbstractWrapper.java | 224 +- .../plugins/multijob/views/BuildState.java | 100 +- .../plugins/multijob/views/ConsoleColumn.java | 46 +- .../plugins/multijob/views/JobColumn.java | 46 +- .../multijob/views/LastDurationColumn.java | 46 +- .../multijob/views/LastFailureColumn.java | 46 +- .../multijob/views/LastSuccessColumn.java | 46 +- .../views/MultiJobListViewColumn.java | 82 +- .../plugins/multijob/views/PhaseWrapper.java | 230 +- .../plugins/multijob/views/StatusColumn.java | 46 +- .../plugins/multijob/views/WeatherColumn.java | 46 +- .../plugins/multijob/MultiJobBuild/main.jelly | 106 +- .../multijob/MultiJobBuild/table.jelly | 176 +- .../multijob/MultiJobBuilder/config.jelly | 190 +- .../multijob/MultiJobProject/main.jelly | 112 +- .../MultiJobProject/newJobDetail.jelly | 54 +- .../MultiJobProject/newJobDetail.properties | 48 +- .../multijob/views/ConsoleColumn/column.jelly | 36 +- .../views/ConsoleColumn/columnHeader.jelly | 6 +- .../multijob/views/JobColumn/column.jelly | 30 +- .../views/JobColumn/columnHeader.jelly | 6 +- .../views/LastDurationColumn/column.jelly | 42 +- .../LastDurationColumn/columnHeader.jelly | 6 +- .../views/LastFailureColumn/column.jelly | 44 +- .../LastFailureColumn/columnHeader.jelly | 6 +- .../views/LastSuccessColumn/column.jelly | 42 +- .../LastSuccessColumn/columnHeader.jelly | 6 +- .../MultiJobView/configure-entries.jelly | 14 +- .../multijob/views/StatusColumn/column.jelly | 16 +- .../views/StatusColumn/columnHeader.jelly | 6 +- .../multijob/views/WeatherColumn/column.jelly | 10 +- .../views/WeatherColumn/columnHeader.jelly | 6 +- src/main/resources/index.jelly | 16 +- .../webapp/help-continuationCondition.html | 4 +- src/main/webapp/help-currParams.html | 6 +- src/main/webapp/help-globalConfig.html | 16 +- src/main/webapp/help-jobAlias.html | 6 +- src/main/webapp/help-jobName.html | 4 +- src/main/webapp/help-jobProperties.html | 4 +- src/main/webapp/help-phaseName.html | 4 +- .../multijob/test/CompatibilityTest.java | 92 +- .../test/CompatibilityTest/config.xml | 114 +- .../CompatibilityTest/jobs/old/config.xml | 88 +- 51 files changed, 3437 insertions(+), 3437 deletions(-) diff --git a/.gitignore b/.gitignore index 01086973..2b6e4863 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -/target -/work -.settings/ -.DS_Store -.classpath -.project -.idea -*.iml +/target +/work +.settings/ +.DS_Store +.classpath +.project +.idea +*.iml diff --git a/README.md b/README.md index 58eb8195..994d3086 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -## When to use MultiJob (tikal-multijob-plugin) plugin ? -- If you'd like to stop the mess with downstream / upstream jobs chains definitions -- When you want to add full hierarchy of Jenkins jobs that will be executed in sequence or in parallel -- Add context to your buildflow implementing parameter inheritance from the MultiJob to all its Phases and Jobs, Phases are sequential whilst jobs inside each Phase are parallel - -### More info on wiki page @: https://wiki.jenkins-ci.org/display/JENKINS/Multijob+Plugin - -### Found a bug ? require a new feature ? -#### Feel free to open an issue: https://github.com/jenkinsci/tikal-multijob-plugin/issues -**** -### Plugin CI job: https://jenkins.ci.cloudbees.com/job/plugins/job/tikal-multijob-plugin/ +## When to use MultiJob (tikal-multijob-plugin) plugin ? +- If you'd like to stop the mess with downstream / upstream jobs chains definitions +- When you want to add full hierarchy of Jenkins jobs that will be executed in sequence or in parallel +- Add context to your buildflow implementing parameter inheritance from the MultiJob to all its Phases and Jobs, Phases are sequential whilst jobs inside each Phase are parallel + +### More info on wiki page @: https://wiki.jenkins-ci.org/display/JENKINS/Multijob+Plugin + +### Found a bug ? require a new feature ? +#### Feel free to open an issue: https://github.com/jenkinsci/tikal-multijob-plugin/issues +**** +### Plugin CI job: https://jenkins.ci.cloudbees.com/job/plugins/job/tikal-multijob-plugin/ diff --git a/RetryFailedSubjobs.md b/RetryFailedSubjobs.md index 10a40e40..cae53156 100644 --- a/RetryFailedSubjobs.md +++ b/RetryFailedSubjobs.md @@ -1,44 +1,44 @@ -# Retrying failed subjobs # - -The MultiJob plugin provides a retry functionality for subjobs. - -1. On the Jenkins master server: store a text file with the content from the Jenkins output to parse, e.g. in `/etc/jenkins/retryrules/myrule`. If the defined text has been found and the subjob's status is `UNSTABLE`: restart that subjob. - - * Example: Retry when a subjob is failed, but not unstable (pre plugin version 1.25 behaviour): - - ```Finished: FAILURE``` - - * Example: Retry when some action in the job fails due to an exception (in this case some sort of timeout): - - ```java.util.concurrent.TimeoutException``` - - * Example: Retry anyway when the status is `FAILURE`, regardless of output: - - ```.*``` - -2. Go to Jenkins -> *Manage Jenkins* -> *Configure System* -> *MultiJob Retry Rules* - * Add a new *Parsing Rule* - * Pick a descriptive *Name*, e.g. `myrule` - * Point *Parsing Rules File* to the file on the Jenkins master, e.g. `/etc/jenkins/retryrules/myrule` - * *Save* the configuration - -3. Go to your MultiJob Jenkins job -> *MultiJob Phase* -> *Phase Jobs* -> *[YOUR_SUBJOB]* - * Tick *Enable Retry* - * Select the appropriate strategy, e.g. `myrule` - * Set the amount of *retries*. Note that 3 retries means that a job will be executed at most 4 times. - -4. In the *Console log* of your MultiJob you can see the retries, e.g: - ``` - Finished Build : #1 of Job : Y with status : FAILURE - Scanning failed job console output using parsing rule file /etc/jenkins/retryrules/myrule. - Known failure detected, retrying this build. Try 1 of 2. - - Finished Build : #2 of Job : Y with status : FAILURE - Scanning failed job console output using parsing rule file /etc/jenkins/retryrules/myrule. - Known failure detected, retrying this build. Try 2 of 2. - - Finished Build : #3 of Job : Y with status : FAILURE - Known failure detected, max retries (2) exceeded. - - Build step 'MultiJob Phase' marked build as failure - ``` +# Retrying failed subjobs # + +The MultiJob plugin provides a retry functionality for subjobs. + +1. On the Jenkins master server: store a text file with the content from the Jenkins output to parse, e.g. in `/etc/jenkins/retryrules/myrule`. If the defined text has been found and the subjob's status is `UNSTABLE`: restart that subjob. + + * Example: Retry when a subjob is failed, but not unstable (pre plugin version 1.25 behaviour): + + ```Finished: FAILURE``` + + * Example: Retry when some action in the job fails due to an exception (in this case some sort of timeout): + + ```java.util.concurrent.TimeoutException``` + + * Example: Retry anyway when the status is `FAILURE`, regardless of output: + + ```.*``` + +2. Go to Jenkins -> *Manage Jenkins* -> *Configure System* -> *MultiJob Retry Rules* + * Add a new *Parsing Rule* + * Pick a descriptive *Name*, e.g. `myrule` + * Point *Parsing Rules File* to the file on the Jenkins master, e.g. `/etc/jenkins/retryrules/myrule` + * *Save* the configuration + +3. Go to your MultiJob Jenkins job -> *MultiJob Phase* -> *Phase Jobs* -> *[YOUR_SUBJOB]* + * Tick *Enable Retry* + * Select the appropriate strategy, e.g. `myrule` + * Set the amount of *retries*. Note that 3 retries means that a job will be executed at most 4 times. + +4. In the *Console log* of your MultiJob you can see the retries, e.g: + ``` + Finished Build : #1 of Job : Y with status : FAILURE + Scanning failed job console output using parsing rule file /etc/jenkins/retryrules/myrule. + Known failure detected, retrying this build. Try 1 of 2. + + Finished Build : #2 of Job : Y with status : FAILURE + Scanning failed job console output using parsing rule file /etc/jenkins/retryrules/myrule. + Known failure detected, retrying this build. Try 2 of 2. + + Finished Build : #3 of Job : Y with status : FAILURE + Known failure detected, max retries (2) exceeded. + + Build step 'MultiJob Phase' marked build as failure + ``` diff --git a/hudson-pom.xml b/hudson-pom.xml index bee2a620..df637c31 100644 --- a/hudson-pom.xml +++ b/hudson-pom.xml @@ -1,41 +1,41 @@ - - 4.0.0 - - org.jvnet.hudson.plugins - plugin - 1.395 - - - com.tikal.hudson.plugins - hudson-multijob-plugin - 1.0-SNAPSHOT - hpi - - - tikal-public - ${public.repository.url} - - - - - - tikal-public - ${public.repository.url} - - - - - - http://dev.tikalk.com/nexus/content/groups/public/ - - - - org.jvnet.hudson.main - hudson-war - war - ${project.parent.version} - test - - - + + 4.0.0 + + org.jvnet.hudson.plugins + plugin + 1.395 + + + com.tikal.hudson.plugins + hudson-multijob-plugin + 1.0-SNAPSHOT + hpi + + + tikal-public + ${public.repository.url} + + + + + + tikal-public + ${public.repository.url} + + + + + + http://dev.tikalk.com/nexus/content/groups/public/ + + + + org.jvnet.hudson.main + hudson-war + war + ${project.parent.version} + test + + + diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuild.java b/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuild.java index 9460ee59..cc7f148b 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuild.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuild.java @@ -1,348 +1,348 @@ -package com.tikal.jenkins.plugins.multijob; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -import javax.servlet.ServletException; - -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import org.kohsuke.stapler.export.Exported; -import org.kohsuke.stapler.export.ExportedBean; - -import hudson.model.Run; -import hudson.model.AbstractProject; -import hudson.model.Action; -import hudson.model.Build; -import hudson.model.BuildListener; -import hudson.model.ParameterValue; -import hudson.model.ParametersAction; -import hudson.model.Result; -import hudson.model.Run; -import hudson.model.StringParameterValue; -import hudson.scm.ChangeLogSet; -import hudson.scm.ChangeLogSet.Entry; -import javax.annotation.CheckForNull; -import jenkins.model.Jenkins; - -@ExportedBean(defaultVisibility = 999) -public class MultiJobBuild extends Build { - - private List subBuilds; - private MultiJobChangeLogSet changeSets = new MultiJobChangeLogSet(this); - private Map subBuildsMap = new HashMap(); - private MultiJobTestResults multiJobTestResults; - - - public MultiJobBuild(MultiJobProject project) throws IOException { - super(project); - } - - @Override - public ChangeLogSet getChangeSet() { - return super.getChangeSet(); - } - - public void addChangeLogSet(ChangeLogSet changeLogSet) { - if (changeLogSet != null) { - this.changeSets.addChangeLogSet(changeLogSet); - } - } - - public MultiJobBuild(MultiJobProject project, File buildDir) - throws IOException { - super(project, buildDir); - } - - @Override - public synchronized void doStop(StaplerRequest req, StaplerResponse rsp) - throws IOException, ServletException { - super.doStop(req, rsp); - } - - @Override - public void addAction(Action a) { - super.addAction(a); - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - execute(new MultiJobRunnerImpl()); - } - - public List getBuilders() { - MultiJobBuild multiJobBuild = getParent().getNearestBuild(getNumber()); - return multiJobBuild.getSubBuilds(); - } - - public String getBuildParams(SubBuild subBuild) { - try { - AbstractProject project = (AbstractProject) Jenkins.getInstance() - .getItem(subBuild.getJobName(), this.getParent(), AbstractProject.class); - Run build = project.getBuildByNumber(subBuild.getBuildNumber()); - ParametersAction action = build.getAction(ParametersAction.class); - List parameters = action.getParameters(); - StringBuffer buffer = new StringBuffer(); - for (ParameterValue parameterValue : parameters) { - StringParameterValue stringParameter; - try { - stringParameter = ((StringParameterValue) parameterValue); - } catch (Exception e) { - continue; - } - String value = (String) stringParameter.getValue(); - String name = stringParameter.getName(); - buffer.append("") - .append(" ") - .append("") - .append("
"); - } - return buffer.toString(); - } catch (Exception e) { - return "Failed to retrieve build parameters."; - } - } - - public void addSubBuild(SubBuild subBuild) { - String key = subBuild.getPhaseName().concat(subBuild.getJobName()) - .concat(String.valueOf(subBuild.getBuildNumber())); - if (subBuildsMap.containsKey(key)) { - SubBuild e = subBuildsMap.get(key); - Collections.replaceAll(getSubBuilds(), e, subBuild); - } else { - getSubBuilds().add(subBuild); - } - subBuildsMap.put(key, subBuild); - } - - @Exported - public List getSubBuilds() { - if (subBuilds == null) - subBuilds = new CopyOnWriteArrayList(); - return subBuilds; - } - - public MultiJobTestResults getMultiJobTestResults() { - return multiJobTestResults; - } - - public void addTestsResult() { - multiJobTestResults = new MultiJobTestResults(); - this.addAction(multiJobTestResults); - } - - protected class MultiJobRunnerImpl extends - Build.BuildExecution { - @Override - public Result run(BuildListener listener) throws Exception { - Result result = super.run(listener); - if (isAborted()) { - result = Result.ABORTED; - } else if (isNotBuilt()) { - result = Result.NOT_BUILT; - } else if (isFailure()) { - result = Result.FAILURE; - } else if (isUnstable()) { - result = Result.UNSTABLE; - } - - if (!Result.SUCCESS.equals(result)) { - MultiJobResumeBuild action = new MultiJobResumeBuild(super.getBuild()); - super.getBuild().addAction(action); - } - - return result; - } - - private boolean isAborted() { - return evaluateResult(Result.NOT_BUILT); - } - - private boolean isNotBuilt() { - return evaluateResult(Result.FAILURE); - } - - private boolean isFailure() { - return evaluateResult(Result.UNSTABLE); - } - - private boolean isUnstable() { - return evaluateResult(Result.SUCCESS); - } - - private boolean evaluateResult(Result result) { - List builders = getBuilders(); - for (SubBuild subBuild : builders) { - if (!subBuild.isRetry() && !subBuild.isAbort()) { - Result buildResult = subBuild.getResult(); - if (buildResult != null && buildResult.isWorseThan(result)) { - return true; - } - } - } - return false; - } - } - - @ExportedBean(defaultVisibility = 999) - public static class SubBuild { - - private final String parentJobName; - private final int parentBuildNumber; - private final String jobName; - private final String jobAlias; - private final int buildNumber; - private final String phaseName; - private final Result result; - private final String icon; - private final String duration; - private final String url; - private final boolean retry; - private final boolean aborted; - private String buildID; - - public SubBuild(String parentJobName, int parentBuildNumber, - String jobName, String jobAlias, int buildNumber, String phaseName, - Result result, String icon, String duration, String url, - Run build) { - this.parentJobName = parentJobName; - this.parentBuildNumber = parentBuildNumber; - this.jobName = jobName; - this.jobAlias = jobAlias; - this.buildNumber = buildNumber; - this.phaseName = phaseName; - this.result = result; - this.icon = icon; - this.duration = duration; - this.url = url; - this.retry = false; - this.aborted = false; - buildID = build.getExternalizableId(); - } - - public SubBuild(String parentJobName, int parentBuildNumber, - String jobName, String jobAlias, int buildNumber, String phaseName, - Result result, String icon, String duration, String url, - boolean retry, boolean aborted, Run build) { - this.parentJobName = parentJobName; - this.parentBuildNumber = parentBuildNumber; - this.jobName = jobName; - this.jobAlias = jobAlias; - this.buildNumber = buildNumber; - this.phaseName = phaseName; - this.result = result; - this.icon = icon; - this.duration = duration; - this.url = url; - this.retry = retry; - this.aborted = aborted; - buildID = build.getExternalizableId(); - } - - @Exported - public String getDuration() { - return duration; - } - - @Exported - public boolean isRetry() { - return retry; - } - - - @Exported - public boolean isAbort() { - return aborted; - } - - @Exported - public String getIcon() { - return icon; - } - - @Exported - public String getUrl() { - return url; - } - - @Exported - public String getPhaseName() { - return phaseName; - } - - @Exported - public String getParentJobName() { - return parentJobName; - } - - @Exported - public int getParentBuildNumber() { - return parentBuildNumber; - } - - @Exported - public String getJobName() { - return jobName; - } - - @Exported - public String getJobAlias() { - if (jobAlias == null) { - return ""; - } - - return jobAlias; - } - - @Exported - public int getBuildNumber() { - return buildNumber; - } - - @Exported - public Result getResult() { - return result; - } - - @Override - public String toString() { - return "SubBuild [parentJobName=" + parentJobName - + ", parentBuildNumber=" + parentBuildNumber + ", jobName=" - + jobName + ", jobAlias=" + jobAlias - + ", buildNumber=" + buildNumber + "]"; - } - - @Exported - @CheckForNull - public Run getBuild() { - if (buildID != null) { - Run build = Run.fromExternalizableId(buildID); - if (build instanceof Run) { - return (Run) build; - } - } // else null if loaded from historical data prior to JENKINS-49328 - return null; - } - - @Exported - public boolean isMultiJobBuild() { - if (buildID != null) { - Run build = Run.fromExternalizableId(buildID); - if (build instanceof MultiJobBuild) { - return true; - } - } - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.servlet.ServletException; + +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.export.Exported; +import org.kohsuke.stapler.export.ExportedBean; + +import hudson.model.Run; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.Build; +import hudson.model.BuildListener; +import hudson.model.ParameterValue; +import hudson.model.ParametersAction; +import hudson.model.Result; +import hudson.model.Run; +import hudson.model.StringParameterValue; +import hudson.scm.ChangeLogSet; +import hudson.scm.ChangeLogSet.Entry; +import javax.annotation.CheckForNull; +import jenkins.model.Jenkins; + +@ExportedBean(defaultVisibility = 999) +public class MultiJobBuild extends Build { + + private List subBuilds; + private MultiJobChangeLogSet changeSets = new MultiJobChangeLogSet(this); + private Map subBuildsMap = new HashMap(); + private MultiJobTestResults multiJobTestResults; + + + public MultiJobBuild(MultiJobProject project) throws IOException { + super(project); + } + + @Override + public ChangeLogSet getChangeSet() { + return super.getChangeSet(); + } + + public void addChangeLogSet(ChangeLogSet changeLogSet) { + if (changeLogSet != null) { + this.changeSets.addChangeLogSet(changeLogSet); + } + } + + public MultiJobBuild(MultiJobProject project, File buildDir) + throws IOException { + super(project, buildDir); + } + + @Override + public synchronized void doStop(StaplerRequest req, StaplerResponse rsp) + throws IOException, ServletException { + super.doStop(req, rsp); + } + + @Override + public void addAction(Action a) { + super.addAction(a); + } + + @SuppressWarnings("unchecked") + @Override + public void run() { + execute(new MultiJobRunnerImpl()); + } + + public List getBuilders() { + MultiJobBuild multiJobBuild = getParent().getNearestBuild(getNumber()); + return multiJobBuild.getSubBuilds(); + } + + public String getBuildParams(SubBuild subBuild) { + try { + AbstractProject project = (AbstractProject) Jenkins.getInstance() + .getItem(subBuild.getJobName(), this.getParent(), AbstractProject.class); + Run build = project.getBuildByNumber(subBuild.getBuildNumber()); + ParametersAction action = build.getAction(ParametersAction.class); + List parameters = action.getParameters(); + StringBuffer buffer = new StringBuffer(); + for (ParameterValue parameterValue : parameters) { + StringParameterValue stringParameter; + try { + stringParameter = ((StringParameterValue) parameterValue); + } catch (Exception e) { + continue; + } + String value = (String) stringParameter.getValue(); + String name = stringParameter.getName(); + buffer.append("") + .append(" ") + .append("") + .append("
"); + } + return buffer.toString(); + } catch (Exception e) { + return "Failed to retrieve build parameters."; + } + } + + public void addSubBuild(SubBuild subBuild) { + String key = subBuild.getPhaseName().concat(subBuild.getJobName()) + .concat(String.valueOf(subBuild.getBuildNumber())); + if (subBuildsMap.containsKey(key)) { + SubBuild e = subBuildsMap.get(key); + Collections.replaceAll(getSubBuilds(), e, subBuild); + } else { + getSubBuilds().add(subBuild); + } + subBuildsMap.put(key, subBuild); + } + + @Exported + public List getSubBuilds() { + if (subBuilds == null) + subBuilds = new CopyOnWriteArrayList(); + return subBuilds; + } + + public MultiJobTestResults getMultiJobTestResults() { + return multiJobTestResults; + } + + public void addTestsResult() { + multiJobTestResults = new MultiJobTestResults(); + this.addAction(multiJobTestResults); + } + + protected class MultiJobRunnerImpl extends + Build.BuildExecution { + @Override + public Result run(BuildListener listener) throws Exception { + Result result = super.run(listener); + if (isAborted()) { + result = Result.ABORTED; + } else if (isNotBuilt()) { + result = Result.NOT_BUILT; + } else if (isFailure()) { + result = Result.FAILURE; + } else if (isUnstable()) { + result = Result.UNSTABLE; + } + + if (!Result.SUCCESS.equals(result)) { + MultiJobResumeBuild action = new MultiJobResumeBuild(super.getBuild()); + super.getBuild().addAction(action); + } + + return result; + } + + private boolean isAborted() { + return evaluateResult(Result.NOT_BUILT); + } + + private boolean isNotBuilt() { + return evaluateResult(Result.FAILURE); + } + + private boolean isFailure() { + return evaluateResult(Result.UNSTABLE); + } + + private boolean isUnstable() { + return evaluateResult(Result.SUCCESS); + } + + private boolean evaluateResult(Result result) { + List builders = getBuilders(); + for (SubBuild subBuild : builders) { + if (!subBuild.isRetry() && !subBuild.isAbort()) { + Result buildResult = subBuild.getResult(); + if (buildResult != null && buildResult.isWorseThan(result)) { + return true; + } + } + } + return false; + } + } + + @ExportedBean(defaultVisibility = 999) + public static class SubBuild { + + private final String parentJobName; + private final int parentBuildNumber; + private final String jobName; + private final String jobAlias; + private final int buildNumber; + private final String phaseName; + private final Result result; + private final String icon; + private final String duration; + private final String url; + private final boolean retry; + private final boolean aborted; + private String buildID; + + public SubBuild(String parentJobName, int parentBuildNumber, + String jobName, String jobAlias, int buildNumber, String phaseName, + Result result, String icon, String duration, String url, + Run build) { + this.parentJobName = parentJobName; + this.parentBuildNumber = parentBuildNumber; + this.jobName = jobName; + this.jobAlias = jobAlias; + this.buildNumber = buildNumber; + this.phaseName = phaseName; + this.result = result; + this.icon = icon; + this.duration = duration; + this.url = url; + this.retry = false; + this.aborted = false; + buildID = build.getExternalizableId(); + } + + public SubBuild(String parentJobName, int parentBuildNumber, + String jobName, String jobAlias, int buildNumber, String phaseName, + Result result, String icon, String duration, String url, + boolean retry, boolean aborted, Run build) { + this.parentJobName = parentJobName; + this.parentBuildNumber = parentBuildNumber; + this.jobName = jobName; + this.jobAlias = jobAlias; + this.buildNumber = buildNumber; + this.phaseName = phaseName; + this.result = result; + this.icon = icon; + this.duration = duration; + this.url = url; + this.retry = retry; + this.aborted = aborted; + buildID = build.getExternalizableId(); + } + + @Exported + public String getDuration() { + return duration; + } + + @Exported + public boolean isRetry() { + return retry; + } + + + @Exported + public boolean isAbort() { + return aborted; + } + + @Exported + public String getIcon() { + return icon; + } + + @Exported + public String getUrl() { + return url; + } + + @Exported + public String getPhaseName() { + return phaseName; + } + + @Exported + public String getParentJobName() { + return parentJobName; + } + + @Exported + public int getParentBuildNumber() { + return parentBuildNumber; + } + + @Exported + public String getJobName() { + return jobName; + } + + @Exported + public String getJobAlias() { + if (jobAlias == null) { + return ""; + } + + return jobAlias; + } + + @Exported + public int getBuildNumber() { + return buildNumber; + } + + @Exported + public Result getResult() { + return result; + } + + @Override + public String toString() { + return "SubBuild [parentJobName=" + parentJobName + + ", parentBuildNumber=" + parentBuildNumber + ", jobName=" + + jobName + ", jobAlias=" + jobAlias + + ", buildNumber=" + buildNumber + "]"; + } + + @Exported + @CheckForNull + public Run getBuild() { + if (buildID != null) { + Run build = Run.fromExternalizableId(buildID); + if (build instanceof Run) { + return (Run) build; + } + } // else null if loaded from historical data prior to JENKINS-49328 + return null; + } + + @Exported + public boolean isMultiJobBuild() { + if (buildID != null) { + Run build = Run.fromExternalizableId(buildID); + if (build instanceof MultiJobBuild) { + return true; + } + } + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuilder.java b/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuilder.java index ff635d65..5dcb808e 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuilder.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobBuilder.java @@ -1,1243 +1,1243 @@ -package com.tikal.jenkins.plugins.multijob; - -import com.tikal.jenkins.plugins.multijob.MultiJobBuild.SubBuild; -import com.tikal.jenkins.plugins.multijob.PhaseJobsConfig.KillPhaseOnJobResultCondition; -import com.tikal.jenkins.plugins.multijob.counters.CounterHelper; -import com.tikal.jenkins.plugins.multijob.counters.CounterManager; -import groovy.util.Eval; -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.Util; -import hudson.console.HyperlinkNote; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.Job; -import hudson.model.Run; -import hudson.model.Action; -import hudson.model.BallColor; -import hudson.model.Build; -import hudson.model.BuildListener; -import hudson.model.DependecyDeclarer; -import hudson.model.DependencyGraph; -import hudson.model.DependencyGraph.Dependency; -import hudson.model.Executor; -import hudson.model.Item; -import hudson.model.Queue.QueueAction; -import hudson.model.Result; -import hudson.model.TaskListener; -import hudson.model.queue.QueueTaskFuture; -import hudson.scm.ChangeLogSet; -import hudson.scm.ChangeLogSet.Entry; -import hudson.tasks.BuildStepDescriptor; -import hudson.tasks.Builder; -import jenkins.model.Jenkins; -import net.sf.json.JSONObject; -import jenkins.model.Jenkins; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileNotFoundException; - -import org.apache.commons.lang.StringUtils; -import org.jenkinsci.lib.envinject.EnvInjectException; -import org.jenkinsci.lib.envinject.EnvInjectLogger; -import org.jenkinsci.plugins.envinject.EnvInjectBuilder; -import org.jenkinsci.plugins.envinject.EnvInjectBuilderContributionAction; -import org.jenkinsci.plugins.envinject.service.EnvInjectActionSetter; -import org.jenkinsci.plugins.envinject.service.EnvInjectEnvVars; -import org.jenkinsci.plugins.envinject.service.EnvInjectVariableGetter; -import org.jenkinsci.plugins.tokenmacro.TokenMacro; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.StaplerRequest; - -import java.io.*; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.CompletionService; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.regex.Pattern; - -import org.jenkinsci.plugins.tokenmacro.TokenMacro; - -import groovy.util.*; -import hudson.plugins.parameterizedtrigger.AbstractBuildParameters.DontTriggerException; -import java.util.concurrent.CancellationException; -import jenkins.model.CauseOfInterruption; - -public class MultiJobBuilder extends Builder implements DependecyDeclarer { - /** - * The name of the parameter in the build.getBuildVariables() to enable the job build, regardless - * of scm changes. - */ - public static final String BUILD_ALWAYS_KEY = "hudson.scm.multijob.build.always"; - - private String phaseName; - private List phaseJobs; - private ContinuationCondition continuationCondition = ContinuationCondition.SUCCESSFUL; - private ExecutionType executionType; - - final static Pattern PATTERN = Pattern.compile("(\\$\\{.+?\\})", Pattern.CASE_INSENSITIVE); - - - /** - * The name of the new variable which stores the status of the current job. - * The state is the name of the corresponding value in {@link StatusJob} enum. - * @since 1.0.0 - * @see StatusJob#isBuildable() - */ - public static final String JOB_STATUS = "JOB_STATUS"; - - /** - * The name of the new variable which stores if the job is buildable or not. - * This value is getted from the {@link StatusJob#isBuildable()}. - * The only values of this variable are true when the job is buildable, - * or false when the job is not buildable. - * - * @since 1.0.0 - * @see StatusJob#isBuildable() - */ - public static final String JOB_IS_BUILDABLE = "JOB_IS_BUILDABLE"; - - /** - * A prefix for env vars which should be loaded in {@link #prebuild(Build, BuildListener)}. - * this will happen only when build was triggered by the {@link MultiJobResumeControl} action - * - * @since 1.0.0 - */ - public static final String PERSISTENT_VARS_PREFIX = "RESUMABLE_"; - - @Deprecated - public MultiJobBuilder(String phaseName, List phaseJobs, - ContinuationCondition continuationCondition) { - this(phaseName, phaseJobs, continuationCondition, ExecutionType.PARALLEL); - } - - @DataBoundConstructor - public MultiJobBuilder(String phaseName, List phaseJobs, - ContinuationCondition continuationCondition, ExecutionType executionType) { - this.phaseName = phaseName; - this.phaseJobs = Util.fixNull(phaseJobs); - this.continuationCondition = continuationCondition; - this.executionType = executionType; - } - - public String expandToken(String toExpand, final AbstractBuild build, final BuildListener listener) { - String expandedExpression = toExpand; - try { - expandedExpression = TokenMacro.expandAll(build, listener, toExpand, false, null); - } catch (Exception e) { - listener.getLogger().println(e.getMessage()); - } - - return PATTERN.matcher(expandedExpression).replaceAll(""); - } - - /** - * Reports the status of the job. - *

The sequence of the checks are the following (the first winner stops the sequence and returns):

- * - *
    - *
  1. If job is disabled - * then returns {@link StatusJob#IS_DISABLED}.
  2. - *
  3. If job is disabled at phase configuration - * then returns {@link StatusJob#IS_DISABLED_AT_PHASECONFIG}.
  4. - *
  5. If BuildOnlyIfSCMChanges is disabled - * then returns {@link StatusJob#BUILD_ONLY_IF_SCM_CHANGES_DISABLED}.
  6. - *
  7. If 'Build Always' feature is enabled - * then returns {@link StatusJob#BUILD_ALWAYS_IS_ENABLED}.
  8. - *
  9. If job doesn't contains lastbuild - * then returns {@link StatusJob#DOESNT_CONTAINS_LASTBUILD}.
  10. - *
  11. If lastbuild result of the job is worse than unstable - * then returns {@link StatusJob#LASTBUILD_RESULT_IS_WORSE_THAN_UNSTABLE}.
  12. - *
  13. If job's workspace is empty - * then returns {@link StatusJob#WORKSPACE_IS_EMPTY}.
  14. - *
  15. If job contains scm changes - * then returns {@link StatusJob#CHANGED_SINCE_LAST_BUILD}.
  16. - *
  17. If job's doesn't contains scm changes - * then returns {@link StatusJob#NOT_CHANGED_SINCE_LAST_BUILD}.
  18. - *
- */ - private StatusJob getScmChange(Job subjob,PhaseJobsConfig phaseConfig,AbstractBuild build, BuildListener listener,Launcher launcher) - throws IOException, InterruptedException { - /* https://issues.jenkins-ci.org/browse/JENKINS-33821 - if ( subjob.disabled ) { - return StatusJob.IS_DISABLED; - } - */ - if( phaseConfig.isDisableJob() ) { - return StatusJob.IS_DISABLED_AT_PHASECONFIG; - } - if ( !phaseConfig.isBuildOnlyIfSCMChanges() ){ - return StatusJob.BUILD_ONLY_IF_SCM_CHANGES_DISABLED; - } - final boolean buildAlways = Boolean.valueOf((String)(build.getBuildVariables().get(BUILD_ALWAYS_KEY))); - - if ( buildAlways ) { - return StatusJob.BUILD_ALWAYS_IS_ENABLED; - } - final Run lastBuild = subjob.getLastBuild(); - if ( lastBuild == null ) { - return StatusJob.DOESNT_CONTAINS_LASTBUILD; - } - if ( lastBuild.getResult() != null && lastBuild.getResult().isWorseThan(Result.UNSTABLE) ) { - return StatusJob.LASTBUILD_RESULT_IS_WORSE_THAN_UNSTABLE; - } - - /* need to pass in filepath - if ( lastBuild.getWorkspace() == null ) { - return StatusJob.DOESNT_CONTAINS_WORKSPACE; - } - - if ( !lastBuild.getWorkspace().exists() ) { - return StatusJob.WORKSPACE_IS_EMPTY; - } - */ - /* not sure about this one - if ( subjob.poll(listener).hasChanges() ) { - return StatusJob.CHANGED_SINCE_LAST_BUILD; - } - */ - - return StatusJob.NOT_CHANGED_SINCE_LAST_BUILD; - } - - public boolean evalCondition(final String condition, final AbstractBuild build, final BuildListener listener) { - try { - return (Boolean) Eval.me(expandToken(condition, build, listener).trim()); - } catch (Exception e) { - listener.getLogger().println("Can't evaluate expression, false is assumed: " + e.toString()); - } - return false; - } - - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public boolean perform(final AbstractBuild build, final Launcher launcher, final BuildListener listener) throws InterruptedException, IOException { - Jenkins jenkins = Jenkins.getInstance(); - MultiJobBuild multiJobBuild = (MultiJobBuild) build; - MultiJobProject thisProject = multiJobBuild.getProject(); - - boolean resume = false; - Map successBuildMap = new HashMap(); - Map resumeBuildMap = new HashMap(); - MultiJobResumeControl control = build.getAction(MultiJobResumeControl.class); - if (null != control) { - MultiJobBuild prevBuild = (MultiJobBuild) control.getRun(); - - boolean willResumeBuild = true; - if (thisProject.getCheckResumeEnvVars()) { - String[] variables = thisProject.getResumeEnvVars().split(","); - for( int i = 0; i < variables.length; i++ ) { - String previousValue = prevBuild.getEnvironment(listener).get(variables[i]); - String currentValue = build.getEnvironment(listener).get(variables[i]); - if( !StringUtils.equals(previousValue, currentValue) ) { - willResumeBuild = false; - listener.getLogger().println(String.format("Cannot resume the build, values for '%s' do not match: [%s][%s]", variables[i], previousValue, currentValue)); - break; - } - } - } - if(willResumeBuild) { - for (SubBuild subBuild : prevBuild.getSubBuilds()) { - Item item = Jenkins.getInstance().getItem(subBuild.getJobName(), prevBuild.getParent(), - AbstractProject.class); - if (item instanceof AbstractProject) { - AbstractProject childProject = (AbstractProject) item; - AbstractBuild childBuild = childProject.getBuildByNumber(subBuild.getBuildNumber()); - if (null != childBuild) { - if (Result.SUCCESS.equals(childBuild.getResult())) { - successBuildMap.put(childProject.getUrl(), subBuild); - } else { - resume = true; - resumeBuildMap.put(childProject.getUrl(), subBuild); - } - } - } - } - } - - if (!resume) { - successBuildMap.clear(); - } - } - - Map phaseSubJobs = new LinkedHashMap( - phaseJobs.size()); - final CounterManager phaseCounters = new CounterManager(); - boolean aggragatedTestResults = false; - for (PhaseJobsConfig phaseJobConfig : phaseJobs) { - Item item = jenkins.getItem(phaseJobConfig.getJobName(), multiJobBuild.getParent(), Job.class); - if (item instanceof Job) { - Job job = (Job) item; - phaseSubJobs.put(new PhaseSubJob(job), phaseJobConfig); - } - if (phaseJobConfig.isAggregatedTestResults()) { - aggragatedTestResults = true; - } - } - - if (aggragatedTestResults) { - multiJobBuild.addTestsResult(); - } - - List subTasks = new ArrayList(); - int index = 0; - for (PhaseSubJob phaseSubJob : phaseSubJobs.keySet()) { - index++; - - Job subJob = phaseSubJob.job; - - // To be coherent with final results, we need to do this here. - PhaseJobsConfig phaseConfig = phaseSubJobs.get(phaseSubJob); - StatusJob jobStatus = getScmChange(subJob,phaseConfig,multiJobBuild ,listener,launcher ); - listener.getLogger().println(jobStatus.getMessage(subJob)); - // We are ready to inject vars about scm status. It is useful at condition level. - Map jobScmVars = new HashMap(); -// New injected variable. It stores the status of the last job executed. It is useful at condition level. - jobScmVars.put(JOB_STATUS, jobStatus.name()); -// New injected variable. It reports if the job is buildable. - jobScmVars.put(JOB_IS_BUILDABLE, String.valueOf(jobStatus.isBuildable())); - injectEnvVars(build, listener, jobScmVars); - - if (jobStatus == StatusJob.IS_DISABLED) { - phaseCounters.processSkipped(); - continue; - } - - // If we want to apply the condition only if there were no SCM change - // We can do it by using the "Apply condition only if no SCM changes were found" - boolean conditionExistsAndEvaluatedToTrue = false; - - if (phaseConfig.getEnableCondition() && phaseConfig.getCondition() != null) { - String subJobDisplayName = subJob.getName(); - if (phaseConfig.getJobAlias() != null && !phaseConfig.getJobAlias().equals("")) { - subJobDisplayName += " (" + phaseConfig.getJobAlias() + ")"; - } - - // if SCM has changes or set to always build and condition should always be evaluated - if(jobStatus.isBuildable() && !phaseConfig.isApplyConditionOnlyIfNoSCMChanges()) { - if (evalCondition(phaseConfig.getCondition(), build, listener)) { - listener.getLogger().println(String.format("Triggering %s. Condition was evaluated to true.", subJobDisplayName)); - conditionExistsAndEvaluatedToTrue = true; - } else { - listener.getLogger().println(String.format("Skipping %s. Condition was evaluated to false.", subJobDisplayName)); - phaseCounters.processSkipped(); - continue; - } - } - // if SCM has no changes but condition is set to be evaluated in this case - else if(!jobStatus.isBuildable() && phaseConfig.isApplyConditionOnlyIfNoSCMChanges()) { - if (evalCondition(phaseConfig.getCondition(), build, listener)) { - listener.getLogger().println(String.format("Triggering %s. Condition was evaluated to true.", subJobDisplayName)); - conditionExistsAndEvaluatedToTrue = true; - } else { - listener.getLogger().println(String.format("Skipping %s. Condition was evaluated to false.", subJobDisplayName)); - phaseCounters.processSkipped(); - continue; - } - } - // no SCM changes and no condition evaluation - else if(!jobStatus.isBuildable() && !phaseConfig.isApplyConditionOnlyIfNoSCMChanges()) { - listener.getLogger().println(String.format("Skipping %s. No SCM changes found and condition is skipped.", subJobDisplayName)); - phaseCounters.processSkipped(); - continue; - } else { - if (!evalCondition(phaseConfig.getCondition(), build, listener)) { - listener.getLogger().println(String.format("Skipping %s. Condition was evaluated to false.", subJobDisplayName)); - phaseCounters.processSkipped(); - continue; - } else { - listener.getLogger().println(String.format("Triggering %s. Condition was evaluated to true.", subJobDisplayName)); - conditionExistsAndEvaluatedToTrue = true; - } - } - // This is needed because if no condition to eval, the legacy buildOnlyIfSCMChanges feature is still available, - // so we don't need to change our job configuration. - } - if ( ! jobStatus.isBuildable() && !conditionExistsAndEvaluatedToTrue) { - phaseCounters.processSkipped(); - continue; - } - - reportStart(listener, subJob, phaseConfig); - List actions = new ArrayList(); - - if (resume) { - SubBuild subBuild = resumeBuildMap.get(subJob.getUrl()); - if (null != subBuild) { - AbstractProject prj = Jenkins.getInstance().getItem(subBuild.getJobName(), multiJobBuild.getParent(), - AbstractProject.class); - AbstractBuild childBuild = prj.getBuildByNumber(subBuild.getBuildNumber()); - MultiJobResumeControl childControl = new MultiJobResumeControl(childBuild); - actions.add(childControl); - } - } - - prepareActions(multiJobBuild, subJob, phaseConfig, listener, actions, index); - - if ( jobStatus == StatusJob.IS_DISABLED_AT_PHASECONFIG ) { - phaseCounters.processSkipped(); - continue; - } else { - boolean shouldTrigger = null == successBuildMap.get(subJob.getUrl()) ? true : false; - subTasks.add(new SubTask(subJob, phaseConfig, actions, multiJobBuild, shouldTrigger)); - } - } - - if (subTasks.size() < 1) { - // We inject the variables also when no jobs will be triggered. - injectEnvVars(build, listener, phaseCounters.toMap()); - return true; - } - - if (null == executionType) { - executionType = ExecutionType.PARALLEL; - } - int poolSize = executionType.isParallel() ? subTasks.size() : 1; - ExecutorService executor = Executors.newFixedThreadPool(poolSize); - Set jobResults = new HashSet(); - BlockingQueue queue = new ArrayBlockingQueue(subTasks.size()); - for (SubTask subTask : subTasks) { - SubBuild subBuild = successBuildMap.get(subTask.subJob.getUrl()); - if (null == subBuild) { - CompletionService completion = new ExecutorCompletionService(executor); - Callable worker = new SubJobWorker(thisProject, listener, subTask, queue); - completion.submit(worker); - if (!executionType.isParallel()) { - Future future = completion.take(); - try { - future.get(); - if (checkPhaseTermination(subTask, subTasks, listener)) { - break; - } - } catch (ExecutionException e) { - listener.getLogger().println("Error while execution subtask " + subTask.subJob.getDisplayName()); - } - } - } else { - Run jobBuild = subTask.subJob.getBuildByNumber(subBuild.getBuildNumber()); - updateSubBuild(multiJobBuild, thisProject, jobBuild, subBuild.getResult(), subBuild.getJobAlias()); - } - } - - try { - executor.shutdown(); - int resultCounter = 0; - while (!executor.isTerminated()) { - SubTask subTask = queue.poll(5, TimeUnit.SECONDS); - if (subTask != null) { - resultCounter++; - if (subTask.result != null) { - jobResults.add(subTask.result); - phaseCounters.process(subTask.result); - checkPhaseTermination(subTask, subTasks, listener); - } - } - if (subTasks.size() <= resultCounter) { - break; - } - } - executor.shutdownNow(); - - } catch (InterruptedException exception) { - listener.getLogger().println("Aborting all subjobs."); - for (SubTask _subTask : subTasks) { - _subTask.cancelJob(); - phaseCounters.processAborted(); - } - int i = 0; - while (!executor.isTerminated() && i < 20) { - Thread.sleep(1000); - i++; - } - throw new InterruptedException(); - - } - injectEnvVars(build, listener, phaseCounters.toMap()); - - for (Result result : jobResults) { - if (!continuationCondition.isContinue(result)) { - return false; - } - } - return true; - } - - public final class SubJobWorker implements Callable { - final private MultiJobProject multiJobProject; - final private BuildListener listener; - private SubTask subTask; - private BlockingQueue queue; - private List compiledPatterns; - - public SubJobWorker(MultiJobProject multiJobProject, - BuildListener listener, - SubTask subTask, - BlockingQueue queue) { - this.multiJobProject = multiJobProject; - this.listener = listener; - this.subTask = subTask; - this.queue = queue; - } - - /** - * Subtask execution - * @return value is not used in the builder. Can be used to return some state of the task in the future. - */ - public Object call() { - Result result = null; - Run jobBuild = null; - try { - int maxRetries = subTask.phaseConfig.getMaxRetries(); - if (!subTask.phaseConfig.getEnableRetryStrategy()) { - maxRetries = 0; - } - - int retry = 0; - boolean finish = false; - - while (retry <= maxRetries && !finish) { - retry++; - if (subTask.isShouldTrigger()) { - subTask.generateFuture(); - } - QueueTaskFuture future = subTask.future; - while (true) { - if (subTask.isCancelled()) { - if (jobBuild != null) { - Executor exect = jobBuild.getExecutor(); - if (exect != null) { - exect.interrupt(Result.ABORTED); - } - - reportFinish(listener, jobBuild, Result.ABORTED, subTask.phaseConfig); - abortSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, subTask.phaseConfig.getJobAlias()); - - finish = true; - break; - } - } - - try { - jobBuild = (Run)future.getStartCondition().get(5, TimeUnit.SECONDS); - } catch (Exception e) { - if (e instanceof TimeoutException) - continue; - else { - throw e; - } - } - updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, subTask.phaseConfig.getJobAlias()); - if (future.isDone()) { - break; - } - Thread.sleep(2500); - } - if (jobBuild != null && !finish) { - result = jobBuild.getResult(); - reportFinish(listener, jobBuild, result, subTask.phaseConfig); - - if (result.isWorseOrEqualTo(Result.UNSTABLE) && result.isCompleteBuild() && subTask.phaseConfig.getEnableRetryStrategy()) { - if (isKnownRandomFailure(jobBuild)) { - if (retry <= maxRetries) { - listener.getLogger().println("Known failure detected, retrying this build. Try " + retry + " of " + maxRetries + "."); - updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, true, subTask.phaseConfig.getJobAlias()); - - //subTask.generateFuture(); - } else { - listener.getLogger().println("Known failure detected, max retries (" + maxRetries + ") exceeded."); - updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, subTask.phaseConfig.getJobAlias()); - } - } else { - listener.getLogger().println("Failed the build, the failure doesn't match the rules."); - updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, subTask.phaseConfig.getJobAlias()); - finish = true; - } - } else { - updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, subTask.phaseConfig.getJobAlias()); - finish = true; - } - - //ChangeLogSet changeLogSet = jobBuild.getChangeSet(); - //subTask.multiJobBuild.addChangeLogSet(changeLogSet); - addBuildEnvironmentVariables(subTask.multiJobBuild, jobBuild, listener); - subTask.result = result; - } - } - - if (subTask.phaseConfig.isAggregatedTestResults()) { - MultiJobTestAggregator.aggregateResultsFromBuild(jobBuild, subTask.multiJobBuild.getMultiJobTestResults(), listener); - } - } catch (Exception e) { - if (e instanceof InterruptedException) { - if (jobBuild != null) { - reportFinish(listener, jobBuild, Result.ABORTED, subTask.phaseConfig); - abortSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, subTask.phaseConfig.getJobAlias()); - subTask.result = Result.ABORTED; - } - } else { - listener.getLogger().println(e.toString()); - } - } - - if (jobBuild == null) { - updateSubBuild(subTask.multiJobBuild, multiJobProject, subTask.phaseConfig); - } - queue.add(subTask); - - return Boolean.TRUE; - } - - private List getCompiledPattern() throws FileNotFoundException, InterruptedException { - if (compiledPatterns == null) { - compiledPatterns = new ArrayList(); - try { - listener.getLogger().println("Scanning failed job console output using parsing rule file " + subTask.phaseConfig.getParsingRulesPath() + "."); - final File rulesFile = new File(subTask.phaseConfig.getParsingRulesPath()); - FileInputStream fis = new FileInputStream(rulesFile.getAbsoluteFile()); - InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8); - final BufferedReader reader = new BufferedReader(isr); - try { - String line; - while ((line = reader.readLine()) != null) { - compiledPatterns.add(Pattern.compile(line)); - } - } finally { - if (reader != null) { - reader.close(); - } - } - } catch (Exception e) { - if (e instanceof InterruptedException) { - throw new InterruptedException(); - } else if (e instanceof FileNotFoundException) { - throw new FileNotFoundException(); - } else { - listener.getLogger().println(e.toString()); - e.printStackTrace(); - } - } - } - return compiledPatterns; - } - - private final class LineAnalyser extends Thread { - final private BufferedReader reader; - final private List patterns; - private BlockingQueue finishQueue; - - public LineAnalyser(BufferedReader reader, List patterns, BlockingQueue finishQueue) { - this.reader = reader; - this.patterns = patterns; - this.finishQueue = finishQueue; - } - - public void run() { - boolean errorFound = false; - try { - String line; - while (reader.ready() && !errorFound) { - line = reader.readLine(); - if (line != null) { - for (Pattern pattern : patterns) { - if (pattern.matcher(line).find()) { - errorFound = true; - break; - } - } - } - } - } catch (Exception e) { - if (e instanceof IOException) { - // Nothing - } else { - listener.getLogger().println(e.toString()); - e.printStackTrace(); - } - } finally { - finishQueue.add(new LineQueue(errorFound)); - } - } - } - - private boolean isKnownRandomFailure(Run build) throws InterruptedException { - boolean failure = false; - try { - final List patterns = getCompiledPattern(); - final File logFile = build.getLogFile(); - FileInputStream fis = new FileInputStream(logFile); - InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8); - final BufferedReader reader = new BufferedReader(isr); - try { - int numberOfThreads = 10; // Todo : Add this in Configure section - if (numberOfThreads < 0) { - numberOfThreads = 1; - } - ExecutorService executorAnalyser = Executors.newFixedThreadPool(numberOfThreads); - BlockingQueue finishQueue = new ArrayBlockingQueue(numberOfThreads); - - for (int i = 0; i < numberOfThreads; i++) { - Runnable worker = new LineAnalyser(reader, patterns, finishQueue); - executorAnalyser.execute(worker); - } - - executorAnalyser.shutdown(); - int resultCounter = 0; - while (!executorAnalyser.isTerminated()) { - resultCounter++; - LineQueue lineQueue = finishQueue.take(); - if (lineQueue.hasError()) { - failure = true; - break; - } else if (numberOfThreads == resultCounter) { - break; - } - } - executorAnalyser.shutdownNow(); - } finally { - if (reader != null) { - reader.close(); - } - } - } catch (Exception e) { - if (e instanceof InterruptedException) { - throw new InterruptedException(); - } else if (e instanceof FileNotFoundException) { - listener.getLogger().println("Parser rules file not found."); - failure = false; - } else { - listener.getLogger().println(e.toString()); - e.printStackTrace(); - } - } - return failure; - } - } - - protected boolean checkPhaseTermination(SubTask subTask, List subTasks, final BuildListener listener) { - try { - KillPhaseOnJobResultCondition killCondition = subTask.phaseConfig.getKillPhaseOnJobResultCondition(); - if (killCondition.equals(KillPhaseOnJobResultCondition.NEVER) && subTask.result != Result.ABORTED) { - return false; - } - if (killCondition.isKillPhase(subTask.result)) { - if (subTask.result != Result.ABORTED && subTask.phaseConfig.getAbortAllJob()) { - for (SubTask _subTask : subTasks) { - _subTask.cancelJob(); - } - return true; - } - } - } catch (Exception e) { - listener.getLogger().printf(e.toString()); - return false; - } - return false; - } - - private void reportStart(BuildListener listener, Job subJob, PhaseJobsConfig phaseConfig) { - String jobDisplayName = subJob.getFullName(); - if (phaseConfig.getJobAlias() != null && !phaseConfig.getJobAlias().equals("")) { - jobDisplayName += " (" + phaseConfig.getJobAlias() + ")"; - } - listener.getLogger().printf( - "Starting build job %s.\n", - HyperlinkNote.encodeTo('/' + subJob.getUrl(), - jobDisplayName)); - } - - private void reportFinish(BuildListener listener, Run jobBuild, - Result result, PhaseJobsConfig phaseConfig) { - String jobDisplayName = jobBuild.getParent().getFullName(); - if (phaseConfig.getJobAlias() != null && !phaseConfig.getJobAlias().equals("")) { - jobDisplayName += " (" + phaseConfig.getJobAlias() + ")"; - } - - listener.getLogger().println( - "Finished Build : " - + HyperlinkNote.encodeTo("/" + jobBuild.getUrl() + "/", - String.valueOf(jobBuild.getDisplayName())) - + " of Job : " - + HyperlinkNote.encodeTo('/' + jobBuild.getParent() - .getUrl(), jobDisplayName) - + " with status : " - + HyperlinkNote.encodeTo('/' + jobBuild.getUrl() - + "/console", result.toString())); - } - - private void updateSubBuild(MultiJobBuild multiJobBuild, - MultiJobProject multiJobProject, PhaseJobsConfig phaseConfig) { - SubBuild subBuild = new SubBuild(multiJobProject.getName(), - multiJobBuild.getNumber(), phaseConfig.getJobName(), phaseConfig.getJobAlias(), 0, - phaseName, null, BallColor.NOTBUILT.getImage(), "not built", "", null); - multiJobBuild.addSubBuild(subBuild); - } - - private void updateSubBuild(MultiJobBuild multiJobBuild, - MultiJobProject multiJobProject, Run jobBuild, String jobAlias) { - SubBuild subBuild = new SubBuild(multiJobProject.getName(), - multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, - jobBuild.getNumber(), phaseName, null, jobBuild.getIconColor() - .getImage(), jobBuild.getDurationString(), - jobBuild.getUrl(), jobBuild); - multiJobBuild.addSubBuild(subBuild); - } - - private void updateSubBuild(MultiJobBuild multiJobBuild, - MultiJobProject multiJobProject, Run jobBuild, - Result result, String jobAlias) { - SubBuild subBuild = new SubBuild(multiJobProject.getName(), - multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, - jobBuild.getNumber(), phaseName, result, jobBuild.getIconColor().getImage(), - jobBuild.getDurationString(), jobBuild.getUrl(), jobBuild); - multiJobBuild.addSubBuild(subBuild); - } - - private void updateSubBuild(MultiJobBuild multiJobBuild, - MultiJobProject multiJobProject, Run jobBuild, - Result result, boolean retry, String jobAlias) { - SubBuild subBuild = new SubBuild(multiJobProject.getName(), - multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, - jobBuild.getNumber(), phaseName, result, jobBuild.getIconColor().getImage(), - jobBuild.getDurationString(), jobBuild.getUrl(), retry, false, jobBuild); - multiJobBuild.addSubBuild(subBuild); - } - - private void abortSubBuild(MultiJobBuild multiJobBuild, MultiJobProject multiJobProject, - Run jobBuild, String jobAlias) { - SubBuild subBuild = new SubBuild(multiJobProject.getName(), - multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, - jobBuild.getNumber(), phaseName, Result.ABORTED, BallColor.ABORTED.getImage(), "", jobBuild.getUrl(), false, true, jobBuild); - multiJobBuild.addSubBuild(subBuild); - } - - @SuppressWarnings("rawtypes") - private synchronized void addBuildEnvironmentVariables(MultiJobBuild thisBuild, - Run jobBuild, BuildListener listener) { - // Env variables map - Map variables = new HashMap(); - - // Fetch the map of existing environment variables - try { - - EnvInjectLogger logger = new EnvInjectLogger(listener); - EnvInjectVariableGetter variableGetter = new EnvInjectVariableGetter(); - Map previousEnvVars = variableGetter - .getEnvVarsPreviousSteps(thisBuild, logger); - - // Get current envVars - variables = new HashMap( - previousEnvVars); - - } catch (Throwable throwable) { - listener.getLogger() - .println( - "[MultiJob] - [ERROR] - Problems occurs on fetching env vars as a build step: " - + throwable.getMessage()); - } - - String jobName = jobBuild.getParent().getName(); - String jobNameSafe = jobName.replaceAll("[^A-Za-z0-9]", "_") - .toUpperCase(); - String buildNumber = Integer.toString(jobBuild.getNumber()); - String buildResult = jobBuild.getResult().toString(); - String buildName = jobBuild.getDisplayName().toString(); - - // If the job is run a second time, store the first job's number and result with unique keys - if (variables.get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe) != null) { - String runCount = variables.get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe); - if (runCount.equals("1")) { - String firstBuildNumber = variables.get(jobNameSafe + "_BUILD_NUMBER"); - String firstBuildResult = variables.get(jobNameSafe + "_BUILD_RESULT"); - variables.put(jobNameSafe + "_" + runCount + "_BUILD_NUMBER", firstBuildNumber); - variables.put(jobNameSafe + "_" + runCount + "_BUILD_RESULT", firstBuildResult); - } - } - - // These will always reference the last build - variables.put("LAST_TRIGGERED_JOB_NAME", jobName); - variables.put(jobNameSafe + "_BUILD_NUMBER", buildNumber); - variables.put(jobNameSafe + "_BUILD_RESULT", buildResult); - variables.put(jobNameSafe + "_BUILD_NAME", buildName); - - if (variables.get("TRIGGERED_JOB_NAMES") == null) { - variables.put("TRIGGERED_JOB_NAMES", jobName); - } else { - String triggeredJobNames = variables.get("TRIGGERED_JOB_NAMES") - + "," + jobName; - variables.put("TRIGGERED_JOB_NAMES", triggeredJobNames); - } - - if (variables.get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe) == null) { - variables.put("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe, "1"); - } else { - String runCount = Integer.toString(Integer.parseInt(variables - .get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe)) + 1); - variables.put("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe, runCount); - variables.put(jobNameSafe + "_" + runCount + "_BUILD_NUMBER", buildNumber); - variables.put(jobNameSafe + "_" + runCount + "_BUILD_RESULT", buildResult); - } - // Set the new build variables map - injectEnvVars(thisBuild, listener, variables); - } - - /** - * Method for properly injecting environment variables via EnvInject plugin. - * Method based off logic in {@link EnvInjectBuilder#perform} - */ - private void injectEnvVars(AbstractBuild build, - BuildListener listener, Map incomingVars) { - if (build != null && incomingVars != null) { - EnvInjectLogger logger = new EnvInjectLogger(listener); - FilePath ws = build.getWorkspace(); - EnvInjectActionSetter envInjectActionSetter = new EnvInjectActionSetter( ws ); - EnvInjectEnvVars envInjectEnvVarsService = new EnvInjectEnvVars( logger ); - - try { - EnvInjectVariableGetter variableGetter = new EnvInjectVariableGetter(); - Map previousEnvVars = variableGetter - .getEnvVarsPreviousSteps(build, logger); - - // Get current envVars - Map variables = new HashMap(previousEnvVars); - // Acumule PHASE, PHASENAME and MULTIJOB counters. - // Values are in variables (current values) and incomingVars. - Map mixtured = CounterHelper.putPhaseAddMultijobAndMergeTheRest(listener, this.phaseName, incomingVars, variables); - // Resolve variables - final Map resultVariables = envInjectEnvVarsService - .getMergedVariables(variables, mixtured); - - // Set the new build variables map - build.addAction(new EnvInjectBuilderContributionAction( - resultVariables)); - - // Add or get the existing action to add new env vars - envInjectActionSetter.addEnvVarsToEnvInjectBuildAction(build, - resultVariables); - } catch (Throwable throwable) { - listener.getLogger() - .println( - "[MultiJob] - [ERROR] - Problems occurs on injecting env vars as a build step: " - + throwable.getMessage()); - throwable.printStackTrace(); - } - } - } - - @SuppressWarnings("rawtypes") - private void prepareActions(AbstractBuild build, Job project, - PhaseJobsConfig projectConfig, BuildListener listener, - List actions, int index) throws IOException, InterruptedException { - List parametersActions = null; - // if (projectConfig.hasProperties()) { - parametersActions = (List) projectConfig.getActions(build, listener, project, projectConfig.isCurrParams()); - actions.addAll(parametersActions); - // } - actions.add(new MultiJobAction(build, index)); - - } - - private class MultiJobAction implements Action, QueueAction { - public int buildNumber; - public int index; - - /** - * @deprecated - * Maintain backwards compatibility with previous versions of this class. - * See https://wiki.jenkins-ci.org/display/JENKINS/Hint+on+retaining+backward+compatibility - */ - @Deprecated - private transient AbstractBuild build; - - public MultiJobAction(AbstractBuild build, int index) { - this.buildNumber = build.getNumber(); - this.index = index; - } - - public boolean shouldSchedule(List actions) { - boolean matches = true; - - for (MultiJobAction action : Util.filter(actions, MultiJobAction.class)) { - if (action.index != index) { - matches = false; - } - if (action.buildNumber != buildNumber) { - matches = false; - } - } - - return !matches; - } - - protected Object readResolve() { - if (build != null) { - buildNumber = build.getNumber(); - } - return this; - } - - public String getIconFileName() { - return null; - } - - public String getDisplayName() { - return "this shouldn't be displayed"; - } - - public String getUrlName() { - return null; - } - } - - public String getPhaseName() { - return phaseName; - } - - public void setPhaseName(String phaseName) { - this.phaseName = phaseName; - } - - public List getPhaseJobs() { - return phaseJobs; - } - - public void setPhaseJobs(List phaseJobs) { - this.phaseJobs = phaseJobs; - } - - public boolean phaseNameExist(String phaseName) { - for (PhaseJobsConfig phaseJob : phaseJobs) { - if (phaseJob.getDisplayName().equals(phaseName)) { - return true; - } - } - return false; - } - - private final static class PhaseSubJob { - Job job; - - PhaseSubJob(Job job) { - this.job = job; - } - } - - @Extension - public static class DescriptorImpl extends BuildStepDescriptor { - - @SuppressWarnings("rawtypes") - @Override - public boolean isApplicable(Class jobType) { - return jobType.equals(MultiJobProject.class); - } - - @Override - public String getDisplayName() { - return "MultiJob Phase"; - } - - @Override - public Builder newInstance(StaplerRequest req, JSONObject formData) - throws FormException { - return req.bindJSON(MultiJobBuilder.class, formData); - } - - @Override - public boolean configure(StaplerRequest req, JSONObject formData) { - save(); - return true; - } - } - - @SuppressWarnings("rawtypes") - public void buildDependencyGraph(AbstractProject owner, - DependencyGraph graph) { - Jenkins jenkins = Jenkins.getInstance(); - List phaseJobsConfigs = getPhaseJobs(); - - if (phaseJobsConfigs == null) - return; - for (PhaseJobsConfig project : phaseJobsConfigs) { - Item topLevelItem = jenkins.getItem(project.getJobName(), owner.getParent(), AbstractProject.class); - if (topLevelItem instanceof AbstractProject) { - Dependency dependency = new Dependency(owner, - (AbstractProject) topLevelItem) { - - @Override - public boolean shouldTriggerBuild(AbstractBuild build, - TaskListener listener, List actions) { - return false; - } - - }; - graph.addDependency(dependency); - } - } - } - - public boolean onJobRenamed(String oldName, String newName) { - boolean changed = false; - for (Iterator i = phaseJobs.iterator(); i.hasNext();) { - PhaseJobsConfig phaseJobs = (PhaseJobsConfig) i.next(); - String jobName = phaseJobs.getJobName(); - if (jobName.trim().equals(oldName)) { - if (newName != null) { - phaseJobs.setJobName(newName); - changed = true; - } else { - i.remove(); - changed = true; - } - } - } - return changed; - } - - public boolean onJobDeleted(String oldName) { - return onJobRenamed(oldName, null); - } - - public static enum ContinuationCondition { - - ALWAYS("Always") { - @Override - public boolean isContinue(Result result) { - return true; - } - }, - SUCCESSFUL("Successful") { - @Override - public boolean isContinue(Result result) { - return result.equals(Result.SUCCESS); - } - }, - COMPLETED("Completed") { - @Override - public boolean isContinue(Result result) { - return result.isCompleteBuild(); - } - }, - UNSTABLE("Stable or Unstable but not Failed") { - @Override - public boolean isContinue(Result result) { - return result.isBetterOrEqualTo(Result.UNSTABLE); - } - }, - FAILURE("Failed") { - @Override - public boolean isContinue(Result result) { - return result.isWorseOrEqualTo(Result.FAILURE); - } - }; - - abstract public boolean isContinue(Result result); - - private ContinuationCondition(String label) { - this.label = label; - } - - final private String label; - - public String getLabel() { - return label; - } - } - - public ContinuationCondition getContinuationCondition() { - return continuationCondition; - } - - public void setContinuationCondition( - ContinuationCondition continuationCondition) { - this.continuationCondition = continuationCondition; - } - - public enum ExecutionType { - - PARALLEL("Running phase jobs in parallel") { - @Override - public boolean isParallel() { - return true; - } - }, - SEQUENTIALLY("Running phase jobs sequentially") { - @Override - public boolean isParallel() { - return false; - } - }; - - final private String label; - - ExecutionType(String label) { - this.label = label; - } - - public String getLabel() { - return label; - } - - abstract public boolean isParallel(); - } - - public void setExecutionType(ExecutionType executionType) { - this.executionType = executionType; - } - - public ExecutionType getExecutionType() { - return executionType; - } - - public boolean prebuild(Build build, BuildListener listener) { - boolean resume = false; - MultiJobResumeControl control = build.getAction(MultiJobResumeControl.class); - if (null != control) { - MultiJobBuild prevBuild = (MultiJobBuild) control.getRun(); - for (SubBuild subBuild : prevBuild.getSubBuilds()) { - Item item = Jenkins.getInstance().getItem(subBuild.getJobName(), prevBuild.getParent(), - AbstractProject.class); - if (item instanceof AbstractProject) { - AbstractProject childProject = (AbstractProject) item; - AbstractBuild childBuild = childProject.getBuildByNumber(subBuild.getBuildNumber()); - if (null != childBuild) { - if (childBuild.getResult().equals(Result.FAILURE)) { - resume = true; - } - } - } - } - if (resume) { - EnvInjectLogger logger = new EnvInjectLogger(listener); - EnvInjectVariableGetter variableGetter = new EnvInjectVariableGetter(); - try { - Map previousEnvVars = variableGetter.getEnvVarsPreviousSteps(prevBuild, logger); - Map persistentEnvVars = new HashMap(); - for (String key : previousEnvVars.keySet()) { - if(key.startsWith(PERSISTENT_VARS_PREFIX)) { - persistentEnvVars.put(key, previousEnvVars.get(key)); - } - } - persistentEnvVars.put("RESUMED_BUILD", "true"); - injectEnvVars(build, listener, persistentEnvVars); - } catch (Throwable throwable) { - listener.getLogger().println("[MultiJob] - [ERROR] - Problems occurs on injecting env vars in prebuild: " - + throwable.getMessage()); - throwable.printStackTrace(); - } - } - } - return true; - } -} +package com.tikal.jenkins.plugins.multijob; + +import com.tikal.jenkins.plugins.multijob.MultiJobBuild.SubBuild; +import com.tikal.jenkins.plugins.multijob.PhaseJobsConfig.KillPhaseOnJobResultCondition; +import com.tikal.jenkins.plugins.multijob.counters.CounterHelper; +import com.tikal.jenkins.plugins.multijob.counters.CounterManager; +import groovy.util.Eval; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.Util; +import hudson.console.HyperlinkNote; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.Action; +import hudson.model.BallColor; +import hudson.model.Build; +import hudson.model.BuildListener; +import hudson.model.DependecyDeclarer; +import hudson.model.DependencyGraph; +import hudson.model.DependencyGraph.Dependency; +import hudson.model.Executor; +import hudson.model.Item; +import hudson.model.Queue.QueueAction; +import hudson.model.Result; +import hudson.model.TaskListener; +import hudson.model.queue.QueueTaskFuture; +import hudson.scm.ChangeLogSet; +import hudson.scm.ChangeLogSet.Entry; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Builder; +import jenkins.model.Jenkins; +import net.sf.json.JSONObject; +import jenkins.model.Jenkins; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; + +import org.apache.commons.lang.StringUtils; +import org.jenkinsci.lib.envinject.EnvInjectException; +import org.jenkinsci.lib.envinject.EnvInjectLogger; +import org.jenkinsci.plugins.envinject.EnvInjectBuilder; +import org.jenkinsci.plugins.envinject.EnvInjectBuilderContributionAction; +import org.jenkinsci.plugins.envinject.service.EnvInjectActionSetter; +import org.jenkinsci.plugins.envinject.service.EnvInjectEnvVars; +import org.jenkinsci.plugins.envinject.service.EnvInjectVariableGetter; +import org.jenkinsci.plugins.tokenmacro.TokenMacro; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.StaplerRequest; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Pattern; + +import org.jenkinsci.plugins.tokenmacro.TokenMacro; + +import groovy.util.*; +import hudson.plugins.parameterizedtrigger.AbstractBuildParameters.DontTriggerException; +import java.util.concurrent.CancellationException; +import jenkins.model.CauseOfInterruption; + +public class MultiJobBuilder extends Builder implements DependecyDeclarer { + /** + * The name of the parameter in the build.getBuildVariables() to enable the job build, regardless + * of scm changes. + */ + public static final String BUILD_ALWAYS_KEY = "hudson.scm.multijob.build.always"; + + private String phaseName; + private List phaseJobs; + private ContinuationCondition continuationCondition = ContinuationCondition.SUCCESSFUL; + private ExecutionType executionType; + + final static Pattern PATTERN = Pattern.compile("(\\$\\{.+?\\})", Pattern.CASE_INSENSITIVE); + + + /** + * The name of the new variable which stores the status of the current job. + * The state is the name of the corresponding value in {@link StatusJob} enum. + * @since 1.0.0 + * @see StatusJob#isBuildable() + */ + public static final String JOB_STATUS = "JOB_STATUS"; + + /** + * The name of the new variable which stores if the job is buildable or not. + * This value is getted from the {@link StatusJob#isBuildable()}. + * The only values of this variable are true when the job is buildable, + * or false when the job is not buildable. + * + * @since 1.0.0 + * @see StatusJob#isBuildable() + */ + public static final String JOB_IS_BUILDABLE = "JOB_IS_BUILDABLE"; + + /** + * A prefix for env vars which should be loaded in {@link #prebuild(Build, BuildListener)}. + * this will happen only when build was triggered by the {@link MultiJobResumeControl} action + * + * @since 1.0.0 + */ + public static final String PERSISTENT_VARS_PREFIX = "RESUMABLE_"; + + @Deprecated + public MultiJobBuilder(String phaseName, List phaseJobs, + ContinuationCondition continuationCondition) { + this(phaseName, phaseJobs, continuationCondition, ExecutionType.PARALLEL); + } + + @DataBoundConstructor + public MultiJobBuilder(String phaseName, List phaseJobs, + ContinuationCondition continuationCondition, ExecutionType executionType) { + this.phaseName = phaseName; + this.phaseJobs = Util.fixNull(phaseJobs); + this.continuationCondition = continuationCondition; + this.executionType = executionType; + } + + public String expandToken(String toExpand, final AbstractBuild build, final BuildListener listener) { + String expandedExpression = toExpand; + try { + expandedExpression = TokenMacro.expandAll(build, listener, toExpand, false, null); + } catch (Exception e) { + listener.getLogger().println(e.getMessage()); + } + + return PATTERN.matcher(expandedExpression).replaceAll(""); + } + + /** + * Reports the status of the job. + *

The sequence of the checks are the following (the first winner stops the sequence and returns):

+ * + *
    + *
  1. If job is disabled + * then returns {@link StatusJob#IS_DISABLED}.
  2. + *
  3. If job is disabled at phase configuration + * then returns {@link StatusJob#IS_DISABLED_AT_PHASECONFIG}.
  4. + *
  5. If BuildOnlyIfSCMChanges is disabled + * then returns {@link StatusJob#BUILD_ONLY_IF_SCM_CHANGES_DISABLED}.
  6. + *
  7. If 'Build Always' feature is enabled + * then returns {@link StatusJob#BUILD_ALWAYS_IS_ENABLED}.
  8. + *
  9. If job doesn't contains lastbuild + * then returns {@link StatusJob#DOESNT_CONTAINS_LASTBUILD}.
  10. + *
  11. If lastbuild result of the job is worse than unstable + * then returns {@link StatusJob#LASTBUILD_RESULT_IS_WORSE_THAN_UNSTABLE}.
  12. + *
  13. If job's workspace is empty + * then returns {@link StatusJob#WORKSPACE_IS_EMPTY}.
  14. + *
  15. If job contains scm changes + * then returns {@link StatusJob#CHANGED_SINCE_LAST_BUILD}.
  16. + *
  17. If job's doesn't contains scm changes + * then returns {@link StatusJob#NOT_CHANGED_SINCE_LAST_BUILD}.
  18. + *
+ */ + private StatusJob getScmChange(Job subjob,PhaseJobsConfig phaseConfig,AbstractBuild build, BuildListener listener,Launcher launcher) + throws IOException, InterruptedException { + /* https://issues.jenkins-ci.org/browse/JENKINS-33821 + if ( subjob.disabled ) { + return StatusJob.IS_DISABLED; + } + */ + if( phaseConfig.isDisableJob() ) { + return StatusJob.IS_DISABLED_AT_PHASECONFIG; + } + if ( !phaseConfig.isBuildOnlyIfSCMChanges() ){ + return StatusJob.BUILD_ONLY_IF_SCM_CHANGES_DISABLED; + } + final boolean buildAlways = Boolean.valueOf((String)(build.getBuildVariables().get(BUILD_ALWAYS_KEY))); + + if ( buildAlways ) { + return StatusJob.BUILD_ALWAYS_IS_ENABLED; + } + final Run lastBuild = subjob.getLastBuild(); + if ( lastBuild == null ) { + return StatusJob.DOESNT_CONTAINS_LASTBUILD; + } + if ( lastBuild.getResult() != null && lastBuild.getResult().isWorseThan(Result.UNSTABLE) ) { + return StatusJob.LASTBUILD_RESULT_IS_WORSE_THAN_UNSTABLE; + } + + /* need to pass in filepath + if ( lastBuild.getWorkspace() == null ) { + return StatusJob.DOESNT_CONTAINS_WORKSPACE; + } + + if ( !lastBuild.getWorkspace().exists() ) { + return StatusJob.WORKSPACE_IS_EMPTY; + } + */ + /* not sure about this one + if ( subjob.poll(listener).hasChanges() ) { + return StatusJob.CHANGED_SINCE_LAST_BUILD; + } + */ + + return StatusJob.NOT_CHANGED_SINCE_LAST_BUILD; + } + + public boolean evalCondition(final String condition, final AbstractBuild build, final BuildListener listener) { + try { + return (Boolean) Eval.me(expandToken(condition, build, listener).trim()); + } catch (Exception e) { + listener.getLogger().println("Can't evaluate expression, false is assumed: " + e.toString()); + } + return false; + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public boolean perform(final AbstractBuild build, final Launcher launcher, final BuildListener listener) throws InterruptedException, IOException { + Jenkins jenkins = Jenkins.getInstance(); + MultiJobBuild multiJobBuild = (MultiJobBuild) build; + MultiJobProject thisProject = multiJobBuild.getProject(); + + boolean resume = false; + Map successBuildMap = new HashMap(); + Map resumeBuildMap = new HashMap(); + MultiJobResumeControl control = build.getAction(MultiJobResumeControl.class); + if (null != control) { + MultiJobBuild prevBuild = (MultiJobBuild) control.getRun(); + + boolean willResumeBuild = true; + if (thisProject.getCheckResumeEnvVars()) { + String[] variables = thisProject.getResumeEnvVars().split(","); + for( int i = 0; i < variables.length; i++ ) { + String previousValue = prevBuild.getEnvironment(listener).get(variables[i]); + String currentValue = build.getEnvironment(listener).get(variables[i]); + if( !StringUtils.equals(previousValue, currentValue) ) { + willResumeBuild = false; + listener.getLogger().println(String.format("Cannot resume the build, values for '%s' do not match: [%s][%s]", variables[i], previousValue, currentValue)); + break; + } + } + } + if(willResumeBuild) { + for (SubBuild subBuild : prevBuild.getSubBuilds()) { + Item item = Jenkins.getInstance().getItem(subBuild.getJobName(), prevBuild.getParent(), + AbstractProject.class); + if (item instanceof AbstractProject) { + AbstractProject childProject = (AbstractProject) item; + AbstractBuild childBuild = childProject.getBuildByNumber(subBuild.getBuildNumber()); + if (null != childBuild) { + if (Result.SUCCESS.equals(childBuild.getResult())) { + successBuildMap.put(childProject.getUrl(), subBuild); + } else { + resume = true; + resumeBuildMap.put(childProject.getUrl(), subBuild); + } + } + } + } + } + + if (!resume) { + successBuildMap.clear(); + } + } + + Map phaseSubJobs = new LinkedHashMap( + phaseJobs.size()); + final CounterManager phaseCounters = new CounterManager(); + boolean aggragatedTestResults = false; + for (PhaseJobsConfig phaseJobConfig : phaseJobs) { + Item item = jenkins.getItem(phaseJobConfig.getJobName(), multiJobBuild.getParent(), Job.class); + if (item instanceof Job) { + Job job = (Job) item; + phaseSubJobs.put(new PhaseSubJob(job), phaseJobConfig); + } + if (phaseJobConfig.isAggregatedTestResults()) { + aggragatedTestResults = true; + } + } + + if (aggragatedTestResults) { + multiJobBuild.addTestsResult(); + } + + List subTasks = new ArrayList(); + int index = 0; + for (PhaseSubJob phaseSubJob : phaseSubJobs.keySet()) { + index++; + + Job subJob = phaseSubJob.job; + + // To be coherent with final results, we need to do this here. + PhaseJobsConfig phaseConfig = phaseSubJobs.get(phaseSubJob); + StatusJob jobStatus = getScmChange(subJob,phaseConfig,multiJobBuild ,listener,launcher ); + listener.getLogger().println(jobStatus.getMessage(subJob)); + // We are ready to inject vars about scm status. It is useful at condition level. + Map jobScmVars = new HashMap(); +// New injected variable. It stores the status of the last job executed. It is useful at condition level. + jobScmVars.put(JOB_STATUS, jobStatus.name()); +// New injected variable. It reports if the job is buildable. + jobScmVars.put(JOB_IS_BUILDABLE, String.valueOf(jobStatus.isBuildable())); + injectEnvVars(build, listener, jobScmVars); + + if (jobStatus == StatusJob.IS_DISABLED) { + phaseCounters.processSkipped(); + continue; + } + + // If we want to apply the condition only if there were no SCM change + // We can do it by using the "Apply condition only if no SCM changes were found" + boolean conditionExistsAndEvaluatedToTrue = false; + + if (phaseConfig.getEnableCondition() && phaseConfig.getCondition() != null) { + String subJobDisplayName = subJob.getName(); + if (phaseConfig.getJobAlias() != null && !phaseConfig.getJobAlias().equals("")) { + subJobDisplayName += " (" + phaseConfig.getJobAlias() + ")"; + } + + // if SCM has changes or set to always build and condition should always be evaluated + if(jobStatus.isBuildable() && !phaseConfig.isApplyConditionOnlyIfNoSCMChanges()) { + if (evalCondition(phaseConfig.getCondition(), build, listener)) { + listener.getLogger().println(String.format("Triggering %s. Condition was evaluated to true.", subJobDisplayName)); + conditionExistsAndEvaluatedToTrue = true; + } else { + listener.getLogger().println(String.format("Skipping %s. Condition was evaluated to false.", subJobDisplayName)); + phaseCounters.processSkipped(); + continue; + } + } + // if SCM has no changes but condition is set to be evaluated in this case + else if(!jobStatus.isBuildable() && phaseConfig.isApplyConditionOnlyIfNoSCMChanges()) { + if (evalCondition(phaseConfig.getCondition(), build, listener)) { + listener.getLogger().println(String.format("Triggering %s. Condition was evaluated to true.", subJobDisplayName)); + conditionExistsAndEvaluatedToTrue = true; + } else { + listener.getLogger().println(String.format("Skipping %s. Condition was evaluated to false.", subJobDisplayName)); + phaseCounters.processSkipped(); + continue; + } + } + // no SCM changes and no condition evaluation + else if(!jobStatus.isBuildable() && !phaseConfig.isApplyConditionOnlyIfNoSCMChanges()) { + listener.getLogger().println(String.format("Skipping %s. No SCM changes found and condition is skipped.", subJobDisplayName)); + phaseCounters.processSkipped(); + continue; + } else { + if (!evalCondition(phaseConfig.getCondition(), build, listener)) { + listener.getLogger().println(String.format("Skipping %s. Condition was evaluated to false.", subJobDisplayName)); + phaseCounters.processSkipped(); + continue; + } else { + listener.getLogger().println(String.format("Triggering %s. Condition was evaluated to true.", subJobDisplayName)); + conditionExistsAndEvaluatedToTrue = true; + } + } + // This is needed because if no condition to eval, the legacy buildOnlyIfSCMChanges feature is still available, + // so we don't need to change our job configuration. + } + if ( ! jobStatus.isBuildable() && !conditionExistsAndEvaluatedToTrue) { + phaseCounters.processSkipped(); + continue; + } + + reportStart(listener, subJob, phaseConfig); + List actions = new ArrayList(); + + if (resume) { + SubBuild subBuild = resumeBuildMap.get(subJob.getUrl()); + if (null != subBuild) { + AbstractProject prj = Jenkins.getInstance().getItem(subBuild.getJobName(), multiJobBuild.getParent(), + AbstractProject.class); + AbstractBuild childBuild = prj.getBuildByNumber(subBuild.getBuildNumber()); + MultiJobResumeControl childControl = new MultiJobResumeControl(childBuild); + actions.add(childControl); + } + } + + prepareActions(multiJobBuild, subJob, phaseConfig, listener, actions, index); + + if ( jobStatus == StatusJob.IS_DISABLED_AT_PHASECONFIG ) { + phaseCounters.processSkipped(); + continue; + } else { + boolean shouldTrigger = null == successBuildMap.get(subJob.getUrl()) ? true : false; + subTasks.add(new SubTask(subJob, phaseConfig, actions, multiJobBuild, shouldTrigger)); + } + } + + if (subTasks.size() < 1) { + // We inject the variables also when no jobs will be triggered. + injectEnvVars(build, listener, phaseCounters.toMap()); + return true; + } + + if (null == executionType) { + executionType = ExecutionType.PARALLEL; + } + int poolSize = executionType.isParallel() ? subTasks.size() : 1; + ExecutorService executor = Executors.newFixedThreadPool(poolSize); + Set jobResults = new HashSet(); + BlockingQueue queue = new ArrayBlockingQueue(subTasks.size()); + for (SubTask subTask : subTasks) { + SubBuild subBuild = successBuildMap.get(subTask.subJob.getUrl()); + if (null == subBuild) { + CompletionService completion = new ExecutorCompletionService(executor); + Callable worker = new SubJobWorker(thisProject, listener, subTask, queue); + completion.submit(worker); + if (!executionType.isParallel()) { + Future future = completion.take(); + try { + future.get(); + if (checkPhaseTermination(subTask, subTasks, listener)) { + break; + } + } catch (ExecutionException e) { + listener.getLogger().println("Error while execution subtask " + subTask.subJob.getDisplayName()); + } + } + } else { + Run jobBuild = subTask.subJob.getBuildByNumber(subBuild.getBuildNumber()); + updateSubBuild(multiJobBuild, thisProject, jobBuild, subBuild.getResult(), subBuild.getJobAlias()); + } + } + + try { + executor.shutdown(); + int resultCounter = 0; + while (!executor.isTerminated()) { + SubTask subTask = queue.poll(5, TimeUnit.SECONDS); + if (subTask != null) { + resultCounter++; + if (subTask.result != null) { + jobResults.add(subTask.result); + phaseCounters.process(subTask.result); + checkPhaseTermination(subTask, subTasks, listener); + } + } + if (subTasks.size() <= resultCounter) { + break; + } + } + executor.shutdownNow(); + + } catch (InterruptedException exception) { + listener.getLogger().println("Aborting all subjobs."); + for (SubTask _subTask : subTasks) { + _subTask.cancelJob(); + phaseCounters.processAborted(); + } + int i = 0; + while (!executor.isTerminated() && i < 20) { + Thread.sleep(1000); + i++; + } + throw new InterruptedException(); + + } + injectEnvVars(build, listener, phaseCounters.toMap()); + + for (Result result : jobResults) { + if (!continuationCondition.isContinue(result)) { + return false; + } + } + return true; + } + + public final class SubJobWorker implements Callable { + final private MultiJobProject multiJobProject; + final private BuildListener listener; + private SubTask subTask; + private BlockingQueue queue; + private List compiledPatterns; + + public SubJobWorker(MultiJobProject multiJobProject, + BuildListener listener, + SubTask subTask, + BlockingQueue queue) { + this.multiJobProject = multiJobProject; + this.listener = listener; + this.subTask = subTask; + this.queue = queue; + } + + /** + * Subtask execution + * @return value is not used in the builder. Can be used to return some state of the task in the future. + */ + public Object call() { + Result result = null; + Run jobBuild = null; + try { + int maxRetries = subTask.phaseConfig.getMaxRetries(); + if (!subTask.phaseConfig.getEnableRetryStrategy()) { + maxRetries = 0; + } + + int retry = 0; + boolean finish = false; + + while (retry <= maxRetries && !finish) { + retry++; + if (subTask.isShouldTrigger()) { + subTask.generateFuture(); + } + QueueTaskFuture future = subTask.future; + while (true) { + if (subTask.isCancelled()) { + if (jobBuild != null) { + Executor exect = jobBuild.getExecutor(); + if (exect != null) { + exect.interrupt(Result.ABORTED); + } + + reportFinish(listener, jobBuild, Result.ABORTED, subTask.phaseConfig); + abortSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, subTask.phaseConfig.getJobAlias()); + + finish = true; + break; + } + } + + try { + jobBuild = (Run)future.getStartCondition().get(5, TimeUnit.SECONDS); + } catch (Exception e) { + if (e instanceof TimeoutException) + continue; + else { + throw e; + } + } + updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, subTask.phaseConfig.getJobAlias()); + if (future.isDone()) { + break; + } + Thread.sleep(2500); + } + if (jobBuild != null && !finish) { + result = jobBuild.getResult(); + reportFinish(listener, jobBuild, result, subTask.phaseConfig); + + if (result.isWorseOrEqualTo(Result.UNSTABLE) && result.isCompleteBuild() && subTask.phaseConfig.getEnableRetryStrategy()) { + if (isKnownRandomFailure(jobBuild)) { + if (retry <= maxRetries) { + listener.getLogger().println("Known failure detected, retrying this build. Try " + retry + " of " + maxRetries + "."); + updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, true, subTask.phaseConfig.getJobAlias()); + + //subTask.generateFuture(); + } else { + listener.getLogger().println("Known failure detected, max retries (" + maxRetries + ") exceeded."); + updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, subTask.phaseConfig.getJobAlias()); + } + } else { + listener.getLogger().println("Failed the build, the failure doesn't match the rules."); + updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, subTask.phaseConfig.getJobAlias()); + finish = true; + } + } else { + updateSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, result, subTask.phaseConfig.getJobAlias()); + finish = true; + } + + //ChangeLogSet changeLogSet = jobBuild.getChangeSet(); + //subTask.multiJobBuild.addChangeLogSet(changeLogSet); + addBuildEnvironmentVariables(subTask.multiJobBuild, jobBuild, listener); + subTask.result = result; + } + } + + if (subTask.phaseConfig.isAggregatedTestResults()) { + MultiJobTestAggregator.aggregateResultsFromBuild(jobBuild, subTask.multiJobBuild.getMultiJobTestResults(), listener); + } + } catch (Exception e) { + if (e instanceof InterruptedException) { + if (jobBuild != null) { + reportFinish(listener, jobBuild, Result.ABORTED, subTask.phaseConfig); + abortSubBuild(subTask.multiJobBuild, multiJobProject, jobBuild, subTask.phaseConfig.getJobAlias()); + subTask.result = Result.ABORTED; + } + } else { + listener.getLogger().println(e.toString()); + } + } + + if (jobBuild == null) { + updateSubBuild(subTask.multiJobBuild, multiJobProject, subTask.phaseConfig); + } + queue.add(subTask); + + return Boolean.TRUE; + } + + private List getCompiledPattern() throws FileNotFoundException, InterruptedException { + if (compiledPatterns == null) { + compiledPatterns = new ArrayList(); + try { + listener.getLogger().println("Scanning failed job console output using parsing rule file " + subTask.phaseConfig.getParsingRulesPath() + "."); + final File rulesFile = new File(subTask.phaseConfig.getParsingRulesPath()); + FileInputStream fis = new FileInputStream(rulesFile.getAbsoluteFile()); + InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8); + final BufferedReader reader = new BufferedReader(isr); + try { + String line; + while ((line = reader.readLine()) != null) { + compiledPatterns.add(Pattern.compile(line)); + } + } finally { + if (reader != null) { + reader.close(); + } + } + } catch (Exception e) { + if (e instanceof InterruptedException) { + throw new InterruptedException(); + } else if (e instanceof FileNotFoundException) { + throw new FileNotFoundException(); + } else { + listener.getLogger().println(e.toString()); + e.printStackTrace(); + } + } + } + return compiledPatterns; + } + + private final class LineAnalyser extends Thread { + final private BufferedReader reader; + final private List patterns; + private BlockingQueue finishQueue; + + public LineAnalyser(BufferedReader reader, List patterns, BlockingQueue finishQueue) { + this.reader = reader; + this.patterns = patterns; + this.finishQueue = finishQueue; + } + + public void run() { + boolean errorFound = false; + try { + String line; + while (reader.ready() && !errorFound) { + line = reader.readLine(); + if (line != null) { + for (Pattern pattern : patterns) { + if (pattern.matcher(line).find()) { + errorFound = true; + break; + } + } + } + } + } catch (Exception e) { + if (e instanceof IOException) { + // Nothing + } else { + listener.getLogger().println(e.toString()); + e.printStackTrace(); + } + } finally { + finishQueue.add(new LineQueue(errorFound)); + } + } + } + + private boolean isKnownRandomFailure(Run build) throws InterruptedException { + boolean failure = false; + try { + final List patterns = getCompiledPattern(); + final File logFile = build.getLogFile(); + FileInputStream fis = new FileInputStream(logFile); + InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8); + final BufferedReader reader = new BufferedReader(isr); + try { + int numberOfThreads = 10; // Todo : Add this in Configure section + if (numberOfThreads < 0) { + numberOfThreads = 1; + } + ExecutorService executorAnalyser = Executors.newFixedThreadPool(numberOfThreads); + BlockingQueue finishQueue = new ArrayBlockingQueue(numberOfThreads); + + for (int i = 0; i < numberOfThreads; i++) { + Runnable worker = new LineAnalyser(reader, patterns, finishQueue); + executorAnalyser.execute(worker); + } + + executorAnalyser.shutdown(); + int resultCounter = 0; + while (!executorAnalyser.isTerminated()) { + resultCounter++; + LineQueue lineQueue = finishQueue.take(); + if (lineQueue.hasError()) { + failure = true; + break; + } else if (numberOfThreads == resultCounter) { + break; + } + } + executorAnalyser.shutdownNow(); + } finally { + if (reader != null) { + reader.close(); + } + } + } catch (Exception e) { + if (e instanceof InterruptedException) { + throw new InterruptedException(); + } else if (e instanceof FileNotFoundException) { + listener.getLogger().println("Parser rules file not found."); + failure = false; + } else { + listener.getLogger().println(e.toString()); + e.printStackTrace(); + } + } + return failure; + } + } + + protected boolean checkPhaseTermination(SubTask subTask, List subTasks, final BuildListener listener) { + try { + KillPhaseOnJobResultCondition killCondition = subTask.phaseConfig.getKillPhaseOnJobResultCondition(); + if (killCondition.equals(KillPhaseOnJobResultCondition.NEVER) && subTask.result != Result.ABORTED) { + return false; + } + if (killCondition.isKillPhase(subTask.result)) { + if (subTask.result != Result.ABORTED && subTask.phaseConfig.getAbortAllJob()) { + for (SubTask _subTask : subTasks) { + _subTask.cancelJob(); + } + return true; + } + } + } catch (Exception e) { + listener.getLogger().printf(e.toString()); + return false; + } + return false; + } + + private void reportStart(BuildListener listener, Job subJob, PhaseJobsConfig phaseConfig) { + String jobDisplayName = subJob.getFullName(); + if (phaseConfig.getJobAlias() != null && !phaseConfig.getJobAlias().equals("")) { + jobDisplayName += " (" + phaseConfig.getJobAlias() + ")"; + } + listener.getLogger().printf( + "Starting build job %s.\n", + HyperlinkNote.encodeTo('/' + subJob.getUrl(), + jobDisplayName)); + } + + private void reportFinish(BuildListener listener, Run jobBuild, + Result result, PhaseJobsConfig phaseConfig) { + String jobDisplayName = jobBuild.getParent().getFullName(); + if (phaseConfig.getJobAlias() != null && !phaseConfig.getJobAlias().equals("")) { + jobDisplayName += " (" + phaseConfig.getJobAlias() + ")"; + } + + listener.getLogger().println( + "Finished Build : " + + HyperlinkNote.encodeTo("/" + jobBuild.getUrl() + "/", + String.valueOf(jobBuild.getDisplayName())) + + " of Job : " + + HyperlinkNote.encodeTo('/' + jobBuild.getParent() + .getUrl(), jobDisplayName) + + " with status : " + + HyperlinkNote.encodeTo('/' + jobBuild.getUrl() + + "/console", result.toString())); + } + + private void updateSubBuild(MultiJobBuild multiJobBuild, + MultiJobProject multiJobProject, PhaseJobsConfig phaseConfig) { + SubBuild subBuild = new SubBuild(multiJobProject.getName(), + multiJobBuild.getNumber(), phaseConfig.getJobName(), phaseConfig.getJobAlias(), 0, + phaseName, null, BallColor.NOTBUILT.getImage(), "not built", "", null); + multiJobBuild.addSubBuild(subBuild); + } + + private void updateSubBuild(MultiJobBuild multiJobBuild, + MultiJobProject multiJobProject, Run jobBuild, String jobAlias) { + SubBuild subBuild = new SubBuild(multiJobProject.getName(), + multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, + jobBuild.getNumber(), phaseName, null, jobBuild.getIconColor() + .getImage(), jobBuild.getDurationString(), + jobBuild.getUrl(), jobBuild); + multiJobBuild.addSubBuild(subBuild); + } + + private void updateSubBuild(MultiJobBuild multiJobBuild, + MultiJobProject multiJobProject, Run jobBuild, + Result result, String jobAlias) { + SubBuild subBuild = new SubBuild(multiJobProject.getName(), + multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, + jobBuild.getNumber(), phaseName, result, jobBuild.getIconColor().getImage(), + jobBuild.getDurationString(), jobBuild.getUrl(), jobBuild); + multiJobBuild.addSubBuild(subBuild); + } + + private void updateSubBuild(MultiJobBuild multiJobBuild, + MultiJobProject multiJobProject, Run jobBuild, + Result result, boolean retry, String jobAlias) { + SubBuild subBuild = new SubBuild(multiJobProject.getName(), + multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, + jobBuild.getNumber(), phaseName, result, jobBuild.getIconColor().getImage(), + jobBuild.getDurationString(), jobBuild.getUrl(), retry, false, jobBuild); + multiJobBuild.addSubBuild(subBuild); + } + + private void abortSubBuild(MultiJobBuild multiJobBuild, MultiJobProject multiJobProject, + Run jobBuild, String jobAlias) { + SubBuild subBuild = new SubBuild(multiJobProject.getName(), + multiJobBuild.getNumber(), jobBuild.getParent().getName(), jobAlias, + jobBuild.getNumber(), phaseName, Result.ABORTED, BallColor.ABORTED.getImage(), "", jobBuild.getUrl(), false, true, jobBuild); + multiJobBuild.addSubBuild(subBuild); + } + + @SuppressWarnings("rawtypes") + private synchronized void addBuildEnvironmentVariables(MultiJobBuild thisBuild, + Run jobBuild, BuildListener listener) { + // Env variables map + Map variables = new HashMap(); + + // Fetch the map of existing environment variables + try { + + EnvInjectLogger logger = new EnvInjectLogger(listener); + EnvInjectVariableGetter variableGetter = new EnvInjectVariableGetter(); + Map previousEnvVars = variableGetter + .getEnvVarsPreviousSteps(thisBuild, logger); + + // Get current envVars + variables = new HashMap( + previousEnvVars); + + } catch (Throwable throwable) { + listener.getLogger() + .println( + "[MultiJob] - [ERROR] - Problems occurs on fetching env vars as a build step: " + + throwable.getMessage()); + } + + String jobName = jobBuild.getParent().getName(); + String jobNameSafe = jobName.replaceAll("[^A-Za-z0-9]", "_") + .toUpperCase(); + String buildNumber = Integer.toString(jobBuild.getNumber()); + String buildResult = jobBuild.getResult().toString(); + String buildName = jobBuild.getDisplayName().toString(); + + // If the job is run a second time, store the first job's number and result with unique keys + if (variables.get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe) != null) { + String runCount = variables.get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe); + if (runCount.equals("1")) { + String firstBuildNumber = variables.get(jobNameSafe + "_BUILD_NUMBER"); + String firstBuildResult = variables.get(jobNameSafe + "_BUILD_RESULT"); + variables.put(jobNameSafe + "_" + runCount + "_BUILD_NUMBER", firstBuildNumber); + variables.put(jobNameSafe + "_" + runCount + "_BUILD_RESULT", firstBuildResult); + } + } + + // These will always reference the last build + variables.put("LAST_TRIGGERED_JOB_NAME", jobName); + variables.put(jobNameSafe + "_BUILD_NUMBER", buildNumber); + variables.put(jobNameSafe + "_BUILD_RESULT", buildResult); + variables.put(jobNameSafe + "_BUILD_NAME", buildName); + + if (variables.get("TRIGGERED_JOB_NAMES") == null) { + variables.put("TRIGGERED_JOB_NAMES", jobName); + } else { + String triggeredJobNames = variables.get("TRIGGERED_JOB_NAMES") + + "," + jobName; + variables.put("TRIGGERED_JOB_NAMES", triggeredJobNames); + } + + if (variables.get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe) == null) { + variables.put("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe, "1"); + } else { + String runCount = Integer.toString(Integer.parseInt(variables + .get("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe)) + 1); + variables.put("TRIGGERED_BUILD_RUN_COUNT_" + jobNameSafe, runCount); + variables.put(jobNameSafe + "_" + runCount + "_BUILD_NUMBER", buildNumber); + variables.put(jobNameSafe + "_" + runCount + "_BUILD_RESULT", buildResult); + } + // Set the new build variables map + injectEnvVars(thisBuild, listener, variables); + } + + /** + * Method for properly injecting environment variables via EnvInject plugin. + * Method based off logic in {@link EnvInjectBuilder#perform} + */ + private void injectEnvVars(AbstractBuild build, + BuildListener listener, Map incomingVars) { + if (build != null && incomingVars != null) { + EnvInjectLogger logger = new EnvInjectLogger(listener); + FilePath ws = build.getWorkspace(); + EnvInjectActionSetter envInjectActionSetter = new EnvInjectActionSetter( ws ); + EnvInjectEnvVars envInjectEnvVarsService = new EnvInjectEnvVars( logger ); + + try { + EnvInjectVariableGetter variableGetter = new EnvInjectVariableGetter(); + Map previousEnvVars = variableGetter + .getEnvVarsPreviousSteps(build, logger); + + // Get current envVars + Map variables = new HashMap(previousEnvVars); + // Acumule PHASE, PHASENAME and MULTIJOB counters. + // Values are in variables (current values) and incomingVars. + Map mixtured = CounterHelper.putPhaseAddMultijobAndMergeTheRest(listener, this.phaseName, incomingVars, variables); + // Resolve variables + final Map resultVariables = envInjectEnvVarsService + .getMergedVariables(variables, mixtured); + + // Set the new build variables map + build.addAction(new EnvInjectBuilderContributionAction( + resultVariables)); + + // Add or get the existing action to add new env vars + envInjectActionSetter.addEnvVarsToEnvInjectBuildAction(build, + resultVariables); + } catch (Throwable throwable) { + listener.getLogger() + .println( + "[MultiJob] - [ERROR] - Problems occurs on injecting env vars as a build step: " + + throwable.getMessage()); + throwable.printStackTrace(); + } + } + } + + @SuppressWarnings("rawtypes") + private void prepareActions(AbstractBuild build, Job project, + PhaseJobsConfig projectConfig, BuildListener listener, + List actions, int index) throws IOException, InterruptedException { + List parametersActions = null; + // if (projectConfig.hasProperties()) { + parametersActions = (List) projectConfig.getActions(build, listener, project, projectConfig.isCurrParams()); + actions.addAll(parametersActions); + // } + actions.add(new MultiJobAction(build, index)); + + } + + private class MultiJobAction implements Action, QueueAction { + public int buildNumber; + public int index; + + /** + * @deprecated + * Maintain backwards compatibility with previous versions of this class. + * See https://wiki.jenkins-ci.org/display/JENKINS/Hint+on+retaining+backward+compatibility + */ + @Deprecated + private transient AbstractBuild build; + + public MultiJobAction(AbstractBuild build, int index) { + this.buildNumber = build.getNumber(); + this.index = index; + } + + public boolean shouldSchedule(List actions) { + boolean matches = true; + + for (MultiJobAction action : Util.filter(actions, MultiJobAction.class)) { + if (action.index != index) { + matches = false; + } + if (action.buildNumber != buildNumber) { + matches = false; + } + } + + return !matches; + } + + protected Object readResolve() { + if (build != null) { + buildNumber = build.getNumber(); + } + return this; + } + + public String getIconFileName() { + return null; + } + + public String getDisplayName() { + return "this shouldn't be displayed"; + } + + public String getUrlName() { + return null; + } + } + + public String getPhaseName() { + return phaseName; + } + + public void setPhaseName(String phaseName) { + this.phaseName = phaseName; + } + + public List getPhaseJobs() { + return phaseJobs; + } + + public void setPhaseJobs(List phaseJobs) { + this.phaseJobs = phaseJobs; + } + + public boolean phaseNameExist(String phaseName) { + for (PhaseJobsConfig phaseJob : phaseJobs) { + if (phaseJob.getDisplayName().equals(phaseName)) { + return true; + } + } + return false; + } + + private final static class PhaseSubJob { + Job job; + + PhaseSubJob(Job job) { + this.job = job; + } + } + + @Extension + public static class DescriptorImpl extends BuildStepDescriptor { + + @SuppressWarnings("rawtypes") + @Override + public boolean isApplicable(Class jobType) { + return jobType.equals(MultiJobProject.class); + } + + @Override + public String getDisplayName() { + return "MultiJob Phase"; + } + + @Override + public Builder newInstance(StaplerRequest req, JSONObject formData) + throws FormException { + return req.bindJSON(MultiJobBuilder.class, formData); + } + + @Override + public boolean configure(StaplerRequest req, JSONObject formData) { + save(); + return true; + } + } + + @SuppressWarnings("rawtypes") + public void buildDependencyGraph(AbstractProject owner, + DependencyGraph graph) { + Jenkins jenkins = Jenkins.getInstance(); + List phaseJobsConfigs = getPhaseJobs(); + + if (phaseJobsConfigs == null) + return; + for (PhaseJobsConfig project : phaseJobsConfigs) { + Item topLevelItem = jenkins.getItem(project.getJobName(), owner.getParent(), AbstractProject.class); + if (topLevelItem instanceof AbstractProject) { + Dependency dependency = new Dependency(owner, + (AbstractProject) topLevelItem) { + + @Override + public boolean shouldTriggerBuild(AbstractBuild build, + TaskListener listener, List actions) { + return false; + } + + }; + graph.addDependency(dependency); + } + } + } + + public boolean onJobRenamed(String oldName, String newName) { + boolean changed = false; + for (Iterator i = phaseJobs.iterator(); i.hasNext();) { + PhaseJobsConfig phaseJobs = (PhaseJobsConfig) i.next(); + String jobName = phaseJobs.getJobName(); + if (jobName.trim().equals(oldName)) { + if (newName != null) { + phaseJobs.setJobName(newName); + changed = true; + } else { + i.remove(); + changed = true; + } + } + } + return changed; + } + + public boolean onJobDeleted(String oldName) { + return onJobRenamed(oldName, null); + } + + public static enum ContinuationCondition { + + ALWAYS("Always") { + @Override + public boolean isContinue(Result result) { + return true; + } + }, + SUCCESSFUL("Successful") { + @Override + public boolean isContinue(Result result) { + return result.equals(Result.SUCCESS); + } + }, + COMPLETED("Completed") { + @Override + public boolean isContinue(Result result) { + return result.isCompleteBuild(); + } + }, + UNSTABLE("Stable or Unstable but not Failed") { + @Override + public boolean isContinue(Result result) { + return result.isBetterOrEqualTo(Result.UNSTABLE); + } + }, + FAILURE("Failed") { + @Override + public boolean isContinue(Result result) { + return result.isWorseOrEqualTo(Result.FAILURE); + } + }; + + abstract public boolean isContinue(Result result); + + private ContinuationCondition(String label) { + this.label = label; + } + + final private String label; + + public String getLabel() { + return label; + } + } + + public ContinuationCondition getContinuationCondition() { + return continuationCondition; + } + + public void setContinuationCondition( + ContinuationCondition continuationCondition) { + this.continuationCondition = continuationCondition; + } + + public enum ExecutionType { + + PARALLEL("Running phase jobs in parallel") { + @Override + public boolean isParallel() { + return true; + } + }, + SEQUENTIALLY("Running phase jobs sequentially") { + @Override + public boolean isParallel() { + return false; + } + }; + + final private String label; + + ExecutionType(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } + + abstract public boolean isParallel(); + } + + public void setExecutionType(ExecutionType executionType) { + this.executionType = executionType; + } + + public ExecutionType getExecutionType() { + return executionType; + } + + public boolean prebuild(Build build, BuildListener listener) { + boolean resume = false; + MultiJobResumeControl control = build.getAction(MultiJobResumeControl.class); + if (null != control) { + MultiJobBuild prevBuild = (MultiJobBuild) control.getRun(); + for (SubBuild subBuild : prevBuild.getSubBuilds()) { + Item item = Jenkins.getInstance().getItem(subBuild.getJobName(), prevBuild.getParent(), + AbstractProject.class); + if (item instanceof AbstractProject) { + AbstractProject childProject = (AbstractProject) item; + AbstractBuild childBuild = childProject.getBuildByNumber(subBuild.getBuildNumber()); + if (null != childBuild) { + if (childBuild.getResult().equals(Result.FAILURE)) { + resume = true; + } + } + } + } + if (resume) { + EnvInjectLogger logger = new EnvInjectLogger(listener); + EnvInjectVariableGetter variableGetter = new EnvInjectVariableGetter(); + try { + Map previousEnvVars = variableGetter.getEnvVarsPreviousSteps(prevBuild, logger); + Map persistentEnvVars = new HashMap(); + for (String key : previousEnvVars.keySet()) { + if(key.startsWith(PERSISTENT_VARS_PREFIX)) { + persistentEnvVars.put(key, previousEnvVars.get(key)); + } + } + persistentEnvVars.put("RESUMED_BUILD", "true"); + injectEnvVars(build, listener, persistentEnvVars); + } catch (Throwable throwable) { + listener.getLogger().println("[MultiJob] - [ERROR] - Problems occurs on injecting env vars in prebuild: " + + throwable.getMessage()); + throwable.printStackTrace(); + } + } + } + return true; + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobChangeLogSet.java b/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobChangeLogSet.java index 0ee8794d..8723a437 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobChangeLogSet.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/MultiJobChangeLogSet.java @@ -1,31 +1,31 @@ -package com.tikal.jenkins.plugins.multijob; - -import java.util.Iterator; - -import hudson.model.AbstractBuild; -import hudson.scm.ChangeLogSet; - -public class MultiJobChangeLogSet extends ChangeLogSet { - - protected MultiJobChangeLogSet(AbstractBuild build) { - super(build); - // TODO Auto-generated constructor stub - } - - public Iterator iterator() { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isEmptySet() { - // TODO Auto-generated method stub - return false; - } - - public void addChangeLogSet(ChangeLogSet changeLogSet) { - // TODO Auto-generated method stub - - } - -} +package com.tikal.jenkins.plugins.multijob; + +import java.util.Iterator; + +import hudson.model.AbstractBuild; +import hudson.scm.ChangeLogSet; + +public class MultiJobChangeLogSet extends ChangeLogSet { + + protected MultiJobChangeLogSet(AbstractBuild build) { + super(build); + // TODO Auto-generated constructor stub + } + + public Iterator iterator() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isEmptySet() { + // TODO Auto-generated method stub + return false; + } + + public void addChangeLogSet(ChangeLogSet changeLogSet) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/PhaseJobsConfig.java b/src/main/java/com/tikal/jenkins/plugins/multijob/PhaseJobsConfig.java index 4b6f7269..78546879 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/PhaseJobsConfig.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/PhaseJobsConfig.java @@ -1,575 +1,575 @@ -package com.tikal.jenkins.plugins.multijob; - -import hudson.Extension; -import hudson.Util; -import hudson.model.Action; -import hudson.model.AutoCompletionCandidates; -import hudson.model.Describable; -import hudson.model.JobProperty; -import hudson.model.JobPropertyDescriptor; -import hudson.model.ParameterValue; -import hudson.model.Result; -import hudson.model.TaskListener; -import hudson.model.AbstractBuild; -import hudson.model.AbstractProject; -import hudson.model.Job; -import hudson.model.BooleanParameterDefinition; -import hudson.model.ChoiceParameterDefinition; -import hudson.model.Descriptor; -import hudson.model.FileParameterValue; -import hudson.model.Hudson; -import hudson.model.ParameterDefinition; -import hudson.model.ParametersAction; -import hudson.model.ParametersDefinitionProperty; -import hudson.model.StringParameterDefinition; -import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; -import hudson.plugins.parameterizedtrigger.AbstractBuildParameters.DontTriggerException; -import hudson.plugins.parameterizedtrigger.FileBuildParameters; -import hudson.plugins.parameterizedtrigger.PredefinedBuildParameters; -import hudson.tasks.Builder; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import jenkins.model.Jenkins; -import net.sf.json.JSONObject; - -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; - -//import com.tikal.jenkins.plugins.multijob.scm.MultiJobScm; -public class PhaseJobsConfig implements Describable { - private String jobName; - private String jobAlias; - private String jobProperties; - private boolean currParams; - private boolean aggregatedTestResults; - private boolean exposedSCM; - private boolean disableJob; - private String parsingRulesPath; - private int maxRetries; - private boolean enableRetryStrategy; - private boolean enableCondition; - private boolean abortAllJob; - private String condition; - private List configs; - private KillPhaseOnJobResultCondition killPhaseOnJobResultCondition = KillPhaseOnJobResultCondition.NEVER; - private boolean buildOnlyIfSCMChanges = false; - private boolean applyConditionOnlyIfNoSCMChanges = false; - - public boolean isBuildOnlyIfSCMChanges() { - return this.buildOnlyIfSCMChanges; - } - - public void setBuildOnlyIfSCMChanges(boolean triggerOnlyIfSCMChanges) { - this.buildOnlyIfSCMChanges = triggerOnlyIfSCMChanges; - } - - public boolean isApplyConditionOnlyIfNoSCMChanges() { - return this.applyConditionOnlyIfNoSCMChanges; - } - - public void setApplyConditionOnlyIfNoSCMChanges(boolean applyConditionOnlyIfNoSCMChanges) { - this.applyConditionOnlyIfNoSCMChanges = applyConditionOnlyIfNoSCMChanges; - } - - public void setParsingRulesPath(String parsingRulesPath) { - this.parsingRulesPath = parsingRulesPath; - } - - public String getParsingRulesPath() { - return parsingRulesPath; - } - - public void setCondition(String condition) { - this.condition = condition; - } - - public String getCondition() { - return condition; - } - - public void setMaxRetries(int maxRetries) { - this.maxRetries = maxRetries; - } - - public int getMaxRetries() { - return maxRetries; - } - - public void setEnableRetryStrategy(boolean enableRetryStrategy) { - this.enableRetryStrategy = enableRetryStrategy; - } - - public boolean getEnableRetryStrategy() { - return enableRetryStrategy; - } - - public void setEnableCondition(boolean enableCondition) { - this.enableCondition = enableCondition; - } - - public boolean getEnableCondition() { - return enableCondition; - } - - public void setAbortAllJob(boolean abortAllJob) { - this.abortAllJob = abortAllJob; - } - - public boolean getAbortAllJob() { - return abortAllJob; - } - - public boolean isDisableJob() { - return disableJob; - } - - public void setDisableJob(boolean disableJob) { - this.disableJob = disableJob; - } - - public KillPhaseOnJobResultCondition getKillPhaseOnJobResultCondition() { - return killPhaseOnJobResultCondition; - } - - public void setKillPhaseOnJobResultCondition( - KillPhaseOnJobResultCondition killPhaseOnJobResultCondition) { - this.killPhaseOnJobResultCondition = killPhaseOnJobResultCondition; - } - - public boolean isExposedSCM() { - return exposedSCM; - } - - public void setExposedSCM(boolean exposedSCM) { - this.exposedSCM = exposedSCM; - } - - public boolean isCurrParams() { - return currParams; - } - - public void setCurrParams(boolean currParams) { - this.currParams = currParams; - } - - public boolean isAggregatedTestResults() { - return aggregatedTestResults; - } - - public void setAggregatedTestResults(boolean aggregatedTestResults) { - this.aggregatedTestResults = aggregatedTestResults; - } - - public String getJobProperties() { - return jobProperties; - } - - public void setJobProperties(String jobProperties) { - this.jobProperties = jobProperties; - } - - public String getJobName() { return jobName; } - - public void setJobName(String jobName) { - this.jobName = jobName; - } - - public String getJobAlias() { - if (jobAlias == null) { - return ""; - } - return jobAlias; - } - - public void setJobAlias(String jobAlias) { this.jobAlias = jobAlias; } - - public Descriptor getDescriptor() { - return Hudson.getInstance().getDescriptorOrDie(getClass()); - } - - public String getDisplayName() { - return getClass().getSimpleName(); - } - - public PhaseJobsConfig(String jobName, String jobAlias,String jobProperties, - boolean currParams, List configs, - KillPhaseOnJobResultCondition killPhaseOnJobResultCondition, - boolean disableJob, boolean enableRetryStrategy, - String parsingRulesPath, int maxRetries, boolean enableCondition, - boolean abortAllJob, String condition, boolean buildOnlyIfSCMChanges, - boolean applyConditionOnlyIfNoSCMChanges) { - this(jobName, jobAlias, jobProperties, currParams, configs, killPhaseOnJobResultCondition, - disableJob, enableRetryStrategy, parsingRulesPath, maxRetries, enableCondition, - abortAllJob, condition, buildOnlyIfSCMChanges, applyConditionOnlyIfNoSCMChanges, false); - } - - @DataBoundConstructor - public PhaseJobsConfig(String jobName, String jobAlias, String jobProperties, - boolean currParams, List configs, - KillPhaseOnJobResultCondition killPhaseOnJobResultCondition, - boolean disableJob, boolean enableRetryStrategy, - String parsingRulesPath, int maxRetries, boolean enableCondition, - boolean abortAllJob, String condition, boolean buildOnlyIfSCMChanges, - boolean applyConditionOnlyIfNoSCMChanges, boolean aggregatedTestResults) { - this.jobName = jobName; - this.jobAlias = jobAlias; - this.jobProperties = jobProperties; - this.currParams = currParams; - this.killPhaseOnJobResultCondition = killPhaseOnJobResultCondition; - this.disableJob = disableJob; - this.configs = Util.fixNull(configs); - this.enableRetryStrategy = enableRetryStrategy; - this.maxRetries = maxRetries; - if (this.maxRetries < 0) { - this.maxRetries = 0; - } - this.parsingRulesPath = Util.fixNull(parsingRulesPath); - this.enableCondition = enableCondition; - this.abortAllJob = abortAllJob; - this.condition = Util.fixNull(condition); - this.buildOnlyIfSCMChanges = buildOnlyIfSCMChanges; - this.applyConditionOnlyIfNoSCMChanges = applyConditionOnlyIfNoSCMChanges; - this.aggregatedTestResults = aggregatedTestResults; - } - - public List getConfigs() { - return configs; - } - - @Extension(optional = true) - public static class DescriptorImpl extends Descriptor { - private ParserRuleFile[] parsingRulesGlobal = new ParserRuleFile[0]; - - public DescriptorImpl() { - load(); - } - - @Override - public String getDisplayName() { - return "Phase Jobs Config"; - } - - public List> getBuilderConfigDescriptors() { - return Hudson - .getInstance() - .> getDescriptorList( - AbstractBuildParameters.class); - } - - public AutoCompletionCandidates doAutoCompleteJobName( - @QueryParameter String value) { - AutoCompletionCandidates c = new AutoCompletionCandidates(); - for (String localJobName : Hudson.getInstance().getJobNames()) { - if (localJobName.toLowerCase().startsWith(value.toLowerCase())) - c.add(localJobName); - } - return c; - } - - private void savePhaseJobConfigParameters(String localJobName) { - AbstractProject project = ((AbstractProject) Jenkins.getInstance() - .getItemByFullName(localJobName)); - List parameterDefinitions = getParameterDefinition(project); - StringBuilder sb = new StringBuilder(); - // ArrayList scmLocation = null; - for (ParameterDefinition pdef : parameterDefinitions) { - String paramValue = null; - if (pdef instanceof StringParameterDefinition) { - StringParameterDefinition stringParameterDefinition = (StringParameterDefinition) pdef; - paramValue = (String) stringParameterDefinition - .getDefaultParameterValue().getValue(); - } else if (pdef instanceof BooleanParameterDefinition) { - BooleanParameterDefinition booleanParameterDefinition = (BooleanParameterDefinition) pdef; - paramValue = String.valueOf(booleanParameterDefinition - .getDefaultParameterValue().getValue()); - } - sb.append(pdef.getName()).append("=").append(paramValue) - .append("\n"); - } - - AbstractProject item = getCurrentJob(); - if (item instanceof MultiJobProject) { - MultiJobProject parentProject = (MultiJobProject) item; - List builders = parentProject.getBuilders(); - if (builders != null) { - for (Builder builder : builders) { - if (builder instanceof MultiJobBuilder) { - MultiJobBuilder multiJobBuilder = (MultiJobBuilder) builder; - List phaseJobs = multiJobBuilder - .getPhaseJobs(); - for (PhaseJobsConfig phaseJob : phaseJobs) { - if (phaseJob.getJobName().equals(localJobName)) { - phaseJob.setJobProperties(sb.toString()); - save(); - } - } - } - } - - } - } - } - - private static String getCurrentJobName() { - String path = Descriptor.getCurrentDescriptorByNameUrl(); - String[] parts = path.split("/"); - StringBuilder builder = new StringBuilder(); - for (int i = 2; i < parts.length; i += 2) { - if (i > 2) - builder.append('/'); - builder.append(parts[i]); - } - return builder.toString(); - } - - private static AbstractProject getCurrentJob() { - String jobName = getCurrentJobName(); - return (AbstractProject) Jenkins.getInstance().getItemByFullName( - jobName); - } - - public List getParameterDefinition( - AbstractProject project) { - List list = new ArrayList(); - Map map = project - .getProperties(); - for (Map.Entry entry : map - .entrySet()) { - JobProperty property = entry.getValue(); - if (property instanceof ParametersDefinitionProperty) { - ParametersDefinitionProperty pdp = (ParametersDefinitionProperty) property; - for (ParameterDefinition parameterDefinition : pdp - .getParameterDefinitions()) { - if (parameterDefinition instanceof StringParameterDefinition - || parameterDefinition instanceof BooleanParameterDefinition - || parameterDefinition instanceof ChoiceParameterDefinition) { - list.add(parameterDefinition); - } - } - } - } - return list; - } - - public String doFillJobProperties(@QueryParameter String jobName) { - - return "fill=in"; - } - - public ParserRuleFile[] getParsingRulesGlobal() { - return parsingRulesGlobal; - } - - @Override - public boolean configure(final StaplerRequest req, final JSONObject json) - throws FormException { - parsingRulesGlobal = req.bindParametersToList(ParserRuleFile.class, - "jenkins-multijob-plugin.").toArray(new ParserRuleFile[0]); - save(); - return true; - } - - } - - public List getJobParameters(AbstractBuild build, - TaskListener listener) { - ParametersAction action = build.getAction(ParametersAction.class); - List values = new ArrayList(action - .getParameters().size()); - if (action != null) { - for (ParameterValue value : action.getParameters()) - // FileParameterValue is currently not reusable, so omit these: - if (!(value instanceof FileParameterValue)) - values.add(value); - } - - return values; - - } - - private static MultiJobParametersAction mergeParameters(MultiJobParametersAction base, - MultiJobParametersAction overlay) { - LinkedHashMap params = new LinkedHashMap(); - for (ParameterValue param : base.getParameters()) - if (param != null) - params.put(param.getName(), param); - for (ParameterValue param : overlay.getParameters()) - params.put(param.getName(), param); - return new MultiJobParametersAction(params.values().toArray(new ParameterValue[params.size()])); - } - - /** - * Create a list of actions to pass to the triggered build of project. - * - * This will create a single ParametersAction which will use the defaults - * from the project being triggered and override these, With the current - * parameters defined in this build. if configured. With any matching items - * defined in the different configs, e.g. predefined parameters. - * - * @param build - * build that is triggering project - * @param listener - * Project's listener - * @param project - * Project that is being triggered - * @param isCurrentInclude - * Include parameters from the current build. - * @return - * retuen List - * @throws IOException - * throws IOException - * @throws InterruptedException - * throws InterruptedException - */ - public List getActions(AbstractBuild build, TaskListener listener, - Job project, boolean isCurrentInclude) - throws IOException, InterruptedException { - List actions = new ArrayList(); - MultiJobParametersAction params = null; - LinkedList paramsValuesList = new LinkedList(); - - Map originalActions = project.getProperties(); - - // Check to see if the triggered project has Parameters defined. - ParametersDefinitionProperty parameters = null; - for (Object object : originalActions.values()) { - if (object instanceof hudson.model.ParametersDefinitionProperty) - parameters = (ParametersDefinitionProperty) object; - } - // Get and add ParametersAction for default parameters values - // if triggered project is Parameterized. - // Values will get overridden later as required - if (parameters != null) { - for (ParameterDefinition parameterdef : parameters - .getParameterDefinitions()) { - if (parameterdef.getDefaultParameterValue() != null) - paramsValuesList.add(parameterdef - .getDefaultParameterValue()); - } - params = new MultiJobParametersAction( - paramsValuesList - .toArray(new ParameterValue[paramsValuesList.size()])); - - } - - // Merge current parameters with the defaults from the triggered job. - // Current parameters override the defaluts. - if (isCurrentInclude) { - ParametersAction defaultParameters = build.getAction(ParametersAction.class); - if (defaultParameters != null) { - MultiJobParametersAction mjpa = new MultiJobParametersAction(defaultParameters.getParameters()); - if (params != null) { - params = mergeParameters(params, mjpa); - } else { - params = mjpa; - } - } - } - // Backward compatibility - // get actions from configs merge ParametersActions if needed. - if (configs != null) { - for (AbstractBuildParameters config : configs) { - Action a; - try { - a = config.getAction(build, listener); - if (a instanceof ParametersAction) { - MultiJobParametersAction mjpa = new MultiJobParametersAction(((ParametersAction) a).getParameters()); - if (params == null) { - params = mjpa; - } else { - params = mergeParameters(params, mjpa); - } - } else if (a != null) { - actions.add(a); - } - } catch (DontTriggerException e) { - // don't trigger on this configuration - listener.getLogger().println( - "[multiJob] DontTriggerException: " + e); - } - } - } - - if (params != null) - actions.add(params); - - return actions; - } - - public boolean hasProperties() { - return this.jobProperties != null && !this.jobProperties.isEmpty(); - } - - // compatibility with earlier plugins - public Object readResolve() { - if (hasProperties()) { - AbstractBuildParameters buildParameters = new PredefinedBuildParameters( - jobProperties); - if (configs == null) - configs = new ArrayList(); - configs.add(buildParameters); - } - - List oldParams = new ArrayList(); - if (configs != null && configs.size() > 0) { - Iterator parametersIterator = configs.iterator(); - while (parametersIterator.hasNext()) { - Object param = parametersIterator.next(); - if (param instanceof com.tikal.jenkins.plugins.multijob.PredefinedBuildParameters) { - com.tikal.jenkins.plugins.multijob.PredefinedBuildParameters previosStringParam = (com.tikal.jenkins.plugins.multijob.PredefinedBuildParameters) param; - parametersIterator.remove(); - oldParams.add(new PredefinedBuildParameters( - previosStringParam.getJobProperties())); - } else if (param instanceof com.tikal.jenkins.plugins.multijob.FileBuildParameters) { - com.tikal.jenkins.plugins.multijob.FileBuildParameters previosFileParam = (com.tikal.jenkins.plugins.multijob.FileBuildParameters) param; - parametersIterator.remove(); - oldParams.add(new FileBuildParameters(previosFileParam - .getPropertiesFile())); - } - } - configs.addAll(oldParams); - } - return this; - } - - public static enum KillPhaseOnJobResultCondition { - FAILURE("Failure (stop the phase execution if the job is failed)") { - @Override - public boolean isKillPhase(Result result) { - return result.isWorseOrEqualTo(Result.FAILURE); - } - }, - NEVER("Never (ignore the job result and continue the phase execution)") { - @Override - public boolean isKillPhase(Result result) { - return result.equals(Result.ABORTED) ? true : false; - } - }, - UNSTABLE("Unstable (stop the phase execution if the job is unstable)") { - @Override - public boolean isKillPhase(Result result) { - return result.isWorseOrEqualTo(Result.UNSTABLE); - } - }; - - abstract public boolean isKillPhase(Result result); - - private KillPhaseOnJobResultCondition(String label) { - this.label = label; - } - - final private String label; - - public String getLabel() { - return label; - } - } +package com.tikal.jenkins.plugins.multijob; + +import hudson.Extension; +import hudson.Util; +import hudson.model.Action; +import hudson.model.AutoCompletionCandidates; +import hudson.model.Describable; +import hudson.model.JobProperty; +import hudson.model.JobPropertyDescriptor; +import hudson.model.ParameterValue; +import hudson.model.Result; +import hudson.model.TaskListener; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Job; +import hudson.model.BooleanParameterDefinition; +import hudson.model.ChoiceParameterDefinition; +import hudson.model.Descriptor; +import hudson.model.FileParameterValue; +import hudson.model.Hudson; +import hudson.model.ParameterDefinition; +import hudson.model.ParametersAction; +import hudson.model.ParametersDefinitionProperty; +import hudson.model.StringParameterDefinition; +import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; +import hudson.plugins.parameterizedtrigger.AbstractBuildParameters.DontTriggerException; +import hudson.plugins.parameterizedtrigger.FileBuildParameters; +import hudson.plugins.parameterizedtrigger.PredefinedBuildParameters; +import hudson.tasks.Builder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import jenkins.model.Jenkins; +import net.sf.json.JSONObject; + +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; + +//import com.tikal.jenkins.plugins.multijob.scm.MultiJobScm; +public class PhaseJobsConfig implements Describable { + private String jobName; + private String jobAlias; + private String jobProperties; + private boolean currParams; + private boolean aggregatedTestResults; + private boolean exposedSCM; + private boolean disableJob; + private String parsingRulesPath; + private int maxRetries; + private boolean enableRetryStrategy; + private boolean enableCondition; + private boolean abortAllJob; + private String condition; + private List configs; + private KillPhaseOnJobResultCondition killPhaseOnJobResultCondition = KillPhaseOnJobResultCondition.NEVER; + private boolean buildOnlyIfSCMChanges = false; + private boolean applyConditionOnlyIfNoSCMChanges = false; + + public boolean isBuildOnlyIfSCMChanges() { + return this.buildOnlyIfSCMChanges; + } + + public void setBuildOnlyIfSCMChanges(boolean triggerOnlyIfSCMChanges) { + this.buildOnlyIfSCMChanges = triggerOnlyIfSCMChanges; + } + + public boolean isApplyConditionOnlyIfNoSCMChanges() { + return this.applyConditionOnlyIfNoSCMChanges; + } + + public void setApplyConditionOnlyIfNoSCMChanges(boolean applyConditionOnlyIfNoSCMChanges) { + this.applyConditionOnlyIfNoSCMChanges = applyConditionOnlyIfNoSCMChanges; + } + + public void setParsingRulesPath(String parsingRulesPath) { + this.parsingRulesPath = parsingRulesPath; + } + + public String getParsingRulesPath() { + return parsingRulesPath; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public String getCondition() { + return condition; + } + + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + public int getMaxRetries() { + return maxRetries; + } + + public void setEnableRetryStrategy(boolean enableRetryStrategy) { + this.enableRetryStrategy = enableRetryStrategy; + } + + public boolean getEnableRetryStrategy() { + return enableRetryStrategy; + } + + public void setEnableCondition(boolean enableCondition) { + this.enableCondition = enableCondition; + } + + public boolean getEnableCondition() { + return enableCondition; + } + + public void setAbortAllJob(boolean abortAllJob) { + this.abortAllJob = abortAllJob; + } + + public boolean getAbortAllJob() { + return abortAllJob; + } + + public boolean isDisableJob() { + return disableJob; + } + + public void setDisableJob(boolean disableJob) { + this.disableJob = disableJob; + } + + public KillPhaseOnJobResultCondition getKillPhaseOnJobResultCondition() { + return killPhaseOnJobResultCondition; + } + + public void setKillPhaseOnJobResultCondition( + KillPhaseOnJobResultCondition killPhaseOnJobResultCondition) { + this.killPhaseOnJobResultCondition = killPhaseOnJobResultCondition; + } + + public boolean isExposedSCM() { + return exposedSCM; + } + + public void setExposedSCM(boolean exposedSCM) { + this.exposedSCM = exposedSCM; + } + + public boolean isCurrParams() { + return currParams; + } + + public void setCurrParams(boolean currParams) { + this.currParams = currParams; + } + + public boolean isAggregatedTestResults() { + return aggregatedTestResults; + } + + public void setAggregatedTestResults(boolean aggregatedTestResults) { + this.aggregatedTestResults = aggregatedTestResults; + } + + public String getJobProperties() { + return jobProperties; + } + + public void setJobProperties(String jobProperties) { + this.jobProperties = jobProperties; + } + + public String getJobName() { return jobName; } + + public void setJobName(String jobName) { + this.jobName = jobName; + } + + public String getJobAlias() { + if (jobAlias == null) { + return ""; + } + return jobAlias; + } + + public void setJobAlias(String jobAlias) { this.jobAlias = jobAlias; } + + public Descriptor getDescriptor() { + return Hudson.getInstance().getDescriptorOrDie(getClass()); + } + + public String getDisplayName() { + return getClass().getSimpleName(); + } + + public PhaseJobsConfig(String jobName, String jobAlias,String jobProperties, + boolean currParams, List configs, + KillPhaseOnJobResultCondition killPhaseOnJobResultCondition, + boolean disableJob, boolean enableRetryStrategy, + String parsingRulesPath, int maxRetries, boolean enableCondition, + boolean abortAllJob, String condition, boolean buildOnlyIfSCMChanges, + boolean applyConditionOnlyIfNoSCMChanges) { + this(jobName, jobAlias, jobProperties, currParams, configs, killPhaseOnJobResultCondition, + disableJob, enableRetryStrategy, parsingRulesPath, maxRetries, enableCondition, + abortAllJob, condition, buildOnlyIfSCMChanges, applyConditionOnlyIfNoSCMChanges, false); + } + + @DataBoundConstructor + public PhaseJobsConfig(String jobName, String jobAlias, String jobProperties, + boolean currParams, List configs, + KillPhaseOnJobResultCondition killPhaseOnJobResultCondition, + boolean disableJob, boolean enableRetryStrategy, + String parsingRulesPath, int maxRetries, boolean enableCondition, + boolean abortAllJob, String condition, boolean buildOnlyIfSCMChanges, + boolean applyConditionOnlyIfNoSCMChanges, boolean aggregatedTestResults) { + this.jobName = jobName; + this.jobAlias = jobAlias; + this.jobProperties = jobProperties; + this.currParams = currParams; + this.killPhaseOnJobResultCondition = killPhaseOnJobResultCondition; + this.disableJob = disableJob; + this.configs = Util.fixNull(configs); + this.enableRetryStrategy = enableRetryStrategy; + this.maxRetries = maxRetries; + if (this.maxRetries < 0) { + this.maxRetries = 0; + } + this.parsingRulesPath = Util.fixNull(parsingRulesPath); + this.enableCondition = enableCondition; + this.abortAllJob = abortAllJob; + this.condition = Util.fixNull(condition); + this.buildOnlyIfSCMChanges = buildOnlyIfSCMChanges; + this.applyConditionOnlyIfNoSCMChanges = applyConditionOnlyIfNoSCMChanges; + this.aggregatedTestResults = aggregatedTestResults; + } + + public List getConfigs() { + return configs; + } + + @Extension(optional = true) + public static class DescriptorImpl extends Descriptor { + private ParserRuleFile[] parsingRulesGlobal = new ParserRuleFile[0]; + + public DescriptorImpl() { + load(); + } + + @Override + public String getDisplayName() { + return "Phase Jobs Config"; + } + + public List> getBuilderConfigDescriptors() { + return Hudson + .getInstance() + .> getDescriptorList( + AbstractBuildParameters.class); + } + + public AutoCompletionCandidates doAutoCompleteJobName( + @QueryParameter String value) { + AutoCompletionCandidates c = new AutoCompletionCandidates(); + for (String localJobName : Hudson.getInstance().getJobNames()) { + if (localJobName.toLowerCase().startsWith(value.toLowerCase())) + c.add(localJobName); + } + return c; + } + + private void savePhaseJobConfigParameters(String localJobName) { + AbstractProject project = ((AbstractProject) Jenkins.getInstance() + .getItemByFullName(localJobName)); + List parameterDefinitions = getParameterDefinition(project); + StringBuilder sb = new StringBuilder(); + // ArrayList scmLocation = null; + for (ParameterDefinition pdef : parameterDefinitions) { + String paramValue = null; + if (pdef instanceof StringParameterDefinition) { + StringParameterDefinition stringParameterDefinition = (StringParameterDefinition) pdef; + paramValue = (String) stringParameterDefinition + .getDefaultParameterValue().getValue(); + } else if (pdef instanceof BooleanParameterDefinition) { + BooleanParameterDefinition booleanParameterDefinition = (BooleanParameterDefinition) pdef; + paramValue = String.valueOf(booleanParameterDefinition + .getDefaultParameterValue().getValue()); + } + sb.append(pdef.getName()).append("=").append(paramValue) + .append("\n"); + } + + AbstractProject item = getCurrentJob(); + if (item instanceof MultiJobProject) { + MultiJobProject parentProject = (MultiJobProject) item; + List builders = parentProject.getBuilders(); + if (builders != null) { + for (Builder builder : builders) { + if (builder instanceof MultiJobBuilder) { + MultiJobBuilder multiJobBuilder = (MultiJobBuilder) builder; + List phaseJobs = multiJobBuilder + .getPhaseJobs(); + for (PhaseJobsConfig phaseJob : phaseJobs) { + if (phaseJob.getJobName().equals(localJobName)) { + phaseJob.setJobProperties(sb.toString()); + save(); + } + } + } + } + + } + } + } + + private static String getCurrentJobName() { + String path = Descriptor.getCurrentDescriptorByNameUrl(); + String[] parts = path.split("/"); + StringBuilder builder = new StringBuilder(); + for (int i = 2; i < parts.length; i += 2) { + if (i > 2) + builder.append('/'); + builder.append(parts[i]); + } + return builder.toString(); + } + + private static AbstractProject getCurrentJob() { + String jobName = getCurrentJobName(); + return (AbstractProject) Jenkins.getInstance().getItemByFullName( + jobName); + } + + public List getParameterDefinition( + AbstractProject project) { + List list = new ArrayList(); + Map map = project + .getProperties(); + for (Map.Entry entry : map + .entrySet()) { + JobProperty property = entry.getValue(); + if (property instanceof ParametersDefinitionProperty) { + ParametersDefinitionProperty pdp = (ParametersDefinitionProperty) property; + for (ParameterDefinition parameterDefinition : pdp + .getParameterDefinitions()) { + if (parameterDefinition instanceof StringParameterDefinition + || parameterDefinition instanceof BooleanParameterDefinition + || parameterDefinition instanceof ChoiceParameterDefinition) { + list.add(parameterDefinition); + } + } + } + } + return list; + } + + public String doFillJobProperties(@QueryParameter String jobName) { + + return "fill=in"; + } + + public ParserRuleFile[] getParsingRulesGlobal() { + return parsingRulesGlobal; + } + + @Override + public boolean configure(final StaplerRequest req, final JSONObject json) + throws FormException { + parsingRulesGlobal = req.bindParametersToList(ParserRuleFile.class, + "jenkins-multijob-plugin.").toArray(new ParserRuleFile[0]); + save(); + return true; + } + + } + + public List getJobParameters(AbstractBuild build, + TaskListener listener) { + ParametersAction action = build.getAction(ParametersAction.class); + List values = new ArrayList(action + .getParameters().size()); + if (action != null) { + for (ParameterValue value : action.getParameters()) + // FileParameterValue is currently not reusable, so omit these: + if (!(value instanceof FileParameterValue)) + values.add(value); + } + + return values; + + } + + private static MultiJobParametersAction mergeParameters(MultiJobParametersAction base, + MultiJobParametersAction overlay) { + LinkedHashMap params = new LinkedHashMap(); + for (ParameterValue param : base.getParameters()) + if (param != null) + params.put(param.getName(), param); + for (ParameterValue param : overlay.getParameters()) + params.put(param.getName(), param); + return new MultiJobParametersAction(params.values().toArray(new ParameterValue[params.size()])); + } + + /** + * Create a list of actions to pass to the triggered build of project. + * + * This will create a single ParametersAction which will use the defaults + * from the project being triggered and override these, With the current + * parameters defined in this build. if configured. With any matching items + * defined in the different configs, e.g. predefined parameters. + * + * @param build + * build that is triggering project + * @param listener + * Project's listener + * @param project + * Project that is being triggered + * @param isCurrentInclude + * Include parameters from the current build. + * @return + * retuen List + * @throws IOException + * throws IOException + * @throws InterruptedException + * throws InterruptedException + */ + public List getActions(AbstractBuild build, TaskListener listener, + Job project, boolean isCurrentInclude) + throws IOException, InterruptedException { + List actions = new ArrayList(); + MultiJobParametersAction params = null; + LinkedList paramsValuesList = new LinkedList(); + + Map originalActions = project.getProperties(); + + // Check to see if the triggered project has Parameters defined. + ParametersDefinitionProperty parameters = null; + for (Object object : originalActions.values()) { + if (object instanceof hudson.model.ParametersDefinitionProperty) + parameters = (ParametersDefinitionProperty) object; + } + // Get and add ParametersAction for default parameters values + // if triggered project is Parameterized. + // Values will get overridden later as required + if (parameters != null) { + for (ParameterDefinition parameterdef : parameters + .getParameterDefinitions()) { + if (parameterdef.getDefaultParameterValue() != null) + paramsValuesList.add(parameterdef + .getDefaultParameterValue()); + } + params = new MultiJobParametersAction( + paramsValuesList + .toArray(new ParameterValue[paramsValuesList.size()])); + + } + + // Merge current parameters with the defaults from the triggered job. + // Current parameters override the defaluts. + if (isCurrentInclude) { + ParametersAction defaultParameters = build.getAction(ParametersAction.class); + if (defaultParameters != null) { + MultiJobParametersAction mjpa = new MultiJobParametersAction(defaultParameters.getParameters()); + if (params != null) { + params = mergeParameters(params, mjpa); + } else { + params = mjpa; + } + } + } + // Backward compatibility + // get actions from configs merge ParametersActions if needed. + if (configs != null) { + for (AbstractBuildParameters config : configs) { + Action a; + try { + a = config.getAction(build, listener); + if (a instanceof ParametersAction) { + MultiJobParametersAction mjpa = new MultiJobParametersAction(((ParametersAction) a).getParameters()); + if (params == null) { + params = mjpa; + } else { + params = mergeParameters(params, mjpa); + } + } else if (a != null) { + actions.add(a); + } + } catch (DontTriggerException e) { + // don't trigger on this configuration + listener.getLogger().println( + "[multiJob] DontTriggerException: " + e); + } + } + } + + if (params != null) + actions.add(params); + + return actions; + } + + public boolean hasProperties() { + return this.jobProperties != null && !this.jobProperties.isEmpty(); + } + + // compatibility with earlier plugins + public Object readResolve() { + if (hasProperties()) { + AbstractBuildParameters buildParameters = new PredefinedBuildParameters( + jobProperties); + if (configs == null) + configs = new ArrayList(); + configs.add(buildParameters); + } + + List oldParams = new ArrayList(); + if (configs != null && configs.size() > 0) { + Iterator parametersIterator = configs.iterator(); + while (parametersIterator.hasNext()) { + Object param = parametersIterator.next(); + if (param instanceof com.tikal.jenkins.plugins.multijob.PredefinedBuildParameters) { + com.tikal.jenkins.plugins.multijob.PredefinedBuildParameters previosStringParam = (com.tikal.jenkins.plugins.multijob.PredefinedBuildParameters) param; + parametersIterator.remove(); + oldParams.add(new PredefinedBuildParameters( + previosStringParam.getJobProperties())); + } else if (param instanceof com.tikal.jenkins.plugins.multijob.FileBuildParameters) { + com.tikal.jenkins.plugins.multijob.FileBuildParameters previosFileParam = (com.tikal.jenkins.plugins.multijob.FileBuildParameters) param; + parametersIterator.remove(); + oldParams.add(new FileBuildParameters(previosFileParam + .getPropertiesFile())); + } + } + configs.addAll(oldParams); + } + return this; + } + + public static enum KillPhaseOnJobResultCondition { + FAILURE("Failure (stop the phase execution if the job is failed)") { + @Override + public boolean isKillPhase(Result result) { + return result.isWorseOrEqualTo(Result.FAILURE); + } + }, + NEVER("Never (ignore the job result and continue the phase execution)") { + @Override + public boolean isKillPhase(Result result) { + return result.equals(Result.ABORTED) ? true : false; + } + }, + UNSTABLE("Unstable (stop the phase execution if the job is unstable)") { + @Override + public boolean isKillPhase(Result result) { + return result.isWorseOrEqualTo(Result.UNSTABLE); + } + }; + + abstract public boolean isKillPhase(Result result); + + private KillPhaseOnJobResultCondition(String label) { + this.label = label; + } + + final private String label; + + public String getLabel() { + return label; + } + } } \ No newline at end of file diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/AbstractWrapper.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/AbstractWrapper.java index 0c5e0363..6a738902 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/AbstractWrapper.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/AbstractWrapper.java @@ -1,112 +1,112 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import java.io.File; -import java.io.IOException; -import java.util.List; - -import hudson.model.*; -import org.acegisecurity.AccessDeniedException; - -import hudson.search.Search; -import hudson.search.SearchIndex; -import hudson.security.ACL; -import hudson.security.Permission; - -import javax.annotation.Nonnull; - -abstract public class AbstractWrapper implements TopLevelItem { - - protected final int nestLevel; - protected final Job project; - - public AbstractWrapper(Job project, int nestLevel) { - this.project = project; - this.nestLevel = nestLevel; - } - - @Nonnull - @Override - public ACL getACL() { - return project.getACL(); - } - - public int getNestLevel() { - return nestLevel; - } - - public void onLoad(ItemGroup parent, String name) throws IOException { - } - - public void onCopiedFrom(Item src) { - } - - public void onCreatedFromScratch() { - } - - public void save() throws IOException { - } - - public void delete() throws IOException, InterruptedException { - } - - public void checkPermission(Permission permission) throws AccessDeniedException { - } - - public String getUrl() { - return null; - } - - public String getShortUrl() { - return null; - } - - @Deprecated - public String getAbsoluteUrl() { - return project.getAbsoluteUrl(); - } - - public File getRootDir() { - return null; - } - - public Search getSearch() { - return null; - } - - public String getSearchName() { - return null; - } - - public String getSearchUrl() { - return null; - } - - public SearchIndex getSearchIndex() { - return null; - } - - public boolean hasPermission(Permission permission) { - return true; - } - - public Hudson getParent() { - return Hudson.getInstance(); - } - - public TopLevelItemDescriptor getDescriptor() { - return null; - } - - public HealthReport getBuildHealth() { - return null; - } - - public List getBuildHealthReports() { - return null; - } - - public boolean isBuildable() { - return false; - } - -} +package com.tikal.jenkins.plugins.multijob.views; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import hudson.model.*; +import org.acegisecurity.AccessDeniedException; + +import hudson.search.Search; +import hudson.search.SearchIndex; +import hudson.security.ACL; +import hudson.security.Permission; + +import javax.annotation.Nonnull; + +abstract public class AbstractWrapper implements TopLevelItem { + + protected final int nestLevel; + protected final Job project; + + public AbstractWrapper(Job project, int nestLevel) { + this.project = project; + this.nestLevel = nestLevel; + } + + @Nonnull + @Override + public ACL getACL() { + return project.getACL(); + } + + public int getNestLevel() { + return nestLevel; + } + + public void onLoad(ItemGroup parent, String name) throws IOException { + } + + public void onCopiedFrom(Item src) { + } + + public void onCreatedFromScratch() { + } + + public void save() throws IOException { + } + + public void delete() throws IOException, InterruptedException { + } + + public void checkPermission(Permission permission) throws AccessDeniedException { + } + + public String getUrl() { + return null; + } + + public String getShortUrl() { + return null; + } + + @Deprecated + public String getAbsoluteUrl() { + return project.getAbsoluteUrl(); + } + + public File getRootDir() { + return null; + } + + public Search getSearch() { + return null; + } + + public String getSearchName() { + return null; + } + + public String getSearchUrl() { + return null; + } + + public SearchIndex getSearchIndex() { + return null; + } + + public boolean hasPermission(Permission permission) { + return true; + } + + public Hudson getParent() { + return Hudson.getInstance(); + } + + public TopLevelItemDescriptor getDescriptor() { + return null; + } + + public HealthReport getBuildHealth() { + return null; + } + + public List getBuildHealthReports() { + return null; + } + + public boolean isBuildable() { + return false; + } + +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/BuildState.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/BuildState.java index 7c30560d..b7536e63 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/BuildState.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/BuildState.java @@ -1,50 +1,50 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.model.AbstractProject; - -public class BuildState { - - final String jobName; - - final String jobAlias; - - final int previousBuildNumber; - - final int lastBuildNumber; - - final int lastSuccessBuildNumber; - - final int lastFailureBuildNumber; - - public BuildState(String jobName, String jobAlias, int previousBuildNumber, - int lastBuildNumber, int lastSuccessBuildNumber, int lastFailureBuildNumber) { - this.jobName = jobName; - this.jobAlias = jobAlias; - this.previousBuildNumber = previousBuildNumber; - this.lastBuildNumber = lastBuildNumber; - this.lastSuccessBuildNumber = lastSuccessBuildNumber; - this.lastFailureBuildNumber = lastFailureBuildNumber; - } - - public String getJobName() { - return jobName; - } - - public String getJobAlias() { return jobAlias; } - - public int getPreviousBuildNumber() { - return previousBuildNumber; - } - - public int getLastBuildNumber() { - return lastBuildNumber; - } - - public int getLastSuccessBuildNumber() { - return lastSuccessBuildNumber; - } - - public int getLastFailureBuildNumber() { - return lastFailureBuildNumber; - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.model.AbstractProject; + +public class BuildState { + + final String jobName; + + final String jobAlias; + + final int previousBuildNumber; + + final int lastBuildNumber; + + final int lastSuccessBuildNumber; + + final int lastFailureBuildNumber; + + public BuildState(String jobName, String jobAlias, int previousBuildNumber, + int lastBuildNumber, int lastSuccessBuildNumber, int lastFailureBuildNumber) { + this.jobName = jobName; + this.jobAlias = jobAlias; + this.previousBuildNumber = previousBuildNumber; + this.lastBuildNumber = lastBuildNumber; + this.lastSuccessBuildNumber = lastSuccessBuildNumber; + this.lastFailureBuildNumber = lastFailureBuildNumber; + } + + public String getJobName() { + return jobName; + } + + public String getJobAlias() { return jobAlias; } + + public int getPreviousBuildNumber() { + return previousBuildNumber; + } + + public int getLastBuildNumber() { + return lastBuildNumber; + } + + public int getLastSuccessBuildNumber() { + return lastSuccessBuildNumber; + } + + public int getLastFailureBuildNumber() { + return lastFailureBuildNumber; + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn.java index 857449c1..dee96d99 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class ConsoleColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public ConsoleColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return "MultiJob - Console"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class ConsoleColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public ConsoleColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return "MultiJob - Console"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/JobColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/JobColumn.java index daa52f1b..29c853d8 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/JobColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/JobColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class JobColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public JobColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return " MultiJob - Job"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class JobColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public JobColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return " MultiJob - Job"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn.java index 708f2f6f..8360d4c1 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class LastDurationColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public LastDurationColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return "MultiJob - Last Duration"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class LastDurationColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public LastDurationColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return "MultiJob - Last Duration"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn.java index a1bf2576..ec3f9da4 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class LastFailureColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public LastFailureColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return "MultiJob - Last Failure"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class LastFailureColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public LastFailureColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return "MultiJob - Last Failure"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn.java index 0ebd0a44..558e247b 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class LastSuccessColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public LastSuccessColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return "MultiJob - Last Success"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class LastSuccessColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public LastSuccessColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return "MultiJob - Last Success"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/MultiJobListViewColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/MultiJobListViewColumn.java index d146eec3..835ace81 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/MultiJobListViewColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/MultiJobListViewColumn.java @@ -1,41 +1,41 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.DescriptorExtensionList; -import hudson.model.Descriptor; -import hudson.model.Descriptor.FormException; -import hudson.views.BuildButtonColumn; -import hudson.views.ListViewColumn; -import org.jenkins.plugins.builton.BuiltOnColumn; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -abstract public class MultiJobListViewColumn extends ListViewColumn { - public static List createDefaultInitialColumnList() { - // OK, set up default list of columns: - // create all instances - ArrayList r = new ArrayList(); - DescriptorExtensionList> all = ListViewColumn.all(); - - for (Class d : DEFAULT_COLUMNS) { - Descriptor des = all.find(d); - if (des != null) { - try { - r.add(des.newInstance(null, null)); - } catch (FormException e) { - LOGGER.log(Level.WARNING, "Failed to instantiate " + des.clazz, e); - } - } - } - return r; - } - - @SuppressWarnings("unchecked") - private static final List> DEFAULT_COLUMNS = Arrays.asList(StatusColumn.class, WeatherColumn.class, JobColumn.class, - LastSuccessColumn.class, LastFailureColumn.class, LastDurationColumn.class, ConsoleColumn.class, BuildButtonColumn.class, BuiltOnColumn.class); - - private static final Logger LOGGER = Logger.getLogger(MultiJobListViewColumn.class.getName()); -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.DescriptorExtensionList; +import hudson.model.Descriptor; +import hudson.model.Descriptor.FormException; +import hudson.views.BuildButtonColumn; +import hudson.views.ListViewColumn; +import org.jenkins.plugins.builton.BuiltOnColumn; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +abstract public class MultiJobListViewColumn extends ListViewColumn { + public static List createDefaultInitialColumnList() { + // OK, set up default list of columns: + // create all instances + ArrayList r = new ArrayList(); + DescriptorExtensionList> all = ListViewColumn.all(); + + for (Class d : DEFAULT_COLUMNS) { + Descriptor des = all.find(d); + if (des != null) { + try { + r.add(des.newInstance(null, null)); + } catch (FormException e) { + LOGGER.log(Level.WARNING, "Failed to instantiate " + des.clazz, e); + } + } + } + return r; + } + + @SuppressWarnings("unchecked") + private static final List> DEFAULT_COLUMNS = Arrays.asList(StatusColumn.class, WeatherColumn.class, JobColumn.class, + LastSuccessColumn.class, LastFailureColumn.class, LastDurationColumn.class, ConsoleColumn.class, BuildButtonColumn.class, BuiltOnColumn.class); + + private static final Logger LOGGER = Logger.getLogger(MultiJobListViewColumn.class.getName()); +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/PhaseWrapper.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/PhaseWrapper.java index 49c1d3f4..6c058e21 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/PhaseWrapper.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/PhaseWrapper.java @@ -1,115 +1,115 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.model.BallColor; -import hudson.model.Item; -import hudson.model.ItemGroup; -import hudson.model.Result; -import hudson.model.Run; -import hudson.model.Job; -import jenkins.model.Jenkins; -import org.apache.commons.lang.ObjectUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -@SuppressWarnings("rawtypes") -public class PhaseWrapper extends AbstractWrapper { - - final String phaseName; - - final boolean isConditional; - - public PhaseWrapper(Job project, int nestLevel, String phaseName, boolean isConditional) { - super(project, nestLevel); - this.phaseName = phaseName; - this.isConditional = isConditional; - } - - @SuppressWarnings("unchecked") - public Collection getAllJobs() { - return Collections.EMPTY_LIST; - } - - public String getName() { - return phaseName; - } - - public String getFullName() { - return phaseName; - } - - public String getDisplayName() { - return phaseName; - } - - public String getFullDisplayName() { - return phaseName; - } - - public boolean isConditional() { - return isConditional; - } - - public BallColor getIconColor() { - Run worseBuild = null; - for (BuildState buildState : childrenBuildState) { - Job project = (Job) Jenkins.getInstance() - .getItemByFullName(buildState.getJobName()); - if (project == null) - continue; - - Run build = (Run) project - .getBuildByNumber(buildState.getLastBuildNumber()); - if (build == null) - continue; - - if (worseBuild == null) { - worseBuild = build; - } else { - if (build.getResult().isWorseThan(worseBuild.getResult())) { - worseBuild = build; - } - } - } - if (worseBuild != null) { - return worseBuild.getIconColor(); - } - - return BallColor.NOTBUILT; - } - - public String getCss() { - StringBuilder builder = new StringBuilder(); - builder.append("padding-left:"); - builder.append(String.valueOf((nestLevel + 1) * 20)); - builder.append("px;"); - builder.append("font-style:italic;font-size:smaller;font-weight:bold;"); - return builder.toString(); - } - - public String getPhaseName() { - return phaseName; - } - - public boolean isPhase() { - return true; - } - - List childrenBuildState = new ArrayList(); - - public void addChildBuildState(BuildState jobBuildState) { - childrenBuildState.add(jobBuildState); - } - - public String getRelativeNameFrom(ItemGroup g) { - // TODO Auto-generated method stub - return null; - } - - public String getRelativeNameFrom(Item item) { - // TODO Auto-generated method stub - return null; - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.model.BallColor; +import hudson.model.Item; +import hudson.model.ItemGroup; +import hudson.model.Result; +import hudson.model.Run; +import hudson.model.Job; +import jenkins.model.Jenkins; +import org.apache.commons.lang.ObjectUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("rawtypes") +public class PhaseWrapper extends AbstractWrapper { + + final String phaseName; + + final boolean isConditional; + + public PhaseWrapper(Job project, int nestLevel, String phaseName, boolean isConditional) { + super(project, nestLevel); + this.phaseName = phaseName; + this.isConditional = isConditional; + } + + @SuppressWarnings("unchecked") + public Collection getAllJobs() { + return Collections.EMPTY_LIST; + } + + public String getName() { + return phaseName; + } + + public String getFullName() { + return phaseName; + } + + public String getDisplayName() { + return phaseName; + } + + public String getFullDisplayName() { + return phaseName; + } + + public boolean isConditional() { + return isConditional; + } + + public BallColor getIconColor() { + Run worseBuild = null; + for (BuildState buildState : childrenBuildState) { + Job project = (Job) Jenkins.getInstance() + .getItemByFullName(buildState.getJobName()); + if (project == null) + continue; + + Run build = (Run) project + .getBuildByNumber(buildState.getLastBuildNumber()); + if (build == null) + continue; + + if (worseBuild == null) { + worseBuild = build; + } else { + if (build.getResult().isWorseThan(worseBuild.getResult())) { + worseBuild = build; + } + } + } + if (worseBuild != null) { + return worseBuild.getIconColor(); + } + + return BallColor.NOTBUILT; + } + + public String getCss() { + StringBuilder builder = new StringBuilder(); + builder.append("padding-left:"); + builder.append(String.valueOf((nestLevel + 1) * 20)); + builder.append("px;"); + builder.append("font-style:italic;font-size:smaller;font-weight:bold;"); + return builder.toString(); + } + + public String getPhaseName() { + return phaseName; + } + + public boolean isPhase() { + return true; + } + + List childrenBuildState = new ArrayList(); + + public void addChildBuildState(BuildState jobBuildState) { + childrenBuildState.add(jobBuildState); + } + + public String getRelativeNameFrom(ItemGroup g) { + // TODO Auto-generated method stub + return null; + } + + public String getRelativeNameFrom(Item item) { + // TODO Auto-generated method stub + return null; + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/StatusColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/StatusColumn.java index d81357e6..0e5e30e5 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/StatusColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/StatusColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class StatusColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public StatusColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return "MultiJob - Status"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class StatusColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public StatusColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return "MultiJob - Status"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/java/com/tikal/jenkins/plugins/multijob/views/WeatherColumn.java b/src/main/java/com/tikal/jenkins/plugins/multijob/views/WeatherColumn.java index d3c0179a..21c3af7b 100644 --- a/src/main/java/com/tikal/jenkins/plugins/multijob/views/WeatherColumn.java +++ b/src/main/java/com/tikal/jenkins/plugins/multijob/views/WeatherColumn.java @@ -1,23 +1,23 @@ -package com.tikal.jenkins.plugins.multijob.views; - -import hudson.Extension; -import hudson.views.ListViewColumnDescriptor; - -import org.kohsuke.stapler.DataBoundConstructor; - -public class WeatherColumn extends MultiJobListViewColumn { - @DataBoundConstructor - public WeatherColumn() { - } - - @Extension - public static class DescriptorImpl extends ListViewColumnDescriptor { - @Override - public String getDisplayName() { - return "MultiJob - Weather"; - } - public boolean shownByDefault() { - return false; - } - } -} +package com.tikal.jenkins.plugins.multijob.views; + +import hudson.Extension; +import hudson.views.ListViewColumnDescriptor; + +import org.kohsuke.stapler.DataBoundConstructor; + +public class WeatherColumn extends MultiJobListViewColumn { + @DataBoundConstructor + public WeatherColumn() { + } + + @Extension + public static class DescriptorImpl extends ListViewColumnDescriptor { + @Override + public String getDisplayName() { + return "MultiJob - Weather"; + } + public boolean shownByDefault() { + return false; + } + } +} diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/main.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/main.jelly index e878efea..35c7b9bf 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/main.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/main.jelly @@ -1,53 +1,53 @@ - - - - - -
- - - - - - - - - - - - - - - - -
SRJobBuild #DurationConsole
-
- - -
+ + + + + +
+ + + + + + + + + + + + + + + + +
SRJobBuild #DurationConsole
+
+ + +
diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/table.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/table.jelly index 135def87..9c655e4b 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/table.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuild/table.jelly @@ -1,88 +1,88 @@ - - - - - - - - - - - - ${builder.phaseName} - - - - - - - - - - - - - - - Failure, retry this build. - - - - - - - - - ${builder.jobName} (${builder.getJobAlias()}) - - - - ${builder.jobName} - - - - - - - - - build #${builder.buildNumber} - - - - - - - - ( ${builder.duration} ) - - - - - - Console Output - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + ${builder.phaseName} + + + + + + + + + + + + + + + Failure, retry this build. + + + + + + + + + ${builder.jobName} (${builder.getJobAlias()}) + + + + ${builder.jobName} + + + + + + + + + build #${builder.buildNumber} + + + + + + + + ( ${builder.duration} ) + + + + + + Console Output + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuilder/config.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuilder/config.jelly index 86ce5928..db572f6b 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuilder/config.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobBuilder/config.jelly @@ -1,95 +1,95 @@ - - - - - - - - - - - - - - - - - - - ${it.getLabel()} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
-
-
- - - - ${it.getLabel()} - - - - - - ${it.getLabel()} - - -
+ + + + + + + + + + + + + + + + + + + ${it.getLabel()} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ + + + ${it.getLabel()} + + + + + + ${it.getLabel()} + + +
diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/main.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/main.jelly index 0ba73a8c..69a88f65 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/main.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/main.jelly @@ -1,57 +1,57 @@ - - - - - - - - -
- - - - - - - - - - - - ${act.displayName} - - - - ${%Workspace} - - - - - - ${%Recent Changes} - - - - - - ${%Latest Test Result} - - - - -
- - - - - - - + + + + + + + + +
+ + + + + + + + + + + + ${act.displayName} + + + + ${%Workspace} + + + + + + ${%Recent Changes} + + + + + + ${%Latest Test Result} + + + + +
+ + + + + + +
\ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.jelly index 97748b3e..583f693e 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.jelly @@ -1,28 +1,28 @@ - - - -
- ${%body} + + + +
+ ${%body}
\ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.properties b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.properties index e5eb5fae..5e59747a 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.properties +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/MultiJobProject/newJobDetail.properties @@ -1,24 +1,24 @@ -# The MIT License -# -# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:cactusman -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -body=\ - MultiJob Project, suitable for running other jobs +# The MIT License +# +# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, id:cactusman +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +body=\ + MultiJob Project, suitable for running other jobs diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/column.jelly index 2a1d055c..045eabc6 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/column.jelly @@ -1,18 +1,18 @@ - - - - - - - - ${%Console output} - - - - - - - + + + + + + + + ${%Console output} + + + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/columnHeader.jelly index d47723c9..fd3f3139 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/ConsoleColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - Console + + + Console \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/column.jelly index 351bbf88..4800af5e 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/column.jelly @@ -1,15 +1,15 @@ - - - - - - ${job.isConditional() ? "[?]" : ""} ${job.displayName} - - - ${job.displayName} - - - - + + + + + + ${job.isConditional() ? "[?]" : ""} ${job.displayName} + + + ${job.displayName} + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/columnHeader.jelly index af554a8f..89401177 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/JobColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - Job + + + Job \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/column.jelly index fe302e34..5f317fda 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/column.jelly @@ -1,21 +1,21 @@ - - - - - - - ${lBuild.durationString} - - - - - - ${%N/A} - - - - - - + + + + + + + ${lBuild.durationString} + + + + + + ${%N/A} + + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/columnHeader.jelly index 9db22a01..f4a6ba52 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastDurationColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - Last Duration + + + Last Duration \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/column.jelly index 2087ad1a..4ef54fdf 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/column.jelly @@ -1,22 +1,22 @@ - - - - - - - ${lfBuild.timestampString} - - - - - - ${%N/A} - - - - - - - - - + + + + + + + ${lfBuild.timestampString} + + + + + + ${%N/A} + + + + + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/columnHeader.jelly index 66e8d363..e6379cf7 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastFailureColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - Last Failure + + + Last Failure \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/column.jelly index 72df3fba..96b837ec 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/column.jelly @@ -1,21 +1,21 @@ - - - - - - - ${lsBuild.timestampString} - - - - - - ${%N/A} - - - - - - + + + + + + + ${lsBuild.timestampString} + + + + + + ${%N/A} + + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/columnHeader.jelly index 3325b6d8..d9fdcda3 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/LastSuccessColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - Last Success + + + Last Success \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/MultiJobView/configure-entries.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/MultiJobView/configure-entries.jelly index b697965c..b088d2de 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/MultiJobView/configure-entries.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/MultiJobView/configure-entries.jelly @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/column.jelly index 67b296fb..e82fe5af 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/column.jelly @@ -1,8 +1,8 @@ - - - - - - + + + + + + diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/columnHeader.jelly index 0fb372e6..f8e47b09 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/StatusColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - S + + + S \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/column.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/column.jelly index 6f5431ec..73194aaa 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/column.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/column.jelly @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/columnHeader.jelly b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/columnHeader.jelly index 417ecc30..87fbd981 100644 --- a/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/columnHeader.jelly +++ b/src/main/resources/com/tikal/jenkins/plugins/multijob/views/WeatherColumn/columnHeader.jelly @@ -1,4 +1,4 @@ - - - W + + + W \ No newline at end of file diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly index 015b74c8..c8181b36 100644 --- a/src/main/resources/index.jelly +++ b/src/main/resources/index.jelly @@ -1,9 +1,9 @@ - - -
- This plugin is a MultiJob plugin. + + +
+ This plugin is a MultiJob plugin.
\ No newline at end of file diff --git a/src/main/webapp/help-continuationCondition.html b/src/main/webapp/help-continuationCondition.html index 7a964321..17e0f38b 100644 --- a/src/main/webapp/help-continuationCondition.html +++ b/src/main/webapp/help-continuationCondition.html @@ -1,3 +1,3 @@ -
- Define how to decide the status of the whole MultiJob Phase, based on the statuses of phase's sub jobs. If the condition failed the build will fail. +
+ Define how to decide the status of the whole MultiJob Phase, based on the statuses of phase's sub jobs. If the condition failed the build will fail.
\ No newline at end of file diff --git a/src/main/webapp/help-currParams.html b/src/main/webapp/help-currParams.html index e68a22d0..dfe01ce1 100644 --- a/src/main/webapp/help-currParams.html +++ b/src/main/webapp/help-currParams.html @@ -1,3 +1,3 @@ -
- Copy parameters from the current build, except any file parameters will be omitted. -
+
+ Copy parameters from the current build, except any file parameters will be omitted. +
diff --git a/src/main/webapp/help-globalConfig.html b/src/main/webapp/help-globalConfig.html index 2b0e31d0..a86fb740 100644 --- a/src/main/webapp/help-globalConfig.html +++ b/src/main/webapp/help-globalConfig.html @@ -1,8 +1,8 @@ -
-

- This HTML fragment will be injected into the configuration screen - when the user clicks the 'help' icon. See global.jelly for how the - form decides which page to load. - You can have any HTML fragment here. -

-
+
+

+ This HTML fragment will be injected into the configuration screen + when the user clicks the 'help' icon. See global.jelly for how the + form decides which page to load. + You can have any HTML fragment here. +

+
diff --git a/src/main/webapp/help-jobAlias.html b/src/main/webapp/help-jobAlias.html index fd533c72..c70a6d18 100644 --- a/src/main/webapp/help-jobAlias.html +++ b/src/main/webapp/help-jobAlias.html @@ -1,4 +1,4 @@ -
- Define an Alias for this Job, which will be shown in MultiJob Overview. - Helpful when working with the same job multiple times with different configurations. +
+ Define an Alias for this Job, which will be shown in MultiJob Overview. + Helpful when working with the same job multiple times with different configurations.
\ No newline at end of file diff --git a/src/main/webapp/help-jobName.html b/src/main/webapp/help-jobName.html index 708cd668..e901b578 100644 --- a/src/main/webapp/help-jobName.html +++ b/src/main/webapp/help-jobName.html @@ -1,3 +1,3 @@ -
- Define here the name of the Job +
+ Define here the name of the Job
\ No newline at end of file diff --git a/src/main/webapp/help-jobProperties.html b/src/main/webapp/help-jobProperties.html index bca602d3..ed33538d 100644 --- a/src/main/webapp/help-jobProperties.html +++ b/src/main/webapp/help-jobProperties.html @@ -1,3 +1,3 @@ -
- Define here the properties of the Job +
+ Define here the properties of the Job
\ No newline at end of file diff --git a/src/main/webapp/help-phaseName.html b/src/main/webapp/help-phaseName.html index d1fa0186..9b63a402 100644 --- a/src/main/webapp/help-phaseName.html +++ b/src/main/webapp/help-phaseName.html @@ -1,3 +1,3 @@ -
- Define here the name of the MultiJob Phase +
+ Define here the name of the MultiJob Phase
\ No newline at end of file diff --git a/src/test/java/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest.java b/src/test/java/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest.java index aa654b35..a382d6c4 100644 --- a/src/test/java/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest.java +++ b/src/test/java/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest.java @@ -1,46 +1,46 @@ -/* - * The MIT License - * - * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.tikal.jenkins.plugins.multijob.test; - -import hudson.model.AbstractProject; - -import org.jvnet.hudson.test.HudsonTestCase; -import org.jvnet.hudson.test.recipes.LocalData; - -//import com.tikal.jenkins.plugins.multijob.MultiJobBuilder; -//import com.tikal.jenkins.plugins.multijob.MultiJobProject; - -public class CompatibilityTest {//extends MultiJobProjectTestCase { - -/* - @LocalData - public void test() throws Exception { - AbstractProject p = (AbstractProject) hudson.getItem("old"); - MultiJobProject multiJobprojects = (MultiJobProject) p; - assertEquals(2, multiJobprojects.getBuilders().size()); - MultiJobBuilder builder = (MultiJobBuilder)multiJobprojects.getBuilders().get(0); - assertEquals(2, builder.getPhaseJobs().size()); - } -*/ -} +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.tikal.jenkins.plugins.multijob.test; + +import hudson.model.AbstractProject; + +import org.jvnet.hudson.test.HudsonTestCase; +import org.jvnet.hudson.test.recipes.LocalData; + +//import com.tikal.jenkins.plugins.multijob.MultiJobBuilder; +//import com.tikal.jenkins.plugins.multijob.MultiJobProject; + +public class CompatibilityTest {//extends MultiJobProjectTestCase { + +/* + @LocalData + public void test() throws Exception { + AbstractProject p = (AbstractProject) hudson.getItem("old"); + MultiJobProject multiJobprojects = (MultiJobProject) p; + assertEquals(2, multiJobprojects.getBuilders().size()); + MultiJobBuilder builder = (MultiJobBuilder)multiJobprojects.getBuilders().get(0); + assertEquals(2, builder.getPhaseJobs().size()); + } +*/ +} diff --git a/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/config.xml b/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/config.xml index 9dcd81c1..86560578 100644 --- a/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/config.xml +++ b/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/config.xml @@ -1,58 +1,58 @@ - - - 1.0 - 2 - NORMAL - - - - - - - - 0 - - - - All - false - false - - - - Reactor - false - false - - - reactor-1 - test1 - - - - - - - - - - - - - - - - - - - - - - - All - 0 - - - - + + + 1.0 + 2 + NORMAL + + + + + + + + 0 + + + + All + false + false + + + + Reactor + false + false + + + reactor-1 + test1 + + + + + + + + + + + + + + + + + + + + + + + All + 0 + + + + \ No newline at end of file diff --git a/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/jobs/old/config.xml b/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/jobs/old/config.xml index 924b481c..427ad458 100644 --- a/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/jobs/old/config.xml +++ b/src/test/resources/com/tikal/jenkins/plugins/multijob/test/CompatibilityTest/jobs/old/config.xml @@ -1,45 +1,45 @@ - - - - - false - - - true - false - false - false - - false - - - Build - - - test1 - - true - - - Test job 1 - - true - - - SUCCESSFUL - - - Deploy - - - Test job 2 - - true - - - SUCCESSFUL - - - - + + + + + false + + + true + false + false + false + + false + + + Build + + + test1 + + true + + + Test job 1 + + true + + + SUCCESSFUL + + + Deploy + + + Test job 2 + + true + + + SUCCESSFUL + + + + \ No newline at end of file