name: Build and Publish Docker Image on: push: tags: ['v*'] workflow_dispatch: inputs: build_base: description: 'Rebuild base image' required: false default: false type: boolean env: REGISTRY: ghcr.io BASE_IMAGE_NAME: fawney19/aether-base APP_IMAGE_NAME: fawney19/aether # Files that affect base image - used for hash calculation BASE_FILES: "Dockerfile.base pyproject.toml frontend/package.json frontend/package-lock.json" jobs: check-base-changes: runs-on: ubuntu-latest outputs: base_changed: ${{ steps.check.outputs.base_changed }} steps: - uses: actions/checkout@v4 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Check if base image needs rebuild id: check run: | if [ "${{ github.event.inputs.build_base }}" == "true" ]; then echo "base_changed=true" >> $GITHUB_OUTPUT exit 0 fi # Calculate current hash of base-related files CURRENT_HASH=$(cat ${{ env.BASE_FILES }} 2>/dev/null | sha256sum | cut -d' ' -f1) echo "Current base files hash: $CURRENT_HASH" # Try to get hash label from remote image config # Pull the image config and extract labels REMOTE_HASH="" if docker pull ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest 2>/dev/null; then REMOTE_HASH=$(docker inspect ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest --format '{{ index .Config.Labels "org.opencontainers.image.base.hash" }}' 2>/dev/null) || true fi if [ -z "$REMOTE_HASH" ] || [ "$REMOTE_HASH" == "" ]; then # No remote image or no hash label, need to rebuild echo "No remote base image or hash label found, need rebuild" echo "base_changed=true" >> $GITHUB_OUTPUT elif [ "$CURRENT_HASH" != "$REMOTE_HASH" ]; then echo "Hash mismatch: remote=$REMOTE_HASH, current=$CURRENT_HASH" echo "base_changed=true" >> $GITHUB_OUTPUT else echo "Hash matches, no rebuild needed" echo "base_changed=false" >> $GITHUB_OUTPUT fi build-base: needs: check-base-changes if: needs.check-base-changes.outputs.base_changed == 'true' runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Calculate base files hash id: hash run: | HASH=$(cat ${{ env.BASE_FILES }} 2>/dev/null | sha256sum | cut -d' ' -f1) echo "hash=$HASH" >> $GITHUB_OUTPUT - name: Extract metadata for base image id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }} tags: | type=raw,value=latest type=sha,prefix= labels: | org.opencontainers.image.base.hash=${{ steps.hash.outputs.hash }} - name: Build and push base image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.base push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64 build-app: needs: [check-base-changes, build-base] if: always() && (needs.build-base.result == 'success' || needs.build-base.result == 'skipped') runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata for app image id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.APP_IMAGE_NAME }} tags: | type=raw,value=latest,enable={{is_default_branch}} type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix= - name: Update Dockerfile.app to use registry base image run: | sed -i "s|FROM aether-base:latest AS builder|FROM ${{ env.REGISTRY }}/${{ env.BASE_IMAGE_NAME }}:latest AS builder|g" Dockerfile.app - name: Build and push app image uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile.app push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max platforms: linux/amd64,linux/arm64