Compare commits

..

No commits in common. "main" and "test-1" have entirely different histories.
main ... test-1

10 changed files with 103 additions and 816 deletions

View File

@ -1,3 +0,0 @@
web=sha256:cce6d72541facea17321cb7f76fc8947c54335a3675cabe64214efad366240f4
android=sha256:489f3cbd316bd66d3475430b69e1b32f87606a63f8837c3588b04da83b7509ff
linux=sha256:2c77cba8eb20114f45d7b9d97d5d6773fa542c72cb6252d6f1c00de9fb4c7e95

View File

@ -5,10 +5,7 @@ on:
types: [published, prereleased]
permissions:
contents: write
env:
IMAGE_NAME: flutter-sdk
contents: read
jobs:
build-and-push:
@ -17,13 +14,13 @@ jobs:
matrix:
include:
- dockerfile: Dockerfile
variant: web
image_name: gpb-flutter-sdk-web
description: Minimal Flutter SDK for Web/WASM CI builds
- dockerfile: Dockerfile.android
variant: android
image_name: gpb-flutter-sdk-android
description: Flutter SDK for Android CI builds
- dockerfile: Dockerfile.linux
variant: linux
image_name: gpb-flutter-sdk-linux
description: Flutter SDK for Linux desktop CI builds
steps:
- name: Checkout code
@ -33,23 +30,18 @@ jobs:
id: version
run: |
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
# Pre-release: use release tag as version, don't overwrite existing tags
# Extract base Flutter version for build-arg (e.g., 3.38.9 from 3.38.9-rebuild-20260203)
BASE_VERSION=$(echo "${{ github.event.release.tag_name }}" | grep -oP '^[0-9]+\.[0-9]+\.[0-9]+')
echo "flutter_version=${BASE_VERSION}" >> $GITHUB_OUTPUT
echo "tag=${{ matrix.variant }}-${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
echo "secondary_tag=${{ matrix.variant }}-dev" >> $GITHUB_OUTPUT
echo "Pre-release: building Flutter ${BASE_VERSION}, tagging as ${{ github.event.release.tag_name }}"
# Pre-release: fetch latest stable version, tag as dev
FLUTTER_VERSION=$(curl -fsSL https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json \
| jq -r '.current_release.stable as $hash | .releases[] | select(.hash == $hash and .channel == "stable") | .version')
echo "flutter_version=${FLUTTER_VERSION}" >> $GITHUB_OUTPUT
echo "tag=dev" >> $GITHUB_OUTPUT
echo "Using latest Flutter stable ${FLUTTER_VERSION} for pre-release test"
else
# Full release: use the release tag as the Flutter version
echo "flutter_version=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
echo "tag=${{ matrix.variant }}-${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
echo "secondary_tag=${{ matrix.variant }}-latest" >> $GITHUB_OUTPUT
echo "tag=latest" >> $GITHUB_OUTPUT
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@ -69,7 +61,7 @@ jobs:
load: true
build-args: |
FLUTTER_VERSION=${{ steps.version.outputs.flutter_version }}
tags: ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
tags: ${{ vars.REGISTRY }}/${{ matrix.image_name }}:${{ steps.version.outputs.flutter_version }}
- name: Install Docker Scout
run: |
@ -78,59 +70,24 @@ jobs:
- name: Docker Scout CVE Scan
run: |
docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }} --only-severity critical,high
docker scout cves ${{ vars.REGISTRY }}/${{ matrix.image_name }}:${{ steps.version.outputs.flutter_version }} --only-severity critical,high
- name: Build and push with attestations
uses: docker/build-push-action@v5
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64
platforms: linux/amd64
push: true
sbom: true
provenance: mode=max
build-args: |
FLUTTER_VERSION=${{ steps.version.outputs.flutter_version }}
tags: |
${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.secondary_tag }}
${{ vars.REGISTRY }}/${{ matrix.image_name }}:${{ steps.version.outputs.flutter_version }}
${{ vars.REGISTRY }}/${{ matrix.image_name }}:${{ steps.version.outputs.tag }}
labels: |
org.opencontainers.image.title=${{ env.IMAGE_NAME }}
org.opencontainers.image.title=${{ matrix.image_name }}
org.opencontainers.image.description=${{ matrix.description }}
org.opencontainers.image.version=${{ steps.version.outputs.tag }}
org.opencontainers.image.version=${{ steps.version.outputs.flutter_version }}
org.opencontainers.image.revision=${{ github.sha }}
sync-readme:
needs: build-and-push
if: github.event.release.prerelease == false && !contains(github.event.release.tag_name, 'test')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Update README badges
run: |
FLUTTER="${{ github.event.release.tag_name }}"
ANDROID_SDK=$(grep -oP 'ANDROID_BUILD_TOOLS=\K[0-9.]+' Dockerfile.android || echo "")
if [ -n "$ANDROID_SDK" ]; then
sed -i "s|Android%20SDK-[0-9.]*-green|Android%20SDK-${ANDROID_SDK}-green|g" README.md
fi
if [ -n "$FLUTTER" ]; then
sed -i "s|web--[0-9.]*-blue|web--${FLUTTER}-blue|g" README.md
sed -i "s|android--[0-9.]*-blue|android--${FLUTTER}-blue|g" README.md
sed -i "s|linux--[0-9.]*-blue|linux--${FLUTTER}-blue|g" README.md
sed -i "s|/web-[0-9.]*?|/web-${FLUTTER}?|g" README.md
sed -i "s|/android-[0-9.]*?|/android-${FLUTTER}?|g" README.md
sed -i "s|/linux-[0-9.]*?|/linux-${FLUTTER}?|g" README.md
fi
- name: Commit and push
run: |
git diff --quiet README.md && exit 0
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add README.md
git commit -m "Update README badges to Flutter ${{ github.event.release.tag_name }}"
git push

View File

@ -8,9 +8,6 @@ permissions:
contents: read
pull-requests: write
env:
IMAGE_NAME: flutter-sdk
jobs:
scout:
runs-on: ubuntu-latest
@ -18,11 +15,11 @@ jobs:
matrix:
include:
- dockerfile: Dockerfile
variant: web
image_name: gpb-flutter-sdk-web
- dockerfile: Dockerfile.android
variant: android
image_name: gpb-flutter-sdk-android
- dockerfile: Dockerfile.linux
variant: linux
image_name: gpb-flutter-sdk-linux
steps:
- name: Log in to DockerHub
uses: docker/login-action@v3
@ -33,12 +30,12 @@ jobs:
- name: Check if latest image exists
id: should_run
run: |
if docker manifest inspect ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest > /dev/null 2>&1; then
if docker manifest inspect ${{ vars.REGISTRY }}/${{ matrix.image_name }}:latest > /dev/null 2>&1; then
echo "run=true" >> $GITHUB_OUTPUT
echo "${{ matrix.variant }}-latest found, Scout compare will run"
echo "Latest ${{ matrix.image_name }} found, Scout compare will run"
else
echo "run=false" >> $GITHUB_OUTPUT
echo "No ${{ matrix.variant }}-latest found, skipping (nothing to compare against)"
echo "No latest ${{ matrix.image_name }} found, skipping (nothing to compare against)"
fi
- name: Checkout code
@ -58,7 +55,7 @@ jobs:
platforms: linux/amd64
push: false
load: true
tags: ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-pr-${{ github.event.pull_request.number }}
tags: ${{ vars.REGISTRY }}/${{ matrix.image_name }}:pr-${{ github.event.pull_request.number }}
- name: Install Docker Scout
if: steps.should_run.outputs.run == 'true'
@ -69,4 +66,4 @@ jobs:
- name: Docker Scout Compare
if: steps.should_run.outputs.run == 'true'
run: |
docker scout compare ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-pr-${{ github.event.pull_request.number }} --to ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest --ignore-unchanged --only-severity critical,high
docker scout compare ${{ vars.REGISTRY }}/${{ matrix.image_name }}:pr-${{ github.event.pull_request.number }} --to ${{ vars.REGISTRY }}/${{ matrix.image_name }}:latest --ignore-unchanged --only-severity critical,high

View File

@ -1,4 +1,4 @@
name: Check for Flutter SDK, Android SDK, and Base Image Updates
name: Check for Flutter SDK Updates
on:
schedule:
@ -9,9 +9,6 @@ jobs:
check-update:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Get latest Flutter stable version
id: flutter
run: |
@ -20,47 +17,7 @@ jobs:
echo "version=${LATEST}" >> $GITHUB_OUTPUT
echo "Latest Flutter stable: ${LATEST}"
- name: Get latest Android SDK versions
id: android
run: |
REPO_XML=$(curl -fsSL "https://dl.google.com/android/repository/repository2-1.xml")
# Latest stable build-tools (exclude rc/alpha/beta)
BUILD_TOOLS=$(echo "$REPO_XML" | grep -o 'path="build-tools;[0-9]*\.[0-9]*\.[0-9]*"' | \
sed 's/path="build-tools;//;s/"//' | sort -V | tail -1)
# Latest stable platform
PLATFORM=$(echo "$REPO_XML" | grep -o 'path="platforms;android-[0-9]*"' | \
sed 's/path="platforms;android-//;s/"//' | sort -n | tail -1)
# Latest cmdline-tools download ID
CMDLINE_DOWNLOAD=$(echo "$REPO_XML" | grep -o 'commandlinetools-linux-[0-9]*_latest' | \
sed 's/commandlinetools-linux-//;s/_latest//' | sort -n | tail -1)
echo "build_tools=${BUILD_TOOLS}" >> $GITHUB_OUTPUT
echo "platform=${PLATFORM}" >> $GITHUB_OUTPUT
echo "cmdline_tools=${CMDLINE_DOWNLOAD}" >> $GITHUB_OUTPUT
echo "Latest Android build-tools: ${BUILD_TOOLS}"
echo "Latest Android platform: ${PLATFORM}"
echo "Latest cmdline-tools download: ${CMDLINE_DOWNLOAD}"
- name: Check current Android SDK versions in Dockerfile
id: current
run: |
CURRENT_CMDLINE=$(grep -o 'ANDROID_SDK_TOOLS_VERSION=[0-9]*' Dockerfile.android | cut -d= -f2)
CURRENT_BUILD_TOOLS=$(grep -o 'ANDROID_BUILD_TOOLS=[0-9.]*' Dockerfile.android | cut -d= -f2)
CURRENT_PLATFORM=$(grep -o 'ANDROID_COMPILE_SDK=[0-9]*' Dockerfile.android | cut -d= -f2)
echo "cmdline_tools=${CURRENT_CMDLINE}" >> $GITHUB_OUTPUT
echo "build_tools=${CURRENT_BUILD_TOOLS}" >> $GITHUB_OUTPUT
echo "platform=${CURRENT_PLATFORM}" >> $GITHUB_OUTPUT
echo "Current cmdline-tools: ${CURRENT_CMDLINE}"
echo "Current build-tools: ${CURRENT_BUILD_TOOLS}"
echo "Current platform: ${CURRENT_PLATFORM}"
- name: Check if Flutter release already exists
- name: Check if release already exists
id: existing
run: |
VERSION="${{ steps.flutter.outputs.version }}"
@ -74,99 +31,7 @@ jobs:
echo "Release ${VERSION} not found, will create"
fi
- name: Determine if Android SDK update is needed
id: android_update
run: |
NEEDS_UPDATE=false
if [ "${{ steps.android.outputs.cmdline_tools }}" != "${{ steps.current.outputs.cmdline_tools }}" ]; then
echo "cmdline-tools update available: ${{ steps.current.outputs.cmdline_tools }} -> ${{ steps.android.outputs.cmdline_tools }}"
NEEDS_UPDATE=true
fi
if [ "${{ steps.android.outputs.build_tools }}" != "${{ steps.current.outputs.build_tools }}" ]; then
echo "build-tools update available: ${{ steps.current.outputs.build_tools }} -> ${{ steps.android.outputs.build_tools }}"
NEEDS_UPDATE=true
fi
if [ "${{ steps.android.outputs.platform }}" != "${{ steps.current.outputs.platform }}" ]; then
echo "platform update available: ${{ steps.current.outputs.platform }} -> ${{ steps.android.outputs.platform }}"
NEEDS_UPDATE=true
fi
echo "needs_update=${NEEDS_UPDATE}" >> $GITHUB_OUTPUT
- name: Check Wolfi base image updates
id: base_images
run: |
# Get current digests from Docker Hub
WEB_DIGEST=$(curl -s "https://hub.docker.com/v2/repositories/svrnty/base-distro/tags/flutter-sdk-latest" | jq -r '.digest // empty')
ANDROID_DIGEST=$(curl -s "https://hub.docker.com/v2/repositories/svrnty/base-distro/tags/flutter-sdk-android-latest" | jq -r '.digest // empty')
LINUX_DIGEST=$(curl -s "https://hub.docker.com/v2/repositories/svrnty/base-distro/tags/flutter-sdk-linux-latest" | jq -r '.digest // empty')
echo "Current base image digests:"
echo " web: ${WEB_DIGEST}"
echo " android: ${ANDROID_DIGEST}"
echo " linux: ${LINUX_DIGEST}"
# Load stored digests
STORED_WEB=$(grep '^web=' .base-digests 2>/dev/null | cut -d= -f2 || echo "")
STORED_ANDROID=$(grep '^android=' .base-digests 2>/dev/null | cut -d= -f2 || echo "")
STORED_LINUX=$(grep '^linux=' .base-digests 2>/dev/null | cut -d= -f2 || echo "")
# Compare
NEEDS_REBUILD=false
if [ -n "$WEB_DIGEST" ] && [ "$WEB_DIGEST" != "$STORED_WEB" ]; then
echo "Web base image updated"
NEEDS_REBUILD=true
fi
if [ -n "$ANDROID_DIGEST" ] && [ "$ANDROID_DIGEST" != "$STORED_ANDROID" ]; then
echo "Android base image updated"
NEEDS_REBUILD=true
fi
if [ -n "$LINUX_DIGEST" ] && [ "$LINUX_DIGEST" != "$STORED_LINUX" ]; then
echo "Linux base image updated"
NEEDS_REBUILD=true
fi
echo "needs_rebuild=${NEEDS_REBUILD}" >> $GITHUB_OUTPUT
echo "web_digest=${WEB_DIGEST}" >> $GITHUB_OUTPUT
echo "android_digest=${ANDROID_DIGEST}" >> $GITHUB_OUTPUT
echo "linux_digest=${LINUX_DIGEST}" >> $GITHUB_OUTPUT
- name: Trigger rebuild for base image updates
if: steps.base_images.outputs.needs_rebuild == 'true' && steps.existing.outputs.exists == 'true'
run: |
VERSION="${{ steps.flutter.outputs.version }}"
echo "Base image updated, triggering rebuild for Flutter ${VERSION}"
# Update stored digests
cat > .base-digests << EOF
web=${{ steps.base_images.outputs.web_digest }}
android=${{ steps.base_images.outputs.android_digest }}
linux=${{ steps.base_images.outputs.linux_digest }}
EOF
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .base-digests
git commit -m "Update base image digests (Wolfi security update)"
git push
# Trigger rebuild by creating a prerelease
curl -fsSL -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" \
-d "{
\"tag_name\": \"${VERSION}-rebuild-$(date +%Y%m%d)\",
\"name\": \"Security rebuild ${VERSION}\",
\"body\": \"Automated rebuild for Wolfi base image security updates\",
\"draft\": false,
\"prerelease\": true
}"
- name: Create release for new Flutter version
- name: Create release for new version
if: steps.existing.outputs.exists == 'false' && steps.flutter.outputs.version != ''
run: |
VERSION="${{ steps.flutter.outputs.version }}"
@ -182,33 +47,3 @@ jobs:
\"draft\": false,
\"prerelease\": false
}"
- name: Create issue for Android SDK updates
if: steps.android_update.outputs.needs_update == 'true'
run: |
BODY="Android SDK updates are available:\n\n"
BODY+="| Component | Current | Latest |\n"
BODY+="|-----------|---------|--------|\n"
BODY+="| cmdline-tools | ${{ steps.current.outputs.cmdline_tools }} | ${{ steps.android.outputs.cmdline_tools }} |\n"
BODY+="| build-tools | ${{ steps.current.outputs.build_tools }} | ${{ steps.android.outputs.build_tools }} |\n"
BODY+="| platform | ${{ steps.current.outputs.platform }} | ${{ steps.android.outputs.platform }} |\n"
BODY+="\nUpdate \`Dockerfile.android\` to use the latest versions."
# Check if issue already exists
EXISTING=$(curl -s -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/issues?state=open&labels=android-sdk-update" | jq length)
if [ "$EXISTING" = "0" ]; then
curl -fsSL -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/issues" \
-d "{
\"title\": \"Android SDK updates available\",
\"body\": \"$(echo -e "$BODY")\",
\"labels\": [\"android-sdk-update\"]
}"
echo "Created issue for Android SDK updates"
else
echo "Issue for Android SDK updates already exists"
fi

View File

@ -1,131 +0,0 @@
# Flutter SDK Docker Images
<a href="https://git.openharbor.io/svrnty/docker-flutter-sdk" target="_blank"><img src="https://img.shields.io/badge/Git-Repository-orange?logo=gitea" alt="Git Repository"></a>
<a href="https://wolfi.dev" target="_blank"><img src="https://img.shields.io/badge/Base-Wolfi-purple?logo=linux" alt="Wolfi"></a>
Lightweight Flutter SDK images for CI/CD pipelines. Built on [Wolfi](https://wolfi.dev), a security-focused Linux distribution designed for containers.
## Variants
- `web` - Web/WASM builds
- `android` - Android APK/AAB builds
- `linux` - Linux desktop builds
All variants support `linux/amd64` and `linux/arm64`.
## Why Wolfi?
[Wolfi](https://wolfi.dev) is a lightweight Linux distribution built specifically for containers. It provides:
- **Minimal footprint** - Only essential packages, nothing extra
- **Daily security updates** - Patches applied quickly
- **Designed for containers** - No legacy cruft from traditional distros
## Features
- **Lightweight** - Optimized for fast CI/CD pulls
- **Secure** - Built on Wolfi with continuous vulnerability scanning
- **Multi-arch** - Supports both `linux/amd64` and `linux/arm64`
- **Non-root** - Runs as unprivileged user (UID 65532)
- **Supply chain security** - SBOM and SLSA provenance attestations included
## Dockerfile Examples
### Web App (WASM)
```dockerfile
FROM svrnty/flutter-sdk:web-latest AS build
WORKDIR /app
COPY . .
RUN flutter pub get && flutter build web --wasm --release
# Cache-busting: append version to JS/WASM references
RUN VERSION=$(date +%s) && cd build/web && \
sed -i "s|flutter_bootstrap\.js\"|flutter_bootstrap.js?v=${VERSION}\"|g" index.html && \
sed -i "s|main\.dart\.js\"|main.dart.js?v=${VERSION}\"|g" flutter_bootstrap.js && \
sed -i "s|main\.dart\.mjs\"|main.dart.mjs?v=${VERSION}\"|g" flutter_bootstrap.js && \
sed -i "s|main\.dart\.wasm\"|main.dart.wasm?v=${VERSION}\"|g" flutter_bootstrap.js
FROM ghcr.io/static-web-server/static-web-server:2 AS sws
FROM scratch
COPY --from=sws /static-web-server /static-web-server
COPY --from=build /app/build/web /public
COPY sws.toml /sws.toml
EXPOSE 8080
USER 65534
ENTRYPOINT ["/static-web-server", "--config-file", "/sws.toml"]
```
**sws.toml** - Required headers for WASM multi-threading:
```toml
[general]
host = "0.0.0.0"
port = 8080
root = "/public"
page-fallback = "/public/index.html"
compression = true
[[advanced.headers]]
source = "**"
[advanced.headers.headers]
Cross-Origin-Opener-Policy = "same-origin"
Cross-Origin-Embedder-Policy = "require-corp"
```
### Android APK
```dockerfile
FROM svrnty/flutter-sdk:android-latest AS build
WORKDIR /app
COPY . .
RUN flutter pub get && flutter build apk --release
FROM scratch
COPY --from=build /app/build/app/outputs/flutter-apk/app-release.apk /
```
### Linux Desktop
```dockerfile
FROM svrnty/flutter-sdk:linux-latest AS build
WORKDIR /app
COPY . .
RUN flutter pub get && flutter build linux --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y libgtk-3-0 && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/build/linux/arm64/release/bundle /app
ENTRYPOINT ["/app/my_app"]
```
## CI/CD (Gitea/GitHub Actions)
```yaml
jobs:
build:
runs-on: ubuntu-latest
container:
image: svrnty/flutter-sdk:android-latest
steps:
- uses: actions/checkout@v4
- run: flutter pub get
- run: flutter build apk --release
```
## Tags
- `<variant>-latest` - Latest stable Flutter release
- `<variant>-<version>` - Specific Flutter version (e.g., `android-3.29.0`)
## Automatic Updates
Images are automatically rebuilt when:
- New Flutter stable versions are released
- Base image security updates are available
Every build is scanned and includes supply chain attestations (SBOM, SLSA provenance).
## License
MIT

View File

@ -1,20 +1,31 @@
FROM svrnty/base-distro:flutter-sdk-latest
FROM debian:bookworm-slim
ARG FLUTTER_VERSION=3.38.9
LABEL org.opencontainers.image.title="flutter-sdk-web"
LABEL org.opencontainers.image.title="gpb-flutter-sdk-web"
LABEL org.opencontainers.image.description="Minimal Flutter SDK for Web/WASM CI builds"
LABEL org.opencontainers.image.version="${FLUTTER_VERSION}"
USER 0
# Install minimal dependencies for Flutter web builds
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
unzip \
xz-utils \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Clone Flutter SDK from git (supports both amd64 and arm64)
RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git /opt/flutter && \
git config --global --add safe.directory /opt/flutter && \
rm -rf /opt/flutter/dev /opt/flutter/examples && \
chown -R 65532:65532 /opt/flutter
ENV FLUTTER_HOME=/opt/flutter
ENV PATH="${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}"
USER 65532
# Download Flutter SDK from official archive
RUN curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
-o /tmp/flutter.tar.xz && \
tar xf /tmp/flutter.tar.xz -C /opt && \
rm /tmp/flutter.tar.xz
# Mark git directory as safe (tarball is owned by different uid)
RUN git config --global --add safe.directory "${FLUTTER_HOME}"
# Configure for web-only (disable everything else)
RUN flutter config --enable-web \

View File

@ -1,51 +1,54 @@
FROM svrnty/base-distro:flutter-sdk-android-latest
FROM debian:bookworm-slim
ARG FLUTTER_VERSION=3.38.9
ARG ANDROID_SDK_TOOLS_VERSION=14742923
ARG ANDROID_COMPILE_SDK=36
ARG ANDROID_BUILD_TOOLS=36.1.0
ARG ANDROID_SDK_TOOLS_VERSION=11076708
ARG ANDROID_COMPILE_SDK=35
ARG ANDROID_BUILD_TOOLS=35.0.1
LABEL org.opencontainers.image.title="flutter-sdk-android"
LABEL org.opencontainers.image.title="gpb-flutter-sdk-android"
LABEL org.opencontainers.image.description="Flutter SDK for Android CI builds"
LABEL org.opencontainers.image.version="${FLUTTER_VERSION}"
USER 0
# Install dependencies for Flutter + Android SDK
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
unzip \
xz-utils \
ca-certificates \
openjdk-17-jdk-headless \
&& rm -rf /var/lib/apt/lists/*
# Set up Android SDK environment
# Android SDK
ENV ANDROID_HOME=/opt/android-sdk
ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}"
# Download Android SDK command-line tools, install SDK components, and remove vulnerable lint-psi
RUN mkdir -p "${ANDROID_HOME}/cmdline-tools" && \
curl -fsSL "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip" \
-o /tmp/cmdline-tools.zip && \
unzip -q /tmp/cmdline-tools.zip -d /tmp/cmdline-tools && \
mv /tmp/cmdline-tools/cmdline-tools "${ANDROID_HOME}/cmdline-tools/latest" && \
rm -rf /tmp/cmdline-tools.zip /tmp/cmdline-tools && \
yes | sdkmanager --licenses > /dev/null 2>&1 && \
rm -rf /tmp/cmdline-tools.zip /tmp/cmdline-tools
# Accept licenses and install SDK components
RUN yes | sdkmanager --licenses > /dev/null 2>&1 && \
sdkmanager --install \
"platform-tools" \
"platforms;android-${ANDROID_COMPILE_SDK}" \
"build-tools;${ANDROID_BUILD_TOOLS}" && \
# Remove lint-psi to eliminate protobuf-java 2.6.1 CVEs (saves 86MB)
rm -rf "${ANDROID_HOME}/cmdline-tools/latest/lib/external/lint-psi" && \
# Upgrade commons-lang3 from 3.16.0 to 3.18.0 to fix CVE-2025-48924
rm -f "${ANDROID_HOME}/cmdline-tools/latest/lib/external/org/apache/commons/commons-lang3/3.16.0/commons-lang3-3.16.0.jar" && \
mkdir -p "${ANDROID_HOME}/cmdline-tools/latest/lib/external/org/apache/commons/commons-lang3/3.18.0" && \
curl -fsSL "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.jar" \
-o "${ANDROID_HOME}/cmdline-tools/latest/lib/external/org/apache/commons/commons-lang3/3.18.0/commons-lang3-3.18.0.jar"
"build-tools;${ANDROID_BUILD_TOOLS}"
# Clone Flutter SDK from git (supports both amd64 and arm64)
RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git /opt/flutter && \
git config --global --add safe.directory /opt/flutter && \
rm -rf /opt/flutter/dev /opt/flutter/examples
# Flutter SDK
ENV FLUTTER_HOME=/opt/flutter
ENV PATH="${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}"
# Fix ownership before switching to flutter user
RUN chown -R 65532:65532 /opt/flutter "${ANDROID_HOME}"
RUN curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
-o /tmp/flutter.tar.xz && \
tar xf /tmp/flutter.tar.xz -C /opt && \
rm /tmp/flutter.tar.xz
USER 65532
RUN git config --global --add safe.directory "${FLUTTER_HOME}"
# Configure Flutter for Android-only
# Configure for Android-only
RUN flutter config --enable-android \
--no-enable-web \
--no-enable-ios \

View File

@ -1,27 +1,38 @@
FROM svrnty/base-distro:flutter-sdk-linux-latest
FROM debian:bookworm-slim
ARG FLUTTER_VERSION=3.38.9
LABEL org.opencontainers.image.title="flutter-sdk-linux"
LABEL org.opencontainers.image.title="gpb-flutter-sdk-linux"
LABEL org.opencontainers.image.description="Flutter SDK for Linux desktop CI builds"
LABEL org.opencontainers.image.version="${FLUTTER_VERSION}"
USER 0
# Install dependencies for Flutter + Linux desktop builds
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
unzip \
xz-utils \
ca-certificates \
clang \
cmake \
ninja-build \
pkg-config \
libgtk-3-dev \
liblzma-dev \
libstdc++-12-dev \
&& rm -rf /var/lib/apt/lists/*
# Remove rav1e to eliminate CVE in paste crate (not needed for Flutter)
RUN rm -f /usr/bin/rav1e /usr/lib/librav1e.so* /var/lib/db/sbom/rav1e-*.spdx.json
ENV FLUTTER_HOME=/opt/flutter
ENV PATH="${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}"
# Clone Flutter SDK from git (supports both amd64 and arm64)
RUN git clone --depth 1 --branch ${FLUTTER_VERSION} https://github.com/flutter/flutter.git /opt/flutter && \
git config --global --add safe.directory /opt/flutter && \
rm -rf /opt/flutter/dev /opt/flutter/examples
RUN curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
-o /tmp/flutter.tar.xz && \
tar xf /tmp/flutter.tar.xz -C /opt && \
rm /tmp/flutter.tar.xz
# Fix ownership before switching to flutter user
RUN chown -R 65532:65532 /opt/flutter
RUN git config --global --add safe.directory "${FLUTTER_HOME}"
USER 65532
# Configure Flutter for Linux desktop only
# Configure for Linux desktop only
RUN flutter config --enable-linux-desktop \
--no-enable-web \
--no-enable-android \

259
PLAN.md
View File

@ -1,259 +0,0 @@
# Custom Minimal Flutter SDK Distribution
## Goal
Build Flutter SDK from source in a multi-stage Docker build, then copy only the compiled SDK and its runtime dependencies into a custom minimal Linux base image. The result is a secure, minimal container with zero build toolchain bloat.
## Architecture
```
┌─────────────────────────────────────┐
│ Stage 1: BUILD (debian:bookworm) │
│ │
│ - Full build toolchain │
│ - Compile Flutter engine + Dart │
│ - All build-time deps │
│ - Discarded after build │
└──────────────┬──────────────────────┘
│ COPY artifacts
┌─────────────────────────────────────┐
│ Stage 2: RUNTIME (custom base) │
│ │
│ - Minimal glibc + runtime libs │
│ - Flutter SDK (pre-compiled) │
│ - Only what's needed to run │
`flutter build` commands │
└─────────────────────────────────────┘
```
## Approach: apko + melange on Wolfi
Use Chainguard's open-source tooling to build the runtime base image from Wolfi packages. This avoids maintaining a full Linux distribution while achieving the same result.
### Why Wolfi (not Alpine, not from scratch)
- **glibc-based**: Flutter/Dart require glibc (musl/Alpine won't work)
- **apk package format**: Minimal, fast, composable
- **15,000+ packages**: glibc, libstdc++, git, curl, ca-certificates all available
- **Nightly CVE patching**: Chainguard patches 80%+ of vulnerabilities within 24 hours
- **No distro maintenance burden**: We consume their packages, not maintain them
### Tools
| Tool | Purpose |
|---|---|
| **melange** | Build custom APK packages (Flutter SDK, Android SDK) from source |
| **apko** | Compose final OCI image from APK packages (Wolfi base + custom) |
## Stage 1: Build Flutter from Source
Flutter SDK source build requirements:
- git (clone flutter/flutter and flutter/engine repos)
- Python 3 (engine build scripts)
- curl, unzip, xz-utils
- clang/llvm (engine compilation)
- ninja-build, gn (engine build system)
- libstdc++-dev
- Dart SDK (bootstrapped from prebuilt, then rebuilt)
Flutter engine build steps:
1. Clone `flutter/engine` at the pinned revision from `flutter/flutter/bin/internal/engine.version`
2. Run `gclient sync` to fetch dependencies
3. Build with `gn` + `ninja` for target platform
4. Dart SDK is built as part of the engine
### Per-Variant Build Targets
| Variant | Engine build target | Extra build deps | Extra runtime deps |
|---|---|---|---|
| **web** | `host_release` (dart2js/dart2wasm) | None | None |
| **android** | `android_release` + `android_release_arm64` | Android NDK | JDK 17, Android SDK cmdline-tools |
| **linux** | `linux_release_x64` | GTK3-dev, CMake | GTK3 runtime libs |
## Stage 2: Custom Runtime Base via apko
### Web variant (minimal)
```yaml
# apko.web.yaml
contents:
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
repositories:
- https://packages.wolfi.dev/os
- @local /packages # our melange-built Flutter SDK package
packages:
- wolfi-baselayout
- glibc
- libstdc++
- git
- ca-certificates-bundle
- flutter-sdk-web@local
entrypoint:
command: /bin/sh
environment:
FLUTTER_HOME: /opt/flutter
PATH: /opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:/usr/bin:/bin
archs:
- x86_64
```
### Android variant
Same as web plus:
- `openjdk-17-jre` (from Wolfi)
- `flutter-sdk-android@local` (custom package including Android SDK components)
### Linux desktop variant
Same as web plus:
- `gtk+3.0` runtime libs (from Wolfi, or custom melange package if not available)
- `flutter-sdk-linux@local`
## melange Package Definitions
### flutter-sdk-web.melange.yaml (example structure)
```yaml
package:
name: flutter-sdk-web
version: 3.38.9
epoch: 0
description: Flutter SDK for Web/WASM builds (compiled from source)
environment:
contents:
packages:
- build-base
- git
- python3
- curl
- unzip
- xz
- clang
- ninja
- gn
pipeline:
- uses: git-checkout
with:
repository: https://github.com/flutter/flutter.git
tag: ${{package.version}}
destination: flutter
- runs: |
# Bootstrap Dart SDK
flutter/bin/flutter --version
# Configure for web-only
flutter/bin/flutter config --enable-web \
--no-enable-android --no-enable-ios \
--no-enable-linux-desktop --no-enable-macos-desktop \
--no-enable-windows-desktop
# Precache web artifacts
flutter/bin/flutter precache --web \
--no-android --no-ios --no-linux \
--no-macos --no-windows --no-fuchsia --no-universal
# Install to package destination
mkdir -p ${{targets.destdir}}/opt
mv flutter ${{targets.destdir}}/opt/flutter
# Mark git safe directory
git config --global --add safe.directory /opt/flutter
```
## Implementation Phases
### Phase 1: Proof of Concept (web variant only)
1. Install melange and apko locally
2. Write `flutter-sdk-web.melange.yaml` to package Flutter SDK
3. Build the APK: `melange build flutter-sdk-web.melange.yaml`
4. Write `apko.web.yaml` to compose the final image
5. Build the image: `apko build apko.web.yaml flutter-sdk-web:test flutter-sdk-web.tar`
6. Load and test: `docker load < flutter-sdk-web.tar`
7. Verify: `docker run --rm flutter-sdk-web:test flutter doctor -v`
8. Compare image size vs current debian:bookworm-slim approach
### Phase 2: CI Pipeline
1. Add melange + apko build steps to publish.yaml
2. Multi-stage: melange builds APK → apko composes image → push to registry
3. Scout CVE scan on the final image
4. SBOM generation (apko generates SBOMs natively)
### Phase 3: Android + Linux Desktop Variants
1. Write melange packages for Android SDK components
2. Write apko YAML for Android variant (add JDK from Wolfi)
3. Write apko YAML for Linux variant (add GTK3 from Wolfi)
4. Test each variant with real Flutter project builds
### Phase 4: Automated Updates
1. Daily update-check detects new Flutter stable release
2. Triggers melange rebuild of Flutter SDK package
3. apko recomposes image with latest Wolfi packages (picks up CVE fixes)
4. Push new images to registry
## Expected Image Sizes
| Variant | Current (debian:bookworm-slim) | Target (Wolfi/apko) |
|---|---|---|
| Web | ~1.8 GB | ~1.2-1.4 GB |
| Android | ~3.5 GB | ~2.5-3.0 GB |
| Linux | ~2.2 GB | ~1.5-1.8 GB |
Note: Flutter SDK itself is ~1+ GB regardless of base. The savings come from the base OS layer (74 MB debian-slim → ~14 MB Wolfi base) and elimination of build-time packages.
## File Structure (Target)
```
docker-flutter-sdk/
├── melange/
│ ├── flutter-sdk-web.melange.yaml
│ ├── flutter-sdk-android.melange.yaml
│ └── flutter-sdk-linux.melange.yaml
├── apko/
│ ├── web.apko.yaml
│ ├── android.apko.yaml
│ └── linux.apko.yaml
├── Dockerfile # Fallback: current debian approach (web)
├── Dockerfile.android # Fallback: current debian approach (android)
├── Dockerfile.linux # Fallback: current debian approach (linux)
├── .gitea/
│ └── workflows/
│ ├── publish.yaml # Build with melange+apko, push to registry
│ ├── publish-dockerfile.yaml # Fallback Dockerfile builds
│ ├── scout.yaml
│ └── update-check.yaml
└── PLAN.md
```
## Risks and Mitigations
| Risk | Mitigation |
|---|---|
| GTK3 dev libs not in Wolfi | Fall back to Dockerfile.linux with debian-slim for that variant |
| Flutter engine source build is complex | Start with packaging prebuilt SDK, graduate to source build |
| melange/apko learning curve | Well-documented by Chainguard, active community |
| Wolfi package availability changes | Pin package versions, maintain fallback Dockerfiles |
| Build times for source compilation | Cache melange build artifacts, only rebuild on new Flutter release |
## Dependencies
- melange CLI: `go install chainguard.dev/melange@latest`
- apko CLI: `go install chainguard.dev/apko@latest`
- Docker (for testing)
- Signing key pair (for APK package signing)
## References
- [Wolfi Overview](https://edu.chainguard.dev/open-source/wolfi/overview/)
- [Getting Started with apko](https://edu.chainguard.dev/open-source/build-tools/apko/getting-started-with-apko/)
- [Getting Started with melange](https://edu.chainguard.dev/open-source/build-tools/melange/getting-started-with-melange/)
- [Flutter Engine Build Instructions](https://github.com/flutter/flutter/wiki/Compiling-the-engine)
- [wolfi-dev/os packages](https://github.com/wolfi-dev/os)

134
README.md
View File

@ -1,134 +0,0 @@
# Flutter SDK Docker Images
<a href="https://git.openharbor.io/svrnty/docker-flutter-sdk" target="_blank"><img src="https://img.shields.io/badge/Git-Repository-orange?logo=gitea" alt="Git Repository"></a>
<a href="https://hub.docker.com/r/svrnty/flutter-sdk" target="_blank"><img src="https://img.shields.io/badge/Docker%20Hub-svrnty%2Fflutter--sdk-blue?logo=docker" alt="Docker Hub"></a>
<a href="https://hub.docker.com/r/svrnty/flutter-sdk" target="_blank"><img src="https://img.shields.io/docker/pulls/svrnty/flutter-sdk?logo=docker" alt="Docker Pulls"></a>
<a href="https://developer.android.com/tools/releases/build-tools" target="_blank"><img src="https://img.shields.io/badge/Android%20SDK-36.1.0-green?logo=android" alt="Android SDK"></a>
<a href="https://wolfi.dev" target="_blank"><img src="https://img.shields.io/badge/Base-Wolfi-purple?logo=linux" alt="Wolfi"></a>
Lightweight Flutter SDK images for CI/CD pipelines. Built on [Wolfi](https://wolfi.dev), a security-focused Linux distribution designed for containers.
## Images
| Variant | Use Case | Size | Arch |
|---------|----------|------|------|
| <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=web" target="_blank"><img src="https://img.shields.io/badge/flutter--sdk-web--3.38.9-blue?logo=docker" alt="web-3.38.9"></a> | Web/WASM builds | <img src="https://img.shields.io/docker/image-size/svrnty/flutter-sdk/web-3.38.9?label=&logo=docker" alt="size"> | <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=web" target="_blank"><img src="https://img.shields.io/badge/amd64-E65100" alt="amd64"></a> &nbsp; <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=web" target="_blank"><img src="https://img.shields.io/badge/arm64-2e7d32" alt="arm64"></a> |
| <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=android" target="_blank"><img src="https://img.shields.io/badge/flutter--sdk-android--3.38.9-blue?logo=docker" alt="android-3.38.9"></a> | Android APK/AAB builds | <img src="https://img.shields.io/docker/image-size/svrnty/flutter-sdk/android-3.38.9?label=&logo=docker" alt="size"> | <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=android" target="_blank"><img src="https://img.shields.io/badge/amd64-E65100" alt="amd64"></a> &nbsp; <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=android" target="_blank"><img src="https://img.shields.io/badge/arm64-2e7d32" alt="arm64"></a> |
| <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=linux" target="_blank"><img src="https://img.shields.io/badge/flutter--sdk-linux--3.38.9-blue?logo=docker" alt="linux-3.38.9"></a> | Linux desktop builds | <img src="https://img.shields.io/docker/image-size/svrnty/flutter-sdk/linux-3.38.9?label=&logo=docker" alt="size"> | <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=linux" target="_blank"><img src="https://img.shields.io/badge/amd64-E65100" alt="amd64"></a> &nbsp; <a href="https://hub.docker.com/r/svrnty/flutter-sdk/tags?name=linux" target="_blank"><img src="https://img.shields.io/badge/arm64-2e7d32" alt="arm64"></a> |
## Why Wolfi?
[Wolfi](https://wolfi.dev) is a lightweight Linux distribution built specifically for containers. It provides:
- **Minimal footprint** - Only essential packages, nothing extra
- **Daily security updates** - Patches applied quickly
- **Designed for containers** - No legacy cruft from traditional distros
## Features
- **Lightweight** - Optimized for fast CI/CD pulls
- **Secure** - Built on Wolfi with continuous vulnerability scanning
- **Multi-arch** - Supports both `linux/amd64` and `linux/arm64`
- **Non-root** - Runs as unprivileged user (UID 65532)
- **Supply chain security** - SBOM and SLSA provenance attestations included
## Dockerfile Examples
### Web App (WASM)
```dockerfile
FROM svrnty/flutter-sdk:web-latest AS build
WORKDIR /app
COPY . .
RUN flutter pub get && flutter build web --wasm --release
# Cache-busting: append version to JS/WASM references
RUN VERSION=$(date +%s) && cd build/web && \
sed -i "s|flutter_bootstrap\.js\"|flutter_bootstrap.js?v=${VERSION}\"|g" index.html && \
sed -i "s|main\.dart\.js\"|main.dart.js?v=${VERSION}\"|g" flutter_bootstrap.js && \
sed -i "s|main\.dart\.mjs\"|main.dart.mjs?v=${VERSION}\"|g" flutter_bootstrap.js && \
sed -i "s|main\.dart\.wasm\"|main.dart.wasm?v=${VERSION}\"|g" flutter_bootstrap.js
FROM ghcr.io/static-web-server/static-web-server:2 AS sws
FROM scratch
COPY --from=sws /static-web-server /static-web-server
COPY --from=build /app/build/web /public
COPY sws.toml /sws.toml
EXPOSE 8080
USER 65534
ENTRYPOINT ["/static-web-server", "--config-file", "/sws.toml"]
```
**sws.toml** - Required headers for WASM multi-threading:
```toml
[general]
host = "0.0.0.0"
port = 8080
root = "/public"
page-fallback = "/public/index.html"
compression = true
[[advanced.headers]]
source = "**"
[advanced.headers.headers]
Cross-Origin-Opener-Policy = "same-origin"
Cross-Origin-Embedder-Policy = "require-corp"
```
### Android APK
```dockerfile
FROM svrnty/flutter-sdk:android-latest AS build
COPY . /app
WORKDIR /app
RUN flutter pub get && flutter build apk --release
FROM scratch
COPY --from=build /app/build/app/outputs/flutter-apk/app-release.apk /
```
### Linux Desktop
```dockerfile
FROM svrnty/flutter-sdk:linux-latest AS build
WORKDIR /app
COPY . .
RUN flutter pub get && flutter build linux --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y libgtk-3-0 && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/build/linux/arm64/release/bundle /app
ENTRYPOINT ["/app/my_app"]
```
## CI/CD (Gitea/GitHub Actions)
```yaml
jobs:
build:
runs-on: ubuntu-latest
container:
image: svrnty/flutter-sdk:android-latest
steps:
- uses: actions/checkout@v4
- run: flutter pub get
- run: flutter build apk --release
```
## Tags
- `<variant>-latest` - Latest stable Flutter release
- `<variant>-<version>` - Specific Flutter version (e.g., `android-3.38.9`)
## Automatic Updates
Images are automatically rebuilt when:
- New Flutter stable versions are released
- Base image security updates are available
Every build is scanned and includes supply chain attestations (SBOM, SLSA provenance).
## License
MIT