@Override
  public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
      throws InterruptedException, IOException {

    Result result = build.getResult();
    if (result == null) {
      result = Result.SUCCESS;
    }

    String packIdString = getPackageId(build, listener);
    PackId packId = PackId.parsePid(packIdString);
    if (packId == null) {
      listener.fatalError("Failed to parse Package ID: %s%n", packIdString);
      return false;
    }

    String wspFilterString = getWspFilter(build, listener);
    WspFilter filter = WspFilter.parseSimpleSpec(wspFilterString);

    GraniteClientConfig clientConfig =
        new GraniteClientConfig(
            getBaseUrl(build, listener), credentialsId, requestTimeout, serviceTimeout, waitDelay);

    BuildPackageCallable callable =
        new BuildPackageCallable(clientConfig, listener, packId, filter, download);

    final String fLocalDirectory = getLocalDirectory(build, listener);
    result = result.combine(build.getWorkspace().child(fLocalDirectory).act(callable));

    return result.isBetterOrEqualTo(Result.UNSTABLE);
  }
  /**
   * Displays the combined status of all modules.
   *
   * <p>More precisely, this picks up the status of this build itself, plus all the latest builds of
   * the modules that belongs to this build.
   */
  @Override
  public Result getResult() {
    Result r = super.getResult();

    for (IvyBuild b : getModuleLastBuilds().values()) {
      Result br = b.getResult();
      if (r == null) r = br;
      else if (br == Result.NOT_BUILT) continue; // UGLY: when computing combined status, ignore the
      // modules that were not built
      else if (br != null) r = r.combine(br);
    }

    return r;
  }
 @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(
      final AbstractBuild<?, ?> build, final Launcher launcher, final BuildListener listener)
      throws InterruptedException, IOException {
    final PrintStream console = listener.getLogger();
    if (!isBuildGoodEnoughToRun(build, console)) return true;
    final BPBuildEnv currentBuildEnv =
        new BPBuildEnv(
            getEnvironmentVariables(build, listener), build.getWorkspace(), build.getTimestamp());
    BPBuildEnv targetBuildEnv = null;
    if (PROMOTION_CLASS_NAME.equals(build.getClass().getCanonicalName())) {
      AbstractBuild<?, ?> promoted;
      try {
        final Method getTarget = build.getClass().getMethod("getTarget", (Class<?>[]) null);
        promoted = (AbstractBuild) getTarget.invoke(build, (Object[]) null);
      } catch (Exception e) {
        throw new RuntimeException(Messages.exception_failedToGetPromotedBuild(), e);
      }
      targetBuildEnv =
          new BPBuildEnv(
              getEnvironmentVariables(promoted, listener),
              new FilePath(promoted.getArtifactsDir()),
              promoted.getTimestamp());
    }

    final BPBuildInfo buildInfo =
        new BPBuildInfo(
            listener,
            consolePrefix,
            Hudson.getInstance().getRootPath(),
            currentBuildEnv,
            targetBuildEnv);
    fixup(build, buildInfo);
    final Result result = delegate.perform(buildInfo);

    if (build.getResult() == null) build.setResult(result);
    else build.setResult(result.combine(build.getResult()));
    return result.isBetterOrEqualTo(Result.UNSTABLE);
  }
  /**
   * Waits for the given configurations to finish, retrying any that qualify to be rerun.
   *
   * @param execution Provided by the plugin.
   * @param patterns List of regular expression patterns used to scan the log to determine if a
   *     build should be rerun.
   * @param retries Mutable map that tracks the number of times a specific configuration has been
   *     retried.
   * @param configurations The configurations that have already been scheduled to run that should be
   *     waited for to finish.
   * @return The worst result of all the runs. If a build was rerun, only the result of the rerun is
   *     considered.
   * @throws InterruptedException
   * @throws IOException
   */
  private Result waitForMatrixRuns(
      MatrixBuild.MatrixBuildExecution execution,
      List<Pattern> patterns,
      Map<MatrixConfiguration, Integer> retries,
      LinkedList<MatrixConfiguration> configurations)
      throws InterruptedException, IOException {
    BuildListener listener = execution.getListener();
    PrintStream logger = listener.getLogger();

    Map<String, String> whyBlockedMap =
        new HashMap<
            String,
            String>(); // keep track of why builds are blocked so we can print unique messages when
    // they change.
    Result finalResult = Result.SUCCESS;
    int iteration = 0;
    boolean continueRetrying = true;
    while (!configurations.isEmpty()) {
      ++iteration;
      MatrixConfiguration configuration = configurations.removeFirst();
      if (isBuilding(execution, configuration, whyBlockedMap)) {
        if (iteration >= configurations.size()) {
          // Every time we loop through all the configurations, sleep for a bit.
          // This is to prevent polling too often while everything is still building.
          iteration = 0;
          Thread.sleep(1000);
        }
        configurations.add(configuration);
        continue;
      }

      Run parentBuild = execution.getBuild();
      MatrixRun matrixRun = configuration.getBuildByNumber(parentBuild.getNumber());
      Result runResult = matrixRun.getResult();
      if (continueRetrying
          && runResult.isWorseOrEqualTo(getWorseThanOrEqualTo())
          && runResult.isBetterOrEqualTo(getBetterThanOrEqualTo())) {
        if (matchesPattern(matrixRun, patterns)) {
          int retriedCount = retries.get(configuration);
          if (retriedCount < getMaxRetries()) {
            ++retriedCount;
            retries.put(configuration, retriedCount);
            // rerun
            String logMessage =
                String.format(
                    "%s was %s. Matched pattern to rerun. Rerunning (%d).",
                    matrixRun, runResult, retriedCount);
            listener.error(logMessage);

            HealedAction action = parentBuild.getAction(HealedAction.class);
            if (action == null) {
              //noinspection SynchronizationOnLocalVariableOrMethodParameter
              synchronized (parentBuild.getActions()) {
                action = parentBuild.getAction(HealedAction.class);
                if (action == null) {
                  action = new HealedAction(matrixRun.getCharset());
                  parentBuild.addAction(action);
                }
              }
            }
            action.addAutoHealedJob(matrixRun);

            MatrixConfiguration parent = matrixRun.getParent();
            if (parent != null) {
              // I'm paranoid about NPEs
              parent.removeRun(matrixRun);
              matrixRun.delete();
            } else {
              LOGGER.severe(
                  "couldn't remove old run, parent was null. This is a Jenkins core bug.");
            }
            scheduleConfigurationBuild(
                execution, configuration, new SelfHealingCause(parentBuild, retriedCount));
            configurations.add(configuration);
            continue;
          } else {
            String logMessage =
                String.format(
                    "%s was %s. Matched pattern to rerun, but the max number of retries (%d) has been met.",
                    matrixRun, runResult, getMaxRetries());
            listener.error(logMessage);
            if (getStopRetryingAfterOneFails()) {
              listener.error("Not retrying any more builds.");
              continueRetrying = false;
            }
          }
        } else {
          String logMessage =
              String.format(
                  "%s was %s. It did not match the pattern to rerun. Accepting result.",
                  matrixRun, runResult);
          logger.println(logMessage);
        }
      }
      notifyEndRun(matrixRun, execution.getAggregators(), execution.getListener());
      finalResult = finalResult.combine(runResult);
    }
    return finalResult;
  }