v4.6.1 #609
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Docker Build and Publish | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version to build (e.g., 2.21.2-test)' | |
| required: true | |
| default: 'test' | |
| env: | |
| REGISTRY: ghcr.io | |
| jobs: | |
| # Build for amd64 and arm64 using Node.js 24 (Alpine) | |
| build-main: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract version and image name | |
| id: meta | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| VERSION="${{ github.event.inputs.version }}" | |
| else | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "image=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT | |
| - name: Build and push (amd64, arm64) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-main | |
| labels: | | |
| org.opencontainers.image.description=Web tool for monitoring a Meshtastic Node Deployment over TCP/HTTP | |
| cache-from: type=gha,scope=main | |
| cache-to: type=gha,mode=max,scope=main | |
| provenance: true | |
| sbom: true | |
| timeout-minutes: 40 | |
| # Build for armv7 using Node.js 22 (Debian slim) | |
| build-armv7: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract version and image name | |
| id: meta | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| VERSION="${{ github.event.inputs.version }}" | |
| else | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "image=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT | |
| - name: Build and push (armv7) | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ./Dockerfile.armv7 | |
| platforms: linux/arm/v7 | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-armv7 | |
| labels: | | |
| org.opencontainers.image.description=Web tool for monitoring a Meshtastic Node Deployment over TCP/HTTP | |
| cache-from: type=gha,scope=armv7 | |
| cache-to: type=gha,mode=max,scope=armv7 | |
| provenance: true | |
| sbom: true | |
| timeout-minutes: 40 | |
| # Smoke test: verify critical files are non-zero in each platform image | |
| # Prevents publishing broken images (see issue #2046) | |
| smoke-test: | |
| runs-on: ubuntu-latest | |
| needs: [build-main, build-armv7] | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: read | |
| packages: read | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| include: | |
| - tag_suffix: main | |
| platform: linux/amd64 | |
| - tag_suffix: main | |
| platform: linux/arm64 | |
| - tag_suffix: armv7 | |
| platform: linux/arm/v7 | |
| steps: | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract version and image name | |
| id: meta | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| VERSION="${{ github.event.inputs.version }}" | |
| else | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "image=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT | |
| - name: Verify critical files are non-zero | |
| run: | | |
| IMAGE="${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-${{ matrix.tag_suffix }}" | |
| PLATFORM="${{ matrix.platform }}" | |
| echo "Smoke testing $IMAGE ($PLATFORM)" | |
| echo "─────────────────────────────────────" | |
| # Pull the image for this specific platform | |
| docker pull --platform "$PLATFORM" "$IMAGE" | |
| # Critical files that must be non-zero | |
| FILES=( | |
| "/usr/local/bin/docker-entrypoint.sh" | |
| "/app/dist/server/server.js" | |
| "/app/package.json" | |
| "/etc/supervisord.conf" | |
| ) | |
| FAILED=0 | |
| for FILE in "${FILES[@]}"; do | |
| SIZE=$(docker run --rm --entrypoint stat --platform "$PLATFORM" "$IMAGE" -c%s "$FILE" 2>/dev/null || echo "MISSING") | |
| if [ "$SIZE" = "MISSING" ]; then | |
| echo "FAIL: $FILE is missing" | |
| FAILED=1 | |
| elif [ "$SIZE" = "0" ]; then | |
| echo "FAIL: $FILE is 0 bytes" | |
| FAILED=1 | |
| else | |
| echo " OK: $FILE ($SIZE bytes)" | |
| fi | |
| done | |
| # Verify node binary runs | |
| NODE_VERSION=$(docker run --rm --entrypoint node --platform "$PLATFORM" "$IMAGE" --version 2>/dev/null || echo "FAILED") | |
| if [[ "$NODE_VERSION" == v* ]]; then | |
| echo " OK: node $NODE_VERSION" | |
| else | |
| echo "FAIL: node binary not functional" | |
| FAILED=1 | |
| fi | |
| echo "─────────────────────────────────────" | |
| if [ "$FAILED" -ne 0 ]; then | |
| echo "SMOKE TEST FAILED for $PLATFORM" | |
| exit 1 | |
| fi | |
| echo "Smoke test passed for $PLATFORM" | |
| # Create multi-arch manifest combining all platforms | |
| create-manifest: | |
| runs-on: ubuntu-latest | |
| needs: [build-main, build-armv7, smoke-test] | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if release is a pre-release | |
| id: check_prerelease | |
| run: | | |
| if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then | |
| echo "is_prerelease=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "is_prerelease=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Extract version components and image name | |
| id: meta | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| VERSION="${{ github.event.inputs.version }}" | |
| else | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| fi | |
| MAJOR=$(echo $VERSION | cut -d. -f1) | |
| MINOR=$(echo $VERSION | cut -d. -f2) | |
| IMAGE=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "major=$MAJOR" >> $GITHUB_OUTPUT | |
| echo "minor=$MAJOR.$MINOR" >> $GITHUB_OUTPUT | |
| echo "image=$IMAGE" >> $GITHUB_OUTPUT | |
| - name: Create and push manifest for version tag | |
| run: | | |
| docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }} \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-main \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-armv7 | |
| - name: Create and push manifest for minor version tag | |
| if: github.event_name == 'release' | |
| run: | | |
| docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.minor }} \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-main \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-armv7 | |
| - name: Create and push manifest for major version tag | |
| if: github.event_name == 'release' | |
| run: | | |
| docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.major }} \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-main \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-armv7 | |
| - name: Create and push manifest for latest tag | |
| if: github.event_name == 'release' && steps.check_prerelease.outputs.is_prerelease == 'false' | |
| run: | | |
| docker buildx imagetools create -t ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:latest \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-main \ | |
| ${{ env.REGISTRY }}/${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}-armv7 | |
| - name: Clean up intermediate tags | |
| continue-on-error: true | |
| run: | | |
| # Note: GHCR doesn't support deleting tags via API easily | |
| # The intermediate tags (-main, -armv7) will remain but won't affect users | |
| echo "Intermediate tags created: ${{ steps.meta.outputs.version }}-main, ${{ steps.meta.outputs.version }}-armv7" | |
| echo "These are combined into the main manifest tags" |