diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..c90050f6e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,40 @@ +# Version control +.git + +# CI / tooling +.github +.vscode +.claude +fastlane +temp + +# Nix +flake.nix +flake.lock + +# Flutter/Dart local cache — regenerated by flutter pub get inside the build +.dart_tool +**/.dart_tool +**/.flutter-plugins +**/.flutter-plugins-dependencies + +# Build outputs from local dev +build/ +*/build/ +!commet/build/web + +# Native platform runners — not needed for a web build +commet/android +commet/ios +commet/linux +commet/macos +commet/windows +tiamat/android +tiamat/ios +tiamat/linux +tiamat/macos +tiamat/windows + +# Local dev TLS certs (used only by VS Code flutter run --web-tls-*) +widgets/localhost.crt +widgets/localhost.key diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..cf2436509 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,100 @@ +name: build and push image + +on: + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + PROJECT_PATH: commet + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Free disk space + run: | + sudo rm -rf /usr/share/dotnet /usr/local/.ghcup /usr/local/julia* \ + /usr/local/share/chromium /opt/microsoft /opt/google /opt/az \ + /opt/hostedtoolcache + docker system prune -af || true + df -h + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: 'true' + + - name: Setup Flutter + uses: subosito/flutter-action@v2.8.0 + with: + flutter-version: '3.41.1' + channel: 'stable' + cache: true + + - name: Setup Rust (needed for vodozemac WASM) + uses: moonrepo/setup-rust@v1 + with: + cache: true + + - name: Add Rust nightly src component + run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu + + - name: Install flutter_rust_bridge_codegen + run: cargo install flutter_rust_bridge_codegen --force + + - name: Prepare web (build vodozemac WASM) + run: ./scripts/prepare-web.sh + working-directory: ${{ env.PROJECT_PATH }} + + - name: Flutter pub get + run: flutter pub get + working-directory: ${{ env.PROJECT_PATH }} + + - name: Code generation + run: dart run scripts/codegen.dart + working-directory: ${{ env.PROJECT_PATH }} + + - name: Build web + run: | + flutter build web \ + --dart-define BUILD_MODE=release \ + --dart-define PLATFORM=web \ + --dart-define GIT_HASH=${{ github.sha }} \ + --dart-define VERSION_TAG=main \ + --dart-define BUILD_DATE=${{ github.run_id }} + working-directory: ${{ env.PROJECT_PATH }} + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=main + type=sha,prefix=,format=short + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.ci + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..09308b34a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,36 @@ +# syntax=docker/dockerfile:1 + +# --------------------------------------------------------------------------- +# Stage 1 — Flutter web builder +# Pre-requisite: commet/assets/vodozemac/ must contain the pre-built WASM +# files (run commet/scripts/prepare-web.sh once, then commit the output). +# --------------------------------------------------------------------------- +FROM ghcr.io/cirruslabs/flutter:stable AS builder + +WORKDIR /build +COPY . . + +WORKDIR /build/commet + +ARG GIT_HASH=unknown +ARG VERSION_TAG=release +ARG BUILD_DATE=0 + +RUN flutter pub get +RUN dart run scripts/codegen.dart +RUN flutter build web \ + --dart-define BUILD_MODE=release \ + --dart-define PLATFORM=web \ + --dart-define GIT_HASH=$GIT_HASH \ + --dart-define VERSION_TAG=$VERSION_TAG \ + --dart-define BUILD_DATE=$BUILD_DATE + +# --------------------------------------------------------------------------- +# Stage 2 — nginx runtime +# --------------------------------------------------------------------------- +FROM nginx:alpine AS runtime + +COPY --from=builder /build/commet/build/web /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 8080 diff --git a/Dockerfile.ci b/Dockerfile.ci new file mode 100644 index 000000000..c1df7084d --- /dev/null +++ b/Dockerfile.ci @@ -0,0 +1,9 @@ +# Lightweight runtime image — Flutter web is pre-built by CI before this runs. +# Use this instead of the main Dockerfile on GitHub Actions to avoid pulling +# the multi-GB cirruslabs/flutter builder image on the runner. +FROM nginx:alpine + +COPY commet/build/web /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 8080 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 000000000..680209abc --- /dev/null +++ b/nginx.conf @@ -0,0 +1,27 @@ +server { + listen 8080; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # SPA routing — fall back to index.html for unknown paths + location / { + try_files $uri $uri/ /index.html; + } + + # Never cache the entry-point so the browser always fetches the latest manifest + location = /index.html { + add_header Cache-Control "no-cache"; + } + + # Flutter fingerprints all asset URLs — safe to cache for a long time + location /assets/ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + location = /assets/assets/config/global_config.json { + add_header Cache-Control "no-cache"; + } +}