Compare commits
No commits in common. "main" and "0.9.1" have entirely different histories.
19 changed files with 282 additions and 315 deletions
21
.github/workflows/bld.yml
vendored
21
.github/workflows/bld.yml
vendored
|
@ -4,28 +4,29 @@ on: [ push, pull_request, workflow_dispatch ]
|
|||
|
||||
jobs:
|
||||
build-bld-project:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [ 17, 21, 24 ]
|
||||
kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ]
|
||||
os: [ ubuntu-latest, windows-latest, macos-latest ]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
java-version: [ 17, 20 ]
|
||||
|
||||
steps:
|
||||
- name: Checkout source repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "zulu"
|
||||
distribution: 'zulu'
|
||||
java-version: ${{ matrix.java-version }}
|
||||
|
||||
- name: Download dependencies
|
||||
- name: Grant execute permission for bld
|
||||
run: chmod +x bld
|
||||
|
||||
- name: Download the dependencies
|
||||
run: ./bld download
|
||||
|
||||
- name: Run tests
|
||||
- name: Run tests with bld
|
||||
run: ./bld compile test
|
12
.github/workflows/pages.yml
vendored
12
.github/workflows/pages.yml
vendored
|
@ -30,14 +30,14 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout source repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: "zulu"
|
||||
distribution: 'zulu'
|
||||
java-version: 17
|
||||
|
||||
- name: Build Javadocs
|
||||
|
@ -47,11 +47,11 @@ jobs:
|
|||
uses: actions/configure-pages@v3
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
# Upload generated Javadocs repository
|
||||
path: "build/javadoc/"
|
||||
path: 'build/javadoc/'
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v1
|
6
.idea/bld.xml
generated
6
.idea/bld.xml
generated
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="BldConfiguration">
|
||||
<events />
|
||||
</component>
|
||||
</project>
|
4
.idea/copyright/Apache_License.xml
generated
4
.idea/copyright/Apache_License.xml
generated
|
@ -1,6 +1,6 @@
|
|||
<component name="CopyrightManager">
|
||||
<copyright>
|
||||
<option name="notice" value="Copyright 2023-&#36;today.year 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." />
|
||||
<option name="notice" value="Copyright 2023-Copyright &#36;today.yearamp;#36;today.year 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." />
|
||||
<option name="myName" value="Apache License" />
|
||||
</copyright>
|
||||
</component>
|
||||
</component>
|
||||
|
|
13
.idea/icon.svg
generated
13
.idea/icon.svg
generated
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 179 108" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-210.511,-96.3382)">
|
||||
<g transform="matrix(1,0,0,1,-23.3386,-649.816)">
|
||||
<g transform="matrix(0.221288,0,0,0.24,73.9536,390.254)">
|
||||
<path d="M722.568,1482.92L722.568,1921.1L808.968,1921.1L808.968,1888.7C822.168,1907.9 846.168,1930.1 893.568,1930.1C933.168,1930.1 961.968,1917.5 985.368,1893.5C1012.97,1865.9 1027.37,1827.5 1027.37,1786.1C1027.37,1741.7 1011.17,1705.1 985.368,1680.5C961.968,1658.3 928.968,1644.5 892.368,1644.5C862.968,1644.5 830.568,1654.1 808.968,1683.5L808.968,1482.92L722.568,1482.92ZM871.368,1718.9C888.768,1718.9 903.768,1723.7 917.568,1736.9C930.168,1748.9 938.568,1766.3 938.568,1787.9C938.568,1807.7 930.168,1825.1 917.568,1837.1C904.368,1849.7 887.568,1855.7 872.568,1855.7C856.368,1855.7 837.168,1849.1 823.368,1835.9C813.168,1826.3 803.568,1810.1 803.568,1787.9C803.568,1765.1 812.568,1749.5 822.768,1738.7C836.568,1724.3 852.768,1718.9 871.368,1718.9Z" style="fill:rgb(35,146,255);fill-rule:nonzero;"/>
|
||||
<rect x="1083.77" y="1482.92" width="86.4" height="438.182" style="fill:rgb(250,144,82);fill-rule:nonzero;"/>
|
||||
<path d="M1531.37,1482.92L1444.97,1482.92L1444.97,1683.5C1423.37,1654.1 1390.97,1644.5 1361.57,1644.5C1324.97,1644.5 1291.97,1658.3 1268.57,1680.5C1242.77,1705.1 1226.57,1741.7 1226.57,1786.1C1226.57,1827.5 1240.97,1865.9 1268.57,1893.5C1291.97,1917.5 1320.77,1930.1 1360.37,1930.1C1407.77,1930.1 1431.77,1907.9 1444.97,1888.7L1444.97,1921.1L1531.37,1921.1L1531.37,1482.92ZM1382.57,1718.9C1401.17,1718.9 1417.37,1724.3 1431.17,1738.7C1441.37,1749.5 1450.37,1765.1 1450.37,1787.9C1450.37,1810.1 1440.77,1826.3 1430.57,1835.9C1416.77,1849.1 1397.57,1855.7 1381.37,1855.7C1366.37,1855.7 1349.57,1849.7 1336.37,1837.1C1323.77,1825.1 1315.37,1807.7 1315.37,1787.9C1315.37,1766.3 1323.77,1748.9 1336.37,1736.9C1350.17,1723.7 1365.17,1718.9 1382.57,1718.9Z" style="fill:rgb(35,146,255);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
4
.idea/libraries/bld.xml
generated
4
.idea/libraries/bld.xml
generated
|
@ -2,12 +2,12 @@
|
|||
<library name="bld">
|
||||
<CLASSES>
|
||||
<root url="file://$PROJECT_DIR$/lib/bld" />
|
||||
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.bld/dist/bld-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="file://$PROJECT_DIR$/lib/bld" />
|
||||
<root url="jar://$USER_HOME$/.bld/dist/bld-2.2.1-sources.jar!/" />
|
||||
<root url="jar://$USER_HOME$/.bld/dist/bld-1.8.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
<excluded>
|
||||
<root url="jar://$PROJECT_DIR$/lib/bld/bld-wrapper.jar!/" />
|
||||
|
|
4
.idea/libraries/compile.xml
generated
4
.idea/libraries/compile.xml
generated
|
@ -7,7 +7,7 @@
|
|||
<SOURCES>
|
||||
<root url="file://$PROJECT_DIR$/lib/compile" />
|
||||
</SOURCES>
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="true" type="SOURCES" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="false" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/compile" recursive="false" type="SOURCES" />
|
||||
</library>
|
||||
</component>
|
4
.idea/libraries/runtime.xml
generated
4
.idea/libraries/runtime.xml
generated
|
@ -8,7 +8,7 @@
|
|||
<SOURCES>
|
||||
<root url="file://$PROJECT_DIR$/lib/runtime" />
|
||||
</SOURCES>
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="true" type="SOURCES" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="false" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/runtime" recursive="false" type="SOURCES" />
|
||||
</library>
|
||||
</component>
|
4
.idea/libraries/test.xml
generated
4
.idea/libraries/test.xml
generated
|
@ -8,7 +8,7 @@
|
|||
<SOURCES>
|
||||
<root url="file://$PROJECT_DIR$/lib/test" />
|
||||
</SOURCES>
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="true" type="SOURCES" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="false" />
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/test" recursive="false" type="SOURCES" />
|
||||
</library>
|
||||
</component>
|
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
|
@ -5,14 +5,7 @@
|
|||
"type": "java",
|
||||
"name": "Run Tests",
|
||||
"request": "launch",
|
||||
"mainClass": "org.junit.platform.console.ConsoleLauncher",
|
||||
"args": [
|
||||
"--details=verbose",
|
||||
"--scan-classpath",
|
||||
"--disable-banner",
|
||||
"--disable-ansi-colors",
|
||||
"--exclude-engine=junit-platform-suite",
|
||||
"--exclude-engine=junit-vintage"]
|
||||
"mainClass": "rife.bld.extension.ExecOperationTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
|
@ -3,13 +3,13 @@
|
|||
"src/main/java",
|
||||
"src/main/resources",
|
||||
"src/test/java",
|
||||
"src/test/resources",
|
||||
"src/bld/java",
|
||||
"src/bld/resources"
|
||||
"src/bld/java"
|
||||
],
|
||||
"java.configuration.updateBuildConfiguration": "automatic",
|
||||
"java.project.referencedLibraries": [
|
||||
"${HOME}/.bld/dist/bld-2.2.1.jar",
|
||||
"lib/**/*.jar"
|
||||
"${HOME}/.bld/dist/bld-1.8.0.jar",
|
||||
"lib/compile/*.jar",
|
||||
"lib/runtime/*.jar",
|
||||
"lib/test/*.jar"
|
||||
]
|
||||
}
|
||||
|
|
39
README.md
39
README.md
|
@ -2,20 +2,12 @@
|
|||
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
[](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
|
||||
[](https://rife2.com/bld)
|
||||
[](https://rife2.com/bld)
|
||||
[](https://repo.rife2.com/#/releases/com/uwyn/rife2/bld-exec)
|
||||
[](https://repo.rife2.com/#/snapshots/com/uwyn/rife2/bld-exec)
|
||||
[](https://github.com/rife2/bld-exec/actions/workflows/bld.yml)
|
||||
|
||||
To install the latest version, add the following to the `lib/bld/bld-wrapper.properties` file:
|
||||
|
||||
```properties
|
||||
bld.extension-exec=com.uwyn.rife2:bld-exec
|
||||
```
|
||||
|
||||
For more information, please refer to the [extensions](https://github.com/rife2/bld/wiki/Extensions) documentation.
|
||||
|
||||
## Execute a Command
|
||||
To install, please refer to the [extensions documentation](https://github.com/rife2/bld/wiki/Extensions).
|
||||
|
||||
To execute a command at the command line, add the following to your build file:
|
||||
|
||||
|
@ -29,15 +21,16 @@ public void startServer() throws Exception {
|
|||
}
|
||||
```
|
||||
|
||||
## Exit Value
|
||||
### Failure Modes
|
||||
|
||||
Use the `failOnExit` function to specify whether a command non-zero exit value (status) constitutes a failure.
|
||||
Use the `fail` function to specify whether data returned to the standard streams and/or an abnormal exit value
|
||||
constitute a failure.
|
||||
|
||||
```java
|
||||
@BuildCommand
|
||||
public void startServer() throws Exception {
|
||||
final List<String> cmds;
|
||||
if (System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
cmds = List.of("cmd", "/c", "stop.bat");
|
||||
} else {
|
||||
cmds = List.of("./stop.sh");
|
||||
|
@ -45,14 +38,28 @@ public void startServer() throws Exception {
|
|||
new ExecOperation()
|
||||
.fromProject(this)
|
||||
.command(cmds)
|
||||
.failOneExit(false)
|
||||
.fail(ExecFail.STDERR)
|
||||
.execute();
|
||||
}
|
||||
```
|
||||
|
||||
## Work Directory
|
||||
The following predefined values are available:
|
||||
|
||||
You can also specify the work directory:
|
||||
| Name | Failure When |
|
||||
|:------------------|:-----------------------------------------------------------------|
|
||||
| `ExecFail.EXIT` | Exit value > 0 |
|
||||
| `ExecFail.NORMAL` | Exit value > 0 or any data to the standard error stream (stderr) |
|
||||
| `ExecFail.OUTPUT` | Any data to the standard output stream (stdout) or stderr. |
|
||||
| `ExecFail.STDERR` | Any data to stderr. |
|
||||
| `ExecFail.STDOUT` | Any data to stdout. |
|
||||
| `ExecFail.ALL` | Any of the conditions above. |
|
||||
| `ExecFail.NONE` | Never fails. |
|
||||
|
||||
`ExecFail.NORMAL` is the default value.
|
||||
|
||||
## Working Directory
|
||||
|
||||
You can also specify the working directory:
|
||||
|
||||
```java
|
||||
@BuildCommand
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<!-- BEST PRACTICES -->
|
||||
<rule ref="category/java/bestpractices.xml">
|
||||
<exclude name="AvoidPrintStackTrace"/>
|
||||
<exclude name="JUnit4TestShouldUseTestAnnotation"/>
|
||||
<exclude name="JUnitTestContainsTooManyAsserts"/>
|
||||
<exclude name="GuardLogStatement"/>
|
||||
<exclude name="UnitTestContainsTooManyAsserts"/>
|
||||
<exclude name="UnitTestShouldUseTestAnnotation"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/bestpractices.xml/MissingOverride">
|
||||
|
@ -19,13 +19,12 @@
|
|||
</properties>
|
||||
</rule>
|
||||
|
||||
|
||||
<!-- CODE STYLE -->
|
||||
<rule ref="category/java/codestyle.xml">
|
||||
<exclude name="AtLeastOneConstructor"/>
|
||||
<exclude name="ClassNamingConventions"/>
|
||||
<exclude name="CommentDefaultAccessModifier"/>
|
||||
<exclude name="ConfusingTernary"/>
|
||||
<exclude name="CommentDefaultAccessModifier"/>
|
||||
<exclude name="FieldNamingConventions"/>
|
||||
<exclude name="LocalVariableCouldBeFinal"/>
|
||||
<exclude name="LongVariable"/>
|
||||
|
@ -35,9 +34,8 @@
|
|||
<exclude name="ShortClassName"/>
|
||||
<exclude name="ShortMethodName"/>
|
||||
<exclude name="ShortVariable"/>
|
||||
<exclude name="UseExplicitTypes"/>
|
||||
<exclude name="UseUnderscoresInNumericLiterals"/>
|
||||
<exclude name="UselessParentheses"/>
|
||||
<exclude name="UseUnderscoresInNumericLiterals"/>
|
||||
</rule>
|
||||
|
||||
<rule ref="category/java/codestyle.xml/UnnecessaryImport">
|
||||
|
@ -53,6 +51,8 @@
|
|||
<exclude name="AvoidUncheckedExceptionsInSignatures"/>
|
||||
<exclude name="CognitiveComplexity"/>
|
||||
<exclude name="CyclomaticComplexity"/>
|
||||
<exclude name="ExcessiveClassLength"/>
|
||||
<exclude name="ExcessiveMethodLength"/>
|
||||
<exclude name="ExcessiveParameterList"/>
|
||||
<exclude name="ExcessivePublicCount"/>
|
||||
<exclude name="GodClass"/>
|
||||
|
@ -106,4 +106,4 @@
|
|||
<!-- SECURITY -->
|
||||
<rule ref="category/java/security.xml">
|
||||
</rule>
|
||||
</ruleset>
|
||||
</ruleset>
|
Binary file not shown.
|
@ -1,7 +1,8 @@
|
|||
bld.downloadExtensionJavadoc=false
|
||||
bld.downloadExtensionSources=true
|
||||
bld.extension-pmd=com.uwyn.rife2:bld-pmd:0.9.5
|
||||
bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.2
|
||||
bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
|
||||
bld.downloadLocation=
|
||||
bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.2
|
||||
bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES
|
||||
bld.sourceDirectories=
|
||||
bld.version=2.2.1
|
||||
bld.version=1.8.0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2023-2025 the original author or authors.
|
||||
* Copyright 2023-2024 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.
|
||||
|
@ -22,9 +22,11 @@ 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.*;
|
||||
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;
|
||||
|
@ -33,21 +35,19 @@ public class ExecOperationBuild extends Project {
|
|||
public ExecOperationBuild() {
|
||||
pkg = "rife.bld.extension";
|
||||
name = "ExecOperation";
|
||||
version = version(1, 0, 5);
|
||||
version = version(0, 9, 1);
|
||||
|
||||
javaRelease = 17;
|
||||
|
||||
downloadSources = true;
|
||||
autoDownloadPurge = true;
|
||||
repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES);
|
||||
|
||||
repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS);
|
||||
|
||||
scope(compile)
|
||||
.include(dependency("com.uwyn.rife2", "bld", version(2, 2, 1)));
|
||||
.include(dependency("com.uwyn.rife2", "bld", version(1, 8, 0)));
|
||||
scope(test)
|
||||
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1)))
|
||||
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1)))
|
||||
.include(dependency("org.assertj", "assertj-core", version(3, 27, 3)));
|
||||
.include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 1)))
|
||||
.include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 1)))
|
||||
.include(dependency("org.assertj", "assertj-core", version(3, 25, 2)));
|
||||
|
||||
javadocOperation()
|
||||
.javadocOptions()
|
||||
|
@ -58,26 +58,28 @@ public class ExecOperationBuild extends Project {
|
|||
|
||||
publishOperation()
|
||||
.repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2"))
|
||||
.repository(repository("github"))
|
||||
.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/")
|
||||
.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("https://www.apache.org/licenses/LICENSE-2.0.txt")
|
||||
.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")
|
||||
.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"));
|
||||
|
@ -87,8 +89,15 @@ public class ExecOperationBuild extends Project {
|
|||
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() throws Exception {
|
||||
public void pmd() {
|
||||
new PmdOperation()
|
||||
.fromProject(this)
|
||||
.failOnViolation(true)
|
||||
|
|
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-2024 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
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2023-2025 the original author or authors.
|
||||
* Copyright 2023-2024 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.
|
||||
|
@ -18,14 +18,11 @@ package rife.bld.extension;
|
|||
|
||||
import rife.bld.BaseProject;
|
||||
import rife.bld.operations.AbstractOperation;
|
||||
import rife.bld.operations.exceptions.ExitStatusException;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -38,11 +35,10 @@ import java.util.logging.Logger;
|
|||
*/
|
||||
public class ExecOperation extends AbstractOperation<ExecOperation> {
|
||||
private static final Logger LOGGER = Logger.getLogger(ExecOperation.class.getName());
|
||||
private final Collection<String> args_ = new ArrayList<>();
|
||||
private boolean failOnExit_ = true;
|
||||
private final List<String> args_ = new ArrayList<>();
|
||||
private final Set<ExecFail> fail_ = new HashSet<>();
|
||||
private BaseProject project_;
|
||||
private int timeout_ = 30;
|
||||
private File workDir_;
|
||||
private String workDir_;
|
||||
|
||||
/**
|
||||
* Configures the command and arguments to be executed.
|
||||
|
@ -58,19 +54,10 @@ public class ExecOperation extends AbstractOperation<ExecOperation> {
|
|||
* @see #command(Collection)
|
||||
*/
|
||||
public ExecOperation command(String... arg) {
|
||||
return command(List.of(arg));
|
||||
args_.addAll(List.of(arg));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command and arguments to be executed.
|
||||
*
|
||||
* @return the command and arguments
|
||||
*/
|
||||
public Collection<String> command() {
|
||||
return args_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures the command and arguments to be executed.
|
||||
*
|
||||
|
@ -89,62 +76,86 @@ public class ExecOperation extends AbstractOperation<ExecOperation> {
|
|||
@Override
|
||||
public void execute() throws Exception {
|
||||
if (project_ == null) {
|
||||
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
|
||||
LOGGER.severe("A project must be specified.");
|
||||
}
|
||||
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
|
||||
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 {
|
||||
final File workDir = Objects.requireNonNullElseGet(workDir_,
|
||||
() -> new File(project_.workDirectory().getAbsolutePath()));
|
||||
workDir = new File(workDir_);
|
||||
}
|
||||
|
||||
if (workDir.isDirectory()) {
|
||||
var pb = new ProcessBuilder();
|
||||
pb.command(args_);
|
||||
pb.directory(workDir);
|
||||
|
||||
if (LOGGER.isLoggable(Level.INFO)) {
|
||||
LOGGER.info("Working directory: " + workDir.getAbsolutePath());
|
||||
LOGGER.info(String.join(" ", args_));
|
||||
}
|
||||
|
||||
if (workDir.isDirectory()) {
|
||||
var pb = new ProcessBuilder();
|
||||
pb.inheritIO();
|
||||
pb.command(args_.stream().toList());
|
||||
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 (LOGGER.isLoggable(Level.INFO) && !silent()) {
|
||||
LOGGER.info(String.join(" ", args_));
|
||||
}
|
||||
|
||||
var proc = pb.start();
|
||||
var err = proc.waitFor(timeout_, TimeUnit.SECONDS);
|
||||
|
||||
if (!err) {
|
||||
proc.destroy();
|
||||
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
|
||||
LOGGER.severe("The command timed out.");
|
||||
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));
|
||||
}
|
||||
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
|
||||
} else if (proc.exitValue() != 0 && failOnExit_) {
|
||||
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
|
||||
LOGGER.severe("The command exit value/status is: " + proc.exitValue());
|
||||
}
|
||||
ExitStatusException.throwOnFailure(proc.exitValue());
|
||||
} 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 {
|
||||
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
|
||||
LOGGER.severe("Invalid working directory: " + workDir);
|
||||
}
|
||||
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (LOGGER.isLoggable(Level.INFO) && errorMessage.isEmpty() && !stdout.isEmpty()) {
|
||||
for (var l : stdout) {
|
||||
LOGGER.info(l);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errorMessage.append("Invalid working directory: ").append(workDir.getCanonicalPath());
|
||||
}
|
||||
|
||||
if (!errorMessage.isEmpty()) {
|
||||
throw new IOException(errorMessage.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the operation should fail if the command exit value/status is not 0.
|
||||
* Configure the failure mode.
|
||||
* <p>
|
||||
* Default is {@code TRUE}
|
||||
* The failure modes are:
|
||||
* <ul>
|
||||
* <li>{@link ExecFail#EXIT}<p>Exit value > 0</p></li>
|
||||
* <li>{@link ExecFail#NORMAL}<p>Exit value > 0 or any data to the standard error stream (stderr)</p></li>
|
||||
* <li>{@link ExecFail#OUTPUT}<p>Any data to the standard output stream (stdout) or stderr</p></li>
|
||||
* <li>{@link ExecFail#STDERR}<p>Any data to stderr</p></li>
|
||||
* <li>{@link ExecFail#STDOUT}<p>Any data to stdout</p></li>
|
||||
* <li>{@link ExecFail#ALL}<p>Any of the conditions above</p></li>
|
||||
* <li>{@link ExecFail#NONE}<p>Never fails</p></li>
|
||||
* </ul>
|
||||
*
|
||||
* @param failOnExit The fail on exit toggle
|
||||
* @return this operation instance.
|
||||
* @param fail one or more failure modes
|
||||
* @return this operation instance
|
||||
* @see ExecFail
|
||||
*/
|
||||
public ExecOperation failOnExit(boolean failOnExit) {
|
||||
failOnExit_ = failOnExit;
|
||||
public ExecOperation fail(ExecFail... fail) {
|
||||
fail_.addAll(Set.of(fail));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -159,57 +170,16 @@ public class ExecOperation extends AbstractOperation<ExecOperation> {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the operation should fail if the command exit value/status is not 0.
|
||||
*
|
||||
* @return {@code true} or {@code false}
|
||||
*/
|
||||
public boolean isFailOnExit() {
|
||||
return failOnExit_;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the command timeout.
|
||||
*
|
||||
* @param timeout The timeout in seconds
|
||||
* @return this operation instance
|
||||
*/
|
||||
public ExecOperation timeout(int timeout) {
|
||||
timeout_ = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command timeout.
|
||||
*
|
||||
* @return the timeout
|
||||
*/
|
||||
public int timeout() {
|
||||
return timeout_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the working directory.
|
||||
*
|
||||
* @param dir the directory
|
||||
* @return this operation instance
|
||||
*/
|
||||
public ExecOperation workDir(File dir) {
|
||||
workDir_ = dir;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the working directory.
|
||||
*
|
||||
* @param dir the directory
|
||||
* @return this operation instance
|
||||
*/
|
||||
public ExecOperation workDir(Path dir) {
|
||||
return workDir(dir.toFile());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures the working directory.
|
||||
*
|
||||
|
@ -217,15 +187,7 @@ public class ExecOperation extends AbstractOperation<ExecOperation> {
|
|||
* @return this operation instance
|
||||
*/
|
||||
public ExecOperation workDir(String dir) {
|
||||
return workDir(new File(dir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the working directory.
|
||||
*
|
||||
* @return the directory
|
||||
*/
|
||||
public File workDir() {
|
||||
return workDir_;
|
||||
workDir_ = dir;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2023-2025 the original author or authors.
|
||||
* Copyright 2023-2024 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.
|
||||
|
@ -17,30 +17,54 @@
|
|||
package rife.bld.extension;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import rife.bld.BaseProject;
|
||||
import rife.bld.Project;
|
||||
import rife.bld.WebProject;
|
||||
import rife.bld.operations.exceptions.ExitStatusException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
@SuppressWarnings("PMD.AvoidDuplicateLiterals")
|
||||
|
||||
class ExecOperationTest {
|
||||
private static final String FOO = "foo";
|
||||
private static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.US).contains("win");
|
||||
private static final String HELLO = "Hello";
|
||||
|
||||
@Test
|
||||
void testCommand() {
|
||||
var op = new ExecOperation().fromProject(new WebProject())
|
||||
.command(FOO, "bar");
|
||||
assertThat(op.command()).containsExactly(FOO, "bar");
|
||||
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 testCommandList() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command(List.of("logger", "-s", HELLO))
|
||||
.fail(ExecFail.STDERR)
|
||||
.execute()).message().startsWith("STDERR -> ").endsWith(HELLO);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -53,102 +77,64 @@ class ExecOperationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testExitValue() {
|
||||
List<String> cat;
|
||||
if (IS_WINDOWS) {
|
||||
cat = List.of("cmd", "/c", "type", FOO);
|
||||
} else {
|
||||
cat = List.of("cat", FOO);
|
||||
}
|
||||
void testExit() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command(cat)
|
||||
.execute()).isInstanceOf(ExitStatusException.class);
|
||||
.command("tail", FOO)
|
||||
.fail(ExecFail.EXIT)
|
||||
.execute()).message().startsWith("EXIT ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailOnExit() {
|
||||
List<String> cat;
|
||||
if (IS_WINDOWS) {
|
||||
cat = List.of("cmd", "/c", "type", FOO);
|
||||
} else {
|
||||
cat = List.of("cat", FOO);
|
||||
}
|
||||
var op = new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command(cat)
|
||||
.failOnExit(false);
|
||||
assertThat(op.isFailOnExit()).isFalse();
|
||||
assertThatCode(op::execute).doesNotThrowAnyException();
|
||||
|
||||
op.failOnExit(true);
|
||||
assertThat(op.isFailOnExit()).isTrue();
|
||||
void testNone() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new WebProject())
|
||||
.command("cat", FOO)
|
||||
.fail(ExecFail.NONE)
|
||||
.execute()).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTimeout() {
|
||||
List<String> sleep;
|
||||
if (IS_WINDOWS) {
|
||||
sleep = List.of("cmd", "/c", "timeout", "/t", "10");
|
||||
} else {
|
||||
sleep = List.of("sleep", "10");
|
||||
}
|
||||
var op = new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.timeout(5)
|
||||
.command(sleep);
|
||||
assertThat(op.timeout()).isEqualTo(5);
|
||||
assertThatCode(op::execute).isInstanceOf(ExitStatusException.class);
|
||||
void testOutput() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new WebProject())
|
||||
.command("echo")
|
||||
.fail(ExecFail.OUTPUT)
|
||||
.execute()
|
||||
).message().isEqualTo("STDOUT -> ");
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnabledOnOs({OS.LINUX, OS.MAC})
|
||||
void testTouch() throws Exception {
|
||||
var tmpFile = new File("hello.tmp");
|
||||
tmpFile.deleteOnExit();
|
||||
new ExecOperation()
|
||||
.fromProject(new Project())
|
||||
.timeout(10)
|
||||
.command("touch", tmpFile.getName())
|
||||
.execute();
|
||||
void testStdErr() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("logger", "-s", HELLO)
|
||||
.fail(ExecFail.STDERR)
|
||||
.execute()).message().startsWith("STDERR -> ").endsWith(HELLO);
|
||||
}
|
||||
|
||||
assertThat(tmpFile).exists();
|
||||
@Test
|
||||
void testStdOut() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("echo", HELLO)
|
||||
.fail(ExecFail.STDOUT)
|
||||
.execute()).message().isEqualTo("STDOUT -> Hello");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWorkDir() {
|
||||
List<String> echo;
|
||||
if (IS_WINDOWS) {
|
||||
echo = List.of("cmd", "/c", "echo", FOO);
|
||||
} else {
|
||||
echo = List.of("echo", FOO);
|
||||
}
|
||||
var workDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
var op = new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command(echo)
|
||||
.workDir(workDir);
|
||||
assertThat(op.workDir()).as("as file").isEqualTo(workDir);
|
||||
assertThatCode(op::execute).doesNotThrowAnyException();
|
||||
|
||||
var build = "build";
|
||||
op = op.workDir(build);
|
||||
assertThat(op.workDir()).as("as string").isEqualTo(new File(build));
|
||||
assertThatCode(op::execute).doesNotThrowAnyException();
|
||||
|
||||
op = op.workDir(workDir.toPath());
|
||||
assertThat(op.workDir()).as("as path").isEqualTo(workDir);
|
||||
assertThatCode(op::execute).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWorkDirInvalid() {
|
||||
assertThatCode(() ->
|
||||
new ExecOperation()
|
||||
.fromProject(new BaseProject())
|
||||
.command("echo")
|
||||
.workDir(FOO)
|
||||
.execute()).isInstanceOf(ExitStatusException.class);
|
||||
.fail(ExecFail.NORMAL)
|
||||
.execute()).message().startsWith("Invalid working directory: ").endsWith(FOO);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue