name: Build and Publish Docker Images on: push: branches: [ main, develop ] tags: [ 'v*' ] pull_request: branches: [ main ] env: REGISTRY: docker.io IMAGE_NAME_DISCORD_BOT: uptime-kuma-discord-bot IMAGE_NAME_WEB_BACKEND: uptime-kuma-web-backend jobs: build-and-push: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to Docker Hub if: github.event_name != 'pull_request' uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Extract metadata for Discord Bot id: meta-discord-bot uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }} tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=raw,value=latest,enable={{is_default_branch}} - name: Extract metadata for Web Backend id: meta-web-backend uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }} tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=raw,value=latest,enable={{is_default_branch}} - name: Build and push Discord Bot image uses: docker/build-push-action@v5 with: context: ./Bot file: ./Bot/Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta-discord-bot.outputs.tags }} labels: ${{ steps.meta-discord-bot.outputs.labels }} platforms: linux/amd64,linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max - name: Build and push Web Backend image uses: docker/build-push-action@v5 with: context: ./Web file: ./Web/Dockerfile push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta-web-backend.outputs.tags }} labels: ${{ steps.meta-web-backend.outputs.labels }} platforms: linux/amd64,linux/arm64 cache-from: type=gha cache-to: type=gha,mode=max - name: Generate SBOM for Discord Bot if: github.event_name != 'pull_request' uses: anchore/sbom-action@v0 with: image: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:${{ steps.meta-discord-bot.outputs.version }} format: spdx-json output-file: discord-bot-sbom.spdx.json - name: Generate SBOM for Web Backend if: github.event_name != 'pull_request' uses: anchore/sbom-action@v0 with: image: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:${{ steps.meta-web-backend.outputs.version }} format: spdx-json output-file: web-backend-sbom.spdx.json - name: Upload SBOM artifacts if: github.event_name != 'pull_request' uses: actions/upload-artifact@v4 with: name: sbom-artifacts path: | discord-bot-sbom.spdx.json web-backend-sbom.spdx.json retention-days: 30 security-scan: runs-on: ubuntu-latest needs: build-and-push if: github.event_name != 'pull_request' permissions: contents: read security-events: write steps: - name: Checkout repository uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner for Discord Bot uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest format: 'sarif' output: 'discord-bot-trivy-results.sarif' - name: Run Trivy vulnerability scanner for Web Backend uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest format: 'sarif' output: 'web-backend-trivy-results.sarif' - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v3 with: sarif_file: | discord-bot-trivy-results.sarif web-backend-trivy-results.sarif notify: runs-on: ubuntu-latest needs: [build-and-push, security-scan] if: always() && github.event_name != 'pull_request' steps: - name: Notify on success if: needs.build-and-push.result == 'success' && needs.security-scan.result == 'success' run: | echo "✅ Docker images built and published successfully!" echo "Discord Bot: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_DISCORD_BOT }}:latest" echo "Web Backend: ${{ env.REGISTRY }}/${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME_WEB_BACKEND }}:latest" - name: Notify on failure if: needs.build-and-push.result == 'failure' || needs.security-scan.result == 'failure' run: | echo "❌ Docker build or security scan failed!" exit 1