diff --git a/.github/workflows/main-pull-request.yml b/.github/workflows/main-pull-request.yml
new file mode 100644
index 0000000..dd091ab
--- /dev/null
+++ b/.github/workflows/main-pull-request.yml
@@ -0,0 +1,49 @@
+name: Main Pull Request
+
+on:
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: '25'
+ distribution: 'zulu'
+ cache: 'maven'
+
+ - name: Get cache week
+ id: date
+ run: echo "week=$(date +%Y-%W)" >> $GITHUB_OUTPUT
+
+ - name: Cache Docker images
+ id: cache-docker
+ uses: actions/cache@v4
+ with:
+ path: /tmp/docker-images
+ key: docker-images-${{ runner.os }}-${{ steps.date.outputs.week }}
+ restore-keys: |
+ docker-images-${{ runner.os }}-
+
+ - name: Load cached Docker images
+ if: steps.cache-docker.outputs.cache-hit == 'true'
+ run: docker load -i /tmp/docker-images/images.tar
+
+ - name: Pull and save Docker images
+ if: steps.cache-docker.outputs.cache-hit != 'true'
+ run: |
+ docker pull alpine:latest
+ docker pull nginx:latest
+ docker pull rabbitmq:latest
+ mkdir -p /tmp/docker-images
+ docker save alpine:latest nginx:latest rabbitmq:latest -o /tmp/docker-images/images.tar
+
+ - name: Build and Test
+ run: ./mvnw clean install
diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml
new file mode 100644
index 0000000..322a27a
--- /dev/null
+++ b/.github/workflows/publish-snapshot.yml
@@ -0,0 +1,60 @@
+name: Publish Snapshot
+
+on:
+ push:
+ branches: [ main ]
+ workflow_dispatch:
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: '25'
+ distribution: 'zulu'
+ cache: 'maven'
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+
+ - name: Get cache week
+ id: date
+ run: echo "week=$(date +%Y-%W)" >> $GITHUB_OUTPUT
+
+ - name: Cache Docker images
+ id: cache-docker
+ uses: actions/cache@v4
+ with:
+ path: /tmp/docker-images
+ key: docker-images-${{ runner.os }}-${{ steps.date.outputs.week }}
+ restore-keys: |
+ docker-images-${{ runner.os }}-
+
+ - name: Load cached Docker images
+ if: steps.cache-docker.outputs.cache-hit == 'true'
+ run: docker load -i /tmp/docker-images/images.tar
+
+ - name: Pull and save Docker images
+ if: steps.cache-docker.outputs.cache-hit != 'true'
+ run: |
+ docker pull alpine:latest
+ docker pull nginx:latest
+ docker pull rabbitmq:latest
+ mkdir -p /tmp/docker-images
+ docker save alpine:latest nginx:latest rabbitmq:latest -o /tmp/docker-images/images.tar
+
+ - name: Publish Snapshot
+ run: ./mvnw clean deploy
+ env:
+ MAVEN_PUBLISH: "true"
+ MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..18f69db
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,137 @@
+name: Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ release-version:
+ description: 'Release version (e.g. 1.0.0) — leave blank to use current POM version without -SNAPSHOT'
+ required: false
+ next-version:
+ description: 'Next development version (e.g. 1.0.1-SNAPSHOT) — leave blank to use incremented patch version with -SNAPSHOT'
+ required: false
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: '25'
+ distribution: 'zulu'
+ cache: 'maven'
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+
+ - name: Get cache week
+ id: date
+ run: echo "week=$(date +%Y-%W)" >> $GITHUB_OUTPUT
+
+ - name: Cache Docker images
+ id: cache-docker
+ uses: actions/cache@v4
+ with:
+ path: /tmp/docker-images
+ key: docker-images-${{ runner.os }}-${{ steps.date.outputs.week }}
+ restore-keys: |
+ docker-images-${{ runner.os }}-
+
+ - name: Load cached Docker images
+ if: steps.cache-docker.outputs.cache-hit == 'true'
+ run: docker load -i /tmp/docker-images/images.tar
+
+ - name: Pull and save Docker images
+ if: steps.cache-docker.outputs.cache-hit != 'true'
+ run: |
+ docker pull alpine:latest
+ docker pull nginx:latest
+ docker pull rabbitmq:latest
+ mkdir -p /tmp/docker-images
+ docker save alpine:latest nginx:latest rabbitmq:latest -o /tmp/docker-images/images.tar
+
+ - name: Determine Versions
+ id: versions
+ run: |
+ CURRENT_VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
+
+ RELEASE_VERSION="${{ inputs.release-version }}"
+ if [ -z "$RELEASE_VERSION" ]; then
+ RELEASE_VERSION="${CURRENT_VERSION/-SNAPSHOT/}"
+ fi
+
+ NEXT_VERSION="${{ inputs.next-version }}"
+ if [ -z "$NEXT_VERSION" ]; then
+ PATCH=$(echo "$RELEASE_VERSION" | cut -d. -f3)
+ NEXT_PATCH=$((PATCH + 1))
+ NEXT_VERSION=$(echo "$RELEASE_VERSION" | sed "s/\.[0-9]*$/.${NEXT_PATCH}-SNAPSHOT/")
+ fi
+
+ echo "release-version=$RELEASE_VERSION" >> $GITHUB_OUTPUT
+ echo "next-version=$NEXT_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Create Release Branch
+ run: git checkout -b release-${{ steps.versions.outputs.release-version }}
+
+ - name: Set Release Version
+ run: |
+ ./mvnw versions:set-property \
+ -Dproperty=revision \
+ -DnewVersion=${{ steps.versions.outputs.release-version }} \
+ -DgenerateBackupPoms=false
+
+ - name: Build and Deploy Release
+ run: ./mvnw clean deploy
+ env:
+ MAVEN_PUBLISH: "true"
+ MAVEN_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+
+ - name: Commit and Tag Release
+ run: |
+ git add pom.xml
+ git commit -m "Release ${{ steps.versions.outputs.release-version }}"
+ git tag -a v${{ steps.versions.outputs.release-version }} -m "Release ${{ steps.versions.outputs.release-version }}"
+
+ - name: Set Next Development Version
+ run: |
+ ./mvnw versions:set-property \
+ -Dproperty=revision \
+ -DnewVersion=${{ steps.versions.outputs.next-version }} \
+ -DgenerateBackupPoms=false
+
+ - name: Commit Next Development Version
+ run: |
+ git add pom.xml
+ git commit -m "Prepare for next development iteration ${{ steps.versions.outputs.next-version }}"
+
+ - name: Push Release Branch and Tag
+ run: git push --follow-tags origin release-${{ steps.versions.outputs.release-version }}
+
+ - name: Open and Merge PR to main
+ env:
+ GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
+ run: |
+ PR_URL=$(gh pr create \
+ --base main \
+ --head release-${{ steps.versions.outputs.release-version }} \
+ --title "Prepare for development of ${{ steps.versions.outputs.next-version }}" \
+ --body "Prepares next development iteration **${{ steps.versions.outputs.next-version }}**.")
+
+ gh pr merge "$PR_URL" --squash
diff --git a/pom.xml b/pom.xml
index 714798c..635c57e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,8 @@
pom
Spawn Project
+ A Java-based multi-module framework for programmatically launching and controlling processes, JVMs, and Docker containers.
+ https://github.com/Workday/spawn.build
Workday, Inc.
@@ -36,6 +38,15 @@
developer
+
+
+ Reed von Redwitz
+ reed.vonredwitz@workday.com
+
+ architect
+ developer
+
+
@@ -60,13 +71,13 @@
1.1.1
0.21.5
1.18.4
- 2.21.2
+ 2.21.2
6.0.3
2.10.1
2.0.1
5.23.0
4.12.0
- 0.19.0-SNAPSHOT
+ 0.19.0
3.15.0
@@ -119,6 +130,8 @@
${java.version}
${java.version}
+ true
+ -parameters
@@ -168,6 +181,7 @@
flatten-maven-plugin
${maven-flatten-plugin.version}
+ ossrh
true
@@ -323,6 +337,16 @@
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ false
+
+
+
diff --git a/spawn-docker-okhttp/pom.xml b/spawn-docker-okhttp/pom.xml
index 1fd856d..068507a 100644
--- a/spawn-docker-okhttp/pom.xml
+++ b/spawn-docker-okhttp/pom.xml
@@ -103,13 +103,13 @@
com.fasterxml.jackson.core
jackson-core
- ${jackson.version}
+ ${jackson-core.version}
com.fasterxml.jackson.core
jackson-databind
- ${jackson.version}
+ ${jackson-core.version}
diff --git a/spawn-docker/pom.xml b/spawn-docker/pom.xml
index a036f06..b0c1425 100644
--- a/spawn-docker/pom.xml
+++ b/spawn-docker/pom.xml
@@ -55,7 +55,7 @@
com.fasterxml.jackson.core
jackson-databind
- ${jackson.version}
+ ${jackson-core.version}
diff --git a/spawn-local-jdk/src/main/java/build/spawn/platform/local/jdk/JDKHomeBasedPatternDetector.java b/spawn-local-jdk/src/main/java/build/spawn/platform/local/jdk/JDKHomeBasedPatternDetector.java
index 769627e..a484a71 100644
--- a/spawn-local-jdk/src/main/java/build/spawn/platform/local/jdk/JDKHomeBasedPatternDetector.java
+++ b/spawn-local-jdk/src/main/java/build/spawn/platform/local/jdk/JDKHomeBasedPatternDetector.java
@@ -145,12 +145,22 @@ public Stream paths() {
final ArrayList paths = new ArrayList<>();
+ // compute max walk depth from the pattern suffix — patterns without **
+ // can only match at a fixed depth, so there's no need to go deeper.
+ // +1 because Files.walkFileTree calls preVisitDirectory for depths
+ // 0..maxDepth-1 only; at exactly maxDepth, directories are delivered
+ // via visitFile and our preVisitDirectory check never fires.
+ final String patternSuffix = pattern.substring(lastPathSeparator);
+ final int maxDepth = patternSuffix.contains("**")
+ ? Integer.MAX_VALUE
+ : (int) patternSuffix.chars().filter(c -> c == '/').count() + 1;
+
// attempt to find paths matching the glob, iff the base path exists
if (base.toFile().exists()) {
Files.walkFileTree(
base,
EnumSet.of(FileVisitOption.FOLLOW_LINKS),
- Integer.MAX_VALUE,
+ maxDepth,
new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(final Path path,
diff --git a/spawn-local-jdk/src/main/resources/java.home.properties b/spawn-local-jdk/src/main/resources/java.home.properties
index 056b2fe..47cfb83 100644
--- a/spawn-local-jdk/src/main/resources/java.home.properties
+++ b/spawn-local-jdk/src/main/resources/java.home.properties
@@ -49,3 +49,4 @@ unix@local-jdk = /usr/local/*jdk*
unix@local-java = /usr/local/*java*
unix@sdkman = ${user.home}/.sdkman/candidates/java/*
unix@homebrew = /home/linuxbrew/.linuxbrew/opt/openjdk*/libexec
+unix@github-actions = /opt/hostedtoolcache/Java_*/*/*