diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0f63f58..2504763 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,7 +17,16 @@ concurrency:
jobs:
build:
- runs-on: ubuntu-latest
+ name: build${{ matrix.arch == 'arm64' && ' (arm64)' || '' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: amd64
+ runner: ubuntu-latest
+ - arch: arm64
+ runner: ubuntu-24.04-arm
+ runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
@@ -40,7 +49,16 @@ jobs:
# shards inside one launcher process, so the fork boundary and the summary/report
# plumbing are invisible to it; this job is where they break loudly.
coordinated-profile:
- runs-on: ubuntu-latest
+ name: coordinated-profile${{ matrix.arch == 'arm64' && ' (arm64)' || '' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: amd64
+ runner: ubuntu-latest
+ - arch: arm64
+ runner: ubuntu-24.04-arm
+ runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
@@ -104,10 +122,24 @@ jobs:
# registry needs -- no PAT. The smoke test boots the actual image: /healthz must answer
# with a secret set, and an unset COORDINATOR_SECRETS must be a refused start.
container-image:
- runs-on: ubuntu-latest
+ name: container-image${{ matrix.arch == 'arm64' && ' (arm64)' || '' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: amd64
+ runner: ubuntu-latest
+ - arch: arm64
+ runner: ubuntu-24.04-arm
+ runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
+ services:
+ registry:
+ image: registry:2
+ ports:
+ - 5000:5000
steps:
- uses: actions/checkout@v7
@@ -120,11 +152,20 @@ jobs:
25
cache: maven
- - name: Build the coordinator app jar
- run: mvn -B -ntp -DskipTests package
-
- name: Build the image
- run: docker build -t shard4j-coordinator-ci shard4j-coordinator
+ run: mvn -B -ntp -DskipTests package jib:dockerBuild -Djib.to.image=shard4j-coordinator-ci
+
+ - name: Check every platform is in the image index
+ if: matrix.arch == 'amd64'
+ run: |
+ mvn -B -ntp -DskipTests package jib:build -Djib.to.image=localhost:5000/shard4j-coordinator:ci \
+ -Djib.allowInsecureRegistries=true
+ platforms=$(docker buildx imagetools inspect localhost:5000/shard4j-coordinator:ci \
+ --format '{{range .Manifest.Manifests}}{{.Platform.OS}}/{{.Platform.Architecture}} {{end}}')
+ echo "platforms: $platforms"
+ for p in linux/amd64 linux/arm64 linux/ppc64le linux/s390x; do
+ case " $platforms " in *" $p "*) ;; *) echo "missing $p" >&2; exit 1 ;; esac
+ done
- name: Smoke test the image
run: |
@@ -162,18 +203,16 @@ jobs:
fi
- name: Push to GHCR
- if: github.event_name == 'push'
+ if: github.event_name == 'push' && matrix.arch == 'amd64'
run: |
echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin
IMAGE=ghcr.io/${{ github.repository }}
VERSION=$(mvn -B -q help:evaluate -Dexpression=project.version -DforceStdout)
- docker tag shard4j-coordinator-ci "$IMAGE:$VERSION"
- docker push "$IMAGE:$VERSION"
+ tags=()
if [ '${{ github.ref }}' = 'refs/heads/main' ]; then
- docker tag shard4j-coordinator-ci "$IMAGE:sha-${GITHUB_SHA::12}"
- docker push "$IMAGE:sha-${GITHUB_SHA::12}"
+ tags=(-Djib.to.tags="sha-${GITHUB_SHA::12}")
fi
if [ '${{ github.ref_type }}' = 'tag' ]; then
- docker tag shard4j-coordinator-ci "$IMAGE:latest"
- docker push "$IMAGE:latest"
+ tags=(-Djib.to.tags=latest)
fi
+ mvn -B -ntp -DskipTests package jib:build -Djib.to.image="$IMAGE:$VERSION" "${tags[@]}"
diff --git a/pom.xml b/pom.xml
index 491078d..f00a6a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,9 @@
3.12.0
3.2.8
0.11.0
+ 3.5.2
+ 0.1.0
+ true
D82FF933653EB8EC483709C77E738F7E2A355AAE
6.46.1
@@ -213,6 +216,11 @@
spring-boot-maven-plugin
${spring-boot.version}
+
+ com.google.cloud.tools
+ jib-maven-plugin
+ ${jib-maven-plugin.version}
+
diff --git a/shard4j-coordinator/Dockerfile b/shard4j-coordinator/Dockerfile
deleted file mode 100644
index 8d9bdf3..0000000
--- a/shard4j-coordinator/Dockerfile
+++ /dev/null
@@ -1,23 +0,0 @@
-# The coordinator container: the Spring Boot repackaged app jar on the same JRE the
-# integration tests run it under. Build the jar first (`mvn -DskipTests package`), then
-# build with this module directory as the context.
-FROM eclipse-temurin:25-jre
-
-RUN groupadd --system --gid 10001 shard4j \
- && useradd --system --uid 10001 --gid shard4j --home-dir /opt/shard4j \
- --shell /usr/sbin/nologin shard4j \
- && mkdir -p /opt/shard4j /data \
- && chown shard4j:shard4j /opt/shard4j /data
-
-COPY --chown=shard4j:shard4j target/shard4j-coordinator-*-app.jar /opt/shard4j/app.jar
-
-USER shard4j
-
-# The coordinator takes an exclusive lock on the data directory and appends JSONL there;
-# it must live on a mount, never in the container layer.
-ENV COORDINATOR_DATA_DIR=/data
-VOLUME /data
-
-EXPOSE 8080
-
-ENTRYPOINT ["java", "-jar", "/opt/shard4j/app.jar"]
diff --git a/shard4j-coordinator/pom.xml b/shard4j-coordinator/pom.xml
index d2ecc54..81c2572 100644
--- a/shard4j-coordinator/pom.xml
+++ b/shard4j-coordinator/pom.xml
@@ -20,6 +20,7 @@
classpath, so it is free to sit on the current LTS. -->
${coordinator.compiler.release}
${coordinator.rewrite.java.recipe}
+ false
@@ -134,6 +135,81 @@
+
+
+ com.google.cloud.tools
+ jib-maven-plugin
+
+
+ com.google.cloud.tools
+ jib-ownership-extension-maven
+ ${jib-ownership-extension-maven.version}
+
+
+
+
+ eclipse-temurin:25-jre
+
+
+ amd64
+ linux
+
+
+ arm64
+ linux
+
+
+ ppc64le
+ linux
+
+
+ s390x
+ linux
+
+
+
+
+ shard4j-coordinator
+
+
+
+
+ src/main/jib
+ data/.keep
+
+
+
+
+
+ com.google.cloud.tools.jib.maven.extension.ownership.JibOwnershipExtension
+
+
+
+ /data
+ 10001:10001
+
+
+
+
+
+
+ com.marvinformatics.shard4j.coordinator.CoordinatorApplication
+ 10001:10001
+
+ 8080
+
+
+ /data
+
+
+ /data
+
+
+
+
+
org.apache.maven.plugins
maven-failsafe-plugin
diff --git a/shard4j-coordinator/src/main/jib/data/.keep b/shard4j-coordinator/src/main/jib/data/.keep
new file mode 100644
index 0000000..e69de29