diff --git a/.gitignore b/.gitignore index d9faeb3..ff11486 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties +/.idea/ diff --git a/pom.xml b/pom.xml index cab06c9..e204f5d 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,14 @@ + + + org.jenkins-ci.plugins + token-macro + 2.1 + + + UTF-8 1.7 diff --git a/src/main/java/SlackUploader.java b/src/main/java/SlackUploader.java index 52d133b..d6a06f6 100644 --- a/src/main/java/SlackUploader.java +++ b/src/main/java/SlackUploader.java @@ -5,19 +5,35 @@ */ import hudson.Extension; +import hudson.FilePath; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.FreeStyleProject; +import hudson.remoting.Callable; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Publisher; import hudson.tasks.Recorder; +import hudson.util.DirScanner; +import hudson.util.FileVisitor; import hudson.util.FormValidation; +import jenkins.security.MasterToSlaveCallable; + +import java.io.File; import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + import javax.servlet.ServletException; import net.sf.json.JSONObject; + +import org.apache.commons.lang.StringUtils; +import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; +import org.jenkinsci.plugins.tokenmacro.TokenMacro; +import org.jenkinsci.remoting.RoleChecker; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -33,7 +49,7 @@ public class SlackUploader extends Recorder { private final String token; private final String filePath; private static final String CHOICE_OF_SHELL = "/bin/bash"; - + @DataBoundConstructor public SlackUploader(String channel, String token, String filePath) { super(); @@ -53,8 +69,8 @@ public String getFilePath() { public String getToken() { return token; } - - + + @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; @@ -63,18 +79,15 @@ public BuildStepMonitor getRequiredMonitorService() { @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { //To change body of generated methods, choose Tools | Templates. - LogOutput log = new LogOutput(); Runtime runtime = Runtime.getRuntime(); Process process = null; + LogOutput log = new LogOutput(); + try { - String script = generateScript(); - - process = runScript(runtime, script); - - log.logOutput(listener, process); + generateScript(build, launcher, listener); } catch (Throwable cause) { - log.logOutput(listener, process); + throw new RuntimeException(cause); } return true; } @@ -84,14 +97,12 @@ private Process runScript(Runtime runtime, String script) throws IOException { return process; } - + private void generateScript(final AbstractBuild build, final Launcher launcher, final BuildListener listener) throws Throwable { + String mask = TokenMacro.expandAll(build, listener, filePath); - private String generateScript() { - String loop = "for file in $(ls " + filePath + ");"; - loop+="do "; - String curlRequest = loop + "curl -F file=@$file -F channels=" + channel +" -F token=" + token + " https://slack.com/api/files.upload ;"; - String loopDone = curlRequest + "done;"; - return loopDone; + listener.getLogger().println(String.format("Invoking Uploader (channel=%s, filePath=%s)", channel, filePath)); + + launcher.getChannel().callAsync(new FileUploadRunner(listener, mask, channel, token)).get(); } @Override @@ -159,4 +170,68 @@ public SlackUploader newInstance(StaplerRequest req, JSONObject formData) throws } + + public static class FileUploadRunner extends MasterToSlaveCallable implements Serializable { + private static final long serialVersionUID = 1L; + + private final String filePath; + + private final String channel; + + private final String token; + + private final BuildListener listener; + + public FileUploadRunner(BuildListener listener, String filePath, String channel, String token) { + this.listener = listener; + this.filePath = filePath; + this.channel = channel; + this.token = token; + } + + @Override + public Boolean call() throws Throwable { + final List commandList = new ArrayList<>(); + + String dirName = filePath; + String includeMask = "**/*"; + + final int i = filePath.indexOf('*'); + + final File tmpFile = new File(filePath); + + if (-1 != i) { + dirName = filePath.substring(0, i); + includeMask = filePath.substring(i); + } else if (tmpFile.exists() && tmpFile.isFile()) { + dirName = tmpFile.getParentFile().getPath(); + includeMask = tmpFile.getName(); + } + + listener.getLogger().println(String.format("Using dirname=%s and includeMask=%s", dirName, includeMask)); + + new DirScanner.Glob(includeMask, null).scan(new File(dirName), new FileVisitor() { + @Override + public void visit(File f, String relativePath) throws IOException { + if (f.isFile()) { + final String command = String.format("curl -F file=@\"%s\" -F channels=\"%s\" -F token=\"%s\" https://slack.com/api/files.upload", f.getAbsolutePath(), channel, token); + + listener.getLogger().println("Adding file " + f.getAbsolutePath()); + + commandList.add(command); + } + } + }); + + String allCommands = StringUtils.join(commandList, " ; "); + + if (commandList.isEmpty()) { + listener.getLogger().println("No files found for mask=" + this.filePath); + } + + Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", allCommands}); + + return ! commandList.isEmpty(); + } + } }