@SuppressWarnings("UnusedDeclaration")
 public ListBoxModel doFillWorseThanOrEqualToItems() {
   ListBoxModel items = new ListBoxModel();
   items.add("Success", Result.SUCCESS.toString());
   items.add("Unstable", Result.UNSTABLE.toString());
   items.add("Failure", Result.FAILURE.toString());
   items.add("Not Built", Result.NOT_BUILT.toString());
   items.add("Aborted", Result.ABORTED.toString());
   return items;
 }
 @Override
 public Result runBuild(DynamicBuild build, BuildExecutionContext buildExecutionContext, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
     Map<String,Object> buildEnvironment = build.getEnvironmentWithChangeSet(listener);
     ShellCommands checkoutCommands = getCheckoutCommands(buildEnvironment);
     Map config = new GroovyYamlTemplateProcessor(getDotCiYml(build), buildEnvironment).getConfig();
     this.buildConfiguration = getBuildConfiguration(build.getParent().getFullName(),config,build.getBuildId(),checkoutCommands,build.getSha(),build.getNumber());
     build.setAxisList(buildConfiguration.getAxisList());
     Result result ;
     if(buildConfiguration.isParallelized()){
         ShellScriptRunner shellScriptRunner = new ShellScriptRunner(buildExecutionContext, listener);
         Result checkoutResult = shellScriptRunner.runScript(checkoutCommands);
         if(Result.FAILURE.equals(checkoutResult)) return checkoutResult;
         result = runMultiConfigbuildRunner(build, buildConfiguration, listener,launcher);
     }else{
         result = runSubBuild(new Combination(ImmutableMap.of("script", buildConfiguration.getOnlyRun())), buildExecutionContext, listener);
     }
     Result pluginResult = runPlugins(build, buildConfiguration.getPlugins(), listener, launcher);
     Result notifierResult = runNotifiers(build, buildConfiguration.getNotifiers(), listener);
     return  result.combine(pluginResult).combine(notifierResult);
 }
  @Override
  public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) {
    // This method deserves a refactor and cleanup.
    boolean success = true;
    Log log = new Log(listener);
    if (Result.FAILURE.equals(build.getResult())) {
      log.info("Not deploying due to job being in FAILED state.");
      return success;
    }

    logStartHeader(log);
    // todo: getting from descriptor is ugly. refactor?
    getDescriptorImpl().setGlobalConfiguration();
    OctopusApi api = getDescriptorImpl().api;

    VariableResolver resolver = build.getBuildVariableResolver();
    EnvVars envVars;
    try {
      envVars = build.getEnvironment(listener);
    } catch (Exception ex) {
      log.fatal(
          String.format(
              "Failed to retrieve environment variables for this build - '%s'", ex.getMessage()));
      return false;
    }
    EnvironmentVariableValueInjector envInjector =
        new EnvironmentVariableValueInjector(resolver, envVars);
    // NOTE: hiding the member variables of the same name with their env-injected equivalents
    String project = envInjector.injectEnvironmentVariableValues(this.project);
    String releaseVersion = envInjector.injectEnvironmentVariableValues(this.releaseVersion);
    String environment = envInjector.injectEnvironmentVariableValues(this.environment);
    String variables = envInjector.injectEnvironmentVariableValues(this.variables);

    com.octopusdeploy.api.Project p = null;
    try {
      p = api.getProjectByName(project);
    } catch (Exception ex) {
      log.fatal(
          String.format(
              "Retrieving project name '%s' failed with message '%s'", project, ex.getMessage()));
      success = false;
    }
    com.octopusdeploy.api.Environment env = null;
    try {
      env = api.getEnvironmentByName(environment);
    } catch (Exception ex) {
      log.fatal(
          String.format(
              "Retrieving environment name '%s' failed with message '%s'",
              environment, ex.getMessage()));
      success = false;
    }
    if (p == null) {
      log.fatal("Project was not found.");
      success = false;
    }
    if (env == null) {
      log.fatal("Environment was not found.");
      success = false;
    }
    if (!success) // Early exit
    {
      return success;
    }
    Set<com.octopusdeploy.api.Release> releases = null;
    try {
      releases = api.getReleasesForProject(p.getId());
    } catch (Exception ex) {
      log.fatal(
          String.format(
              "Retrieving releases for project '%s' failed with message '%s'",
              project, ex.getMessage()));
      success = false;
    }
    if (releases == null) {
      log.fatal("Releases was not found.");
      return false;
    }
    Release releaseToDeploy = null;
    for (Release r : releases) {
      if (releaseVersion.equals(r.getVersion())) {
        releaseToDeploy = r;
        break;
      }
    }
    if (releaseToDeploy == null) // early exit
    {
      log.fatal(
          String.format(
              "Unable to find release version %s for project %s", releaseVersion, project));
      return false;
    }
    Properties properties = new Properties();
    try {
      properties.load(new StringReader(variables));
    } catch (Exception ex) {
      log.fatal(
          String.format(
              "Unable to load entry variables failed with message '%s'", ex.getMessage()));
      success = false;
    }

    // TODO: Can we tell if we need to call? For now I will always try and get variable and use if I
    // find them
    Set<com.octopusdeploy.api.Variable> variablesForDeploy = null;

    try {
      String releaseId = releaseToDeploy.getId();
      String environmentId = env.getId();
      variablesForDeploy =
          api.getVariablesByReleaseAndEnvironment(releaseId, environmentId, properties);
    } catch (Exception ex) {
      log.fatal(
          String.format(
              "Retrieving variables for release '%s' to environment '%s' failed with message '%s'",
              releaseToDeploy.getId(), env.getName(), ex.getMessage()));
      success = false;
    }
    try {
      String results =
          api.executeDeployment(releaseToDeploy.getId(), env.getId(), variablesForDeploy);
      if (isTaskJson(results)) {
        JSON resultJson = JSONSerializer.toJSON(results);
        String urlSuffix = ((JSONObject) resultJson).getJSONObject("Links").getString("Web");
        String url = getDescriptorImpl().octopusHost;
        if (url.endsWith("/")) {
          url = url.substring(0, url.length() - 2);
        }
        log.info("Deployment executed: \n\t" + url + urlSuffix);
        build.addAction(
            new BuildInfoSummary(
                BuildInfoSummary.OctopusDeployEventType.Deployment, url + urlSuffix));
        if (waitForDeployment) {

          log.info("Waiting for deployment to complete.");
          String resultState = waitForDeploymentCompletion(resultJson, api, log);
          if (resultState == null) {
            log.info("Marking build failed due to failure in waiting for deployment to complete.");
            success = false;
          }

          if ("Failed".equals(resultState)) {
            log.info("Marking build failed due to deployment task status.");
            success = false;
          }
        }
      }
    } catch (IOException ex) {
      log.fatal("Failed to deploy: " + ex.getMessage());
      success = false;
    }

    return success;
  }
  @Override
  public void onCompleted(AbstractBuild run, TaskListener listener) {
    Lamps plugin = Lamps.getInstance();
    Set<String> jobs = plugin.getJobs();
    String jobName = run.getParent().getFullName();
    XfEventMessage xfEventMessage = new XfEventMessage();

    if (jobs.contains(jobName)) {
      Result result = run.getResult();
      Set<Lamp> activeLamps = plugin.getLampsContainingJob(jobName);
      for (Lamp lamp : activeLamps) {
        Result lampResult = result;

        xfEventMessage.sendColorMessage(lamp, lampResult, States.Action.SOLID);

        // Create Notification for LCD
        StringBuilder infoMsg = new StringBuilder(64);
        infoMsg.append(jobName).append(' ').append(run.getDisplayName()).append('\n');
        if (Result.FAILURE.equals(result)) {
          ArrayList<String> blame = Lists.newArrayList();
          if (lamp.isBlame()) {
            Set<User> culprits = run.getCulprits();
            for (User user : culprits) {
              blame.add(user.getDisplayName());
            }
          }
          if (blame.isEmpty()) {
            blame.add("Somebody");
          }
          infoMsg.insert(0, Joiner.on(", ").join(blame) + " broke the build: ");
          infoMsg.append(result.toString());
          listener.getLogger().println("[XFD] Updating Lamp display: " + infoMsg.toString());
        } else if (Result.ABORTED.equals(result)) {
          String causeMsg = "BUILD ABORTED";
          infoMsg.append(causeMsg);
          listener.getLogger().println("[XFD] Updating Lamp display: " + infoMsg.toString());
        } else {
          infoMsg.append(result.toString());
        }
        xfEventMessage.sendLCDMessage(lamp, infoMsg.toString());

        if (lamp.isSfx()) {
          try {
            Thread.sleep(1000);
            xfEventMessage.sendSfxMessage(lamp, lampResult);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

        if (States.resultColorMap.get(lampResult).equals(States.Color.RED) && lamp.isNoisy()) {
          try {
            Thread.sleep(1000);
            xfEventMessage.sendBuzzerMessage(lamp);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
        if (lamp.isAggregate()) {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          plugin.updateAggregateStatus(lamp);
        }
      }
    }
  }
  @Extension
  // for some reason when running mvn from commandline the build fails,
  // if BuildTrigger is not fully qualified here!?
  public static class DescriptorImpl extends hudson.tasks.BuildTrigger.DescriptorImpl {

    public static final String[] THRESHOLD_VALUES = {
      Result.SUCCESS.toString(), Result.UNSTABLE.toString(),
      Result.FAILURE.toString(), Result.ABORTED.toString()
    };

    public static final Strategy[] STRATEGY_VALUES = Strategy.values();

    @Override
    public String getDisplayName() {
      return hudson.plugins.downstream_ext.Messages.DownstreamTrigger_DisplayName();
    }

    @Override
    public String getHelpFile() {
      return "/plugin/downstream-ext/help.html";
    }

    @Override
    public Publisher newInstance(StaplerRequest req, JSONObject formData) throws FormException {
      return new DownstreamTrigger(
          formData.getString("childProjects"),
          formData.getString("threshold"),
          formData.has("onlyIfSCMChanges") && formData.getBoolean("onlyIfSCMChanges"),
          formData.getString("strategy"),
          formData.has("triggerOnlyOnceWhenMatrixEnds")
              && formData.getBoolean("triggerOnlyOnceWhenMatrixEnds"));
    }

    public boolean isMatrixProject(AbstractProject project) {
      return project instanceof MatrixProject;
    }

    @Extension
    public static class ItemListenerImpl extends ItemListener {
      @Override
      public void onRenamed(Item item, String oldName, String newName) {
        // update DownstreamTrigger of other projects that point to this object.
        // can't we generalize this?
        for (Project<?, ?> p : Hudson.getInstance().getProjects()) {
          DownstreamTrigger t = p.getPublishersList().get(DownstreamTrigger.class);
          if (t != null) {
            if (t.onJobRenamed(oldName, newName)) {
              try {
                p.save();
              } catch (IOException e) {
                LOGGER.log(
                    Level.WARNING,
                    "Failed to persist project setting during rename from "
                        + oldName
                        + " to "
                        + newName,
                    e);
              }
            }
          }
        }
      }

      @Override
      public void onDeleted(Item item) {
        executors.remove(item);
      }
    }
  }
 @SuppressWarnings({"rawtypes"})
 private BuildState createBuildState(
     BuildState parentBuildState, MultiJobProject multiJobProject, Job project) {
   int previousBuildNumber = 0;
   int lastBuildNumber = 0;
   int lastSuccessBuildNumber = 0;
   int lastFailureBuildNumber = 0;
   MultiJobBuild previousParentBuild =
       multiJobProject.getBuildByNumber(parentBuildState.getPreviousBuildNumber());
   MultiJobBuild lastParentBuild =
       multiJobProject.getBuildByNumber(parentBuildState.getLastBuildNumber());
   MultiJobBuild lastParentSuccessBuild =
       multiJobProject.getBuildByNumber(parentBuildState.getLastSuccessBuildNumber());
   MultiJobBuild lastParentFailureBuild =
       multiJobProject.getBuildByNumber(parentBuildState.getLastFailureBuildNumber());
   if (previousParentBuild != null) {
     List<SubBuild> subBuilds = previousParentBuild.getSubBuilds();
     for (SubBuild subBuild : subBuilds) {
       if (subBuild.getJobName().equals(project.getName())) {
         previousBuildNumber = subBuild.getBuildNumber();
       }
     }
   }
   if (lastParentBuild != null) {
     List<SubBuild> subBuilds = lastParentBuild.getSubBuilds();
     for (SubBuild subBuild : subBuilds) {
       if (subBuild.getJobName().equals(project.getName())) {
         lastBuildNumber = subBuild.getBuildNumber();
       }
     }
   }
   if (lastParentSuccessBuild != null) {
     List<SubBuild> subBuilds = lastParentSuccessBuild.getSubBuilds();
     for (SubBuild subBuild : subBuilds) {
       if (subBuild.getJobName().equals(project.getName())) {
         AbstractBuild build = (AbstractBuild) project.getBuildByNumber(subBuild.getBuildNumber());
         if (build != null && Result.SUCCESS.equals(build.getResult())) {
           lastSuccessBuildNumber = subBuild.getBuildNumber();
           break;
         } else {
           lastParentSuccessBuild =
               multiJobProject.getBuildByNumber(parentBuildState.getPreviousBuildNumber());
         }
       }
     }
   }
   if (lastParentFailureBuild != null) {
     List<SubBuild> subBuilds = lastParentFailureBuild.getSubBuilds();
     for (SubBuild subBuild : subBuilds) {
       if (subBuild.getJobName().equals(project.getName())) {
         AbstractBuild build = (AbstractBuild) project.getBuildByNumber(subBuild.getBuildNumber());
         if (build != null && Result.FAILURE.equals(((AbstractBuild) build).getResult())) {
           lastFailureBuildNumber = subBuild.getBuildNumber();
           break;
         } else {
           lastParentFailureBuild =
               multiJobProject.getBuildByNumber(parentBuildState.getPreviousBuildNumber());
         }
       }
     }
   }
   return new BuildState(
       project.getName(),
       previousBuildNumber,
       lastBuildNumber,
       lastSuccessBuildNumber,
       lastFailureBuildNumber);
 }