commit 734939fd1205fa126614a9b1aee89e2198819c4f Author: Mathias Beaulieu-Duncan Date: Mon Feb 2 02:32:32 2026 -0500 Initial base distro with apko/Wolfi configs Five minimal OCI image variants built with apko: - base: ~5.5MB glibc runtime (wolfi-baselayout, libstdc++, ca-certs, tzdata) - build: base + build tools (bash, git, curl, wget, unzip, xz) - dotnet-runtime: base + ICU, OpenSSL, zlib for .NET runtime - dotnet-sdk: build + ICU, OpenSSL, zlib for .NET SDK - flutter: build variant configured for Flutter SDK Includes melange package definitions for .NET 10 SDK/runtime and Flutter SDK (for future use when building custom APKs). CI/CD pipelines: publish on release, Scout CVE comparison on PRs, weekly rebuild for Wolfi security patches. Co-Authored-By: Claude Opus 4.5 diff --git a/.gitea/workflows/publish.yaml b/.gitea/workflows/publish.yaml new file mode 100644 index 0000000..11d17a2 --- /dev/null +++ b/.gitea/workflows/publish.yaml @@ -0,0 +1,66 @@ +name: Build and Push Base Distro Images + +on: + release: + types: [published, prereleased] + workflow_dispatch: + +permissions: + contents: read + +env: + IMAGE_NAME: base-distro + +jobs: + build-and-push: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - config: apko/base.yaml + variant: base + - config: apko/build.yaml + variant: build + - config: apko/dotnet-runtime.yaml + variant: dotnet-runtime + - config: apko/dotnet-sdk.yaml + variant: dotnet-sdk + - config: apko/flutter.yaml + variant: flutter + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Determine tag + id: tag + run: | + if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + echo "suffix=dev" >> $GITHUB_OUTPUT + else + echo "suffix=latest" >> $GITHUB_OUTPUT + fi + + - name: Install apko + run: | + curl -fsSL "https://github.com/chainguard-dev/apko/releases/latest/download/apko_$(uname -s)_$(uname -m).tar.gz" | tar xz -C /usr/local/bin apko + + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Build and push image + run: | + apko publish ${{ matrix.config }} \ + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} + + - name: Install Docker Scout + run: | + curl -fsSL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh -o install-scout.sh + sh install-scout.sh + + - name: Docker Scout CVE Scan + run: | + docker pull ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} + docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} --only-severity critical,high diff --git a/.gitea/workflows/rebuild.yaml b/.gitea/workflows/rebuild.yaml new file mode 100644 index 0000000..1db9910 --- /dev/null +++ b/.gitea/workflows/rebuild.yaml @@ -0,0 +1,58 @@ +name: Weekly Rebuild (CVE Updates) + +on: + schedule: + # Rebuild weekly to pick up Wolfi security patches + - cron: '0 6 * * 1' + workflow_dispatch: + +permissions: + contents: read + +env: + IMAGE_NAME: base-distro + +jobs: + rebuild: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - config: apko/base.yaml + variant: base + - config: apko/build.yaml + variant: build + - config: apko/dotnet-runtime.yaml + variant: dotnet-runtime + - config: apko/dotnet-sdk.yaml + variant: dotnet-sdk + - config: apko/flutter.yaml + variant: flutter + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install apko + run: | + curl -fsSL "https://github.com/chainguard-dev/apko/releases/latest/download/apko_$(uname -s)_$(uname -m).tar.gz" | tar xz -C /usr/local/bin apko + + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - name: Rebuild and push with latest Wolfi packages + run: | + apko publish ${{ matrix.config }} \ + ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest + + - name: Install Docker Scout + run: | + curl -fsSL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh -o install-scout.sh + sh install-scout.sh + + - name: Docker Scout CVE Scan + run: | + docker pull ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest + docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-latest --only-severity critical,high diff --git a/.gitea/workflows/scout.yaml b/.gitea/workflows/scout.yaml new file mode 100644 index 0000000..4388a63 --- /dev/null +++ b/.gitea/workflows/scout.yaml @@ -0,0 +1,72 @@ +name: Docker Scout Analysis + +on: + pull_request: + branches: ["**"] + +permissions: + contents: read + pull-requests: write + +env: + IMAGE_NAME: base-distro + +jobs: + scout: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - config: apko/base.yaml + variant: base + - config: apko/build.yaml + variant: build + - config: apko/dotnet-runtime.yaml + variant: dotnet-runtime + - config: apko/dotnet-sdk.yaml + variant: dotnet-sdk + - config: apko/flutter.yaml + variant: flutter + steps: + - name: Login to Docker Registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.REGISTRY_USERNAME }} + password: ${{ secrets.REGISTRY_PASSWORD }} + + - 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 + echo "run=true" >> $GITHUB_OUTPUT + echo "${{ matrix.variant }}-latest found, Scout compare will run" + else + echo "run=false" >> $GITHUB_OUTPUT + echo "No ${{ matrix.variant }}-latest found, skipping" + fi + + - name: Checkout code + if: steps.should_run.outputs.run == 'true' + uses: actions/checkout@v3 + + - name: Install apko + if: steps.should_run.outputs.run == 'true' + run: | + curl -fsSL "https://github.com/chainguard-dev/apko/releases/latest/download/apko_$(uname -s)_$(uname -m).tar.gz" | tar xz -C /usr/local/bin apko + + - name: Build image locally + if: steps.should_run.outputs.run == 'true' + run: | + apko build ${{ matrix.config }} ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-pr-${{ github.event.pull_request.number }} ${{ matrix.variant }}.tar + docker load < ${{ matrix.variant }}.tar + + - name: Install Docker Scout + if: steps.should_run.outputs.run == 'true' + run: | + curl -fsSL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh -o install-scout.sh + sh install-scout.sh + + - 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c30af --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +*.tar +*.tar.gz +packages/ +signing-key.* +!signing-key.rsa.pub.example +sbom-*.spdx.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f5f6c9c --- /dev/null +++ b/Makefile @@ -0,0 +1,67 @@ +REGISTRY ?= svrnty/base-distro +APKO_FLAGS ?= --log-level info + +# Image variants +VARIANTS = base build dotnet-runtime dotnet-sdk flutter + +.PHONY: all clean $(VARIANTS) test + +all: $(VARIANTS) + +# Build each variant with apko +base: + apko build $(APKO_FLAGS) apko/base.yaml $(REGISTRY):base base.tar + docker load < base.tar + @echo "Built $(REGISTRY):base" + +build: + apko build $(APKO_FLAGS) apko/build.yaml $(REGISTRY):build build.tar + docker load < build.tar + @echo "Built $(REGISTRY):build" + +dotnet-runtime: + apko build $(APKO_FLAGS) apko/dotnet-runtime.yaml $(REGISTRY):dotnet-runtime dotnet-runtime.tar + docker load < dotnet-runtime.tar + @echo "Built $(REGISTRY):dotnet-runtime" + +dotnet-sdk: + apko build $(APKO_FLAGS) apko/dotnet-sdk.yaml $(REGISTRY):dotnet-sdk dotnet-sdk.tar + docker load < dotnet-sdk.tar + @echo "Built $(REGISTRY):dotnet-sdk" + +flutter: + apko build $(APKO_FLAGS) apko/flutter.yaml $(REGISTRY):flutter flutter.tar + docker load < flutter.tar + @echo "Built $(REGISTRY):flutter" + +# Test all images +test: all + @echo "=== Testing base ===" + docker run --rm $(REGISTRY):base /bin/sh -c "cat /etc/os-release" + @echo "" + @echo "=== Testing build ===" + docker run --rm $(REGISTRY):build bash -c "git --version && curl --version | head -1" + @echo "" + @echo "=== Testing dotnet-runtime ===" + docker run --rm $(REGISTRY):dotnet-runtime /bin/sh -c "ls /usr/lib/libicu*" + @echo "" + @echo "=== Testing dotnet-sdk ===" + docker run --rm $(REGISTRY):dotnet-sdk bash -c "git --version && ls /usr/lib/libicu*" + @echo "" + @echo "=== Testing flutter ===" + docker run --rm $(REGISTRY):flutter bash -c "git --version && echo PATH=\$$PATH" + @echo "" + @echo "All tests passed!" + +# Show image sizes +sizes: all + @echo "=== Image Sizes ===" + @for variant in $(VARIANTS); do \ + echo -n "$(REGISTRY):$$variant "; \ + docker image inspect $(REGISTRY):$$variant --format '{{.Size}}' | numfmt --to=iec 2>/dev/null || \ + docker image inspect $(REGISTRY):$$variant --format '{{.Size}}'; \ + done + +clean: + rm -f *.tar + rm -rf packages/ diff --git a/apko/base.yaml b/apko/base.yaml new file mode 100644 index 0000000..b8c391d --- /dev/null +++ b/apko/base.yaml @@ -0,0 +1,30 @@ +contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + - wolfi-baselayout + - glibc + - glibc-locale-posix + - libstdc++ + - ca-certificates-bundle + - tzdata + - busybox + +accounts: + groups: + - groupname: app + gid: 65532 + users: + - username: app + uid: 65532 + gid: 65532 + run-as: 65532 + +archs: + - x86_64 + - aarch64 + +environment: + TZ: UTC diff --git a/apko/build.yaml b/apko/build.yaml new file mode 100644 index 0000000..361c19f --- /dev/null +++ b/apko/build.yaml @@ -0,0 +1,42 @@ +contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + # Base runtime + - wolfi-baselayout + - glibc + - glibc-locale-posix + - libstdc++ + - ca-certificates-bundle + - tzdata + # Build tools + - bash + - busybox + - coreutils + - git + - curl + - wget + - unzip + - xz + +accounts: + groups: + - groupname: app + gid: 65532 + users: + - username: app + uid: 65532 + gid: 65532 + run-as: 65532 + +archs: + - x86_64 + - aarch64 + +environment: + TZ: UTC + +entrypoint: + command: /bin/bash diff --git a/apko/dotnet-runtime.yaml b/apko/dotnet-runtime.yaml new file mode 100644 index 0000000..1735dac --- /dev/null +++ b/apko/dotnet-runtime.yaml @@ -0,0 +1,37 @@ +contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + # Base runtime + - wolfi-baselayout + - glibc + - glibc-locale-posix + - libstdc++ + - ca-certificates-bundle + - tzdata + - busybox + # .NET runtime dependencies + - icu + - libssl3 + - zlib + +accounts: + groups: + - groupname: app + gid: 65532 + users: + - username: app + uid: 65532 + gid: 65532 + run-as: 65532 + +archs: + - x86_64 + - aarch64 + +environment: + TZ: UTC + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "false" + DOTNET_RUNNING_IN_CONTAINER: "true" diff --git a/apko/dotnet-sdk.yaml b/apko/dotnet-sdk.yaml new file mode 100644 index 0000000..e1297d2 --- /dev/null +++ b/apko/dotnet-sdk.yaml @@ -0,0 +1,49 @@ +contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + # Base runtime + - wolfi-baselayout + - glibc + - glibc-locale-posix + - libstdc++ + - ca-certificates-bundle + - tzdata + # .NET runtime dependencies + - icu + - libssl3 + - zlib + # Build tools + - bash + - busybox + - coreutils + - git + - curl + - wget + - unzip + - xz + +accounts: + groups: + - groupname: app + gid: 65532 + users: + - username: app + uid: 65532 + gid: 65532 + run-as: 65532 + +archs: + - x86_64 + - aarch64 + +environment: + TZ: UTC + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "false" + DOTNET_RUNNING_IN_CONTAINER: "true" + DOTNET_CLI_TELEMETRY_OPTOUT: "true" + +entrypoint: + command: /bin/bash diff --git a/apko/flutter.yaml b/apko/flutter.yaml new file mode 100644 index 0000000..042fe5f --- /dev/null +++ b/apko/flutter.yaml @@ -0,0 +1,44 @@ +contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + # Base runtime + - wolfi-baselayout + - glibc + - glibc-locale-posix + - libstdc++ + - ca-certificates-bundle + - tzdata + # Build tools + - bash + - busybox + - coreutils + - git + - curl + - wget + - unzip + - xz + +accounts: + groups: + - groupname: flutter + gid: 65532 + users: + - username: flutter + uid: 65532 + gid: 65532 + run-as: 65532 + +archs: + - x86_64 + - aarch64 + +environment: + TZ: UTC + FLUTTER_HOME: /opt/flutter + PATH: /opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:/usr/bin:/bin:/usr/sbin:/sbin + +entrypoint: + command: /bin/bash diff --git a/examples/Dockerfile.dotnet-runtime b/examples/Dockerfile.dotnet-runtime new file mode 100644 index 0000000..2b6600c --- /dev/null +++ b/examples/Dockerfile.dotnet-runtime @@ -0,0 +1,43 @@ +# Example: .NET 10 runtime image using base-distro +# +# Usage in accounting-api or route-api: +# FROM svrnty/base-distro:dotnet-sdk-latest AS build +# ... (build stage with .NET SDK installed on top) ... +# +# FROM svrnty/base-distro:dotnet-runtime-latest AS final +# COPY --from=build /app . +# ENTRYPOINT ["dotnet", "MyApp.dll"] + +# Build stage: use the SDK base + install .NET SDK +FROM svrnty/base-distro:dotnet-sdk-latest AS build + +# Install .NET 10 SDK (not yet in Wolfi, manual tarball install) +USER root +RUN curl -fsSL "https://dotnetcli.azureedge.net/dotnet/Sdk/10.0.100/dotnet-sdk-10.0.100-linux-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/').tar.gz" \ + -o /tmp/dotnet-sdk.tar.gz && \ + mkdir -p /usr/share/dotnet && \ + tar xf /tmp/dotnet-sdk.tar.gz -C /usr/share/dotnet && \ + ln -sf /usr/share/dotnet/dotnet /usr/bin/dotnet && \ + rm /tmp/dotnet-sdk.tar.gz + +WORKDIR /source +COPY . . +RUN dotnet publish -o /app + +# Runtime stage: minimal base + .NET runtime only +FROM svrnty/base-distro:dotnet-runtime-latest AS final + +# Install .NET 10 ASP.NET runtime +USER root +RUN curl -fsSL "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/10.0.0/aspnetcore-runtime-10.0.0-linux-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/').tar.gz" \ + -o /tmp/aspnet-runtime.tar.gz && \ + mkdir -p /usr/share/dotnet && \ + tar xf /tmp/aspnet-runtime.tar.gz -C /usr/share/dotnet && \ + ln -sf /usr/share/dotnet/dotnet /usr/bin/dotnet && \ + rm /tmp/aspnet-runtime.tar.gz + +WORKDIR /app +COPY --from=build /app . + +USER 65532 +ENTRYPOINT ["dotnet", "MyApp.dll"] diff --git a/examples/Dockerfile.flutter-web b/examples/Dockerfile.flutter-web new file mode 100644 index 0000000..7b8b988 --- /dev/null +++ b/examples/Dockerfile.flutter-web @@ -0,0 +1,30 @@ +# Example: Flutter web build image using base-distro +# +# Usage in flutter-admin-console or other Flutter web projects: +# FROM svrnty/base-distro:flutter-latest AS build +# ... (install Flutter SDK, build web app) ... + +FROM svrnty/base-distro:flutter-latest AS build + +# Install Flutter SDK on top of the base +USER root +ARG FLUTTER_VERSION=3.38.9 +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 && \ + mkdir -p /opt && \ + tar xf /tmp/flutter.tar.xz -C /opt && \ + rm /tmp/flutter.tar.xz && \ + git config --global --add safe.directory /opt/flutter && \ + flutter config --enable-web \ + --no-enable-android --no-enable-ios \ + --no-enable-linux-desktop --no-enable-macos-desktop \ + --no-enable-windows-desktop && \ + flutter precache --web \ + --no-android --no-ios --no-linux \ + --no-macos --no-windows --no-fuchsia --no-universal && \ + chown -R 65532:65532 /opt/flutter + +USER 65532 +WORKDIR /app +COPY . . +RUN flutter pub get && flutter build web --wasm --release diff --git a/melange/dotnet-10-runtime.melange.yaml b/melange/dotnet-10-runtime.melange.yaml new file mode 100644 index 0000000..b588226 --- /dev/null +++ b/melange/dotnet-10-runtime.melange.yaml @@ -0,0 +1,42 @@ +package: + name: dotnet-10-runtime + version: 10.0.0 + epoch: 0 + description: ".NET 10 Runtime from official Microsoft binaries" + copyright: + - license: MIT + +environment: + contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + - wolfi-baselayout + - busybox + - curl + +pipeline: + - runs: | + case "$(uname -m)" in + x86_64) ARCH="x64" ;; + aarch64) ARCH="arm64" ;; + *) echo "Unsupported arch: $(uname -m)" && exit 1 ;; + esac + + DOTNET_VERSION="${{package.version}}" + + # Download .NET runtime from Microsoft + curl -fsSL "https://dotnetcli.azureedge.net/dotnet/Runtime/${DOTNET_VERSION}/dotnet-runtime-${DOTNET_VERSION}-linux-${ARCH}.tar.gz" \ + -o /tmp/dotnet-runtime.tar.gz + + # Install to package destination + mkdir -p "${{targets.destdir}}/usr/share/dotnet" + tar xf /tmp/dotnet-runtime.tar.gz -C "${{targets.destdir}}/usr/share/dotnet" + + # Create symlink + mkdir -p "${{targets.destdir}}/usr/bin" + ln -s /usr/share/dotnet/dotnet "${{targets.destdir}}/usr/bin/dotnet" + + rm /tmp/dotnet-runtime.tar.gz diff --git a/melange/dotnet-10-sdk.melange.yaml b/melange/dotnet-10-sdk.melange.yaml new file mode 100644 index 0000000..69eb50a --- /dev/null +++ b/melange/dotnet-10-sdk.melange.yaml @@ -0,0 +1,42 @@ +package: + name: dotnet-10-sdk + version: 10.0.100 + epoch: 0 + description: ".NET 10 SDK from official Microsoft binaries" + copyright: + - license: MIT + +environment: + contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + - wolfi-baselayout + - busybox + - curl + +pipeline: + - runs: | + case "$(uname -m)" in + x86_64) ARCH="x64" ;; + aarch64) ARCH="arm64" ;; + *) echo "Unsupported arch: $(uname -m)" && exit 1 ;; + esac + + SDK_VERSION="${{package.version}}" + + # Download .NET SDK from Microsoft + curl -fsSL "https://dotnetcli.azureedge.net/dotnet/Sdk/${SDK_VERSION}/dotnet-sdk-${SDK_VERSION}-linux-${ARCH}.tar.gz" \ + -o /tmp/dotnet-sdk.tar.gz + + # Install to package destination + mkdir -p "${{targets.destdir}}/usr/share/dotnet" + tar xf /tmp/dotnet-sdk.tar.gz -C "${{targets.destdir}}/usr/share/dotnet" + + # Create symlink + mkdir -p "${{targets.destdir}}/usr/bin" + ln -s /usr/share/dotnet/dotnet "${{targets.destdir}}/usr/bin/dotnet" + + rm /tmp/dotnet-sdk.tar.gz diff --git a/melange/flutter-sdk.melange.yaml b/melange/flutter-sdk.melange.yaml new file mode 100644 index 0000000..645c722 --- /dev/null +++ b/melange/flutter-sdk.melange.yaml @@ -0,0 +1,48 @@ +package: + name: flutter-sdk + version: 3.38.9 + epoch: 0 + description: "Flutter SDK for web/WASM builds" + copyright: + - license: BSD-3-Clause + +environment: + contents: + keyring: + - https://packages.wolfi.dev/os/wolfi-signing.rsa.pub + repositories: + - https://packages.wolfi.dev/os + packages: + - wolfi-baselayout + - busybox + - curl + - git + - xz + +pipeline: + - runs: | + FLUTTER_VERSION="${{package.version}}" + + # Download Flutter SDK tarball (linux x86_64 only for now) + curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \ + -o /tmp/flutter.tar.xz + + # Install to package destination + mkdir -p "${{targets.destdir}}/opt" + tar xf /tmp/flutter.tar.xz -C "${{targets.destdir}}/opt" + + # Mark git safe directory + git config --global --add safe.directory /opt/flutter + + # Configure for web-only + "${{targets.destdir}}/opt/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 + "${{targets.destdir}}/opt/flutter/bin/flutter" precache --web \ + --no-android --no-ios --no-linux \ + --no-macos --no-windows --no-fuchsia --no-universal + + rm /tmp/flutter.tar.xz