-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplify status checks output for "error" and "unstable" steps #125
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,102 +22,109 @@ | |
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Stack; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import java.util.stream.Collectors; | ||
|
||
class FlowExecutionAnalyzer { | ||
private static final Logger LOGGER = Logger.getLogger(FlowExecutionAnalyzer.class.getName()); | ||
private static final String TRUNCATED_MESSAGE = "\n\nOutput truncated."; | ||
|
||
private final Run<?, ?> run; | ||
private final FlowExecution execution; | ||
private final Stack<Integer> indentationStack = new Stack<>(); | ||
private final boolean suppressLogs; | ||
|
||
FlowExecutionAnalyzer(final Run<?, ?> run, final FlowExecution execution, final boolean suppressLogs) { | ||
this.run = run; | ||
this.execution = execution; | ||
this.suppressLogs = suppressLogs; | ||
} | ||
|
||
private static Optional<String> getStageOrBranchName(final FlowNode node) { | ||
return getParallelName(node) | ||
.map(Optional::of) | ||
.orElse(getStageName(node)); | ||
} | ||
|
||
private static Optional<String> getStageName(final FlowNode node) { | ||
return Optional.ofNullable(node) | ||
.filter(n -> n.getAction(ThreadNameAction.class) == null) | ||
.map(n -> n.getAction(LabelAction.class)) | ||
.map(LabelAction::getDisplayName); | ||
} | ||
|
||
private static Optional<String> getParallelName(final FlowNode node) { | ||
return Optional.ofNullable(node) | ||
.filter(n -> n.getAction(LabelAction.class) != null) | ||
.map(n -> n.getAction(ThreadNameAction.class)) | ||
.map(ThreadNameAction::getThreadName); | ||
} | ||
|
||
private Pair<String, String> processStageOrBranchRow(final FlowGraphTable.Row row, | ||
final String stageOrBranchName) { | ||
final StringBuilder nodeTextBuilder = new StringBuilder(); | ||
while (!indentationStack.isEmpty() && row.getTreeDepth() < indentationStack.peek()) { | ||
indentationStack.pop(); | ||
} | ||
if (indentationStack.isEmpty() || row.getTreeDepth() > indentationStack.peek()) { | ||
indentationStack.push(row.getTreeDepth()); | ||
} | ||
nodeTextBuilder.append(String.join("", Collections.nCopies(indentationStack.size(), " "))); | ||
nodeTextBuilder.append("* "); | ||
|
||
nodeTextBuilder.append(stageOrBranchName); | ||
|
||
if (row.getNode().isActive()) { | ||
nodeTextBuilder.append(" *(running)*"); | ||
} | ||
else if (row.getDurationMillis() > 0) { | ||
nodeTextBuilder.append(String.format(" *(%s)*", row.getDurationString())); | ||
} | ||
nodeTextBuilder.append("\n"); | ||
return Pair.of(nodeTextBuilder.toString(), ""); | ||
} | ||
|
||
private Pair<String, String> processErrorOrWarningRow(final FlowGraphTable.Row row, final ErrorAction errorAction, | ||
final WarningAction warningAction) { | ||
FlowNode flowNode = row.getNode(); | ||
|
||
StringBuilder nodeSummaryBuilder = new StringBuilder(); | ||
StringBuilder nodeTextBuilder = new StringBuilder(); | ||
|
||
List<String> location = flowNode.getEnclosingBlocks().stream() | ||
.map(FlowExecutionAnalyzer::getStageOrBranchName) | ||
.filter(Optional::isPresent) | ||
.map(Optional::get) | ||
.collect(Collectors.toList()); | ||
|
||
Collections.reverse(location); | ||
|
||
location.add(flowNode.getDisplayName()); | ||
|
||
nodeSummaryBuilder.append(String.format("### `%s`%n", String.join(" / ", location))); | ||
|
||
nodeSummaryBuilder.append(String.format("%s in `%s` step", errorAction == null ? "Warning" : "Error", | ||
flowNode.getDisplayFunctionName())); | ||
String arguments = ArgumentsAction.getStepArgumentsAsString(flowNode); | ||
if (arguments == null) { | ||
nodeSummaryBuilder.append(".\n"); | ||
} | ||
else { | ||
nodeSummaryBuilder.append(String.format(", with arguments `%s`.%n", arguments)); | ||
if (errorAction != null && isTrivialErrorOrUnstable(flowNode, "error", errorAction.getDisplayName()) | ||
|| warningAction != null && isTrivialErrorOrUnstable(flowNode, "unstable", warningAction.getMessage())) { | ||
Check warning on line 111 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / CheckStyleIndentationCheck
Raw output
Check warning on line 111 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / CheckStyleEmptyBlockCheck
Raw output
|
||
// Suppress the step name and arguments because they | ||
// would be mostly redundant with other text. | ||
} else { | ||
Check warning on line 114 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / CheckStyleRightCurlyCheck
Raw output
|
||
nodeSummaryBuilder.append(String.format("%s in `%s` step", errorAction == null ? "Warning" : "Error", | ||
Check warning on line 115 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / SpotBugsRCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
Raw output
|
||
flowNode.getDisplayFunctionName())); | ||
String arguments = ArgumentsAction.getStepArgumentsAsString(flowNode); | ||
if (arguments == null) { | ||
nodeSummaryBuilder.append(".\n"); | ||
} | ||
else { | ||
nodeSummaryBuilder.append(String.format(", with arguments `%s`.%n", arguments)); | ||
} | ||
} | ||
|
||
nodeTextBuilder.append(String.join("", Collections.nCopies(indentationStack.size() + 1, " "))); | ||
if (warningAction == null) { | ||
Check warning on line 127 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / SpotBugsRCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
Raw output
|
||
nodeTextBuilder.append(String.format("**Error**: *%s*", errorAction.getDisplayName())); | ||
nodeSummaryBuilder.append(String.format("```%n%s%n```%n", errorAction.getDisplayName())); | ||
if (!suppressLogs) { | ||
|
@@ -175,16 +182,61 @@ | |
.build(); | ||
} | ||
|
||
private String getPotentialTitle(final FlowNode flowNode, final ErrorAction errorAction) { | ||
final String whereBuildFailed = String.format("%s in '%s' step", errorAction == null ? "warning" : "error", | ||
flowNode.getDisplayFunctionName()); | ||
private String getPotentialTitle(@NonNull final FlowNode flowNode, final ErrorAction errorAction) { | ||
String whereBuildFailed = null; | ||
if (errorAction != null | ||
&& isTrivialErrorOrUnstable(flowNode, "error", errorAction.getDisplayName())) { | ||
whereBuildFailed = summarize(errorAction.getDisplayName()); | ||
Check warning on line 189 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / SpotBugsNP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE
Raw output
|
||
} | ||
if (whereBuildFailed == null) { | ||
whereBuildFailed = String.format("%s in '%s' step", errorAction == null ? "warning" : "error", | ||
Check warning on line 192 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / SpotBugsRCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
Raw output
|
||
flowNode.getDisplayFunctionName()); | ||
} | ||
|
||
List<FlowNode> enclosingStagesAndParallels = getEnclosingStagesAndParallels(flowNode); | ||
List<String> enclosingBlockNames = getEnclosingBlockNames(enclosingStagesAndParallels); | ||
|
||
return StringUtils.join(new ReverseListIterator(enclosingBlockNames), "/") + ": " + whereBuildFailed; | ||
} | ||
|
||
/** | ||
* Check whether the node is an "error" or "unstable" step with the | ||
* specified message and no other arguments. In that case, the caller | ||
* can simplify the output. | ||
* | ||
* @param node The flow node to examine. | ||
* @param name The expected name of the step; either "error" or "unstable". | ||
* @param message The error or warning that was reported from the node. | ||
*/ | ||
private static boolean isTrivialErrorOrUnstable(@NonNull final FlowNode node, | ||
Check warning on line 211 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / CheckStyleJavadocMethodCheck
Raw output
|
||
@NonNull final String name, | ||
final String message) { | ||
if (node instanceof StepNode) { | ||
StepDescriptor d = ((StepNode) node).getDescriptor(); | ||
if (d != null && d.getFunctionName().equals(name)) { | ||
Map<String, Object> arguments = ArgumentsAction.getArguments(node); | ||
if (arguments != null && arguments.size() == 1) { | ||
Check warning on line 218 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / SpotBugsRCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE
Raw output
|
||
Object argument = arguments.get("message"); | ||
return argument instanceof String | ||
&& argument.equals(message); | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private static String summarize(String message) { | ||
Check warning on line 229 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / PMDMethodArgumentCouldBeFinal
Raw output
|
||
if (message != null) { | ||
final String firstLine = message.split("\r?\n", 2)[0]; | ||
if (!firstLine.isEmpty()) { | ||
return firstLine; | ||
} | ||
} | ||
|
||
return null; | ||
Check warning on line 237 in src/main/java/io/jenkins/plugins/checks/status/FlowExecutionAnalyzer.java ci.jenkins.io / SpotBugsNP_NONNULL_RETURN_VIOLATION
Raw output
|
||
} | ||
|
||
private static boolean isStageNode(@NonNull final FlowNode node) { | ||
if (node instanceof StepNode) { | ||
StepDescriptor d = ((StepNode) node).getDescriptor(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a maximum length on here too in case the first line is something crazy long?