Build and Publish Docker Image #12
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: Build and Publish Docker Image | |
| on: | |
| push: | |
| branches: [ main ] | |
| tags: [ 'v*' ] | |
| pull_request: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| jobs: | |
| build: | |
| name: Build and Test Docker Images | |
| runs-on: [self-hosted, docker, wasm, emscripten] | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| variant: | |
| - name: standard | |
| target: standard | |
| suffix: "" | |
| description: "Standard build with ports system" | |
| - name: minimal | |
| target: minimal | |
| suffix: "-minimal" | |
| description: "Minimal build without ports system" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: arm64 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Check if component images exist | |
| id: check-components | |
| run: | | |
| MISSING="" | |
| if ! docker manifest inspect ghcr.io/discere-os/wasm-builder-llvm:07ca4db1 >/dev/null 2>&1; then | |
| MISSING="${MISSING}llvm " | |
| fi | |
| if ! docker manifest inspect ghcr.io/discere-os/wasm-builder-binaryen:c326e66b >/dev/null 2>&1; then | |
| MISSING="${MISSING}binaryen " | |
| fi | |
| if ! docker manifest inspect ghcr.io/discere-os/wasm-builder-wabt:1.0.38 >/dev/null 2>&1; then | |
| MISSING="${MISSING}wabt " | |
| fi | |
| if ! docker manifest inspect ghcr.io/discere-os/wasm-builder-cache:00c5c7d9 >/dev/null 2>&1; then | |
| MISSING="${MISSING}cache " | |
| fi | |
| if [ -n "$MISSING" ]; then | |
| echo "missing=$MISSING" >> $GITHUB_OUTPUT | |
| echo "all_present=false" >> $GITHUB_OUTPUT | |
| echo "⚠️ Missing component images: $MISSING" | |
| echo "Run the 'Build WASM Builder Components' workflow first" | |
| else | |
| echo "all_present=true" >> $GITHUB_OUTPUT | |
| echo "✓ All component images present" | |
| fi | |
| - name: Extract metadata for ${{ matrix.variant.name }} | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| flavor: | | |
| suffix=${{ matrix.variant.suffix }},onlatest=true | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha,prefix={{branch}}- | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && matrix.variant.name == 'standard' }} | |
| type=raw,value=${{ matrix.variant.name }},enable={{is_default_branch}} | |
| - name: Build and push ${{ matrix.variant.name }} variant | |
| if: steps.check-components.outputs.all_present == 'true' | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| target: ${{ matrix.variant.target }} | |
| platforms: linux/amd64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| annotations: ${{ steps.meta.outputs.annotations }} | |
| cache-from: type=gha,scope=${{ matrix.variant.name }} | |
| cache-to: type=gha,mode=max,scope=${{ matrix.variant.name }} | |
| build-args: | | |
| BUILDTIME=${{ github.event.head_commit.timestamp }} | |
| VERSION=${{ steps.meta.outputs.version }} | |
| REVISION=${{ github.sha }} | |
| - name: Cleanup Docker build cache | |
| if: always() | |
| run: docker builder prune -f --keep-storage 20GB | |
| - name: Test Docker image | |
| run: | | |
| docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} sh -c " | |
| echo '=== Testing WASM Builder Environment ===' && \ | |
| emcc --version && \ | |
| deno --version && \ | |
| node --version && \ | |
| pnpm --version && \ | |
| wrangler --version && \ | |
| meson --version && \ | |
| ninja --version && \ | |
| cmake --version && \ | |
| python3 --version && \ | |
| gcc --version && \ | |
| echo '=== All tools verified successfully ===' \ | |
| " | |
| - name: Test Emscripten compilation | |
| run: | | |
| docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} sh -c " | |
| echo 'int main() { return 42; }' > /tmp/test.c && \ | |
| emcc /tmp/test.c -o /tmp/test.js -O3 && \ | |
| echo '=== Emscripten compilation test passed ===' \ | |
| " | |
| - name: Test ports system state for ${{ matrix.variant.name }} | |
| run: | | |
| if [ "${{ matrix.variant.name }}" = "minimal" ]; then | |
| echo "Testing that ports are removed in minimal variant..." | |
| docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} sh -c " | |
| if [ -d /opt/emscripten/tools/ports ]; then | |
| echo 'ERROR: Ports directory should not exist in minimal variant' | |
| exit 1 | |
| fi | |
| echo '=== Ports correctly removed in minimal variant ===' | |
| " | |
| else | |
| echo "Testing that ports exist in standard variant..." | |
| docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} sh -c " | |
| if [ ! -d /opt/emscripten/tools/ports ]; then | |
| echo 'ERROR: Ports directory should exist in standard variant' | |
| exit 1 | |
| fi | |
| port_count=\$(ls /opt/emscripten/tools/ports/*.py 2>/dev/null | wc -l) | |
| echo \"Found \$port_count port definitions\" | |
| if [ \"\$port_count\" -lt 20 ]; then | |
| echo 'ERROR: Expected at least 20 ports' | |
| exit 1 | |
| fi | |
| echo '=== Ports correctly present in standard variant ===' | |
| " | |
| fi | |
| - name: Generate build summary | |
| run: | | |
| echo "# Docker Image Build Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Image Details" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Registry**: \`${{ env.REGISTRY }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Image**: \`${{ env.IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tags**: \`${{ steps.meta.outputs.tags }}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Installed Tools" >> $GITHUB_STEP_SUMMARY | |
| echo "- Emscripten 4.0.16" >> $GITHUB_STEP_SUMMARY | |
| echo "- Deno 2.5.4" >> $GITHUB_STEP_SUMMARY | |
| echo "- Node.js 22" >> $GITHUB_STEP_SUMMARY | |
| echo "- pnpm + Wrangler" >> $GITHUB_STEP_SUMMARY | |
| echo "- Meson + Ninja" >> $GITHUB_STEP_SUMMARY | |
| echo "- CMake" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Usage" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY | |
| echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY | |
| echo "docker run --rm -v \$(pwd):/workspace ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest deno task build:wasm" >> $GITHUB_STEP_SUMMARY | |
| echo "\`\`\`" >> $GITHUB_STEP_SUMMARY | |
| security-scan: | |
| name: Security Scan | |
| runs-on: [self-hosted, docker, wasm, emscripten] | |
| needs: build | |
| if: github.event_name != 'pull_request' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@0.28.0 | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v3 | |
| with: | |
| sarif_file: 'trivy-results.sarif' |