Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e18d13d
test: trigger CI/CD pipeline to test multiplatform Docker build
manavgup Oct 4, 2025
da4b925
feat: implement multiplatform Docker support (ARM64 + AMD64)
manavgup Oct 4, 2025
a73d1dd
fix: clarify multiplatform manifest handling in docker-release workflow
manavgup Oct 4, 2025
e6e78fe
cleanup: remove test files and local documentation from PR
manavgup Oct 4, 2025
2e470da
chore: add local documentation to gitignore
manavgup Oct 4, 2025
c8d6ad8
perf: optimize multiplatform Docker builds for faster CI
manavgup Oct 4, 2025
17bcc49
fix: resolve yamllint trailing whitespace errors
manavgup Oct 4, 2025
01bc08a
fix: resolve Docker repository name case and simplify multiplatform b…
manavgup Oct 4, 2025
e2a8468
fix: resolve trailing whitespace issues found by pre-commit hooks
manavgup Oct 4, 2025
a941d87
fix: disable slow Simple Multiplatform Docker Build workflow
manavgup Oct 4, 2025
36a05e2
fix: resolve trailing spaces in YAML workflow file
manavgup Oct 4, 2025
616a52e
fix: move GHCR authentication before multiplatform build
manavgup Oct 4, 2025
b8bc595
fix: pull multiplatform image for local scanning
manavgup Oct 4, 2025
e43710f
fix: only upload SARIF files when they exist
manavgup Oct 4, 2025
c7336e7
fix: disable package scriptlets for ARM64 installroot under QEMU
manavgup Oct 4, 2025
7ddacb0
fix: install filesystem and bash before using noscripts
manavgup Oct 4, 2025
114a16a
fix: install ca-certificates with rpm --noscripts to avoid QEMU issues
manavgup Oct 5, 2025
94c2aad
fix: remove --installroot from dnf download command
manavgup Oct 5, 2025
e5a17ac
fix: use dnf reinstall --downloadonly to download ca-certificates
manavgup Oct 5, 2025
484bfaa
refactor: replace --installroot with direct file copying
manavgup Oct 5, 2025
0febe21
fix: create usr/bin and usr/lib64 subdirectories in rootfs
manavgup Oct 5, 2025
c057be3
fix: remove ps from copy list - not present in base image
manavgup Oct 5, 2025
3c59a19
fix: sanitize Dockle SARIF to remove invalid URIs before upload
manavgup Oct 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions .github/workflows/docker-image-multiplatform-optimized.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# ===============================================================
# πŸ“¦ Optimized Multiplatform Docker Build Workflow
# ===============================================================
#
# This workflow builds multiplatform Docker images more efficiently by:
# 1. Building AMD64 and ARM64 images in parallel on native runners
# 2. Using matrix strategy to run builds simultaneously
# 3. Combining results into a single multiplatform manifest
#
# This approach is much faster than cross-compilation with emulation.
# ===============================================================

name: Optimized Multiplatform Docker Build

on:
workflow_dispatch:
inputs:
platforms:
description: 'Platforms to build (comma-separated)'
required: false
default: 'linux/amd64,linux/arm64'
push:
branches: ["main"]
paths:
- 'Containerfile.lite'
- 'mcpgateway/**'
- 'plugins/**'
- 'pyproject.toml'
pull_request:
branches: ["main"]
paths:
- 'Containerfile.lite'
- 'mcpgateway/**'
- 'plugins/**'
- 'pyproject.toml'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
IMAGE_NAME_LOWER: ${{ github.repository }}

jobs:
# Build individual platform images in parallel
build-platform:
runs-on: ${{ matrix.runs-on }}
strategy:
matrix:
include:
- platform: linux/amd64
runs-on: ubuntu-latest
- platform: linux/arm64
runs-on: ubuntu-latest-arm64

steps:
- name: ⬇️ Checkout code
uses: actions/checkout@v4

- name: πŸ› οΈ Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

- name: πŸ”‘ Log in to GHCR
uses: docker/login-action@v3.1.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: πŸ—οΈ Build single-platform image
run: |
TAG=$(date +%s)
IMAGE_NAME_LOWER=$(echo '${{ env.IMAGE_NAME }}' | tr '[:upper:]' '[:lower:]')
echo "πŸ—οΈ Building ${{ matrix.platform }} image..."

docker buildx build \
--platform ${{ matrix.platform }} \
--file Containerfile.lite \
--tag ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:$TAG \
--tag ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:latest \
--push \
--progress=plain \
.

echo "TAG=$TAG" >> $GITHUB_OUTPUT
echo "IMAGE_NAME_LOWER=$IMAGE_NAME_LOWER" >> $GITHUB_OUTPUT

# Combine individual platform images into multiplatform manifest
create-manifest:
needs: build-platform
runs-on: ubuntu-latest

steps:
- name: ⬇️ Checkout code
uses: actions/checkout@v4

- name: πŸ› οΈ Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

- name: πŸ”‘ Log in to GHCR
uses: docker/login-action@v3.1.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: 🏷️ Create multiplatform manifest
run: |
# Get outputs from the first matrix job (both should have same TAG and IMAGE_NAME_LOWER)
TAG=${{ needs.build-platform.outputs.TAG }}
IMAGE_NAME_LOWER=${{ needs.build-platform.outputs.IMAGE_NAME_LOWER }}
echo "🏷️ Creating multiplatform manifest for tag: $TAG"

docker buildx imagetools create \
--tag ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:$TAG \
--tag ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:latest \
${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:$TAG

echo "βœ… Multiplatform manifest created successfully!"

- name: πŸ” Verify multiplatform manifest
run: |
IMAGE_NAME_LOWER=${{ needs.build-platform.outputs.IMAGE_NAME_LOWER }}
docker buildx imagetools inspect ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:latest
89 changes: 89 additions & 0 deletions .github/workflows/docker-image-multiplatform-simple.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# ===============================================================
# πŸ“¦ Simple Multiplatform Docker Build Workflow (DISABLED)
# ===============================================================
#
# ⚠️ DISABLED: This workflow is too slow due to ARM64 emulation on AMD64 runners
#
# This workflow builds multiplatform Docker images using a simpler approach:
# 1. Builds both platforms in a single job
# 2. Uses proper lowercase repository names
# 3. Creates timestamped tags for traceability
# 4. More reliable than matrix-based approach
#
# ❌ PROBLEM: ARM64 builds on AMD64 runners take 3+ hours due to emulation
# βœ… SOLUTION: Use docker-image-multiplatform-optimized.yml instead
# ===============================================================

name: Simple Multiplatform Docker Build (DISABLED - TOO SLOW)

on:
workflow_dispatch:
inputs:
platforms:
description: 'Platforms to build (comma-separated)'
required: false
default: 'linux/amd64,linux/arm64'
# DISABLED: This workflow is too slow due to ARM64 emulation on AMD64 runners
# Use docker-image-multiplatform-optimized.yml instead which uses native ARM64 runners
# push:
# branches: ["main"]
# paths:
# - 'Containerfile.lite'
# - 'mcpgateway/**'
# - 'plugins/**'
# - 'pyproject.toml'
# pull_request:
# branches: ["main"]
# paths:
# - 'Containerfile.lite'
# - 'mcpgateway/**'
# - 'plugins/**'
# - 'pyproject.toml'

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-multiplatform:
runs-on: ubuntu-latest

steps:
- name: ⬇️ Checkout code
uses: actions/checkout@v4

- name: πŸ› οΈ Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

- name: πŸ”‘ Log in to GHCR
uses: docker/login-action@v3.1.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: πŸ—οΈ Build multiplatform image
run: |
TAG=$(date +%s)
IMAGE_NAME_LOWER=$(echo '${{ env.IMAGE_NAME }}' | tr '[:upper:]' '[:lower:]')
echo "πŸ—οΈ Building multiplatform image (linux/amd64,linux/arm64)..."
echo "πŸ“¦ Repository: ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER"
echo "🏷️ Tags: $TAG, latest"
echo "⚠️ Note: ARM64 build on AMD64 runners uses emulation and may take longer"

docker buildx build \
--platform linux/amd64,linux/arm64 \
--file Containerfile.lite \
--tag ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:$TAG \
--tag ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:latest \
--push \
--progress=plain \
.

echo "βœ… Multiplatform image built and pushed successfully!"

- name: πŸ” Verify multiplatform image
run: |
IMAGE_NAME_LOWER=$(echo '${{ env.IMAGE_NAME }}' | tr '[:upper:]' '[:lower:]')
echo "πŸ” Verifying multiplatform image..."
docker buildx imagetools inspect ${{ env.REGISTRY }}/$IMAGE_NAME_LOWER:latest
79 changes: 47 additions & 32 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ jobs:
run: |
curl -sSL https://github.com/hadolint/hadolint/releases/latest/download/hadolint-Linux-x86_64 -o /usr/local/bin/hadolint
chmod +x /usr/local/bin/hadolint
hadolint -f sarif Containerfile.lite > hadolint-results.sarif
hadolint -f sarif Containerfile.lite > hadolint-results.sarif || true
echo "HADOLINT_EXIT=$?" >> "$GITHUB_ENV"
exit 0
- name: ☁️ Upload Hadolint SARIF
if: always()
if: always() && hashFiles('hadolint-results.sarif') != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: hadolint-results.sarif
Expand All @@ -95,25 +94,47 @@ jobs:
restore-keys: ${{ runner.os }}-buildx-

# -------------------------------------------------------------
# 3️⃣ Build & tag image (timestamp + latest)
# 3️⃣ Log in to GHCR (before build)
# -------------------------------------------------------------
- name: πŸ—οΈ Build Docker image
- name: πŸ”‘ Log in to GHCR
uses: docker/login-action@v3.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# -------------------------------------------------------------
# 4️⃣ Build & tag image (timestamp + latest)
# -------------------------------------------------------------
- name: πŸ—οΈ Build multi-platform Docker image
env:
DOCKER_CONTENT_TRUST: "1"
run: |
TAG=$(date +%s)
echo "TAG=$TAG" >> "$GITHUB_ENV"
echo "πŸ—οΈ Building multi-platform image (linux/amd64,linux/arm64)..."
echo "⚠️ Note: ARM64 build on AMD64 runners uses emulation and may take longer"
docker buildx build \
--platform linux/amd64,linux/arm64 \
--file Containerfile.lite \
--tag $IMAGE_NAME:$TAG \
--tag $IMAGE_NAME:latest \
--cache-from type=local,src=${{ env.CACHE_DIR }} \
--cache-to type=local,dest=${{ env.CACHE_DIR }},mode=max \
--load \
--push \
--progress=plain \
. # build context is mandatory

# -------------------------------------------------------------
# 4️⃣ Image lint (Dockle CLI β†’ SARIF)
# 5️⃣ Pull image for scanning (multiplatform builds don't load locally)
# -------------------------------------------------------------
- name: πŸ“₯ Pull image for local scanning
run: |
echo "πŸ“₯ Pulling image for scanning (multiplatform images not available locally after --push)..."
docker pull $IMAGE_NAME:latest

# -------------------------------------------------------------
# 6️⃣ Image lint (Dockle CLI β†’ SARIF)
# -------------------------------------------------------------
- name: πŸ” Image lint (Dockle)
id: dockle
Expand All @@ -125,17 +146,25 @@ jobs:
| tar -xz -C /usr/local/bin dockle
dockle --exit-code 1 --format sarif \
--output dockle-results.sarif \
$IMAGE_NAME:latest
$IMAGE_NAME:latest || true
echo "DOCKLE_EXIT=$?" >> "$GITHUB_ENV"
exit 0
- name: 🧹 Sanitize Dockle SARIF (remove invalid URIs)
if: always() && hashFiles('dockle-results.sarif') != ''
run: |
# Filter out results with invalid URIs (containing spaces or non-file-path characters)
jq '.runs[].results |= map(select(
(.locations // []) | length == 0 or
all(.physicalLocation.artifactLocation.uri | test("^[^\\s]+$"))
))' dockle-results.sarif > dockle-results-clean.sarif || cp dockle-results.sarif dockle-results-clean.sarif
mv dockle-results-clean.sarif dockle-results.sarif
- name: ☁️ Upload Dockle SARIF
if: always()
if: always() && hashFiles('dockle-results.sarif') != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: dockle-results.sarif

# -------------------------------------------------------------
# 5️⃣ Generate SPDX SBOM with Syft
# 7️⃣ Generate SPDX SBOM with Syft
# -------------------------------------------------------------
- name: πŸ“„ Generate SBOM (Syft)
uses: anchore/sbom-action@v0.20.5
Expand All @@ -144,7 +173,7 @@ jobs:
output-file: sbom.spdx.json

# -------------------------------------------------------------
# 6️⃣ Trivy, Grype CVE scan β†’ SARIF
# 8️⃣ Trivy, Grype CVE scan β†’ SARIF
# -------------------------------------------------------------
- name: πŸ›‘οΈ Trivy vulnerability scan
if: env.TRIVY_ENABLED == 'true'
Expand All @@ -158,7 +187,7 @@ jobs:
severity: CRITICAL
exit-code: 0
- name: ☁️ Upload Trivy SARIF
if: always() && env.TRIVY_ENABLED == 'true'
if: always() && env.TRIVY_ENABLED == 'true' && hashFiles('trivy-results.sarif') != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
Expand All @@ -167,35 +196,21 @@ jobs:
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
- name: πŸ” Grype vulnerability scan
continue-on-error: true
run: |
grype ${{ env.IMAGE_NAME }}:latest --scope all-layers --only-fixed
- name: πŸ“„ Generating Grype SARIF report
continue-on-error: true
run: |
grype ${{ env.IMAGE_NAME }}:latest --scope all-layers --output sarif --file grype-results.sarif
- name: ☁️ Upload Grype SARIF
if: always()
if: always() && hashFiles('grype-results.sarif') != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: grype-results.sarif

# -------------------------------------------------------------
# 7️⃣ Push both tags to GHCR
# -------------------------------------------------------------
- name: πŸ”‘ Log in to GHCR
uses: docker/login-action@v3.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: πŸš€ Push image to GHCR
if: github.ref == 'refs/heads/main'
run: |
docker push $IMAGE_NAME:${{ env.TAG }}
docker push $IMAGE_NAME:latest

# -------------------------------------------------------------
# 8️⃣ Key-less Cosign sign + attest (latest **and** timestamp)
# 9️⃣ Key-less Cosign sign + attest (latest **and** timestamp)
# -------------------------------------------------------------
- name: πŸ“₯ Install Cosign
if: github.ref == 'refs/heads/main'
Expand All @@ -218,7 +233,7 @@ jobs:
done

# -------------------------------------------------------------
# 9️⃣ Single gate - fail job on any scanner error
# πŸ”Ÿ Single gate - fail job on any scanner error
# -------------------------------------------------------------
- name: β›” Enforce lint & vuln gates
if: |
Expand Down
Loading
Loading