From 8b251c3ed7d9ac49a9d993858532777e12448d05 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 13 Dec 2023 03:37:27 +0000 Subject: [PATCH] Add a library to get logs for a specific stage in jenkins (#351) Signed-off-by: Sayali Gaikawad (cherry picked from commit fdf57a659177542126488ef4a8fe41d4c9033650) Signed-off-by: github-actions[bot] --- build.gradle | 2 +- vars/getLogsForStage.groovy | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 vars/getLogsForStage.groovy diff --git a/build.gradle b/build.gradle index 8da294375..a2d257090 100644 --- a/build.gradle +++ b/build.gradle @@ -120,7 +120,7 @@ jacocoTestReport { } } -String version = '5.11.1' +String version = '5.12.0' task updateVersion { doLast { diff --git a/vars/getLogsForStage.groovy b/vars/getLogsForStage.groovy new file mode 100644 index 000000000..5a6cea422 --- /dev/null +++ b/vars/getLogsForStage.groovy @@ -0,0 +1,73 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + + /* Thanks to the jenkins community https://community.jenkins.io/t/jenkins-dsl-get-stage-logs-as-list-and-print-the-failures/6111/3 +* Library to get logs of a specific stage in jenkins +* @param args.stageName - Name of the stage to get logs. Returns a List +*/ +import java.util.regex.Matcher +import java.util.regex.Pattern + +import org.jenkinsci.plugins.workflow.actions.LabelAction +import org.jenkinsci.plugins.workflow.actions.LogAction +import org.jenkinsci.plugins.workflow.graph.FlowNode +import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner +import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode +import org.jenkinsci.plugins.workflow.job.WorkflowRun +import java.util.stream.Collectors + +@NonCPS +def call(Map args = [:]) { + if (args.stageName == null || args.stageName.allWhitespace || args.stageName.isEmpty()) { + error('stageName cannot be emppty. Please provide one.') + } + List stageLogs = collectLogsForStage(args.stageName) +} +// Recursively check flowNode parents until we find a stage +@NonCPS +String getFlowNodeStage(FlowNode flowNode) { + for (FlowNode parent : flowNode.getParents()) { + if (parent instanceof StepStartNode && isNamedStageStartNode(parent)) { + return parent.getAction(LabelAction.class).getDisplayName() + } else { + return getFlowNodeStage(parent) + } + } + // Return null if no stage found. Null will be passed through all recursion levels + return null +} + +// Collect logs of each flow node that belongs to stage +@NonCPS +List collectLogsForStage(String stageName) { + currentBuild.rawBuild.save() + List logs = [] + DepthFirstScanner scanner = new DepthFirstScanner() + + scanner.setup(currentBuild.rawBuild.getExecution().getCurrentHeads()) + + for (FlowNode flowNode : scanner) { + // Skip flow nodes that are not part of a requested stage + // If stage not found for the current flow node, getFlowNodeStage() will return null + if(stageName.equals(getFlowNodeStage(flowNode))) { + LogAction logAction = flowNode.getAction(LogAction.class) + if (logAction != null) { + def reader = new BufferedReader(logAction.getLogText().readAll()) + List flowNodeLogs = reader.lines().collect(Collectors.toList()) + logs.addAll(0, flowNodeLogs) + } + } + } + return logs +} + +@NonCPS +private boolean isNamedStageStartNode(FlowNode node) { + return Objects.equals(((StepStartNode) node).getStepName(), "Stage") && !Objects.equals(node.getDisplayFunctionName(), "stage"); +} \ No newline at end of file