From d2f838dbecf0844bd321f7342d6c576439a204b4 Mon Sep 17 00:00:00 2001 From: Reed von Redwitz Date: Tue, 7 Apr 2026 14:27:21 +0200 Subject: [PATCH 1/4] chore: add GitHub Actions workflows for CI, snapshots, and releases --- .github/workflows/main-pull-request.yml | 49 +++++++++ .github/workflows/publish-snapshot.yml | 60 +++++++++++ .github/workflows/release.yml | 137 ++++++++++++++++++++++++ pom.xml | 26 ++++- spawn-docker-okhttp/pom.xml | 4 +- spawn-docker/pom.xml | 2 +- 6 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/main-pull-request.yml create mode 100644 .github/workflows/publish-snapshot.yml create mode 100644 .github/workflows/release.yml 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..3a0e8db 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,7 +71,7 @@ 1.1.1 0.21.5 1.18.4 - 2.21.2 + 2.21.2 6.0.3 2.10.1 2.0.1 @@ -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} From 899b6b83b0c95d9831279e164578b0a3ea2c9708 Mon Sep 17 00:00:00 2001 From: Reed von Redwitz Date: Tue, 7 Apr 2026 14:40:18 +0200 Subject: [PATCH 2/4] use published codemodel --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3a0e8db..635c57e 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 2.0.1 5.23.0 4.12.0 - 0.19.0-SNAPSHOT + 0.19.0 3.15.0 From 24411a99d5f01b05b785521bb630d9e592579907 Mon Sep 17 00:00:00 2001 From: Reed von Redwitz Date: Tue, 7 Apr 2026 14:45:16 +0200 Subject: [PATCH 3/4] detect jdk on github actions --- spawn-local-jdk/src/main/resources/java.home.properties | 1 + 1 file changed, 1 insertion(+) 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_*/*/* From 17bb2970fb21b91c34fb1b78d9d193dde5c66db8 Mon Sep 17 00:00:00 2001 From: Reed von Redwitz Date: Tue, 7 Apr 2026 15:00:09 +0200 Subject: [PATCH 4/4] fix depth calculation --- .../local/jdk/JDKHomeBasedPatternDetector.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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,