From cf2e0fe4b7eeaceee3daf0a0f60259feda342250 Mon Sep 17 00:00:00 2001 From: TheMeinerLP Date: Sun, 21 Jun 2026 17:45:49 +0200 Subject: [PATCH 1/3] feat(ci): publish snapshots and build docker images on every main push The release-please pipeline only published Maven artifacts and Docker images on a real release (`release_created == true`). Two gaps followed: - The release Docker build failed with "Username must not be null!" because docker-publish.yml never received the private OneLiteFeather Maven credentials needed to resolve `net.onelitefeather:vulpes-model`. Fixed upstream in OneLiteFeatherNET/workflows (>= v2.3.0); bump refs. - Snapshot versions (release-please's Java SNAPSHOT bumps, e.g. 2.0.1-SNAPSHOT) never triggered publish or docker, so build.gradle.kts' snapshot-repo routing was dead code. Add a `version` job that reads gradle.properties on non-release pushes and drives new `publish-snapshot` / `docker-snapshot` jobs whenever the version is a SNAPSHOT/BETA/ALPHA. Also add the `workflow_dispatch` trigger the docker job already referenced (manual image re-publish). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/release-please.yml | 66 +++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index ccf8cdf..d54222d 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -2,6 +2,13 @@ name: release-please on: push: branches: [main] + workflow_dispatch: + inputs: + docker_version: + description: "Version to (re)build & push the Docker image for, e.g. 2.0.0 (no leading v). Runs only the docker job." + required: true + type: string + permissions: contents: write pull-requests: write @@ -22,10 +29,37 @@ jobs: config-file: release-please-config.json manifest-file: .release-please-manifest.json + # Resolves the current project version from gradle.properties for non-release + # pushes, so the snapshot jobs below know whether (and at which version) to run. + version: + needs: release-please + if: github.event_name == 'push' && needs.release-please.outputs.release_created != 'true' + runs-on: ubuntu-latest + outputs: + version: ${{ steps.read.outputs.version }} + is_snapshot: ${{ steps.read.outputs.is_snapshot }} + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Read version + id: read + shell: bash + run: | + VERSION="$(grep -E '^version=' gradle.properties | head -n1 | cut -d= -f2 | cut -d'#' -f1 | xargs)" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + # Mirror the snapshot routing in build.gradle.kts (SNAPSHOT/BETA/ALPHA). + case "$VERSION" in + *SNAPSHOT*|*BETA*|*ALPHA*) IS_SNAPSHOT=true ;; + *) IS_SNAPSHOT=false ;; + esac + echo "is_snapshot=$IS_SNAPSHOT" >> "$GITHUB_OUTPUT" + echo "Resolved version '$VERSION' (snapshot=$IS_SNAPSHOT)" + + # ---- Release artifacts (on a real release-please release) ---- publish: needs: release-please if: needs.release-please.outputs.release_created == 'true' - uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.2.0 + uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.3.0 with: java-version: "25.0.3" java-distribution: "temurin" @@ -42,7 +76,7 @@ jobs: always() && ((github.event_name == 'push' && needs.release-please.outputs.release_created == 'true') || github.event_name == 'workflow_dispatch') - uses: OneLiteFeatherNET/workflows/.github/workflows/docker-publish.yml@v2.2.0 + uses: OneLiteFeatherNET/workflows/.github/workflows/docker-publish.yml@v2.3.0 with: image-name: "onelitefeather/vulpes-backend" version: ${{ github.event_name == 'workflow_dispatch' && inputs.docker_version || needs.release-please.outputs.version }} @@ -52,3 +86,31 @@ jobs: blob-chunk: "90000000" # ~90 MB, under the 100 MB Cloudflare cap req-concurrent: "4" secrets: inherit + + # ---- Snapshot artifacts (on every non-release push to main) ---- + publish-snapshot: + needs: [release-please, version] + if: needs.version.outputs.is_snapshot == 'true' + uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.3.0 + with: + java-version: "25.0.3" + java-distribution: "temurin" + secrets: inherit + + docker-snapshot: + name: Build Docker Artifacts (snapshot) + needs: [release-please, version] + permissions: + contents: read + id-token: write # keyless cosign signing via GitHub OIDC + if: needs.version.outputs.is_snapshot == 'true' + uses: OneLiteFeatherNET/workflows/.github/workflows/docker-publish.yml@v2.3.0 + with: + image-name: "onelitefeather/vulpes-backend" + version: ${{ needs.version.outputs.version }} + setup-java: true + build-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" + context: "./build/docker/optimized" + blob-chunk: "90000000" # ~90 MB, under the 100 MB Cloudflare cap + req-concurrent: "4" + secrets: inherit From 7f0fccbf00836be6438a8fb67e8b657a9a69ab52 Mon Sep 17 00:00:00 2001 From: TheMeinerLP Date: Sun, 21 Jun 2026 18:01:08 +0200 Subject: [PATCH 2/3] ci: pass Maven credentials to docker build via build-env The shared docker-publish workflow no longer hardcodes the OneLiteFeather Maven secrets; it exposes a generic `build-env` input instead. Forward the credentials that way so the Gradle build inside the docker / docker- snapshot jobs can resolve the private vulpes-model dependency. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/release-please.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index d54222d..8f4074b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -82,6 +82,9 @@ jobs: version: ${{ github.event_name == 'workflow_dispatch' && inputs.docker_version || needs.release-please.outputs.version }} setup-java: true build-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" + build-env: | + ONELITEFEATHER_MAVEN_USERNAME=${{ secrets.ONELITEFEATHER_MAVEN_USERNAME }} + ONELITEFEATHER_MAVEN_PASSWORD=${{ secrets.ONELITEFEATHER_MAVEN_PASSWORD }} context: "./build/docker/optimized" blob-chunk: "90000000" # ~90 MB, under the 100 MB Cloudflare cap req-concurrent: "4" @@ -110,6 +113,9 @@ jobs: version: ${{ needs.version.outputs.version }} setup-java: true build-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" + build-env: | + ONELITEFEATHER_MAVEN_USERNAME=${{ secrets.ONELITEFEATHER_MAVEN_USERNAME }} + ONELITEFEATHER_MAVEN_PASSWORD=${{ secrets.ONELITEFEATHER_MAVEN_PASSWORD }} context: "./build/docker/optimized" blob-chunk: "90000000" # ~90 MB, under the 100 MB Cloudflare cap req-concurrent: "4" From 2e607e820bb229cc02f7c4dde75aef6beb2f9671 Mon Sep 17 00:00:00 2001 From: TheMeinerLP Date: Sun, 21 Jun 2026 18:22:16 +0200 Subject: [PATCH 3/3] ci: split Gradle context build from Docker publish docker-publish.yml is now a pure, toolchain-agnostic Docker build. Move the Gradle step that generates the Micronaut optimized context into the new reusable gradle-docker-context.yml producer, which uploads the context as an artifact for docker-publish to consume. Both the release and snapshot paths now run: build-context* (Gradle) -> docker* (docker-publish via artifact-name) The producer carries the private Maven credentials (secrets: inherit), so the Docker job no longer needs them. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/release-please.yml | 64 ++++++++++++++++++---------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 8f4074b..878010f 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -55,7 +55,7 @@ jobs: echo "is_snapshot=$IS_SNAPSHOT" >> "$GITHUB_OUTPUT" echo "Resolved version '$VERSION' (snapshot=$IS_SNAPSHOT)" - # ---- Release artifacts (on a real release-please release) ---- + # ---- Maven publish ---- publish: needs: release-please if: needs.release-please.outputs.release_created == 'true' @@ -65,44 +65,68 @@ jobs: java-distribution: "temurin" secrets: inherit + publish-snapshot: + needs: [release-please, version] + if: needs.version.outputs.is_snapshot == 'true' + uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.3.0 + with: + java-version: "25.0.3" + java-distribution: "temurin" + secrets: inherit + + # ---- Release Docker image (Gradle produces the context, docker-publish builds it) ---- + build-context: + name: Build Docker context + needs: release-please + # On a real release, or on a manual dispatch to (re)publish a given version. + if: | + always() && + ((github.event_name == 'push' && needs.release-please.outputs.release_created == 'true') || + github.event_name == 'workflow_dispatch') + uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-docker-context.yml@v2.3.0 + with: + version: ${{ github.event_name == 'workflow_dispatch' && inputs.docker_version || needs.release-please.outputs.version }} + gradle-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" + context-path: "build/docker/optimized" + artifact-name: "docker-context-release" + secrets: inherit + docker: name: Build Docker Artifacts - needs: release-please + needs: [release-please, build-context] permissions: contents: read id-token: write # keyless cosign signing via GitHub OIDC - # Runs on a real release, or on a manual dispatch to (re)publish a given version. if: | - always() && + always() && needs.build-context.result == 'success' && ((github.event_name == 'push' && needs.release-please.outputs.release_created == 'true') || github.event_name == 'workflow_dispatch') uses: OneLiteFeatherNET/workflows/.github/workflows/docker-publish.yml@v2.3.0 with: image-name: "onelitefeather/vulpes-backend" version: ${{ github.event_name == 'workflow_dispatch' && inputs.docker_version || needs.release-please.outputs.version }} - setup-java: true - build-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" - build-env: | - ONELITEFEATHER_MAVEN_USERNAME=${{ secrets.ONELITEFEATHER_MAVEN_USERNAME }} - ONELITEFEATHER_MAVEN_PASSWORD=${{ secrets.ONELITEFEATHER_MAVEN_PASSWORD }} - context: "./build/docker/optimized" + context: "build/docker/optimized" + artifact-name: "docker-context-release" blob-chunk: "90000000" # ~90 MB, under the 100 MB Cloudflare cap req-concurrent: "4" secrets: inherit - # ---- Snapshot artifacts (on every non-release push to main) ---- - publish-snapshot: + # ---- Snapshot Docker image (on every non-release push to main) ---- + build-context-snapshot: + name: Build Docker context (snapshot) needs: [release-please, version] if: needs.version.outputs.is_snapshot == 'true' - uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.3.0 + uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-docker-context.yml@v2.3.0 with: - java-version: "25.0.3" - java-distribution: "temurin" + version: ${{ needs.version.outputs.version }} + gradle-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" + context-path: "build/docker/optimized" + artifact-name: "docker-context-snapshot" secrets: inherit docker-snapshot: name: Build Docker Artifacts (snapshot) - needs: [release-please, version] + needs: [version, build-context-snapshot] permissions: contents: read id-token: write # keyless cosign signing via GitHub OIDC @@ -111,12 +135,8 @@ jobs: with: image-name: "onelitefeather/vulpes-backend" version: ${{ needs.version.outputs.version }} - setup-java: true - build-command: "./gradlew jar optimizedBuildLayers optimizedDockerfile -Pversion=$VERSION" - build-env: | - ONELITEFEATHER_MAVEN_USERNAME=${{ secrets.ONELITEFEATHER_MAVEN_USERNAME }} - ONELITEFEATHER_MAVEN_PASSWORD=${{ secrets.ONELITEFEATHER_MAVEN_PASSWORD }} - context: "./build/docker/optimized" + context: "build/docker/optimized" + artifact-name: "docker-context-snapshot" blob-chunk: "90000000" # ~90 MB, under the 100 MB Cloudflare cap req-concurrent: "4" secrets: inherit