An earlier stage of the OrderHub project built the deploy artifacts by hand — eight Docker images, k8s manifests, a Helm chart — each one compiled and checked on a laptop. Day 45 automates all of it: a .github/workflows/ci.yml that runs the whole Maven reactor on every push, matrix-builds the eight service images, and — very much in the spirit of the project — ships a JUnit test that tests the workflow itself.
Two jobs: a gate and a fan-out
A GitHub Actions pipeline is jobs made of steps on fresh Ubuntu runners. The build-test job is the gate: check out, set up Temurin JDK 21 with a Maven cache, run mvn -B clean verify (all 163 tests), and upload the test reports.
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21' # matches <java.version>21</java.version>
cache: maven # restore/save ~/.m2 keyed on pom hashes
- run: mvn -B clean verify # compile + all 163 tests, every module
- uses: actions/upload-artifact@v4
if: always() # keep reports even when the build failed
with:
name: surefire-reports
path: '**/target/surefire-reports/**'
Two details do the heavy lifting. cache: maven keys ~/.m2 on the pom hashes, so an unchanged dependency tree restores in seconds instead of re-downloading the world. And if: always() uploads the surefire reports even on a red build — the one time you most want to read them.
The matrix builds eight images from one job
The docker-build job needs: build-test, so images only build on a green reactor. It's gated to pushes on master, and a strategy.matrix fans the single step-set out into eight parallel jobs — one per service:
docker-build:
needs: build-test # only on a green reactor
if: github.event_name == 'push'
&& github.ref == 'refs/heads/master' # push-to-master only
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
module: [config-server, eureka-server, api-gateway, order-service,
inventory-service, payment-service, shipping-service, notification-service]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.module }}/Dockerfile # the multi-stage build
push: false # BUILD-ONLY — publish nothing
tags: orderhub/${{ matrix.module }}:${{ github.sha }}
push: false keeps the pipeline honest — it proves every image still builds without needing a registry. Flip it to true and add a commented login step to release. On a pull request, the whole docker-build job is skipped by its if: guard, so PR feedback stays fast while build-test still runs the full JDK-21 verify as a merge gate.
The workflow has a test
You can't run GitHub Actions on the build box, but you can parse the YAML and assert it's wired correctly. A plain JUnit + SnakeYAML CiWorkflowTest reads ci.yml and pins the pipeline's contract:
class CiWorkflowTest { // parses .github/workflows/ci.yml (SnakeYAML)
@Test void workflowTriggersOnPushPullRequestAndDispatch() {
assertTrue(on.containsKey("push")); // (on: reads as Boolean.TRUE — handled)
assertTrue(on.containsKey("pull_request"));
assertTrue(on.containsKey("workflow_dispatch"));
}
@Test void buildTestJobRunsMavenVerifyOnJdk21WithMavenCache() { /* ... */ }
@Test void dockerBuildDependsOnBuildTestAndMatrixesTheEightModules() {
assertEquals("build-test", dockerBuild.get("needs")); // scalar or list
// strategy.matrix lists exactly the 8 service modules
}
@Test void noHardCodedSecretsOnlyExpressionReferences() {
// every secrets.NAME must sit inside an open ${{ … }} expression — never a pasted literal
}
}
There's a genuine gotcha buried here: GitHub's on: key reads as the YAML 1.1 boolean true under SnakeYAML, so the test looks the triggers node up under either the "on" string or Boolean.TRUE. The four assertions pin the triggers, the JDK-21 cached-Maven verify gate, the needs + matrix image build, and the no-hard-coded-secrets rule — so the pipeline can't quietly rot. The reactor grows from 159 tests to 163, BUILD SUCCESS.
The build moves off the laptop: a red test or a broken Dockerfile is now caught before merge, automatically, and the pipeline config is reviewed in the same PR as the code it guards.
Step through the live pipeline run, flip the trigger to a pull request to watch the image job get skipped, and read the full anatomy here: https://dev48v.infy.uk/orderhub/day45-cicd.html
Top comments (0)