Initial commit
This commit is contained in:
commit
c230f7f979
28 changed files with 1040 additions and 0 deletions
96
src/bld/java/rife/bld/extension/ExecOperationBuild.java
Normal file
96
src/bld/java/rife/bld/extension/ExecOperationBuild.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package rife.bld.extension;
|
||||
|
||||
import rife.bld.BuildCommand;
|
||||
import rife.bld.Project;
|
||||
import rife.bld.publish.PublishDeveloper;
|
||||
import rife.bld.publish.PublishLicense;
|
||||
import rife.bld.publish.PublishScm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static rife.bld.dependencies.Repository.MAVEN_CENTRAL;
|
||||
import static rife.bld.dependencies.Repository.RIFE2_RELEASES;
|
||||
import static rife.bld.dependencies.Scope.compile;
|
||||
import static rife.bld.dependencies.Scope.test;
|
||||
import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING;
|
||||
|
||||
public class ExecOperationBuild extends Project {
|
||||
public ExecOperationBuild() {
|
||||
pkg = "rife.bld.extension";
|
||||
name = "ExecOperation";
|
||||
version = version(0, 9, 0, "SNAPSHOT");
|
||||
|
||||
javaRelease = 17;
|
||||
downloadSources = true;
|
||||
autoDownloadPurge = true;
|
||||
repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES);
|
||||
|
||||
scope(compile)
|
||||
.include(dependency("com.uwyn.rife2", "bld", version(1, 7, 2)));
|
||||
scope(test)
|
||||
.include(dependency("org.jsoup", "jsoup", version(1, 16, 1)))
|
||||
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 0)))
|
||||
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 0)))
|
||||
.include(dependency("org.assertj:assertj-joda-time:2.2.0"));
|
||||
|
||||
javadocOperation()
|
||||
.javadocOptions()
|
||||
.docLint(NO_MISSING)
|
||||
.link("https://rife2.github.io/bld/")
|
||||
.link("https://rife2.github.io/rife2/");
|
||||
|
||||
publishOperation()
|
||||
.repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2"))
|
||||
.info()
|
||||
.groupId("com.uwyn.rife2")
|
||||
.artifactId("bld-exec")
|
||||
.description("Command Line Execution Extension for bld ")
|
||||
.url("https://github.com/rife2/bld-exec")
|
||||
.developer(new PublishDeveloper().id("ethauvin").name("Erik C. Thauvin").email("erik@thauvin.net")
|
||||
.url("https://erik.thauvin.net/"))
|
||||
.license(new PublishLicense().name("The Apache License, Version 2.0")
|
||||
.url("http://www.apache.org/licenses/LICENSE-2.0.txt"))
|
||||
.scm(new PublishScm().connection("scm:git:https://github.com/rife2/bld-exec.git")
|
||||
.developerConnection("scm:git:git@github.com:rife2/bld-exec.git")
|
||||
.url("https://github.com/rife2/bld-exec"))
|
||||
.signKey(property("sign.key"))
|
||||
.signPassphrase(property("sign.passphrase"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new ExecOperationBuild().start(args);
|
||||
}
|
||||
|
||||
@BuildCommand(summary = "Generates JaCoCo Reports")
|
||||
public void jacoco() throws IOException {
|
||||
new JacocoReportOperation()
|
||||
.fromProject(this)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@BuildCommand(summary = "Runs PMD analysis")
|
||||
public void pmd() {
|
||||
new PmdOperation()
|
||||
.fromProject(this)
|
||||
.failOnViolation(true)
|
||||
.ruleSets("config/pmd.xml")
|
||||
.execute();
|
||||
}
|
||||
}
|
27
src/main/java/rife/bld/extension/ExecFail.java
Normal file
27
src/main/java/rife/bld/extension/ExecFail.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package rife.bld.extension;
|
||||
|
||||
/**
|
||||
* The failure modes enumeration.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
|
||||
* @since 1.0
|
||||
*/
|
||||
public enum ExecFail {
|
||||
ALL, EXIT, NONE, NORMAL, OUTPUT, STDERR, STDOUT
|
||||
}
|
146
src/main/java/rife/bld/extension/ExecOperation.java
Normal file
146
src/main/java/rife/bld/extension/ExecOperation.java
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package rife.bld.extension;
|
||||
|
||||
import rife.bld.BaseProject;
|
||||
import rife.bld.operations.AbstractOperation;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Executes a command on the command line.
|
||||
*
|
||||
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ExecOperation extends AbstractOperation<ExecOperation> {
|
||||
private static final Logger LOGGER = Logger.getLogger(ExecOperation.class.getName());
|
||||
private final List<String> args_ = new ArrayList<>();
|
||||
private final Set<ExecFail> fail_ = new HashSet<>();
|
||||
private BaseProject project_;
|
||||
private String workDir_;
|
||||
|
||||
/**
|
||||
* Configures the command and arguments to be executed.
|
||||
* <p>
|
||||
* For example:
|
||||
* <p><ul>
|
||||
* <li>{@code command("cmd", "/c", "stop.bat")}</li>
|
||||
* <li>{@code command("./stop.sh"}</li>
|
||||
* </ul></p>
|
||||
*/
|
||||
public ExecOperation command(String... arg) {
|
||||
args_.addAll(List.of(arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the command.
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (project_ == null) {
|
||||
LOGGER.severe("A project must be specified.");
|
||||
}
|
||||
var errorMessage = new StringBuilder(27);
|
||||
|
||||
final File workDir;
|
||||
if (workDir_ == null || workDir_.isBlank()) {
|
||||
workDir = new File(project_.workDirectory().getAbsolutePath());
|
||||
} else {
|
||||
workDir = new File(workDir_);
|
||||
}
|
||||
|
||||
if (workDir.isDirectory()) {
|
||||
var pb = new ProcessBuilder();
|
||||
pb.command(args_);
|
||||
pb.directory(workDir);
|
||||
|
||||
var proc = pb.start();
|
||||
var err = proc.waitFor(30, TimeUnit.SECONDS);
|
||||
var stdout = readStream(proc.getInputStream());
|
||||
var stderr = readStream(proc.getErrorStream());
|
||||
|
||||
if (!err) {
|
||||
errorMessage.append("TIMEOUT");
|
||||
} else if (!fail_.contains(ExecFail.NONE)) {
|
||||
var all = fail_.contains(ExecFail.ALL);
|
||||
var output = fail_.contains(ExecFail.OUTPUT);
|
||||
if ((all || fail_.contains(ExecFail.EXIT) || fail_.contains(ExecFail.NORMAL)) && proc.exitValue() > 0) {
|
||||
errorMessage.append("EXIT ").append(proc.exitValue());
|
||||
if (!stderr.isEmpty()) {
|
||||
errorMessage.append(", STDERR -> ").append(stderr.get(0));
|
||||
} else if (!stdout.isEmpty()) {
|
||||
errorMessage.append(", STDOUT -> ").append(stdout.get(0));
|
||||
}
|
||||
} else if ((all || output || fail_.contains(ExecFail.STDERR) || fail_.contains(ExecFail.NORMAL))
|
||||
&& !stderr.isEmpty()) {
|
||||
errorMessage.append("STDERR -> ").append(stderr.get(0));
|
||||
} else if ((all || output || fail_.contains(ExecFail.STDOUT)) && !stdout.isEmpty()) {
|
||||
errorMessage.append("STDOUT -> ").append(stdout.get(0));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorMessage.append("Invalid working directory: ").append(workDir.getCanonicalPath());
|
||||
}
|
||||
|
||||
if (!errorMessage.isEmpty()) {
|
||||
throw new IOException(errorMessage.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the failure mode.
|
||||
*
|
||||
* @see ExecFail
|
||||
*/
|
||||
public ExecOperation fail(ExecFail... fail) {
|
||||
fail_.addAll(Set.of(fail));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an Exec operation from a {@link BaseProject}.
|
||||
*/
|
||||
public ExecOperation fromProject(BaseProject project) {
|
||||
project_ = project;
|
||||
return this;
|
||||
}
|
||||
|
||||
private List<String> readStream(InputStream stream) {
|
||||
var lines = new ArrayList<String>();
|
||||
try (var scanner = new Scanner(stream)) {
|
||||
while (scanner.hasNextLine()) {
|
||||
lines.add(scanner.nextLine());
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the working directory.
|
||||
*/
|
||||
public ExecOperation workDir(String dir) {
|
||||
workDir_ = dir;
|
||||
return this;
|
||||
}
|
||||
}
|
128
src/test/java/rife/bld/extension/ExecOperationTest.java
Normal file
128
src/test/java/rife/bld/extension/ExecOperationTest.java
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package rife.bld.extension;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import rife.bld.BaseProject;
|
||||
import rife.bld.Project;
|
||||
import rife.bld.WebProject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
|
||||
class ExecOperationTest {
|
||||
private static final String FOO = "foo";
|
||||
|
||||
@Test
|
||||
void testAll() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new Project())
|
||||
.command("date")
|
||||
.fail(ExecFail.ALL)
|
||||
.execute()
|
||||
).isInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCat() throws Exception {
|
||||
var tmpFile = new File("hello.tmp");
|
||||
tmpFile.deleteOnExit();
|
||||
new ExecOperation()
|
||||
.fromProject(new Project())
|
||||
.command("touch", tmpFile.getName())
|
||||
.fail(ExecFail.NORMAL)
|
||||
.execute();
|
||||
|
||||
assertThat(tmpFile).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testException() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command(FOO)
|
||||
.execute()).message().startsWith("Cannot run program \"" + FOO + '"');
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExit() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("tail", FOO)
|
||||
.fail(ExecFail.EXIT)
|
||||
.execute()).message().startsWith("EXIT ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNone() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new WebProject())
|
||||
.command("cat", FOO)
|
||||
.fail(ExecFail.NONE)
|
||||
.execute()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOutput() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new WebProject())
|
||||
.command("echo")
|
||||
.fail(ExecFail.OUTPUT)
|
||||
.execute()
|
||||
).message().isEqualTo("STDOUT -> ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStdErr() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("logger", "-s", "Hello")
|
||||
.fail(ExecFail.STDERR)
|
||||
.execute()).message().startsWith("STDERR -> ").endsWith("Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStdOut() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("echo", "Hello")
|
||||
.fail(ExecFail.STDOUT)
|
||||
.execute()).message().isEqualTo("STDOUT -> Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWorkDir() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("echo")
|
||||
.workDir(FOO)
|
||||
.fail(ExecFail.NORMAL)
|
||||
.execute()).message().startsWith("Invalid working directory: ").endsWith(FOO);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue