Skip to content

Commit

Permalink
Merge pull request #71 from wolfs/wolfs/build-scan-pipeline-support
Browse files Browse the repository at this point in the history
Detect build scans in pipeline logs
  • Loading branch information
wolfs authored Jul 5, 2019
2 parents 921f5c9 + aa09b3b commit a8680c0
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 28 deletions.
22 changes: 18 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,31 @@ jenkinsPlugin {
disabledTestInjection = false
}

sourceCompatibility = '1.7'
sourceCompatibility = '1.8'

dependencies {
jenkinsPlugins 'org.jenkins-ci.plugins:structs:1.3'
jenkinsPlugins 'org.jenkins-ci.plugins:structs:1.5'
jenkinsPlugins 'org.jenkins-ci.plugins.workflow:workflow-api:2.20'
jenkinsPlugins 'org.jenkins-ci.plugins.workflow:workflow-cps:2.24'
jenkinsPlugins 'org.jenkins-ci.plugins.workflow:workflow-job:2.9'
jenkinsPlugins 'org.jenkins-ci.plugins.workflow:workflow-basic-steps:2.3'
jenkinsPlugins 'org.jenkins-ci.plugins.workflow:workflow-durable-task-step:2.8'
jenkinsPlugins 'org.jenkins-ci.plugins.workflow:workflow-step-api:2.10'

signature 'org.codehaus.mojo.signature:java17:1.0@signature'
signature 'org.codehaus.mojo.signature:java18:1.0@signature'

jenkinsTest 'org.jenkins-ci.main:jenkins-test-harness:2.44'
jenkinsTest 'org.jenkins-ci.main:jenkins-test-harness:2.49'
jenkinsTest 'org.jenkins-ci.main:jenkins-test-harness-tools:2.2'
jenkinsTest 'io.jenkins:configuration-as-code:1.4'

jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-aggregator:2.5'
jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-api:2.20'
jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-cps:2.24'
jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-job:2.9'
jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-basic-steps:2.3'
jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-durable-task-step:2.8'
jenkinsServer 'org.jenkins-ci.plugins.workflow:workflow-step-api:2.10'

testImplementation 'org.spockframework:spock-core:1.2-groovy-2.4'
}

Expand Down
4 changes: 3 additions & 1 deletion src/main/java/hudson/plugins/gradle/BuildScanAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public String getUrlName() {
}

public void addScanUrl(String scanUrl) {
scanUrls.add(scanUrl);
if (!scanUrls.contains(scanUrl)) {
scanUrls.add(scanUrl);
}
}

@Exported
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/hudson/plugins/gradle/BuildScanLogScanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package hudson.plugins.gradle;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BuildScanLogScanner {
private final BuildScanPublishedListener listener;
private int linesSinceBuildScanPublishingMessage = Integer.MAX_VALUE;
private static final Pattern BUILD_SCAN_PATTERN = Pattern.compile("Publishing (build scan|build information)\\.\\.\\.");
private static final Pattern URL_PATTERN = Pattern.compile("https?://\\S*");

public static final int MAX_PUBLISHED_MESSAGE_LENGTH = 70;

public BuildScanLogScanner(BuildScanPublishedListener listener) {
this.listener = listener;
}

void scanLine(String line) {
if (linesSinceBuildScanPublishingMessage < 10) {
linesSinceBuildScanPublishingMessage++;
Matcher matcher = URL_PATTERN.matcher(line);
if (matcher.find()) {
linesSinceBuildScanPublishingMessage = Integer.MAX_VALUE;
String buildScanUrl = matcher.group();
listener.onBuildScanPublished(buildScanUrl);
}
}
if (line.length() < MAX_PUBLISHED_MESSAGE_LENGTH && BUILD_SCAN_PATTERN.matcher(line).find()) {
linesSinceBuildScanPublishingMessage = 0;
}

}
}
68 changes: 68 additions & 0 deletions src/main/java/hudson/plugins/gradle/BuildScanPublisher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package hudson.plugins.gradle;

import com.google.common.collect.ImmutableSet;
import hudson.Extension;
import hudson.model.Run;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.steps.SynchronousNonBlockingStepExecution;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
import java.io.BufferedReader;
import java.util.Set;
import java.util.stream.Stream;

public class BuildScanPublisher extends Step {
@DataBoundConstructor
public BuildScanPublisher() {
}

@Override
public StepExecution start(StepContext context) {
return new Execution(context);
}

static class Execution extends SynchronousNonBlockingStepExecution<Void> {
private static final long serialVersionUID = 1L;

protected Execution(@Nonnull StepContext context) {
super(context);
}

@Override
protected Void run() throws Exception {
Run run = getContext().get(Run.class);
BuildScanLogScanner scanner = new BuildScanLogScanner(new DefaultBuildScanPublishedListener(run));
try (
BufferedReader logReader = new BufferedReader(run.getLogReader());
Stream<String> lines = logReader.lines()
) {
lines.forEach(scanner::scanLine);
}
return null;
}
}

@Extension
public static final class DescriptorImpl extends StepDescriptor {

@Override
public Set<? extends Class<?>> getRequiredContext() {
return ImmutableSet.of(Run.class);
}

@Nonnull
@Override
public String getDisplayName() {
return "Find published build scans";
}

@Override
public String getFunctionName() {
return "findBuildScans";
}
}
}
27 changes: 4 additions & 23 deletions src/main/java/hudson/plugins/gradle/GradleConsoleAnnotator.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,25 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author ikikko
* @see <a href="https://github.com/jenkinsci/ant-plugin/blob/master/src/main/java/hudson/tasks/_ant/AntConsoleAnnotator.java">AntConsoleAnnotator</a>
*/
public class GradleConsoleAnnotator extends LineTransformationOutputStream {

private static final Pattern BUILD_SCAN_PATTERN = Pattern.compile("Publishing (build scan|build information)\\.\\.\\.");
private static final Pattern URL_PATTERN = Pattern.compile("https?://\\S*");
private static final int MAX_PUBLISHED_MESSAGE_LENGTH = 70;

private final OutputStream out;
private final Charset charset;
private final boolean annotateGradleOutput;
private final BuildScanPublishedListener buildScanListener;
private final int maxLineLength;

private int linesSinceBuildScanPublishingMessage = Integer.MAX_VALUE;
private final BuildScanLogScanner buildScanLogScanner;

public GradleConsoleAnnotator(OutputStream out, Charset charset, boolean annotateGradleOutput, BuildScanPublishedListener buildScanListener) {
this.out = out;
this.charset = charset;
this.annotateGradleOutput = annotateGradleOutput;
this.buildScanListener = buildScanListener;
this.maxLineLength = annotateGradleOutput ? 500 : MAX_PUBLISHED_MESSAGE_LENGTH;
this.maxLineLength = annotateGradleOutput ? 500 : BuildScanLogScanner.MAX_PUBLISHED_MESSAGE_LENGTH;
this.buildScanLogScanner = new BuildScanLogScanner(buildScanListener);
}

@Override
Expand All @@ -55,18 +47,7 @@ protected void eol(byte[] b, int len) throws IOException {
}
}

if (linesSinceBuildScanPublishingMessage < 10) {
linesSinceBuildScanPublishingMessage++;
Matcher matcher = URL_PATTERN.matcher(line);
if (matcher.find()) {
linesSinceBuildScanPublishingMessage = Integer.MAX_VALUE;
String buildScanUrl = matcher.group();
buildScanListener.onBuildScanPublished(buildScanUrl);
}
}
if (len < MAX_PUBLISHED_MESSAGE_LENGTH && BUILD_SCAN_PATTERN.matcher(line).find()) {
linesSinceBuildScanPublishingMessage = 0;
}
buildScanLogScanner.scanLine(line);
}

out.write(b, 0, len);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div>
Inspect build log for published Gradle build scans.
The build scans will be shown on the pipeline build page.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import hudson.model.FreeStyleProject
import hudson.tasks.BatchFile
import hudson.tasks.Maven
import hudson.tasks.Shell
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition
import org.jenkinsci.plugins.workflow.job.WorkflowJob
import org.jvnet.hudson.test.CreateFileBuilder
import org.jvnet.hudson.test.ExtractResourceSCM
import org.jvnet.hudson.test.JenkinsRule
Expand Down Expand Up @@ -55,6 +57,40 @@ class BuildScanIntegrationTest extends AbstractIntegrationTest {
new URL(action.scanUrls.get(0))
}

def 'detects build scan in pipeline log'() {
given:
gradleInstallationRule.gradleVersion = '5.5'
gradleInstallationRule.addInstallation()
def pipelineJob = j.createProject(WorkflowJob)
pipelineJob.setDefinition(new CpsFlowDefinition("""
node {
stage('Build') {
// Run the maven build
def gradleHome = tool name: '${gradleInstallationRule.gradleVersion}', type: 'gradle'
writeFile file: 'settings.gradle', text: ''
writeFile file: 'build.gradle', text: "buildScan { termsOfServiceUrl = 'https://gradle.com/terms-of-service'; termsOfServiceAgree = 'yes' }"
if (isUnix()) {
sh "'\${gradleHome}/bin/gradle' help --scan"
} else {
bat(/"\${gradleHome}\\bin\\gradle.bat" help --scan/)
}
}
stage('Final') {
findBuildScans()
}
}
""", false))

when:
def build = pipelineJob.scheduleBuild2(0).get()

then:
println JenkinsRule.getLog(build)
def action = build.getAction(BuildScanAction)
action.scanUrls.size() == 1
new URL(action.scanUrls.get(0))
}

def 'build scan is discovered from Maven build'() {
given:
def p = j.createFreeStyleProject()
Expand Down

0 comments on commit a8680c0

Please sign in to comment.