| |
| |
| |
| |
| FROM ubuntu:22.04 |
|
|
| |
| |
| |
| ENV DEBIAN_FRONTEND=noninteractive |
| ENV USER=user |
| ENV HOME=/home/user |
|
|
| |
| ENV ANDROID_SDK_ROOT=/opt/android-sdk |
| ENV ANDROID_HOME=/opt/android-sdk |
| ENV ANDROID_STUDIO=/opt/android-studio |
| ENV PUB_CACHE=/home/user/.pub-cache |
|
|
| |
| ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 |
|
|
| |
| ENV PATH="${ANDROID_STUDIO}/bin:${JAVA_HOME}/bin:/opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${ANDROID_SDK_ROOT}/platform-tools:${ANDROID_SDK_ROOT}/emulator:${PATH}" |
|
|
| |
| ENV DISPLAY=:1 |
| ENV VNC_PORT=5901 |
| ENV NOVNC_PORT=7860 |
| ENV VNC_PASSWORD=android |
|
|
| |
| |
| |
| RUN apt-get update && apt-get install -y \ |
| # Core |
| curl wget git unzip xz-utils zip tar \ |
| # Java 21 |
| openjdk-21-jdk \ |
| # VNC + Desktop |
| tigervnc-standalone-server \ |
| openbox obconf menu \ |
| # noVNC dependencies |
| python3 python3-websockify net-tools \ |
| # Android Studio GUI libs |
| libglu1-mesa libxkbcommon-x11-0 libxcb-xinerama0 \ |
| libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \ |
| libxcb-randr0 libxcb-render-util0 libxcb-shape0 \ |
| libxcb-xfixes0 libxcb-xkb1 libxcb-sync1 \ |
| libxcb-util1 libxcb-xinput0 libfontconfig1 \ |
| libdbus-1-3 libnss3 libxcomposite1 libxcursor1 \ |
| libxi6 libxtst6 libxrandr2 libasound2 \ |
| libatk-bridge2.0-0 libgtk-3-0 libgbm1 \ |
| libegl1 mesa-utils xterm xfce4-terminal \ |
| # Build tools |
| build-essential cmake ninja-build pkg-config \ |
| libgtk-3-dev liblzma-dev libstdc++6 \ |
| # Misc |
| sudo ca-certificates gnupg lsb-release \ |
| && rm -rf /var/lib/apt/lists/* |
|
|
| # -------------------------------------------- |
| # 2. Install noVNC (Web-based VNC client) |
| # -------------------------------------------- |
| RUN mkdir -p /opt/novnc/utils/websockify && \ |
| curl -fsSL https://github.com/novnc/noVNC/archive/refs/tags/v1.4.0.tar.gz | \ |
| tar -xz --strip-components=1 -C /opt/novnc && \ |
| curl -fsSL https://github.com/novnc/websockify/archive/refs/tags/v0.11.0.tar.gz | \ |
| tar -xz --strip-components=1 -C /opt/novnc/utils/websockify |
|
|
| # Create noVNC landing page |
| RUN cp /opt/novnc/vnc.html /opt/novnc/index.html && \ |
| sed -i 's/<title>noVNC<\/title>/<title>Android Studio Web<\/title>/' /opt/novnc/index.html |
|
|
| # -------------------------------------------- |
| # 3. Install Android SDK Command Line Tools |
| # -------------------------------------------- |
| RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \ |
| wget -q "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip" -O /tmp/cmdline-tools.zip && \ |
| unzip -q /tmp/cmdline-tools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools && \ |
| mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest && \ |
| rm -f /tmp/cmdline-tools.zip |
|
|
| # -------------------------------------------- |
| # 4. Install Android Studio (Latest: 2025.3.4) |
| # -------------------------------------------- |
| RUN wget -q --show-progress \ |
| "https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2025.3.4.0/android-studio-2025.3.4.0-linux.tar.gz" \ |
| -O /tmp/android-studio.tar.gz && \ |
| tar -xzf /tmp/android-studio.tar.gz -C /opt && \ |
| rm -f /tmp/android-studio.tar.gz && \ |
| ln -sf /opt/android-studio/bin/studio.sh /usr/local/bin/android-studio |
|
|
| # -------------------------------------------- |
| # 5. Install Flutter SDK (stable) |
| # -------------------------------------------- |
| RUN git clone --depth 1 --branch stable https://github.com/flutter/flutter.git /opt/flutter && \ |
| flutter config --no-analytics && \ |
| flutter doctor --android-licenses || true |
|
|
| # -------------------------------------------- |
| # 6. Accept SDK Licenses & Install Components |
| # -------------------------------------------- |
| RUN yes | sdkmanager --licenses >/dev/null 2>&1 || true && \ |
| sdkmanager \ |
| "platform-tools" \ |
| "platforms;android-34" \ |
| "platforms;android-33" \ |
| "build-tools;34.0.0" \ |
| "build-tools;33.0.2" \ |
| "cmdline-tools;latest" \ |
| "extras;android;m2repository" \ |
| "extras;google;m2repository" \ |
| || true |
|
|
| # -------------------------------------------- |
| # 7. Create User & Set Permissions |
| # -------------------------------------------- |
| RUN useradd -m -u 1000 -s /bin/bash ${USER} && \ |
| echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ |
| mkdir -p \ |
| ${HOME}/.vnc \ |
| ${HOME}/.config/openbox \ |
| ${HOME}/.android \ |
| ${HOME}/Android/Sdk \ |
| ${HOME}/Projects && \ |
| # VNC password |
| echo "${VNC_PASSWORD}" | vncpasswd -f > ${HOME}/.vnc/passwd && \ |
| chmod 600 ${HOME}/.vnc/passwd && \ |
| # Openbox config (minimal desktop) |
| echo '<?xml version="1.0" encoding="UTF-8"?>' > ${HOME}/.config/openbox/rc.xml && \ |
| echo '<openbox_config xmlns="http://openbox.org/3.4/rc"><menu><file>menu.xml</file></menu></openbox_config>' >> ${HOME}/.config/openbox/rc.xml && \ |
| # Ownership |
| chown -R ${USER}:${USER} ${HOME} && \ |
| chown -R ${USER}:${USER} /opt/android-studio && \ |
| chown -R ${USER}:${USER} /opt/flutter && \ |
| chown -R ${USER}:${USER} ${ANDROID_SDK_ROOT} && \ |
| chown -R ${USER}:${USER} /opt/novnc |
|
|
| # -------------------------------------------- |
| # 8. Configure Flutter & Cache |
| # -------------------------------------------- |
| USER ${USER} |
| WORKDIR ${HOME} |
|
|
| RUN flutter config --android-sdk ${ANDROID_SDK_ROOT} && \ |
| flutter precache && \ |
| flutter doctor || true |
|
|
| # Pre-download Gradle wrapper (speeds up first build) |
| RUN mkdir -p ${HOME}/.gradle && \ |
| echo "org.gradle.daemon=true" >> ${HOME}/.gradle/gradle.properties && \ |
| echo "org.gradle.parallel=true" >> ${HOME}/.gradle/gradle.properties |
|
|
| # -------------------------------------------- |
| # 9. Create Startup Script |
| # -------------------------------------------- |
| USER root |
| RUN cat > /usr/local/bin/start-space.sh << 'EOF' |
| #!/bin/bash |
| set -e |
|
|
| echo "==========================================" |
| echo " Android Studio Web - Hugging Face Space" |
| echo "==========================================" |
|
|
| # Clean up old VNC locks (in case of restart) |
| rm -f /tmp/.X1-lock /tmp/.X11-unix/X1 |
|
|
| # Start VNC server |
| echo "[1/4] Starting TigerVNC server on :1..." |
| vncserver :1 \ |
| -geometry 1920x1080 \ |
| -depth 24 \ |
| -localhost no \ |
| --I_KNOW_EXACTLY_WHAT_I_AM_DOING \ |
| > /dev/null 2>&1 |
|
|
| # Start Openbox window manager |
| echo "[2/4] Starting Openbox desktop..." |
| su - ${USER} -c "DISPLAY=:1 openbox &" |
|
|
| # Start noVNC web server (port 7860 for Hugging Face) |
| echo "[3/4] Starting noVNC on port ${NOVNC_PORT}..." |
| /opt/novnc/utils/websockify/run \ |
| --web=/opt/novnc \ |
| --cert=none \ |
| --ssl-only=no \ |
| 0.0.0.0:${NOVNC_PORT} \ |
| localhost:${VNC_PORT} & |
|
|
| # Auto-start Android Studio after brief delay |
| echo "[4/4] Launching Android Studio..." |
| ( |
| sleep 4 |
| su - ${USER} -c "DISPLAY=:1 android-studio > /home/user/.android-studio.log 2>&1 &" |
| ) & |
|
|
| echo "" |
| echo "✅ Android Studio Web is ready!" |
| echo " Open your Hugging Face Space URL to access it" |
| echo "" |
|
|
| # Keep container alive |
| tail -f /dev/null |
| EOF |
| RUN chmod +x /usr/local/bin/start-space.sh |
|
|
| # -------------------------------------------- |
| # 10. Final Config |
| # -------------------------------------------- |
| USER ${USER} |
| WORKDIR ${HOME} |
|
|
| EXPOSE 7860 5901 5554 5555 |
|
|
| CMD ["/usr/local/bin/start-space.sh"] |