From cd4b5267895f64478140876b485d352fc3fb96ee Mon Sep 17 00:00:00 2001 From: Denis Date: Sat, 13 Jan 2018 19:57:53 +0300 Subject: [PATCH] BAEL-1451 Writing a Jenkins plugin (#3396) * BAEL-1451 Writing a Jenkins plugin A sample Jenkins plugin which builds basic project stats * BAEL-1451 Writing a Jenkins plugin Formatting --- jenkins/hello-world/pom.xml | 89 +++++++++++++ .../jenkins/helloworld/ProjectStats.java | 20 +++ .../helloworld/ProjectStatsBuildWrapper.java | 123 ++++++++++++++++++ .../hello-world/src/main/resources/stats.html | 20 +++ 4 files changed, 252 insertions(+) create mode 100644 jenkins/hello-world/pom.xml create mode 100644 jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java create mode 100644 jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java create mode 100644 jenkins/hello-world/src/main/resources/stats.html diff --git a/jenkins/hello-world/pom.xml b/jenkins/hello-world/pom.xml new file mode 100644 index 0000000000..15cf2f8e34 --- /dev/null +++ b/jenkins/hello-world/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 2.33 + + + jenkins-hello-world + 1.0-SNAPSHOT + hpi + + + 2.7.3 + + Hello World Plugin + A sample Jenkins Hello World plugin + https://wiki.jenkins-ci.org/display/JENKINS/TODO+Plugin + + + MIT License + http://opensource.org/licenses/MIT + + + + + org.jenkins-ci.plugins + structs + 1.7 + + + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.12 + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + 2.39 + test + + + org.jenkins-ci.plugins.workflow + workflow-job + 2.11.2 + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + 2.6 + test + + + org.jenkins-ci.plugins.workflow + workflow-durable-task-step + 2.13 + test + + + org.jenkins-ci.plugins.workflow + workflow-api + 2.20 + test + + + org.jenkins-ci.plugins.workflow + workflow-support + 2.14 + test + + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + diff --git a/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java new file mode 100644 index 0000000000..67af636bb4 --- /dev/null +++ b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java @@ -0,0 +1,20 @@ +package com.baeldung.jenkins.helloworld; + +public class ProjectStats { + + private final int classesNumber; + private final int linesNumber; + + public ProjectStats(int classesNumber, int linesNumber) { + this.classesNumber = classesNumber; + this.linesNumber = linesNumber; + } + + public int getClassesNumber() { + return classesNumber; + } + + public int getLinesNumber() { + return linesNumber; + } +} diff --git a/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java new file mode 100644 index 0000000000..9a7213c76f --- /dev/null +++ b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java @@ -0,0 +1,123 @@ +package com.baeldung.jenkins.helloworld; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.BuildListener; +import hudson.tasks.BuildWrapper; +import hudson.tasks.BuildWrapperDescriptor; +import org.kohsuke.stapler.DataBoundConstructor; + +import javax.annotation.Nonnull; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Stack; + +public class ProjectStatsBuildWrapper extends BuildWrapper { + + private static final String REPORT_TEMPLATE_PATH = "/stats.html"; + private static final String PROJECT_NAME_VAR = "$PROJECT_NAME$"; + private static final String CLASSES_NUMBER_VAR = "$CLASSES_NUMBER$"; + private static final String LINES_NUMBER_VAR = "$LINES_NUMBER$"; + + @DataBoundConstructor + public ProjectStatsBuildWrapper() { + } + + @Override + public Environment setUp(AbstractBuild build, final Launcher launcher, BuildListener listener) { + return new Environment() { + @Override + public boolean tearDown(AbstractBuild build, BuildListener listener) + throws IOException, InterruptedException + { + ProjectStats stats = buildStats(build.getWorkspace()); + String report = generateReport(build.getProject().getDisplayName(), stats); + File artifactsDir = build.getArtifactsDir(); + if (!artifactsDir.isDirectory()) { + boolean success = artifactsDir.mkdirs(); + if (!success) { + listener.getLogger().println("Can't create artifacts directory at " + + artifactsDir.getAbsolutePath()); + } + } + String path = artifactsDir.getCanonicalPath() + REPORT_TEMPLATE_PATH; + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), + StandardCharsets.UTF_8))) { + writer.write(report); + } + return super.tearDown(build, listener); + } + }; + } + + private static ProjectStats buildStats(FilePath root) throws IOException, InterruptedException { + int classesNumber = 0; + int linesNumber = 0; + Stack toProcess = new Stack<>(); + toProcess.push(root); + while (!toProcess.isEmpty()) { + FilePath path = toProcess.pop(); + if (path.isDirectory()) { + toProcess.addAll(path.list()); + } else if (path.getName().endsWith(".java")) { + classesNumber++; + linesNumber += countLines(path); + } + } + return new ProjectStats(classesNumber, linesNumber); + } + + private static int countLines(FilePath path) throws IOException, InterruptedException { + byte[] buffer = new byte[1024]; + int result = 1; + try (InputStream in = path.read()) { + while (true) { + int read = in.read(buffer); + if (read < 0) { + return result; + } + for (int i = 0; i < read; i++) { + if (buffer[i] == '\n') { + result++; + } + } + } + } + } + + private static String generateReport(String projectName, ProjectStats stats) throws IOException { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + try (InputStream in = ProjectStatsBuildWrapper.class.getResourceAsStream(REPORT_TEMPLATE_PATH)) { + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) >= 0) { + bOut.write(buffer, 0, read); + } + } + String content = new String(bOut.toByteArray(), StandardCharsets.UTF_8); + content = content.replace(PROJECT_NAME_VAR, projectName); + content = content.replace(CLASSES_NUMBER_VAR, String.valueOf(stats.getClassesNumber())); + content = content.replace(LINES_NUMBER_VAR, String.valueOf(stats.getLinesNumber())); + return content; + } + + @Extension + public static final class DescriptorImpl extends BuildWrapperDescriptor { + + @Override + public boolean isApplicable(AbstractProject item) { + return true; + } + + @Nonnull + @Override + public String getDisplayName() { + return "Construct project stats during build"; + } + + } + +} diff --git a/jenkins/hello-world/src/main/resources/stats.html b/jenkins/hello-world/src/main/resources/stats.html new file mode 100644 index 0000000000..2a14b5de3f --- /dev/null +++ b/jenkins/hello-world/src/main/resources/stats.html @@ -0,0 +1,20 @@ + + + + + $PROJECT_NAME$ + + +Project $PROJECT_NAME$: + + + + + + + + + +
Classes numberLines number
$CLASSES_NUMBER$$LINES_NUMBER$
+ + \ No newline at end of file