下面列出了怎么用hudson.util.ArgumentListBuilder的API类实例代码及写法,或者点击链接到github查看源代码。
private LaunchResult launch(EnvVars env, boolean quiet, FilePath workDir, ArgumentListBuilder argb) throws IOException, InterruptedException {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Executing command \"{0}\"", argb);
}
Launcher.ProcStarter procStarter = launcher.launch();
if (workDir != null) {
procStarter.pwd(workDir);
}
LaunchResult result = new LaunchResult();
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
result.setStatus(procStarter.quiet(quiet).cmds(argb).envs(env).stdout(out).stderr(err).start().joinWithTimeout(CLIENT_TIMEOUT, TimeUnit.SECONDS, launcher.getListener()));
final String charsetName = Charset.defaultCharset().name();
result.setOut(out.toString(charsetName));
result.setErr(err.toString(charsetName));
return result;
}
private LaunchResult launch(@Nonnull EnvVars launchEnv, boolean quiet, FilePath pwd, @Nonnull ArgumentListBuilder args) throws IOException, InterruptedException {
// Prepend the docker command
args.prepend(DockerTool.getExecutable(toolName, node, launcher.getListener(), launchEnv));
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Executing docker command {0}", args.toString());
}
Launcher.ProcStarter procStarter = launcher.launch();
if (pwd != null) {
procStarter.pwd(pwd);
}
LaunchResult result = new LaunchResult();
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();
result.setStatus(procStarter.quiet(quiet).cmds(args).envs(launchEnv).stdout(out).stderr(err).start().joinWithTimeout(CLIENT_TIMEOUT, TimeUnit.SECONDS, launcher.getListener()));
final String charsetName = Charset.defaultCharset().name();
result.setOut(out.toString(charsetName));
result.setErr(err.toString(charsetName));
return result;
}
protected ArgumentListBuilder appendCredentials(ArgumentListBuilder args)
throws IOException, InterruptedException
{
if (credentials instanceof SSHUserPrivateKey) {
SSHUserPrivateKey privateKeyCredentials = (SSHUserPrivateKey)credentials;
key = Utils.createSshKeyFile(key, ws, privateKeyCredentials, copyCredentialsInWorkspace);
args.add("--private-key").add(key);
args.add("-u").add(privateKeyCredentials.getUsername());
if (privateKeyCredentials.getPassphrase() != null) {
script = Utils.createSshAskPassFile(script, ws, privateKeyCredentials, copyCredentialsInWorkspace);
environment.put("SSH_ASKPASS", script.getRemote());
// inspired from https://github.com/jenkinsci/git-client-plugin/pull/168
// but does not work with MacOSX
if (! environment.containsKey("DISPLAY")) {
environment.put("DISPLAY", ":123.456");
}
}
} else if (credentials instanceof UsernamePasswordCredentials) {
args.add("-u").add(credentials.getUsername());
args.add("-k");
}
return args;
}
@Override
protected ArgumentListBuilder buildCommandLine()
throws InterruptedException, AnsibleInvocationException, IOException
{
ArgumentListBuilder args = new ArgumentListBuilder();
prependPasswordCredentials(args);
appendExecutable(args);
appendHostPattern(args);
appendInventory(args);
appendHModule(args);
appendModuleCommand(args);
appendSudo(args);
appendForks(args);
appendCredentials(args);
appendAdditionalParameters(args);
return args;
}
@Override
protected ArgumentListBuilder buildCommandLine() throws InterruptedException, AnsibleInvocationException, IOException {
ArgumentListBuilder args = new ArgumentListBuilder();
prependPasswordCredentials(args);
appendExecutable(args);
appendPlaybook(args);
appendInventory(args);
appendLimit(args);
appendTags(args);
appendSkippedTags(args);
appendStartTask(args);
appendSudo(args);
appendForks(args);
appendCredentials(args);
appendAdditionalParameters(args);
return args;
}
@Test
public void should_generate_simple_invocation() throws Exception {
// Given
Inventory inventory = new InventoryPath("/tmp/hosts");
BuildListener listener = mock(BuildListener.class);
CLIRunner runner = mock(CLIRunner.class);
AbstractBuild<?,?> build = mock(AbstractBuild.class);
when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
AnsibleAdHocCommandInvocation invocation = new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
invocation.setHostPattern("localhost");
invocation.setInventory(inventory);
invocation.setModule("ping");
invocation.setForks(5);
// When
invocation.execute(runner);
// Then
ArgumentCaptor<ArgumentListBuilder> argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
verify(runner).execute(argument.capture(), anyMap());
assertThat(argument.getValue().toString())
.isEqualTo("/usr/local/bin/ansible localhost -i /tmp/hosts -m ping -f 5");
}
@Test
public void should_generate_simple_invocation_with_env() throws Exception {
// Given
Inventory inventory = new InventoryPath("/tmp/hosts");
BuildListener listener = mock(BuildListener.class);
CLIRunner runner = mock(CLIRunner.class);
AbstractBuild<?,?> build = mock(AbstractBuild.class);
when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
AnsibleAdHocCommandInvocation invocation = new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
invocation.setHostPattern("localhost");
invocation.setInventory(inventory);
invocation.setModule("ping");
invocation.setForks(5);
invocation.setColorizedOutput(true);
invocation.setHostKeyCheck(false);
invocation.setUnbufferedOutput(true);
// When
invocation.execute(runner);
// Then
ArgumentCaptor<Map> argument = ArgumentCaptor.forClass(Map.class);
verify(runner).execute(any(ArgumentListBuilder.class), argument.capture());
assertThat((Map<String, String>)argument.getValue())
.containsEntry("PYTHONUNBUFFERED", "1")
.containsEntry("ANSIBLE_FORCE_COLOR", "true")
.containsEntry("ANSIBLE_HOST_KEY_CHECKING", "False");
}
@Test
@Ignore("build.getWorkspace() cannot be mocked")
public void should_handle_private_key_credentials() throws Exception {
// Given
Inventory inventory = new InventoryPath("/tmp/hosts");
SSHUserPrivateKey pkey = mock(SSHUserPrivateKey.class);
when(pkey.getUsername()).thenReturn("mylogin");
BuildListener listener = mock(BuildListener.class);
CLIRunner runner = mock(CLIRunner.class);
AbstractBuild<?,?> build = mock(AbstractBuild.class);
when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
AnsibleAdHocCommandInvocation invocation = new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
invocation.setHostPattern("localhost");
invocation.setInventory(inventory);
invocation.setModule("ping");
invocation.setCredentials(pkey);
invocation.setForks(5);
// When
invocation.execute(runner);
// Then
ArgumentCaptor<ArgumentListBuilder> argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
verify(runner).execute(argument.capture(), anyMap());
assertThat(argument.getValue().toString())
.matches("/usr/local/bin/ansible localhost -i /tmp/hosts -m ping -f 5 --private-key .+ -u mylogin");
}
@Test
public void should_handle_variables() throws Exception {
// Given
Inventory inventory = new InventoryPath("/tmp/hosts");
BuildListener listener = mock(BuildListener.class);
CLIRunner runner = mock(CLIRunner.class);
AbstractBuild<?,?> build = mock(AbstractBuild.class);
EnvVars vars = new EnvVars();
vars.put("MODULE", "ping");
when(build.getEnvironment(any(TaskListener.class))).thenReturn(vars);
AnsibleAdHocCommandInvocation invocation = new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
invocation.setHostPattern("localhost");
invocation.setInventory(inventory);
invocation.setModule("${MODULE}");
invocation.setForks(5);
// When
invocation.execute(runner);
// Then
ArgumentCaptor<ArgumentListBuilder> argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
verify(runner).execute(argument.capture(), anyMap());
assertThat(argument.getValue().toString())
.isEqualTo("/usr/local/bin/ansible localhost -i /tmp/hosts -m ping -f 5");
}
/** {@inheritDoc} */
@Override
public void reset(boolean hard) throws GitException, InterruptedException {
try {
validateRevision("HEAD");
} catch (GitException e) {
listener.getLogger().println("No valid HEAD. Skipping the resetting");
return;
}
listener.getLogger().println("Resetting working tree");
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("reset");
if (hard) {
args.add("--hard");
}
launchCommand(args);
}
/** {@inheritDoc} */
@Override
public void prune(RemoteConfig repository) throws GitException, InterruptedException {
String repoName = repository.getName();
String repoUrl = getRemoteUrl(repoName);
if (repoUrl != null && !repoUrl.isEmpty()) {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("remote", "prune", repoName);
StandardCredentials cred = credentials.get(repoUrl);
if (cred == null) cred = defaultCredentials;
try {
launchCommandWithCredentials(args, workspace, cred, new URIish(repoUrl));
} catch (URISyntaxException ex) {
throw new GitException("Invalid URL " + repoUrl, ex);
}
}
}
/** {@inheritDoc} */
@Override
public List<String> showRevision(ObjectId from, ObjectId to, Boolean useRawOutput) throws GitException, InterruptedException {
ArgumentListBuilder args = new ArgumentListBuilder("log", "--full-history", "--no-abbrev", "--format=raw", "-M", "-m");
if (useRawOutput) {
args.add("--raw");
}
if (from != null){
args.add(from.name() + ".." + to.name());
} else {
args.add("-1", to.name());
}
StringWriter writer = new StringWriter();
writer.write(launchCommand(args));
return new ArrayList<>(Arrays.asList(writer.toString().split("\\n")));
}
/** {@inheritDoc} */
@Override
public Set<String> getRemoteTagNames(String tagPattern) throws GitException {
try {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("ls-remote", "--tags");
String remoteUrl = getRemoteUrl("origin");
if (remoteUrl != null) {
addCheckedRemoteUrl(args, remoteUrl);
}
if (tagPattern != null)
args.add(tagPattern);
String result = launchCommandIn(args, workspace);
Set<String> tags = new HashSet<>();
BufferedReader rdr = new BufferedReader(new StringReader(result));
String tag;
while ((tag = rdr.readLine()) != null) {
// Add the tag name without the SHA1
tags.add(tag.replaceFirst(".*refs/tags/", ""));
}
return tags;
} catch (GitException | IOException | InterruptedException e) {
throw new GitException("Error retrieving remote tag names", e);
}
}
/** {@inheritDoc} */
@Override
public Set<String> getTagNames(String tagPattern) throws GitException {
try {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("tag", "-l", tagPattern);
String result = launchCommandIn(args, workspace);
Set<String> tags = new HashSet<>();
BufferedReader rdr = new BufferedReader(new StringReader(result));
String tag;
while ((tag = rdr.readLine()) != null) {
// Add the SHA1
tags.add(tag);
}
return tags;
} catch (GitException | IOException | InterruptedException e) {
throw new GitException("Error retrieving tag names", e);
}
}
/** {@inheritDoc} */
@Override
public Map<String, ObjectId> getHeadRev(String url) throws GitException, InterruptedException {
ArgumentListBuilder args = new ArgumentListBuilder("ls-remote");
args.add("-h");
addCheckedRemoteUrl(args, url);
StandardCredentials cred = credentials.get(url);
if (cred == null) cred = defaultCredentials;
String result = launchCommandWithCredentials(args, null, cred, url);
Map<String, ObjectId> heads = new HashMap<>();
String[] lines = result.split("\n");
for (String line : lines) {
if (line.length() >= 41) {
heads.put(line.substring(41), ObjectId.fromString(line.substring(0, 40)));
} else {
listener.getLogger().println("Unexpected ls-remote output line '" + line + "'");
}
}
return heads;
}
/** {@inheritDoc} */
@Override
public ObjectId getHeadRev(String url, String branchSpec) throws GitException, InterruptedException {
final String branchName = extractBranchNameFromBranchSpec(branchSpec);
ArgumentListBuilder args = new ArgumentListBuilder("ls-remote");
if(!branchName.startsWith("refs/tags/")) {
args.add("-h");
}
StandardCredentials cred = credentials.get(url);
if (cred == null) cred = defaultCredentials;
addCheckedRemoteUrl(args, url);
if (branchName.startsWith("refs/tags/")) {
args.add(branchName+"^{}"); // JENKINS-23299 - tag SHA1 needs to be converted to commit SHA1
} else {
args.add(branchName);
}
String result = launchCommandWithCredentials(args, null, cred, url);
return result.length()>=40 ? ObjectId.fromString(result.substring(0, 40)) : null;
}
/** {@inheritDoc} */
@Deprecated
@Override
public void push(RemoteConfig repository, String refspec) throws GitException, InterruptedException {
ArgumentListBuilder args = new ArgumentListBuilder();
URIish uri = repository.getURIs().get(0);
String url = uri.toPrivateString();
StandardCredentials cred = credentials.get(url);
if (cred == null) cred = defaultCredentials;
args.add("push");
addCheckedRemoteUrl(args, url);
if (refspec != null)
args.add(refspec);
launchCommandWithCredentials(args, workspace, cred, uri);
// Ignore output for now as there's many different formats
// That are possible.
}
@Override
public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map<String, String> volumes, @Nonnull Collection<String> volumesFromContainers, @Nonnull EnvVars containerEnv, @Nonnull String user, @Nonnull String... command) throws IOException, InterruptedException {
ArgumentListBuilder argb = new ArgumentListBuilder("docker", "run", "-d", "-t");
if (args != null) {
argb.addTokenized(args);
}
if (workdir != null) {
argb.add("-w", workdir);
}
for (Map.Entry<String, String> volume : volumes.entrySet()) {
argb.add("-v", volume.getKey() + ":" + volume.getValue());
}
for (String containerId : volumesFromContainers) {
argb.add("--volumes-from", containerId);
}
for (Map.Entry<String, String> variable : containerEnv.entrySet()) {
argb.add("-e");
argb.addMasked(variable.getKey()+"="+variable.getValue());
}
argb.add(image).add(command);
LaunchResult result = launch(launchEnv, false, null, argb);
if (result.getStatus() == 0) {
return result.getOut();
} else {
throw new IOException(String.format("Failed to run image '%s'. Error: %s", image, result.getErr()));
}
}
/**
* Run a docker image.
*
* @param launchEnv Docker client launch environment.
* @param image The image name.
* @param args Any additional arguments for the {@code docker run} command.
* @param workdir The working directory in the container, or {@code null} for default.
* @param volumes Volumes to be bound. Supply an empty list if no volumes are to be bound.
* @param volumesFromContainers Mounts all volumes from the given containers.
* @param containerEnv Environment variables to set in container.
* @param user The <strong>uid:gid</strong> to execute the container command as. Use {@link #whoAmI()}.
* @param command The command to execute in the image container being run.
* @return The container ID.
*/
public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map<String, String> volumes, @Nonnull Collection<String> volumesFromContainers, @Nonnull EnvVars containerEnv, @Nonnull String user, @Nonnull String... command) throws IOException, InterruptedException {
ArgumentListBuilder argb = new ArgumentListBuilder();
argb.add("run", "-t", "-d");
// Username might be empty because we are running on Windows
if (StringUtils.isNotEmpty(user)) {
argb.add("-u", user);
}
if (args != null) {
argb.addTokenized(args);
}
if (workdir != null) {
argb.add("-w", workdir);
}
for (Map.Entry<String, String> volume : volumes.entrySet()) {
argb.add("-v", volume.getKey() + ":" + volume.getValue() + ":rw,z");
}
for (String containerId : volumesFromContainers) {
argb.add("--volumes-from", containerId);
}
for (Map.Entry<String, String> variable : containerEnv.entrySet()) {
argb.add("-e");
argb.addMasked(variable.getKey()+"="+variable.getValue());
}
argb.add(image).add(command);
LaunchResult result = launch(launchEnv, false, null, argb);
if (result.getStatus() == 0) {
return result.getOut();
} else {
throw new IOException(String.format("Failed to run image '%s'. Error: %s", image, result.getErr()));
}
}
@Override protected Void run() throws Exception {
Launcher.ProcStarter ps = getContext().get(Launcher.class).launch();
ps.envs("SENSITIVE=s3cr3t");
if (masking) {
ps.cmds(new ArgumentListBuilder("echo", "goodbye", "from").addMasked(Secret.fromString("mystery")).add("step"));
} else {
ps.cmds("echo", "hello", "from", "some", "step");
}
ps.stdout(getContext().get(TaskListener.class));
if (ps.join() != 0) {
throw new IOException("failed to run echo");
}
return null;
}
protected ArgumentListBuilder appendInventory(ArgumentListBuilder args)
throws IOException, InterruptedException, AnsibleInvocationException
{
if (inventory == null) {
// throw new AnsibleInvocationException(
// "The inventory of hosts and groups is not defined. Check the job configuration.");
return args;
}
inventory.addArgument(args, ws, envVars, listener);
return args;
}
protected ArgumentListBuilder appendSudo(ArgumentListBuilder args) {
if (sudo) {
args.add("-s");
if (StringUtils.isNotBlank(sudoUser)) {
args.add("-U").add(envVars.expand(sudoUser));
}
}
return args;
}
protected ArgumentListBuilder prependPasswordCredentials(ArgumentListBuilder args) {
if (credentials instanceof UsernamePasswordCredentials) {
UsernamePasswordCredentials passwordCredentials = (UsernamePasswordCredentials)credentials;
args.add("sshpass").addMasked("-p" + Secret.toString(passwordCredentials.getPassword()));
}
return args;
}
public boolean execute(ArgumentListBuilder args, Map<String, String> environment)
throws IOException, InterruptedException
{
return launcher.launch()
.pwd(ws)
.envs(environment)
.cmds(args)
.stdout(listener).join() == 0;
}
@Test
@Ignore("Secret can neither be instanced nor mocked")
public void should_handle_password_credentials() throws Exception {
// Given
Inventory inventory = new InventoryPath("/tmp/hosts");
StandardUsernamePasswordCredentials password = mock(StandardUsernamePasswordCredentials.class);
when(password.getUsername()).thenReturn("mylogin");
when(password.getPassword()).thenReturn(Secret.fromString("aStrongSecretPassword"));
BuildListener listener = mock(BuildListener.class);
CLIRunner runner = mock(CLIRunner.class);
AbstractBuild<?,?> build = mock(AbstractBuild.class);
when(build.getEnvironment(any(TaskListener.class))).thenReturn(new EnvVars());
AnsibleAdHocCommandInvocation invocation = new AnsibleAdHocCommandInvocation("/usr/local/bin/ansible", build, listener);
invocation.setHostPattern("localhost");
invocation.setInventory(inventory);
invocation.setModule("ping");
invocation.setCredentials(password);
invocation.setForks(5);
// When
invocation.execute(runner);
// Then
ArgumentCaptor<ArgumentListBuilder> argument = ArgumentCaptor.forClass(ArgumentListBuilder.class);
verify(runner).execute(argument.capture(), anyMap());
assertThat(argument.getValue().toString())
.isEqualTo("sshpass ****** /usr/local/bin/ansible localhost -i /tmp/hosts -m ping -f 5 " +
"-u" +
" mylogin -k");
}
private ArgumentListBuilder getConduitCommand() {
ArgumentListBuilder builder = new ArgumentListBuilder(this.arcPath, this.methodName);
builder.add(arguments);
if (!CommonUtils.isBlank(this.conduitUrl)) {
builder.add("--conduit-uri=" + this.conduitUrl);
}
if (!CommonUtils.isBlank(this.conduitToken)) {
builder.addMasked("--conduit-token=" + this.conduitToken);
}
return builder;
}
@Override
public KeyMaterial materialize() throws IOException, InterruptedException {
FilePath dockerConfig = createSecretsDirectory();
// read the existing docker config file, which might hold some important settings (e.b. proxies)
FilePath configJsonPath = FilePath.getHomeDirectory(this.launcher.getChannel()).child(".docker").child(DOCKER_CONFIG_FILENAME);
if (configJsonPath.exists()) {
String configJson = configJsonPath.readToString();
if (StringUtils.isNotBlank(configJson)) {
launcher.getListener().getLogger().print("Using the existing docker config file.");
JSONObject json = JSONObject.fromObject(configJson);
for (String property : BLACKLISTED_PROPERTIES) {
Object value = json.remove(property);
if (value != null) {
launcher.getListener().getLogger().print("Removing blacklisted property: " + property);
}
}
dockerConfig.child(DOCKER_CONFIG_FILENAME).write(json.toString(), StandardCharsets.UTF_8.name());
}
}
try {
// TODO on Docker 17.07+ use --password-stdin
EnvVars envWithConfig = new EnvVars(env);
envWithConfig.put("DOCKER_CONFIG", dockerConfig.getRemote());
if (launcher.launch().cmds(new ArgumentListBuilder(dockerExecutable, "login", "-u", username, "-p").add(password, true).add(endpoint)).envs(envWithConfig).stdout(listener).join() != 0) {
throw new AbortException("docker login failed");
}
} catch (IOException | InterruptedException x) {
try {
dockerConfig.deleteRecursive();
} catch (Exception x2) {
x.addSuppressed(x2);
}
throw x;
}
return new RegistryKeyMaterial(dockerConfig, new EnvVars("DOCKER_CONFIG", dockerConfig.getRemote()));
}
/**
* SECURITY-1534 found that arguments
* added to a git URL from the user interface can allow a user
* with job creation permissions to execute an arbitrary program
* on the git server if the git server is configured to allow
* custom pack programs. Reject a URL if it includes invalid
* content.
*/
private void addCheckedRemoteUrl(@NonNull ArgumentListBuilder args, @NonNull String url) {
String trimmedUrl = url.trim();
/* Don't check for invalid args if URL starts with known good cases.
* Known good cases include:
* '/' - Unix local file
* 'C:' - Windows local file
* 'file:', 'git:', 'http:', 'https:', 'ssh:', and '[email protected]' - known protocols
*/
if (CHECK_REMOTE_URL
&& !trimmedUrl.startsWith("/")
&& !trimmedUrl.startsWith("\\\\")
&& !trimmedUrl.startsWith("file:")
&& !trimmedUrl.startsWith("git:")
&& !trimmedUrl.startsWith("[email protected]")
&& !trimmedUrl.startsWith("http:")
&& !trimmedUrl.startsWith("https:")
&& !trimmedUrl.startsWith("ssh:")
&& !trimmedUrl.matches("^[A-Za-z]:.+")) {
/* Not one of the known good cases, check if this could be a bad case
* Bad cases include:
* '-' - starts with 'dash' as possible argument
* '`' - includes backquote in the string (command execution)
* ' ' - includes a space (not guaranteed a threat, but threat risk increases)
*/
if (trimmedUrl.startsWith("-")
|| trimmedUrl.contains("`")
|| trimmedUrl.contains("--upload-pack=")
|| trimmedUrl.matches(".*\\s+.*")) {
throw new GitException("Invalid remote URL: " + url);
}
}
// Mark the end of options for git versions that support it.
// Tells command line git that later arguments are operands, not options.
// See POSIX 1-2017 guideline 10.
if (isAtLeastVersion(2, 8, 0, 0)) {
args.add("--"); // SECURITY-1534 - end of options, causes tests to fail with git 2.7.4 and older
}
args.add(trimmedUrl);
}
/** {@inheritDoc} */
@Override
public void fetch(String remoteName, RefSpec... refspec) throws GitException, InterruptedException {
listener.getLogger().println(
"Fetching upstream changes"
+ (remoteName != null ? " from " + remoteName : ""));
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("fetch", "-t");
if (USE_FORCE_FETCH && isAtLeastVersion(2, 20, 0, 0)) {
/* CLI git 2.20.0 fixed a long-standing bug that now requires --force to update existing tags */
args.add("--force");
}
if (remoteName == null)
remoteName = getDefaultRemote();
String url = getRemoteUrl(remoteName);
if (url == null)
throw new GitException("remote." + remoteName + ".url not defined");
addCheckedRemoteUrl(args, url);
if (refspec != null && refspec.length > 0)
for (RefSpec rs: refspec)
if (rs != null)
args.add(rs.toString());
StandardCredentials cred = credentials.get(url);
if (cred == null) cred = defaultCredentials;
launchCommandWithCredentials(args, workspace, cred, url);
}
/**
* Reset submodules
*
* @param recursive if true, will recursively reset submodules (requres git>=1.6.5)
* @param hard if true, the --hard argument will be passed to submodule reset
* @throws hudson.plugins.git.GitException if executing the git command fails
* @throws java.lang.InterruptedException if git command interrupted
*/
public void submoduleReset(boolean recursive, boolean hard) throws GitException, InterruptedException {
ArgumentListBuilder args = new ArgumentListBuilder();
args.add("submodule", "foreach");
if (recursive) {
args.add("--recursive");
}
args.add("git reset" + (hard ? " --hard" : ""));
launchCommand(args);
}