Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,23 @@
import hudson.cli.handlers.GenericItemOptionHandler;
import hudson.model.Job;
import hudson.model.Run;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.io.IOUtils;
import org.jenkinsci.plugins.workflow.cps.replay.Messages;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.kohsuke.args4j.OptionDef;
import org.kohsuke.args4j.spi.Setter;


@Extension public class ReplayPipelineCommand extends CLICommand {

@Argument(required=true, index=0, metaVar="JOB", usage="Name of the job to replay.", handler=JobHandler.class)
Expand All @@ -51,6 +58,9 @@
@Option(name="-s", aliases="--script", metaVar="SCRIPT", usage="Name of script to edit, such as Script3, if not the main Jenkinsfile.")
public String script;

@Option(name="-S", aliases="--scripts", usage="Pass tarball of override scripts to override build scripts on remote")
private boolean scripts;

@Override public String getShortDescription() {
return Messages.ReplayCommand_shortDescription();
}
Expand All @@ -70,20 +80,80 @@
if (!action.isEnabled()) {
throw new AbortException("Not authorized to replay builds of this job");
}
String text = IOUtils.toString(stdin);
if (script != null) {
Map<String,String> replacementLoadedScripts = new HashMap<String,String>(action.getOriginalLoadedScripts());
if (!replacementLoadedScripts.containsKey(script)) {
throw new AbortException("Unrecognized script name among " + replacementLoadedScripts.keySet());
if (scripts) {
ArrayList<SimpleEntry<String,String>> replacementScripts = parseScripts(stdin);
Map<String,String> replacementLoadedScripts = new HashMap<>(action.getOriginalLoadedScripts());
Boolean overrideJenkinsfile = false;
String jenkinsfileContent = "";

for (SimpleEntry<String,String> pair : replacementScripts) {
String scriptName = pair.getKey();
String scriptContent = pair.getValue();

if (scriptName.equals("Jenkinsfile")) {
overrideJenkinsfile = true;
jenkinsfileContent = scriptContent;
} else {
if (!replacementLoadedScripts.containsKey(scriptName)) {
throw new AbortException("Unrecognized script name " + scriptName + " among " + replacementLoadedScripts.keySet());
}
replacementLoadedScripts.put(scriptName, scriptContent);
}
}
replacementLoadedScripts.put(script, text);
action.run(action.getOriginalScript(), replacementLoadedScripts);

if (!overrideJenkinsfile)
jenkinsfileContent = action.getOriginalScript();

action.run(jenkinsfileContent, replacementLoadedScripts);
} else {
action.run(text, action.getOriginalLoadedScripts());
String text = IOUtils.toString(stdin);
if (script != null) {
Map<String, String> replacementLoadedScripts = new HashMap<String, String>(action.getOriginalLoadedScripts());
if (!replacementLoadedScripts.containsKey(script)) {
throw new AbortException("Unrecognized script name among " + replacementLoadedScripts.keySet());
}
replacementLoadedScripts.put(script, text);
action.run(action.getOriginalScript(), replacementLoadedScripts);
} else {
action.run(text, action.getOriginalLoadedScripts());
}
}
return 0;
}

protected ArrayList<SimpleEntry<String,String>> parseScripts(InputStream tarInputStream) throws Exception {
ArrayList<SimpleEntry<String, String>> list = new ArrayList<>();

try ( TarArchiveInputStream tarIn = new TarArchiveInputStream(tarInputStream) ) {
TarArchiveEntry tarEntry = tarIn.getNextTarEntry();

while (tarEntry != null) {
String scriptName = tarEntry.getName();

int BUFFER_SIZE = 512;
int count;
byte data[] = new byte[BUFFER_SIZE];

ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
BufferedOutputStream outStream = new BufferedOutputStream(byteStream, BUFFER_SIZE);
while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) {
outStream.write(data, 0, count);
}
outStream.flush();
String scriptContent = byteStream.toString("UTF-8");

SimpleEntry<String, String> entry = new SimpleEntry<>(scriptName, scriptContent);
list.add(entry);

tarEntry = tarIn.getNextTarEntry();
}
} catch ( Exception e) {
throw e;
}

return list;
}

@SuppressWarnings("rawtypes")
public static class JobHandler extends GenericItemOptionHandler<Job> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import hudson.security.GlobalMatrixAuthorizationStrategy;
import hudson.security.Permission;
import java.io.File;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import jenkins.model.Jenkins;
Expand Down Expand Up @@ -287,6 +288,26 @@ private static boolean canRebuild(WorkflowRun b, String user) {
});
}

@Test public void cliMultipleFiles() throws Exception {
story.addStep(new Statement() {
@Override public void evaluate() throws Throwable {
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "p");
// As in loadStep, will set up a main and auxiliary script.
FilePath f = story.j.jenkins.getWorkspaceFor(p).child("f.groovy");
f.write("'original text'", null);
p.setDefinition(new CpsFlowDefinition("node {def t = load 'f.groovy'; echo \"got ${t}\"}", true));
WorkflowRun b1 = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
story.j.assertLogContains("got original text", b1);
InputStream tarStream = this.getClass().getResourceAsStream("scripts.tar");
assertEquals(0, new CLICommandInvoker(story.j, "replay-pipeline").withStdin(tarStream).invokeWithArgs("p", "-S").returnCode());
story.j.waitUntilNoActivity();
WorkflowRun b2 = p.getLastBuild();
assertEquals(2, b2.getNumber());
story.j.assertLogContains("received new text", b2);
}
});
}

@Issue("JENKINS-47339")
@Test
public void rebuild() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node {
def t = load 'f.groovy'
echo "received ${t}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'new text'
Binary file not shown.