Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 7 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "devBenches / flutterBench",
"initializeCommand": "bash ${localWorkspaceFolder}/../scripts/ensure-sonarqube-mcp.sh && bash ${localWorkspaceFolder}/scripts/ensure-images.sh",
"postAttachCommand": "bash /workspace/projects/workBenches/devBenches/flutterBench/scripts/ensure-latest-vscode-extension.sh --background",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Use a workspace-relative postAttach path

When the bench is opened from any host path other than $HOME/projects/workBenches/devBenches/flutterBench, this points at a non-existent script inside the container, so the extension refresh never starts and VS Code reports the lifecycle command failure. The rest of this devcontainer already uses ${localWorkspaceFolder} on the host side, but this new container-side hook hard-codes one checkout layout; use the mounted workspace/repo location instead of the fixed /workspace/projects/workBenches/... path.

Useful? React with 👍 / 👎.

"dockerComposeFile": "docker-compose.yml",
"service": "flutter-bench",
"shutdownAction": "stopCompose",
Expand Down Expand Up @@ -46,9 +47,11 @@
"dart.hotReloadOnSave": "allIfDirty",
"dart.showTodos": true,
"flutter.experimentalRefactors": true,
"flutter.showWebRecorderButton": true,
"sonarlint.pathToNodeExecutable": "/usr/bin/node",
"files.associations": {
"flutter.showWebRecorderButton": true,
"sonarlint.pathToNodeExecutable": "/usr/bin/node",
"extensions.autoCheckUpdates": true,
"extensions.autoUpdate": true,
"files.associations": {
"*.dart": "dart"
}
}
Expand Down Expand Up @@ -91,6 +94,7 @@
"source=${localEnv:HOME}/.agents,target=/home/${localEnv:USER}/.agents,type=bind,consistency=cached",
"source=${localEnv:HOME}/.claude,target=/home/${localEnv:USER}/.claude,type=bind,consistency=cached",
"source=${localEnv:HOME}/.claude.json,target=/home/${localEnv:USER}/.claude.json,type=bind,consistency=cached",
"source=${localEnv:HOME}/.claude-profiles,target=/home/${localEnv:USER}/.claude-profiles,type=bind,consistency=cached",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Guard the optional Claude profiles mount

For users who do not have ~/.claude-profiles on the host, this new bind mount prevents the devcontainer from starting because Docker --mount bind semantics error when the source path is absent. Since these portable profiles are optional credentials, create the directory during initialization or avoid adding the mount unless the source exists.

Useful? React with 👍 / 👎.

"source=${localEnv:HOME}/.codex,target=/home/${localEnv:USER}/.codex,type=bind,consistency=cached",
"source=${localEnv:HOME}/.pi,target=/home/${localEnv:USER}/.pi,type=bind,consistency=cached",
"source=${localEnv:HOME}/.config/sonarqube,target=/home/${localEnv:USER}/.config/sonarqube,type=bind,readonly",
Expand Down
4 changes: 2 additions & 2 deletions CONTAINER_COMPARISON.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ We use two different container approaches for Flutter development:

### Project Container (LIGHTWEIGHT) Includes:
- **Core Utilities**: curl, wget, git, unzip, xz-utils, ca-certificates
- **Flutter Essentials**: Flutter SDK 3.24.0 (stable only)
- **Flutter Essentials**: Flutter SDK 3.44.4 (stable only)
- **Android Basics**: ADB, fastboot, minimal platform-tools
- **Java Runtime**: OpenJDK 17 (for Android builds)
- **Shell**: zsh, bash, Oh My Zsh (for better UX)
Expand Down Expand Up @@ -127,4 +127,4 @@ cd FlutterBench # Back to the monster for deployment tasks

Both containers connect to the **same shared ADB infrastructure**, so you get consistent device connectivity regardless of which container you're using.

This dual-container approach optimizes for both **developer productivity** (FlutterBench) and **resource efficiency** (Project containers).
This dual-container approach optimizes for both **developer productivity** (FlutterBench) and **resource efficiency** (Project containers).
157 changes: 115 additions & 42 deletions Dockerfile.layer2
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1.7
# Layer 2: Flutter Bench Image
# Extends Layer 1 (dev-bench-base) with Flutter-specific tools
# Includes: Flutter SDK, Android SDK, Dart tools, mobile dev tools
Comment on lines 2 to 4
Expand All @@ -8,7 +9,7 @@ FROM ${BASE_IMAGE}
# Container version labels
LABEL layer="2"
LABEL layer.name="flutter-bench"
LABEL layer.version="1.0.1"
LABEL layer.version="1.0.7"
LABEL layer.description="Flutter and mobile development tools"
LABEL bench.type="flutter"

Expand Down Expand Up @@ -50,7 +51,7 @@ RUN apt-get update && apt-get install -y \
# ========================================

RUN mkdir -p /opt/android-sdk \
&& wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdtools.zip \
&& wget -q https://dl.google.com/android/repository/commandlinetools-linux-14742923_latest.zip -O /tmp/cmdtools.zip \
&& unzip -q /tmp/cmdtools.zip -d /opt/android-sdk \
&& rm /tmp/cmdtools.zip \
&& mv /opt/android-sdk/cmdline-tools /opt/android-sdk/cmdline-tools-temp \
Expand All @@ -65,61 +66,131 @@ ENV ANDROID_HOME="/opt/android-sdk"
ENV ANDROID_SDK_ROOT="/opt/android-sdk"
ENV PATH="$PATH:/opt/android-sdk/platform-tools:/opt/android-sdk/emulator:/opt/android-sdk/cmdline-tools/latest/bin"

# Install essential Android SDK components
RUN yes | /opt/android-sdk/cmdline-tools/latest/bin/sdkmanager --licenses \
&& /opt/android-sdk/cmdline-tools/latest/bin/sdkmanager \
"platform-tools" \
"emulator" \
"cmake;3.22.1" \
"ndk;27.0.12077973" \
"build-tools;33.0.0" \
"build-tools;34.0.0" \
"platforms;android-30" \
"platforms;android-31" \
"platforms;android-32" \
"platforms;android-33" \
"platforms;android-34" \
"platforms;android-35" \
"platforms;android-36" \
"system-images;android-34;google_apis;x86_64" \
&& chmod -R 775 /opt/android-sdk
# Install current Android platform-tools directly. The full sdkmanager package
# set is opt-in because Google package downloads can hang for hours mid-build.
ARG ANDROID_PLATFORM_TOOLS_VERSION=37.0.0
RUN --mount=type=cache,target=/var/cache/android-sdk-downloads,sharing=locked set -eux; \
archive="/var/cache/android-sdk-downloads/platform-tools_r${ANDROID_PLATFORM_TOOLS_VERSION}-linux.zip"; \
for attempt in 1 2 3 4 5; do \
curl -fL --retry 2 --connect-timeout 30 --max-time 900 -C - \
-o "$archive" \
"https://dl.google.com/android/repository/platform-tools_r${ANDROID_PLATFORM_TOOLS_VERSION}-linux.zip" \
&& break; \
sleep 5; \
done; \
unzip -q -o "$archive" -d /opt/android-sdk; \
chmod -R 775 /opt/android-sdk
Comment on lines +73 to +82

ARG INSTALL_ANDROID_SDK_PACKAGES=false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Install Android SDK packages in the default bench build

With this default set to false, the normal devcontainer build path (scripts/ensure-images.shscripts/rebuild-stack.shscripts/build-layer2.sh) never passes INSTALL_ANDROID_SDK_PACKAGES, so only platform-tools are installed and the packages that were previously always present (emulator, ndk;27.0.12077973, build-tools, and platforms) are skipped. Any FlutterBench workflow that launches an Android emulator or relies on a preinstalled NDK/build-tools now fails or downloads tools at runtime despite the bench docs still advertising a full Android SDK; either make the default true for the bench image or have the build script pass the opt-in explicitly.

Useful? React with 👍 / 👎.

ARG ANDROID_SDK_PACKAGE_INSTALL_TIMEOUT=600
RUN if [ "$INSTALL_ANDROID_SDK_PACKAGES" = "true" ]; then \
Comment on lines +84 to +86
set -eux; \
yes | /opt/android-sdk/cmdline-tools/latest/bin/sdkmanager --licenses; \
for package in \
"emulator" \
"cmake;3.22.1" \
"ndk;27.0.12077973" \
"build-tools;33.0.0" \
"build-tools;34.0.0" \
"platforms;android-30" \
"platforms;android-31" \
"platforms;android-32" \
"platforms;android-33" \
"platforms;android-34" \
"platforms;android-35" \
"platforms;android-36" \
"system-images;android-34;google_apis;x86_64"; do \
timeout "$ANDROID_SDK_PACKAGE_INSTALL_TIMEOUT" \
/opt/android-sdk/cmdline-tools/latest/bin/sdkmanager "$package" || \
echo "Android SDK package ${package} timed out or failed; continuing."; \
done; \
chmod -R 775 /opt/android-sdk; \
else \
echo "Skipping heavyweight Android SDK package preloads; set INSTALL_ANDROID_SDK_PACKAGES=true to include them."; \
fi

# ========================================
# FLUTTER ECOSYSTEM
# ========================================

RUN apt-get update && apt-get install -y --no-install-recommends aria2 \
&& rm -rf /var/lib/apt/lists/*

# Install Flutter SDKs
ENV FLUTTER_HOME="/opt/flutter"
ENV FLUTTER_LTS_327_HOME="/opt/flutter-3.27.0"
ENV PATH="$PUB_CACHE/bin:$FLUTTER_HOME/bin:$FLUTTER_HOME/bin/cache/dart-sdk/bin:$PATH"

# Install Flutter with fallback
ARG FLUTTER_VERSION=3.41.6
RUN (git clone --depth 1 --branch stable https://github.com/flutter/flutter.git $FLUTTER_HOME \
&& $FLUTTER_HOME/bin/flutter config --no-analytics) || \
(echo "⚠️ Flutter git clone failed, using direct download fallback..." && \
curl -fsSL --retry 3 --user-agent "Mozilla/5.0" \
https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz | tar -xJC /opt \
&& $FLUTTER_HOME/bin/flutter config --no-analytics) || \
echo "❌ Flutter installation failed completely, skipping..."
# Install the versioned Flutter SDK directly. The git clone path can hang for a
# long time inside Docker Desktop without producing progress output.
ARG FLUTTER_VERSION=3.44.4
RUN --mount=type=cache,target=/var/cache/flutter-downloads,sharing=locked set -eux; \
mkdir -p /var/cache/flutter-downloads; \
download_archive() { \
url="$1"; \
output="$2"; \
if aria2c --allow-overwrite=true --auto-file-renaming=false --continue=true \
--max-connection-per-server=8 --split=8 --min-split-size=16M \
--max-tries=10 --retry-wait=5 --connect-timeout=30 --timeout=120 \
--summary-interval=30 --console-log-level=warn \
-d "$(dirname "$output")" -o "$(basename "$output")" "$url"; then \
return 0; \
fi; \
for attempt in 1 2 3 4 5 6 7 8; do \
curl -fL --retry 3 --connect-timeout 30 --max-time 1800 -C - \
--user-agent "Mozilla/5.0" -o "$output" "$url" && return 0; \
sleep 5; \
done; \
return 1; \
}; \
archive_path="/var/cache/flutter-downloads/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz"; \
download_archive \
"https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
"$archive_path"; \
tmp_root="$(mktemp -d)"; \
tar -xJf "$archive_path" -C "$tmp_root"; \
rm -rf "$FLUTTER_HOME"; \
mv "$tmp_root/flutter" "$FLUTTER_HOME"; \
rm -rf "$tmp_root"; \
git config --system --add safe.directory "$FLUTTER_HOME"; \
"$FLUTTER_HOME/bin/flutter" config --no-analytics

# Install the long-term supported Flutter 3.27 SDK alongside the shared stable
# toolchain so pinned repos can avoid first-run downloads inside the bench.
ARG FLUTTER_LTS_327_VERSION=3.27.0
RUN set -eux; \
tmp_root="$(mktemp -d)"; \
archive_path="$tmp_root/flutter_linux_${FLUTTER_LTS_327_VERSION}-stable.tar.xz"; \
curl -fsSL --retry 3 --user-agent "Mozilla/5.0" \
RUN --mount=type=cache,target=/var/cache/flutter-downloads,sharing=locked set -eux; \
mkdir -p /var/cache/flutter-downloads; \
download_archive() { \
url="$1"; \
output="$2"; \
if aria2c --allow-overwrite=true --auto-file-renaming=false --continue=true \
--max-connection-per-server=8 --split=8 --min-split-size=16M \
--max-tries=10 --retry-wait=5 --connect-timeout=30 --timeout=120 \
--summary-interval=30 --console-log-level=warn \
-d "$(dirname "$output")" -o "$(basename "$output")" "$url"; then \
return 0; \
fi; \
for attempt in 1 2 3 4 5 6 7 8; do \
curl -fL --retry 3 --connect-timeout 30 --max-time 1800 -C - \
--user-agent "Mozilla/5.0" -o "$output" "$url" && return 0; \
sleep 5; \
done; \
return 1; \
}; \
archive_path="/var/cache/flutter-downloads/flutter_linux_${FLUTTER_LTS_327_VERSION}-stable.tar.xz"; \
download_archive \
"https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_LTS_327_VERSION}-stable.tar.xz" \
-o "$archive_path"; \
"$archive_path"; \
tmp_root="$(mktemp -d)"; \
tar -xJf "$archive_path" -C "$tmp_root"; \
rm -rf "$FLUTTER_LTS_327_HOME"; \
mv "$tmp_root/flutter" "$FLUTTER_LTS_327_HOME"; \
rm -rf "$tmp_root"; \
"$FLUTTER_LTS_327_HOME/bin/flutter" config --no-analytics
git config --system --add safe.directory "$FLUTTER_LTS_327_HOME"

# Install shared Dart global tools into a system cache, then expose them via
# /usr/local/bin so Layer 3 users can run them without inheriting root's home.
ARG DART_PUB_ACTIVATE_TIMEOUT=600
RUN set -eux; \
mkdir -p "$PUB_CACHE"; \
for package in \
Expand All @@ -138,8 +209,9 @@ RUN set -eux; \
get_cli \
patrol_cli \
melos; do \
$FLUTTER_HOME/bin/dart pub global activate "$package" || \
echo "❌ ${package} installation failed, skipping..."; \
echo "Activating Dart package: ${package}"; \
timeout "$DART_PUB_ACTIVATE_TIMEOUT" "$FLUTTER_HOME/bin/dart" pub global activate "$package" || \
echo "Dart package ${package} installation failed or timed out, skipping..."; \
done; \
if [ -d "$PUB_CACHE/bin" ]; then \
find "$PUB_CACHE/bin" -maxdepth 1 -type f -exec chmod a+rx {} +; \
Expand Down Expand Up @@ -242,9 +314,10 @@ RUN (curl -fsSL --retry 3 --user-agent "Mozilla/5.0" \
echo "❌ Sentry CLI installation failed completely, skipping..."

# Install Shorebird for code push updates with fallback
RUN (curl -fsSL --retry 3 --user-agent "Mozilla/5.0" \
https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh | bash) || \
echo "❌ Shorebird installation failed, skipping..."
ARG SHOREBIRD_INSTALL_TIMEOUT=600
RUN timeout "$SHOREBIRD_INSTALL_TIMEOUT" bash -lc 'curl -fsSL --retry 3 --connect-timeout 30 --max-time 300 --user-agent "Mozilla/5.0" \
https://raw.githubusercontent.com/shorebirdtech/install/main/install.sh | bash' || \
echo "❌ Shorebird installation failed or timed out, skipping..."

# Make Shorebird available to Layer 3 users without relying on root's home directory.
RUN if [ -d /root/.shorebird ]; then \
Expand Down Expand Up @@ -296,9 +369,9 @@ RUN echo '' >> /etc/skel/.zshrc && \
echo 'alias dart-sonar-coverage="sonarcloud-dart-flutter"' >> /etc/skel/.zshrc

# Pre-download Flutter dependencies and accept Android licenses
RUN flutter doctor --android-licenses || true \
RUN timeout 5m bash -lc 'yes | flutter doctor --android-licenses' || true \
&& flutter precache || true \
&& "$FLUTTER_LTS_327_HOME/bin/flutter" precache || true
&& timeout 20m "$FLUTTER_LTS_327_HOME/bin/flutter" precache || true

# Root prewarming creates new cache files; restore group-write only where later
# Layer 3 users actually need it. The Android SDK tree is already normalized
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ PROJECT_NAME=dartwing
APP_CONTAINER_SUFFIX=app # Results in: dartwing-app
SERVICE_CONTAINER_SUFFIX=gateway # Results in: dartwing-gateway
USER_UID=1000
FLUTTER_VERSION=3.24.0
FLUTTER_VERSION=3.44.4
COMPOSE_PROJECT_NAME=dartwingers
```

Expand Down
4 changes: 2 additions & 2 deletions docs/CONTAINER_COMPARISON.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ We use two different container approaches for Flutter development:

### Project Container (LIGHTWEIGHT) Includes:
- **Core Utilities**: curl, wget, git, unzip, xz-utils, ca-certificates
- **Flutter Essentials**: Flutter SDK 3.24.0 (stable only)
- **Flutter Essentials**: Flutter SDK 3.44.4 (stable only)
- **Android Basics**: ADB, fastboot, minimal platform-tools
- **Java Runtime**: OpenJDK 17 (for Android builds)
- **Shell**: zsh, bash, Oh My Zsh (for better UX)
Expand Down Expand Up @@ -127,4 +127,4 @@ cd FlutterBench # Back to the monster for deployment tasks

Both containers connect to the **same shared ADB infrastructure**, so you get consistent device connectivity regardless of which container you're using.

This dual-container approach optimizes for both **developer productivity** (FlutterBench) and **resource efficiency** (Project containers).
This dual-container approach optimizes for both **developer productivity** (FlutterBench) and **resource efficiency** (Project containers).
6 changes: 3 additions & 3 deletions docs/centralized-configuration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ USER_GID=1000 # Group ID (run 'id -g')
### Development Environment
```bash
# Flutter SDK
FLUTTER_VERSION=3.24.0 # Flutter version to install
FLUTTER_VERSION=3.44.4 # Flutter version to install
ANDROID_HOME=/home/vscode/android-sdk # Android SDK path
FLUTTER_PUB_CACHE=/home/vscode/.pub-cache # Flutter pub cache path

Expand Down Expand Up @@ -349,7 +349,7 @@ SERVICE_PORT=5000
```bash
# .env.development
PROJECT_NAME=myapp-dev
FLUTTER_VERSION=3.24.0
FLUTTER_VERSION=3.44.4
CONTAINER_MEMORY=8g

# .env.production
Expand All @@ -361,7 +361,7 @@ CONTAINER_MEMORY=4g
### Multiple Flutter Versions
```bash
# project1/.env
FLUTTER_VERSION=3.24.0 # Latest
FLUTTER_VERSION=3.44.4 # Latest

# project2/.env
FLUTTER_VERSION=3.19.6 # Legacy compatibility
Expand Down
Loading