Files
BikeControl/Dockerfile.builder
dtourolleandClaude Opus 5 da8ad4c1b3
🚴 Build and Test BikeControl / Workspace tests (push) Failing after 4m32s
🚴 Build and Test BikeControl / Android compile check (push) Skipped
Unpack the Android tools where sdkmanager expects to find them
The image had never been built successfully, which is why the registry
has never held it and why every CI run since the workflows landed died
at the pull.

The commandlinetools zip carries a single top-level `cmdline-tools/`.
Unzipping it straight into $ANDROID_HOME therefore lands `bin/` exactly
where `latest/` has to go, and the `mv cmdline-tools/cmdline-tools/*`
that followed matched nothing:

    mv: cannot stat '/opt/android-sdk/cmdline-tools/cmdline-tools/*'

So unpack into /tmp and move that directory into place instead.
sdkmanager derives the SDK root from its own path and refuses to run
from anywhere but cmdline-tools/latest/, so the layout is not cosmetic —
and a `test -x` on it now fails the build here rather than three layers
later, where the error is a licence prompt that never returns.

Verified in the pushed image: node 20.20.2, npm 10.8.2, jq 1.7, JDK 17,
tauri-cli 2.11.4, NDK 27.0.11902837, and all three Android targets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 22:48:32 +02:00

111 lines
4.5 KiB
Docker

# BikeControl builder image.
#
# One image that can do every job CI asks of it:
# - `cargo test` / `clippy` on the host target, which means the *Linux BLE*
# stack must be present: btleplug talks to BlueZ over D-Bus, so libdbus is a
# hard build dependency, not an optional extra.
# - the Tauri desktop bundle (deb / AppImage), which needs the webkit2gtk set.
# - the Android APK: SDK, NDK, JDK, and the three Android Rust targets.
#
# Build and push:
# docker build -f Dockerfile.builder -t gitea.tourolle.paris/dtourolle/bikecontrol-builder:latest .
# docker push gitea.tourolle.paris/dtourolle/bikecontrol-builder:latest
# or run scripts/build-builder-image.sh.
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive \
ANDROID_HOME=/opt/android-sdk \
ANDROID_SDK_ROOT=/opt/android-sdk \
NDK_VERSION=27.0.11902837 \
SDK_VERSION=36 \
BUILD_TOOLS_VERSION=35.0.0 \
RUST_BACKTRACE=1 \
CARGO_HOME=/root/.cargo \
PATH="/root/.cargo/bin:$PATH"
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
wget \
git \
ca-certificates \
unzip \
jq \
file \
pkg-config \
openjdk-17-jdk-headless \
libssl-dev \
libclang-dev \
llvm-dev \
# BLE on the host target: btleplug's Linux backend is bluez-async over the
# `dbus` crate, which links against libdbus-1. Without this the workspace
# does not build at all — `crates/ble` is not optional.
libdbus-1-dev \
# Tauri v2 desktop (needed for `cargo test`/`clippy` on the host target and
# for the deb/AppImage bundle).
libglib2.0-dev \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libjavascriptcoregtk-4.1-dev \
libsoup-3.0-dev \
librsvg2-dev \
libayatana-appindicator3-dev \
# AppImage bundling reaches for these at bundle time.
fuse3 \
desktop-file-utils \
&& rm -rf /var/lib/apt/lists/*
# Node 20 for the Vite/Svelte frontend. The UI is plain npm (ui/package-lock.json
# is the lockfile CI installs from), so no bun here.
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y --no-install-recommends nodejs && \
rm -rf /var/lib/apt/lists/*
# Rust, with every target the release job builds for.
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal && \
. "$CARGO_HOME/env" && \
rustup component add rustfmt clippy && \
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
# The Tauri CLI as a cargo subcommand. The frontend package.json deliberately
# does not carry @tauri-apps/cli — the shell is a cargo workspace member, so the
# CLI belongs to the toolchain, not to the UI's dependency tree.
RUN . "$CARGO_HOME/env" && cargo install tauri-cli --locked --version "^2"
# ---------------------------------------------------------------------------
# Android SDK / NDK.
RUN mkdir -p "$ANDROID_HOME" /root/.android && \
printf '### User Sources for `android` cmd line tool ###\ncount=0\n' > /root/.android/repositories.cfg && \
wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/cmdline-tools.zip && \
# The zip holds a single top-level cmdline-tools/, and sdkmanager insists on
# living in cmdline-tools/latest/ — it derives the SDK root from its own
# path and refuses to run from anywhere else. So unpack somewhere neutral
# and move that directory into place; unzipping straight into $ANDROID_HOME
# puts bin/ where latest/ has to go.
unzip -q /tmp/cmdline-tools.zip -d /tmp/cmdline-tools && \
rm /tmp/cmdline-tools.zip && \
mkdir -p "$ANDROID_HOME/cmdline-tools" && \
mv /tmp/cmdline-tools/cmdline-tools "$ANDROID_HOME/cmdline-tools/latest" && \
rmdir /tmp/cmdline-tools && \
test -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
# Licences up front so Gradle never blocks on a prompt in CI.
RUN yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --sdk_root="$ANDROID_HOME" --licenses > /dev/null
RUN "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --sdk_root="$ANDROID_HOME" \
"platform-tools" \
"platforms;android-$SDK_VERSION" \
"build-tools;$BUILD_TOOLS_VERSION" \
"ndk;$NDK_VERSION" \
--channel=0 2>&1 | grep -v "Warning" || true
ENV NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION \
ANDROID_NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION \
ANDROID_NDK_ROOT=$ANDROID_HOME/ndk/$NDK_VERSION \
PATH="/opt/android-sdk/platform-tools:/opt/android-sdk/cmdline-tools/latest/bin:$PATH"
WORKDIR /app
ENTRYPOINT ["/bin/bash"]