Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a run system command in a path macro and fixes existing docu #6250

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
80 changes: 80 additions & 0 deletions biz.aQute.bndlib.tests/test/test/MacroTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.condition.OS;

import aQute.bnd.osgi.About;
import aQute.bnd.osgi.Analyzer;
Expand Down Expand Up @@ -1376,6 +1377,85 @@ public void testSystemAllowFail() throws Exception {
}
}

@Test
@DisabledOnOs(WINDOWS)
public void testSystemInPath() throws Exception {
try (Processor p = new Processor()) {
Macro macro = new Macro(p);
assertEquals("/tmp", macro.process("${system-in-path;/tmp;pwd}"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of a hard coded path, you should create a folder in the @InjectTemporaryDirectory temp folder. Then these test can run on all OS.

} catch (IOException e) {
fail(e);
}
}

@Test
@DisabledOnOs({
OS.MAC, OS.LINUX
})
Comment on lines +1392 to +1394
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@DisabledOnOs({
OS.MAC, OS.LINUX
})
@EnabledOnOs(OS.Windows)

public void testSystemInPathWindows() throws Exception {
try (Processor p = new Processor()) {
Macro macro = new Macro(p);
assertEquals("c:\\", macro.process("${system-in-path;c:/;echo %cd%}"));
} catch (IOException e) {
fail(e);
}
}

@Test
@DisabledOnOs(WINDOWS)
/**
* Verify system-allow-fail command
*/
public void testSystemInPathAllowFailWorks() throws Exception {
try (Processor p = new Processor()) {
Macro macro = new Macro(p);
assertEquals("/tmp", macro.process("${system-in-path-allow-fail;/tmp;pwd}"));
} catch (IOException e) {
fail(e);
}
}

@Test
@DisabledOnOs({
OS.MAC, OS.LINUX
})
public void testSystemInPathWindowsAllowFailWorks() throws Exception {
try (Processor p = new Processor()) {
Macro macro = new Macro(p);
assertEquals("c:\\", macro.process("${system-in-path-allow-fail;c:/;echo %cd%}"));
} catch (IOException e) {
fail(e);
}
}

@Test
@DisabledOnOs(WINDOWS)
/**
* Verify system-allow-fail command
*/
public void testSystemInPathAllowFail() throws Exception {
try (Processor p = new Processor()) {
Macro macro = new Macro(p);
assertEquals("", macro.process("${system-in-path-allow-fail;/tmp;mostidioticcommandthatwillsurelyfail}"));
} catch (IOException e) {
fail(e);
}
}

@Test
@DisabledOnOs({
OS.MAC, OS.LINUX
})
public void testSystemInPathWindowsAllowFail() throws Exception {
try (Processor p = new Processor()) {
Macro macro = new Macro(p);
assertEquals("",
macro.process("${system-in-path-allow-fail;c:/;mostidioticcommandthatwillsurelyfail}"));
} catch (IOException e) {
fail(e);
}
}

/**
* Check that variables override macros.
*/
Expand Down
53 changes: 44 additions & 9 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Macro.java
Original file line number Diff line number Diff line change
Expand Up @@ -1195,21 +1195,37 @@ boolean isLocalTarget(String string) {
* System command. Execute a command and insert the result.
*/
public String system_internal(boolean allowFail, String[] args) throws Exception {
return system_internal(allowFail, false, args);
}

/**
* System command. Execute a command and insert the result.
*/
public String system_internal(boolean allowFail, boolean extractPath, String[] args) throws Exception {
if (nosystem)
throw new RuntimeException("Macros in this mode cannot excute system commands");

verifyCommand(args, allowFail ? _system_allow_failHelp : _systemHelp, null, 2, 3);
String command = args[1];
String command = null;
String path = null;
String input = null;

if (args.length > 2) {
input = args[2];
if (extractPath) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section looks overly complex and ripe for errors.

verifyCommand(args, allowFail ? _system_in_path_allow_failHelp : _system_in_pathHelp, null, 3, 4);
path = args[1];
command = args[2];
if (args.length > 3) {
input = args[3];
}
return domain.system(allowFail, path, command, input);
} else {
verifyCommand(args, allowFail ? _system_allow_failHelp : _systemHelp, null, 2, 3);
command = args[1];
if (args.length > 2) {
input = args[2];
}
return domain.system(allowFail, command, input);
}

return domain.system(allowFail, command, input);
}

static final String _systemHelp = "${system;<command>[;<in>]}, execute a system command";
static final String _systemHelp = "${system;<command>[;<in>]}, execute a system command in the projects directory";

public String _system(String[] args) throws Exception {
return system_internal(false, args);
Expand All @@ -1228,6 +1244,25 @@ public String _system_allow_fail(String[] args) throws Exception {
}
}

static final String _system_in_pathHelp = "${system-in-path;<path>;<command>[;<in>]}, execute a system command in the given path";

public String _system_in_path(String[] args) throws Exception {
return system_internal(false, true, args);
}

static final String _system_in_path_allow_failHelp = "${system-in-path-allow-fail;<path>;;<command>[;<in>]}, execute a system command allowing command failure in the given path";

public String _system_in_path_allow_fail(String[] args) throws Exception {
String result = "";
try {
result = system_internal(true, true, args);
return result == null ? "" : result;
} catch (Throwable t) {
/* ignore */
return "";
}
}

static final String _envHelp = "${env;<name>[;alternative]}, get the environment variable";

public String _env(String[] args) {
Expand Down
12 changes: 11 additions & 1 deletion biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,16 @@ public Parameters getParameters(String key, boolean allowDuplicates) {
}

public String system(boolean allowFail, String command, String input) throws IOException, InterruptedException {
return system(allowFail, getBase(), command, input);
}

public String system(boolean allowFail, String runDirectory, String command, String input)
throws IOException, InterruptedException, InterruptedException {
return system(allowFail, new File(runDirectory), command, input);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be IO.getFile(runDirectory) since that will handle some interesting cases like ~ expansion?

}

public String system(boolean allowFail, File runDirectory, String command, String input)
throws IOException, InterruptedException {
List<String> args;
if (IO.isWindows()) {
args = Lists.of("cmd", "/c", Command.windowsQuote(command));
Expand All @@ -2445,7 +2455,7 @@ public String system(boolean allowFail, String command, String input) throws IOE
.collect(toList());
}

Process process = new ProcessBuilder(args).directory(getBase())
Process process = new ProcessBuilder(args).directory(runDirectory)
.start();

try (OutputStream stdin = process.getOutputStream()) {
Expand Down
74 changes: 11 additions & 63 deletions docs/_macros/system.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,20 @@
---
layout: default
class: Macro
title: system ';' STRING ( ';' STRING )?
title: system ';' CMD ( ';' INPUT )?
summary: Execute a system command
---

Executes a System command in the current project directory.
This can be used to execute command line tools. If an INPUT is given, it will be given to the process via the Standard Input.

public String _system(String args[]) throws Exception {
return system_internal(false, args);
}
If the Process exits with anything other than `0`, the result will be become an error.

public String _system_allow_fail(String args[]) throws Exception {
String result = "";
try {
result = system_internal(true, args);
}
catch (Throwable t) {
/* ignore */
}
return result;
}
Usage Example:
```
# Extracts the current git SHA for the Project
Git-SHA: ${system;git rev-list -1 --no-abbrev-commit HEAD}

/**
* System command. Execute a command and insert the result.
*
* @param args
* @param help
* @param patterns
* @param low
* @param high
*/
public String system_internal(boolean allowFail, String args[]) throws Exception {
if (nosystem)
throw new RuntimeException("Macros in this mode cannot excute system commands");

verifyCommand(args, "${" + (allowFail ? "system-allow-fail" : "system")
+ ";<command>[;<in>]}, execute a system command", null, 2, 3);
String command = args[1];
String input = null;

if (args.length > 2) {
input = args[2];
}

if ( File.separatorChar == '\\')
command = "cmd /c \"" + command + "\"";


Process process = Runtime.getRuntime().exec(command, null, domain.getBase());
if (input != null) {
process.getOutputStream().write(input.getBytes("UTF-8"));
}
process.getOutputStream().close();

String s = IO.collect(process.getInputStream(), "UTF-8");
int exitValue = process.waitFor();
if (exitValue != 0)
return exitValue + "";

if (exitValue != 0) {
if (!allowFail) {
domain.error("System command " + command + " failed with exit code " + exitValue);
} else {
domain.warning("System command " + command + " failed with exit code " + exitValue + " (allowed)");

}
}

return s.trim();
}

# Extracts the current git SHA for the Project and enters the password as input
Git-SHA: ${system;git rev-list -1 --no-abbrev-commit HEAD;mypassword}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a helpful example since the command does not care about stdin :-)

```
75 changes: 11 additions & 64 deletions docs/_macros/system_allow_fail.md
Original file line number Diff line number Diff line change
@@ -1,73 +1,20 @@
---
layout: default
class: Macro
title: system_allow_fail ';' STRING ( ';' STRING )?
title: system-allow-fail ';' CMD ( ';' INPUT )?
summary: Execute a system command but ignore any failures
---

Executes a System command in the current project directory.
This can be used to execute command line tools. If an INPUT is given, it will be given to the process via the Standard Input.

If the Process exits with anything other than `0`, the result will be be marked as a warning.

public String _system(String args[]) throws Exception {
return system_internal(false, args);
}
Usage Example:
```
# Extracts the current git SHA for the Project
Git-SHA: ${system-allow-fail;git rev-list -1 --no-abbrev-commit HEAD}

public String _system_allow_fail(String args[]) throws Exception {
String result = "";
try {
result = system_internal(true, args);
}
catch (Throwable t) {
/* ignore */
}
return result;
}

/**
* System command. Execute a command and insert the result.
*
* @param args
* @param help
* @param patterns
* @param low
* @param high
*/
public String system_internal(boolean allowFail, String args[]) throws Exception {
if (nosystem)
throw new RuntimeException("Macros in this mode cannot excute system commands");

verifyCommand(args, "${" + (allowFail ? "system-allow-fail" : "system")
+ ";<command>[;<in>]}, execute a system command", null, 2, 3);
String command = args[1];
String input = null;

if (args.length > 2) {
input = args[2];
}

if ( File.separatorChar == '\\')
command = "cmd /c \"" + command + "\"";


Process process = Runtime.getRuntime().exec(command, null, domain.getBase());
if (input != null) {
process.getOutputStream().write(input.getBytes("UTF-8"));
}
process.getOutputStream().close();

String s = IO.collect(process.getInputStream(), "UTF-8");
int exitValue = process.waitFor();
if (exitValue != 0)
return exitValue + "";

if (exitValue != 0) {
if (!allowFail) {
domain.error("System command " + command + " failed with exit code " + exitValue);
} else {
domain.warning("System command " + command + " failed with exit code " + exitValue + " (allowed)");

}
}

return s.trim();
}

# Extracts the current git SHA for the Project and enters the password as input
Git-SHA: ${system-allow-fail;git rev-list -1 --no-abbrev-commit HEAD;mypassword}
```
20 changes: 20 additions & 0 deletions docs/_macros/system_in_path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
layout: default
class: Macro
title: system-in-path ';' PATH ';' CMD ( ';' INPUT )?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro names are not awesome. Naming is hard :-)

summary: Execute a system command
---

Executes a System command in the given path. The path can be anywhere, even outside the current Project.
This can be used to execute command line tools. If an INPUT is given, it will be given to the process via the Standard Input.

If the Process exits with anything other than `0`, the result will be become an error.

Usage Example:
```
# Extracts the current git SHA in the given path
Git-SHA: ${system-in-path; ~/git/someproject; git rev-list -1 --no-abbrev-commit HEAD}
# Extracts the current git SHA in the given path and enters the password as input
Git-SHA: ${system-in-path; ~/git/someproject; git rev-list -1 --no-abbrev-commit HEAD;mypassword}
```
Loading