Skip to content

Commit 62b2151

Browse files
author
s003958
committed
Added -S option to replay-pipeline CLI command. Allows streaming a tarball with multiple script files to override at once
1 parent a63e684 commit 62b2151

File tree

5 files changed

+105
-9
lines changed

5 files changed

+105
-9
lines changed

src/main/java/org/jenkinsci/plugins/workflow/cps/replay/ReplayPipelineCommand.java

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,23 @@
3030
import hudson.cli.handlers.GenericItemOptionHandler;
3131
import hudson.model.Job;
3232
import hudson.model.Run;
33+
import java.io.BufferedOutputStream;
34+
import java.io.ByteArrayOutputStream;
35+
import java.io.InputStream;
36+
import java.util.AbstractMap.SimpleEntry;
37+
import java.util.ArrayList;
3338
import java.util.HashMap;
3439
import java.util.Map;
40+
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
41+
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
3542
import org.apache.commons.io.IOUtils;
36-
import org.jenkinsci.plugins.workflow.cps.replay.Messages;
3743
import org.kohsuke.args4j.Argument;
3844
import org.kohsuke.args4j.CmdLineParser;
3945
import org.kohsuke.args4j.Option;
4046
import org.kohsuke.args4j.OptionDef;
4147
import org.kohsuke.args4j.spi.Setter;
4248

49+
4350
@Extension public class ReplayPipelineCommand extends CLICommand {
4451

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

61+
@Option(name="-S", aliases="--scripts", usage="Pass tarball of override scripts to override build scripts on remote")
62+
private boolean scripts;
63+
5464
@Override public String getShortDescription() {
5565
return Messages.ReplayCommand_shortDescription();
5666
}
@@ -70,20 +80,80 @@
7080
if (!action.isEnabled()) {
7181
throw new AbortException("Not authorized to replay builds of this job");
7282
}
73-
String text = IOUtils.toString(stdin);
74-
if (script != null) {
75-
Map<String,String> replacementLoadedScripts = new HashMap<String,String>(action.getOriginalLoadedScripts());
76-
if (!replacementLoadedScripts.containsKey(script)) {
77-
throw new AbortException("Unrecognized script name among " + replacementLoadedScripts.keySet());
83+
if (scripts) {
84+
ArrayList<SimpleEntry<String,String>> replacementScripts = parseScripts(stdin);
85+
Map<String,String> replacementLoadedScripts = new HashMap<>(action.getOriginalLoadedScripts());
86+
Boolean overrideJenkinsfile = false;
87+
String jenkinsfileContent = "";
88+
89+
for (SimpleEntry<String,String> pair : replacementScripts) {
90+
String scriptName = pair.getKey();
91+
String scriptContent = pair.getValue();
92+
93+
if (scriptName.equals("Jenkinsfile")) {
94+
overrideJenkinsfile = true;
95+
jenkinsfileContent = scriptContent;
96+
} else {
97+
if (!replacementLoadedScripts.containsKey(scriptName)) {
98+
throw new AbortException("Unrecognized script name " + scriptName + " among " + replacementLoadedScripts.keySet());
99+
}
100+
replacementLoadedScripts.put(scriptName, scriptContent);
101+
}
78102
}
79-
replacementLoadedScripts.put(script, text);
80-
action.run(action.getOriginalScript(), replacementLoadedScripts);
103+
104+
if (!overrideJenkinsfile)
105+
jenkinsfileContent = action.getOriginalScript();
106+
107+
action.run(jenkinsfileContent, replacementLoadedScripts);
81108
} else {
82-
action.run(text, action.getOriginalLoadedScripts());
109+
String text = IOUtils.toString(stdin);
110+
if (script != null) {
111+
Map<String, String> replacementLoadedScripts = new HashMap<String, String>(action.getOriginalLoadedScripts());
112+
if (!replacementLoadedScripts.containsKey(script)) {
113+
throw new AbortException("Unrecognized script name among " + replacementLoadedScripts.keySet());
114+
}
115+
replacementLoadedScripts.put(script, text);
116+
action.run(action.getOriginalScript(), replacementLoadedScripts);
117+
} else {
118+
action.run(text, action.getOriginalLoadedScripts());
119+
}
83120
}
84121
return 0;
85122
}
86123

124+
protected ArrayList<SimpleEntry<String,String>> parseScripts(InputStream tarInputStream) throws Exception {
125+
ArrayList<SimpleEntry<String, String>> list = new ArrayList<>();
126+
127+
try ( TarArchiveInputStream tarIn = new TarArchiveInputStream(tarInputStream) ) {
128+
TarArchiveEntry tarEntry = tarIn.getNextTarEntry();
129+
130+
while (tarEntry != null) {
131+
String scriptName = tarEntry.getName();
132+
133+
int BUFFER_SIZE = 512;
134+
int count;
135+
byte data[] = new byte[BUFFER_SIZE];
136+
137+
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
138+
BufferedOutputStream outStream = new BufferedOutputStream(byteStream, BUFFER_SIZE);
139+
while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) {
140+
outStream.write(data, 0, count);
141+
}
142+
outStream.flush();
143+
String scriptContent = new String(byteStream.toByteArray());
144+
145+
SimpleEntry<String, String> entry = new SimpleEntry<>(scriptName, scriptContent);
146+
list.add(entry);
147+
148+
tarEntry = tarIn.getNextTarEntry();
149+
}
150+
} catch ( Exception e) {
151+
throw e;
152+
}
153+
154+
return list;
155+
}
156+
87157
@SuppressWarnings("rawtypes")
88158
public static class JobHandler extends GenericItemOptionHandler<Job> {
89159

src/test/java/org/jenkinsci/plugins/workflow/cps/replay/ReplayActionTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import hudson.security.GlobalMatrixAuthorizationStrategy;
4646
import hudson.security.Permission;
4747
import java.io.File;
48+
import java.io.InputStream;
4849
import java.util.Collections;
4950
import java.util.List;
5051
import jenkins.model.Jenkins;
@@ -287,6 +288,26 @@ private static boolean canRebuild(WorkflowRun b, String user) {
287288
});
288289
}
289290

291+
@Test public void cliMultipleFiles() throws Exception {
292+
story.addStep(new Statement() {
293+
@Override public void evaluate() throws Throwable {
294+
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "p");
295+
// As in loadStep, will set up a main and auxiliary script.
296+
FilePath f = story.j.jenkins.getWorkspaceFor(p).child("f.groovy");
297+
f.write("'original text'", null);
298+
p.setDefinition(new CpsFlowDefinition("node {def t = load 'f.groovy'; echo \"got ${t}\"}", true));
299+
WorkflowRun b1 = story.j.assertBuildStatusSuccess(p.scheduleBuild2(0));
300+
story.j.assertLogContains("got original text", b1);
301+
InputStream tarStream = this.getClass().getResourceAsStream("scripts.tar");
302+
assertEquals(0, new CLICommandInvoker(story.j, "replay-pipeline").withStdin(tarStream).invokeWithArgs("p", "-S").returnCode());
303+
story.j.waitUntilNoActivity();
304+
WorkflowRun b2 = p.getLastBuild();
305+
assertEquals(2, b2.getNumber());
306+
story.j.assertLogContains("received new text", b2);
307+
}
308+
});
309+
}
310+
290311
@Issue("JENKINS-47339")
291312
@Test
292313
public void rebuild() throws Exception {
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node {
2+
def t = load 'f.groovy'
3+
echo "received ${t}"
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
'new text'
Binary file not shown.

0 commit comments

Comments
 (0)