/** * Determines if the given configuration is currently running. If any of the configurations are * currently stuck in the queue, it is logged. * * @param execution Contains information about the general build, including the listener used to * log queue blockage. * @param configuration The configuration being checked to see if it's running. * @param mutableWhyMap Mutable map used to track the reasons a configuration is stuck in the * queue. This prevents duplicate reasons from flooding the logs. * @return True if the build represented by the given configuration is currently running or stuck * in the queue. False if the build has finished running. */ private boolean isBuilding( MatrixBuild.MatrixBuildExecution execution, MatrixConfiguration configuration, Map<String, String> mutableWhyMap) { MatrixRun build = configuration.getBuildByNumber(execution.getBuild().getNumber()); if (build != null) { return build.isBuilding(); } Queue.Item queueItem = configuration.getQueueItem(); if (queueItem != null) { String why = queueItem.getWhy(); String key = queueItem.task.getFullDisplayName() + " " + queueItem.id; String oldWhy = mutableWhyMap.get(key); if (why == null) { mutableWhyMap.remove(key); } if (why != null && !why.equals(oldWhy)) { mutableWhyMap.put(key, why); BuildListener listener = execution.getListener(); PrintStream logger = listener.getLogger(); logger.print( "Configuration " + ModelHyperlinkNote.encodeTo(configuration) + " is still in the queue: "); queueItem.getCauseOfBlockage().print(listener); // this is still shown on the same line } } return true; }
private void addTestflightLinks( AbstractBuild<?, ?> build, BuildListener listener, Map parsedMap) { TestflightBuildAction installAction = new TestflightBuildAction(); String installUrl = (String) parsedMap.get("install_url"); installAction.displayName = Messages.TestflightRecorder_InstallLinkText(); installAction.iconFileName = "package.gif"; installAction.urlName = installUrl; build.addAction(installAction); listener.getLogger().println(Messages.TestflightRecorder_InfoInstallLink(installUrl)); TestflightBuildAction configureAction = new TestflightBuildAction(); String configUrl = (String) parsedMap.get("config_url"); configureAction.displayName = Messages.TestflightRecorder_ConfigurationLinkText(); configureAction.iconFileName = "gear2.gif"; configureAction.urlName = configUrl; build.addAction(configureAction); listener.getLogger().println(Messages.TestflightRecorder_InfoConfigurationLink(configUrl)); build.addAction(new EnvAction()); // Add info about the selected build into the environment EnvAction envData = build.getAction(EnvAction.class); if (envData != null) { envData.add("TESTFLIGHT_INSTALL_URL", installUrl); envData.add("TESTFLIGHT_CONFIG_URL", configUrl); } }
@Override public boolean perform( AbstractBuild<?, ?> build, Launcher launcher, final BuildListener listener) { if (build.getResult().isWorseOrEqualTo(Result.FAILURE)) return false; listener.getLogger().println(Messages.TestflightRecorder_InfoUploading()); try { EnvVars vars = build.getEnvironment(listener); String workspace = vars.expand("$WORKSPACE"); List<TestflightUploader.UploadRequest> urList = new ArrayList<TestflightUploader.UploadRequest>(); for (TestflightTeam team : createDefaultPlusAdditionalTeams()) { try { TestflightUploader.UploadRequest ur = createPartialUploadRequest(team, vars, build); urList.add(ur); } catch (MisconfiguredJobException mje) { listener.getLogger().println(mje.getConfigurationMessage()); return false; } } for (TestflightUploader.UploadRequest ur : urList) { TestflightRemoteRecorder remoteRecorder = new TestflightRemoteRecorder(workspace, ur, listener); final List<Map> parsedMaps; try { Object result = launcher.getChannel().call(remoteRecorder); parsedMaps = (List<Map>) result; } catch (UploadException ue) { listener .getLogger() .println(Messages.TestflightRecorder_IncorrectResponseCode(ue.getStatusCode())); listener.getLogger().println(ue.getResponseBody()); return false; } if (parsedMaps.size() == 0) { listener.getLogger().println(Messages.TestflightRecorder_NoUploadedFile(ur.filePaths)); return false; } for (Map parsedMap : parsedMaps) { addTestflightLinks(build, listener, parsedMap); } } } catch (Throwable e) { listener.getLogger().println(e); e.printStackTrace(listener.getLogger()); return false; } return true; }
/** * Logic is more-or-less copied from {@link * DefaultMatrixExecutionStrategyImpl#notifyStartBuild(java.util.List)} * * <p>Triggers the startBuild event on all aggregators. This should be called before any run is * started. * * @param aggregators The aggregators to be notified. * @param listener Listener from parent build that can be logged to. * @return True if all aggregators return true. If any aggregator returns false, false is * immediately returned and no new aggregators are called. * @throws IOException * @throws InterruptedException */ private boolean notifyStartBuild(List<MatrixAggregator> aggregators, BuildListener listener) throws IOException, InterruptedException { for (MatrixAggregator aggregator : aggregators) { if (!aggregator.startBuild()) { listener.error("Aggregator terminated build: " + aggregator.toString()); return false; } } return true; }
/** * Schedules the given configuration. * * <p>Copied from the {@link * DefaultMatrixExecutionStrategyImpl#scheduleConfigurationBuild(hudson.matrix.MatrixBuild.MatrixBuildExecution, * hudson.matrix.MatrixConfiguration)} * * @param execution Contains information about the general build, including the listener used to * log queue blockage. * @param configuration The configuration to schedule. * @param upstreamCause The cause of the build. Will either be an {@link * hudson.model.Cause.UpstreamCause} or {@link * com.attask.jenkins.healingmatrixproject.SelfHealingCause}. */ private void scheduleConfigurationBuild( MatrixBuild.MatrixBuildExecution execution, MatrixConfiguration configuration, Cause.UpstreamCause upstreamCause) throws InterruptedException { MatrixBuild build = (MatrixBuild) execution.getBuild(); execution .getListener() .getLogger() .println(Messages.MatrixBuild_Triggering(ModelHyperlinkNote.encodeTo(configuration))); // filter the parent actions for those that can be passed to the individual jobs. List<MatrixChildAction> childActions = Util.filter(build.getActions(), MatrixChildAction.class); BuildListener listener = execution.getListener(); while (!configuration.scheduleBuild(childActions, upstreamCause)) { String msg = "Unable to schedule build " + configuration.getFullDisplayName() + ". Retrying."; listener.error(msg); Thread.sleep(500); } }
/** * Logic is copied from {@link * DefaultMatrixExecutionStrategyImpl#notifyEndBuild(hudson.matrix.MatrixRun, java.util.List)} */ private void notifyEndRun( MatrixRun run, List<MatrixAggregator> aggregators, BuildListener listener) throws InterruptedException, IOException { if (run == null) return; // can happen if the configuration run gets cancelled before it gets started. for (MatrixAggregator aggregator : aggregators) { if (!aggregator.endRun(run)) { listener.error("Aggregator terminated build: " + aggregator.toString()); throw new AbortException(); } } }
/** * 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; }