From ada985be0e4870ebbb4d075c04ca5ea7588d6690 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 29 May 2026 14:30:59 +0000 Subject: [PATCH] ci: build, test, and publish the web Docker image on release Make the tested Docker pipeline own the image the web/k8s deployment pulls (ghcr.io/openms/flashapp:latest) so a release ships a validated image, built after the Windows installer it bundles. - build-windows-executable-app.yaml: add a `publish-web-image` job that, on a published release, calls build-and-test.yml (reusable workflow) after the Windows installer is uploaded as a release asset. This preserves the ordering the Docker build relies on (it bundles the installer via `gh release download`). - build-and-test.yml: add a `workflow_call` trigger and drop the racy `tags: ['v*']` push trigger; enable the `:latest` (and SIF `:latest`) tags on release as well as develop; gate the multi-arch manifest + `:latest` promotion behind the apptainer/nginx/traefik tests by adding them to create-manifest's `needs`. - Dockerfile/Dockerfile.arm: add a RELEASE_TAG arg to pin the bundled installer to the release being published (falls back to latest release for develop/manual builds). - Remove publish-docker-images.yml, the untested release-publish workflow now superseded by build-and-test.yml (whose workflow_dispatch remains the manual rebuild fallback). https://claude.ai/code/session_011vfx32E4R7HTs19TtHZps9 --- .github/workflows/build-and-test.yml | 17 +- .../build-windows-executable-app.yaml | 15 ++ .github/workflows/publish-docker-images.yml | 173 ------------------ Dockerfile | 10 +- Dockerfile.arm | 10 +- 5 files changed, 44 insertions(+), 181 deletions(-) delete mode 100644 .github/workflows/publish-docker-images.yml diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 6a28ce7..27cb859 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -5,7 +5,7 @@ on: branches: [develop] push: branches: [develop] - tags: ['v*'] + workflow_call: workflow_dispatch: env: @@ -97,7 +97,7 @@ jobs: type=ref,event=branch,suffix=-${{ matrix.variant }}-amd64 type=ref,event=tag,suffix=-${{ matrix.variant }}-amd64 type=sha,prefix=,suffix=-${{ matrix.variant }}-amd64 - type=raw,value=latest-amd64,enable=${{ matrix.variant == 'full' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} + type=raw,value=latest-amd64,enable=${{ matrix.variant == 'full' && (github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/develop')) }} - name: Build and conditionally push uses: docker/build-push-action@v5 @@ -118,6 +118,7 @@ jobs: cache-to: ${{ github.event_name != 'pull_request' && format('type=registry,ref={0}/{1}/cache:{2}-amd64,mode=max', env.REGISTRY, env.IMAGE_NAME_LC, matrix.variant) || '' }} build-args: | GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG=${{ github.event_name == 'release' && github.ref_name || '' }} - name: Retag for kind (image name the kustomize overlay points at) run: | @@ -201,7 +202,7 @@ jobs: type=ref,event=branch,suffix=-${{ matrix.variant }}-arm64 type=ref,event=tag,suffix=-${{ matrix.variant }}-arm64 type=sha,prefix=,suffix=-${{ matrix.variant }}-arm64 - type=raw,value=latest-arm64,enable=${{ matrix.variant == 'full' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} + type=raw,value=latest-arm64,enable=${{ matrix.variant == 'full' && (github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/develop')) }} - name: Build and conditionally push uses: docker/build-push-action@v5 @@ -218,6 +219,7 @@ jobs: provenance: false build-args: | GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG=${{ github.event_name == 'release' && github.ref_name || '' }} - name: Retag for kind (image name the kustomize overlay points at) run: | @@ -244,7 +246,10 @@ jobs: # consumers (k8s overlays, docker-compose users, `docker pull` callers) # keep working transparently — docker now auto-selects the right arch # on pull. PRs don't push per-arch tags, so there's nothing to merge. - needs: [build-amd64, build-arm64] + # Also gate on the integration tests (apptainer/nginx/traefik): the + # multi-arch `:latest` + versioned manifest that prod pulls must only be + # promoted after the freshly built image passes its tests. + needs: [build-amd64, build-arm64, test-apptainer, test-nginx, test-traefik] if: github.event_name != 'pull_request' runs-on: ubuntu-latest permissions: @@ -277,7 +282,7 @@ jobs: type=ref,event=branch,suffix=-${{ matrix.variant }} type=ref,event=tag,suffix=-${{ matrix.variant }} type=sha,prefix=,suffix=-${{ matrix.variant }} - type=raw,value=latest,enable=${{ matrix.variant == 'full' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} + type=raw,value=latest,enable=${{ matrix.variant == 'full' && (github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/develop')) }} - name: Create and push multi-arch manifests # Iterate over manifest tags (newline-separated from metadata-action) @@ -522,7 +527,7 @@ jobs: type=ref,event=branch,suffix=-${{ matrix.variant }} type=ref,event=tag,suffix=-${{ matrix.variant }} type=sha,prefix=,suffix=-${{ matrix.variant }} - type=raw,value=latest,enable=${{ matrix.variant == 'full' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }} + type=raw,value=latest,enable=${{ matrix.variant == 'full' && (github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/develop')) }} - name: Log in to GHCR for ORAS push env: diff --git a/.github/workflows/build-windows-executable-app.yaml b/.github/workflows/build-windows-executable-app.yaml index 826d278..c8a832a 100644 --- a/.github/workflows/build-windows-executable-app.yaml +++ b/.github/workflows/build-windows-executable-app.yaml @@ -490,3 +490,18 @@ jobs: asset_content_type: application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-web-image: + # On a published Release, run the full Build-and-Test pipeline AFTER the + # Windows installer (OpenMS-App.zip) has been uploaded as a release asset, + # so the Docker build's `gh release download` bundles the matching + # installer. build-and-test.yml builds the image, runs the apptainer + + # nginx + traefik integration tests, and only then stitches and pushes the + # multi-arch `:latest` (and versioned) tags that the k8s prod overlay pulls. + needs: build-executable + if: github.event_name == 'release' + permissions: + contents: read + packages: write + uses: ./.github/workflows/build-and-test.yml + secrets: inherit diff --git a/.github/workflows/publish-docker-images.yml b/.github/workflows/publish-docker-images.yml deleted file mode 100644 index 714e854..0000000 --- a/.github/workflows/publish-docker-images.yml +++ /dev/null @@ -1,173 +0,0 @@ -name: Publish Docker Images to GHCR - -on: - workflow_run: - workflows: ["Build executable for Windows"] - types: [completed] - workflow_dispatch: - inputs: - tag: - description: 'Release tag to build (e.g., v0.9.15)' - required: false - -jobs: - - resolve-tag: - # Only run on successful completion of a release-triggered Windows build, - # or on manual dispatch - if: > - github.event_name == 'workflow_dispatch' || - (github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.event == 'release') - runs-on: ubuntu-latest - outputs: - version: ${{ steps.tag.outputs.version }} - sha: ${{ steps.tag.outputs.sha }} - steps: - - name: Resolve release tag - id: tag - env: - GH_TOKEN: ${{ github.token }} - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - TAG="${{ github.event.inputs.tag }}" - if [ -n "$TAG" ]; then - SHA=$(gh api repos/${{ github.repository }}/git/ref/tags/${TAG} --jq '.object.sha') - VERSION="${TAG#v}" - else - # No tag — build from branch HEAD - SHA="${{ github.sha }}" - VERSION="${{ github.ref_name }}" - fi - else - # workflow_run: get the tag from the head branch (release events set head_branch to the tag) - TAG="${{ github.event.workflow_run.head_branch }}" - SHA="${{ github.event.workflow_run.head_sha }}" - VERSION="${TAG#v}" - fi - - # Sanitize VERSION for valid Docker tags: replace / with -, strip invalid chars - VERSION=$(echo "$VERSION" | tr '/' '-' | tr -cd 'A-Za-z0-9_.-') - - echo "Resolved tag=${TAG} version=${VERSION} sha=${SHA}" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "sha=${SHA}" >> "$GITHUB_OUTPUT" - - build-amd64: - needs: resolve-tag - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - env: - IMAGE: ghcr.io/openms/flashapp - steps: - - name: Free disk space - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache - sudo apt-get clean - df -h - - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ needs.resolve-tag.outputs.sha }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push amd64 image - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfile - push: true - tags: ${{ env.IMAGE }}:${{ needs.resolve-tag.outputs.version }}-amd64 - provenance: false - build-args: | - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} - cache-from: type=registry,ref=${{ env.IMAGE }}:buildcache-amd64 - cache-to: type=registry,ref=${{ env.IMAGE }}:buildcache-amd64,mode=max - - build-arm64: - needs: resolve-tag - runs-on: ubuntu-24.04-arm - permissions: - contents: read - packages: write - env: - IMAGE: ghcr.io/openms/flashapp - steps: - - name: Free disk space - run: | - sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache - sudo apt-get clean - df -h - - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ needs.resolve-tag.outputs.sha }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push arm64 image - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfile.arm - push: true - tags: ${{ env.IMAGE }}:${{ needs.resolve-tag.outputs.version }}-arm64 - provenance: false - build-args: | - GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} - cache-from: type=registry,ref=${{ env.IMAGE }}:buildcache-arm64 - cache-to: type=registry,ref=${{ env.IMAGE }}:buildcache-arm64,mode=max - - create-manifest: - needs: [resolve-tag, build-amd64, build-arm64] - runs-on: ubuntu-latest - permissions: - packages: write - env: - IMAGE: ghcr.io/openms/flashapp - steps: - - name: Log in to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create and push multi-arch manifests - run: | - VERSION="${{ needs.resolve-tag.outputs.version }}" - - # Create versioned manifest - docker manifest create ${{ env.IMAGE }}:${VERSION} \ - ${{ env.IMAGE }}:${VERSION}-amd64 \ - ${{ env.IMAGE }}:${VERSION}-arm64 - - docker manifest push ${{ env.IMAGE }}:${VERSION} - - # Create/update latest manifest - docker manifest create ${{ env.IMAGE }}:latest \ - ${{ env.IMAGE }}:${VERSION}-amd64 \ - ${{ env.IMAGE }}:${VERSION}-arm64 - - docker manifest push ${{ env.IMAGE }}:latest diff --git a/Dockerfile b/Dockerfile index 21c387c..0f72d95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -218,9 +218,17 @@ RUN jq '.online_deployment = true' settings.json > tmp.json && mv tmp.json setti ARG GITHUB_TOKEN ARG GITHUB_USER=OpenMS ARG GITHUB_REPO=FLASHApp +# RELEASE_TAG pins the download to the release being published (set by the +# build-and-test workflow on release events). When empty we fall back to the +# latest release, preserving the previous behavior for develop/manual builds. +ARG RELEASE_TAG RUN if [ -n "$GITHUB_TOKEN" ]; then \ echo "Downloading release asset..."; \ - GH_TOKEN="$GITHUB_TOKEN" gh release download -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + if [ -n "$RELEASE_TAG" ]; then \ + GH_TOKEN="$GITHUB_TOKEN" gh release download "$RELEASE_TAG" -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + else \ + GH_TOKEN="$GITHUB_TOKEN" gh release download -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + fi; \ else \ echo "No token, skipping download."; \ fi diff --git a/Dockerfile.arm b/Dockerfile.arm index 54083d8..9fe055e 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -256,9 +256,17 @@ RUN jq '.online_deployment = true' settings.json > tmp.json && mv tmp.json setti ARG GITHUB_TOKEN ARG GITHUB_USER=OpenMS ARG GITHUB_REPO=FLASHApp +# RELEASE_TAG pins the download to the release being published (set by the +# build-and-test workflow on release events). When empty we fall back to the +# latest release, preserving the previous behavior for develop/manual builds. +ARG RELEASE_TAG RUN if [ -n "$GITHUB_TOKEN" ]; then \ echo "Downloading release asset..."; \ - GH_TOKEN="$GITHUB_TOKEN" gh release download -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + if [ -n "$RELEASE_TAG" ]; then \ + GH_TOKEN="$GITHUB_TOKEN" gh release download "$RELEASE_TAG" -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + else \ + GH_TOKEN="$GITHUB_TOKEN" gh release download -R ${GITHUB_USER}/${GITHUB_REPO} -p "OpenMS-App.zip" -D /app; \ + fi; \ else \ echo "No token, skipping download."; \ fi