diff --git a/.gitignore b/.gitignore index d824965..e1031f2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ AndroidSDK */build repos flutter +cache diff --git a/Dockerfile b/Dockerfile index 955d4a8..5f375ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -115,13 +115,36 @@ ENV PATH "/usr/bin/flutter/bin:$PATH" RUN git config --global --add safe.directory /usr/bin/flutter -#RUN apt-get update \ -# && DEBIAN_FRONTEND=noninteractive apt-get install -y \ -# clang++ \ -# cmake \ -# ninja-build \ -# pkg-config \ -# && apt-get clean \ -# && rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + clang \ + cmake \ + ninja-build \ + pkg-config \ + libgtk-3-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/{apt,dpkg,cache,log} + +# Install Chrome (flutter) +RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb +RUN apt install -y ./google-chrome-stable_current_amd64.deb + +RUN mkdir -p /opt/android-studio ; wget -O - https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2024.1.1.11/android-studio-2024.1.1.11-linux.tar.gz | tar -xvz -C /opt + +# Install VNC (remote gui editing) +# +ENV DISPLAY :20 +# Port is 5900+${DISPLAY} +# +EXPOSE 5920 +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + tightvncserver fluxbox + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y \ + qemu-system-x86 \ + libpulse-dev COPY /scripts /scripts + diff --git a/README.md b/README.md index 8f09e30..3f4b1be 100644 --- a/README.md +++ b/README.md @@ -7,44 +7,54 @@ infrastructure configured as well. ## Build the Android SDK base container This will seed the base container with an initial version of the Android SDK. -After it is created, the SDK is copied to a volume bind mount on the host to -allow multiple containers to use the same base SDK, as well as to perform -SDK updates, upgrades, and additional package installations. ```bash docker compose build ``` +This will build the base 'android-sdk-manager' container as well as the unseeded +'android-dev-container'. + +Inside the 'anrdoid-sdk-manager' container the directory '/sdk' will contain the original SDK. + +After it is created, the SDK is copied to a volume bind mount on the host to +allow multiple containers to use the same base SDK, as well as to perform +SDK updates, upgrades, and additional package installations. The initial +seeding of the AndroidSDK on the host is performed via: + +```bash +docker compose run -it --rm \ + android-sdk-manager \ + init +``` + +## Install packages into SDK + +The following will install Android platform 34: + +https://docs.flutter.dev/get-started/install/linux/android?tab=physical#configure-android-development + +```bash +docker compose run -it --rm \ + android-sdk-manager -- \ + sdkmanager \ + "emulator" \ + "cmdline-tools;latest" \ + "build-tools;34.0.0" \ + "platforms;android-34" \ + "platform-tools" \ + "system-images;android-34;google_apis_playstore;x86_64" +``` + +At this point, ./AndroidSDK contains the latest SDK. The docker-compose.yml +for the android-dev-container will mount ./AndroidSDK to /opt/android-sdk. + ## Seed the flutter repository ``` git clone https://github.com/flutter/flutter.git ``` -## Copy the base SDK to the mounted 'sdk' directory - -```bash -docker compose run -it --rm \ - android-dev-container -- \ - bash -c 'cp -a $ANDROID_HOME/. /sdk/' -``` - -## Install packages into SDK - -The following will install Android platform 35: - -```bash -ANDROID_PLATFORM=35 -docker compose run -it --rm \ - android-dev-container -- \ - sdkmanager \ - "build-tools;${ANDROID_PLATFORM}.0.0" \ - "platforms;android-${ANDROID_PLATFORM}" -``` - -At this point, ./AndroidSDK contains the latest SDK. The docker-compose.yml -for the android-dev-container will mount ./AndroidSDK to /opt/android-sdk. - ## To seed a new project (once per project) ```bash diff --git a/AndroidSDK/.keep b/cache/android/.keep similarity index 100% rename from AndroidSDK/.keep rename to cache/android/.keep diff --git a/cache/pub-cache/.keep b/cache/pub-cache/.keep new file mode 100644 index 0000000..e69de29 diff --git a/develop.sh b/develop.sh index 72f6e38..55eb657 100755 --- a/develop.sh +++ b/develop.sh @@ -4,28 +4,22 @@ fail() { exit 1 } -declare project_type=$1 -declare project=$2 +declare project=$1 -if [[ "${project}" == "" ]] || [[ "${project_type}" == "" ]]; then - echo "usage: ./launch expo|flutter PROJECT" >&2 +if [[ "${project}" == "" ]]; then + echo "usage: $0 PROJECT" >&2 exit 1 fi -if [[ ! -d "projects/${project}" ]]; then - if ! mkdir -p "projects/${project}"; then - fail "mkdir -p projects/${project}" - fi -fi declare full_path=$(realpath "projects/${project}") if ! docker compose run -it --rm \ --name "${project}-seed" \ - -v $(pwd)/scripts:/scripts \ -v "${full_path}:/projects/${project}" \ + -p 5920:5920 \ + -e CONTAINER="${project}" \ android-dev-container -- \ develop \ - "${project_type}" \ "${project}"; then - fail "Unable to launch ${project_type} develop container for ${project}" + fail "Unable to launch development container for ${project}" fi diff --git a/docker-compose.yml b/docker-compose.yml index fc2210b..1e56879 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,9 +1,23 @@ services: + android-sdk-manager: + build: + context: . + environment: + - CONTAINER_MODE=android-sdk-manager + tty: true + ports: + - '5037:5037' + volumes: + - ./AndroidSDK:/sdk:rw +# - ./AndroidSDK:/opt/android-sdk:rw + - ./scripts:/scripts:rw + - ./devices:/opt/devices:rw + healthcheck: + disable: true + android-dev-container: build: context: . - args: - - NODE_ENV=development environment: - NODE_ENV=development tty: true @@ -11,6 +25,9 @@ services: - '19006:19006' - '19001:19001' - '19002:19002' + - '5920:5920' + devices: + - /dev/kvm volumes: - ./react_native_app:/opt/react_native_app/app:delegated - ./react_native_app/package.json:/opt/react_native_app/package.json @@ -18,10 +35,14 @@ services: - ./flutter:/usr/bin/flutter - notused:/opt/react_native_app/app/node_modules - ./repos:/opt/repos +# - ./AndroidSDK:/opt/android-sdk:ro - ./AndroidSDK:/opt/android-sdk:ro - ./scripts:/scripts:rw + - ./devices:/opt/devices:rw + - ./cache/pub-cache:/root/.pub-cache:rw + - ./cache/android:/root/.android:rw healthcheck: disable: true volumes: - notused: \ No newline at end of file + notused: diff --git a/launch.sh b/launch.sh new file mode 100755 index 0000000..45559fe --- /dev/null +++ b/launch.sh @@ -0,0 +1,25 @@ +#!/bin/bash +fail() { + echo "$*" >&2 + exit 1 +} + +declare project=$1 + +if [[ "${project}" == "" ]]; then + echo "usage: $0 PROJECT" >&2 + exit 1 +fi + +declare full_path=$(realpath "projects/${project}") + +if ! docker compose run --rm \ + --name "${project}-seed" \ + -v "${full_path}:/projects/${project}" \ + -p 5920:5920 \ + -e CONTAINER="${project}" \ + android-dev-container -- \ + run \ + "${project}"; then + fail "Unable to start container for ${project}" +fi diff --git a/license_accepter.sh b/license_accepter.sh index f4adf95..fd272a1 100755 --- a/license_accepter.sh +++ b/license_accepter.sh @@ -29,6 +29,8 @@ accept_all_android_licenses() { accept_license_of android-sdk-preview-license 504667f4c0de7af1a06de9f4b1727b84351f2910 accept_license_of google-gdk-license 33b6a2b64607f11b759f320ef9dff4ae5c47d97a accept_license_of intel-android-extra-license d975f751698a77b662f1254ddbeed3901e976f5a + accept_license_of android-sdk-arm-dbt-license 859f317696f67ef3d7f30a50a5560e7834b43903 + accept_license_of mips-android-sysimage-license e9acab5b5fbb560a72cfaecce8946896ff6aab9d } accept_license_of() { diff --git a/scripts/entrypoint b/scripts/entrypoint index 48f16f6..e08d4d8 100755 --- a/scripts/entrypoint +++ b/scripts/entrypoint @@ -9,6 +9,8 @@ arguments=( declare -i parse_error=0 declare command="" +declare XVNC_ARGS="-depth 24 -geometry 1280x1024 -ac" + # # Parse command line arguments # @@ -61,29 +63,113 @@ if (( parse_error )); then fi case "${command}" in -*bash|shell) - /bin/bash "${options[@]}" +init) + if [[ "${CONTAINER_MODE}" != "android-sdk-manager" ]]; then + fail "'${command}' must be run on android-sdk-manager not android-dev-container" + fi + + if [[ ! -e "${ANDROID_HOME}"/license/android-sdk-license ]]; then + if ! cp -a "${ANDROID_HOME}"/. /sdk; then + fail "Unable to copy ${ANDROID_HOME} to /sdk" + fi + fi + echo "AndroidSDK transfered to host bind mount." + ;; + +seed-project) + if [[ "${CONTAINER_MODE}" == "android-sdk-manager" ]]; then + fail "'${command}' must be run on android-dev-container not android-sdk-manager" + fi + seed-project "${options[@]}" exit $? ;; develop) - project_type="${1}" - project="${2}" + if [[ "${CONTAINER_MODE}" == "android-sdk-manager" ]]; then + fail "'${command}' must be run on android-dev-container not android-sdk-manager" + fi + project="${1}" + project_type=$(cat "/projects/${project}/project_type") cd "/projects/${project}" + if [[ -d ".git" ]]; then + git config --global --add safe.directory "$(pwd)" + fi case "${project_type}" in "expo") echo "Launching web app in ${project}" npm run web ;; "flutter") + cat << EOF > ~/.flutter +{ + "firstRun": false, + "clientId": "b88ac7f3-4d6f-41e0-bef6-f377e4eaceb7", + "enabled": false +} +EOF + Xvnc ${DISPLAY} ${XVNC_ARGS} >/dev/null 2>&1 & + fluxbox >/dev/null 2>&1 & + echo "X11 VNC running on ${DISPLAY} with password '${CONTAINER}'" + if [[ ! -d "/opt/devices/pixel-6a" ]]; then + echo "Creating Pixel 6A with Android 35 and Google APIs" + avdmanager create avd --force --path /opt/devices/pixel-6a --name Pixel-6a -k "system-images;android-34;google_apis_playstore;x86_64" --device pixel_6a + fi + echo "Launching emulator. Connect via vnc to view." + emulator -avd Pixel-6a & echo "Launching terminal in ${project}" /bin/bash ;; esac ;; -seed-project) - seed-project "${options[@]}" +run) + if [[ "${CONTAINER_MODE}" == "android-sdk-manager" ]]; then + fail "'${command}' must be run on android-dev-container not android-sdk-manager" + fi + project="${1}" + cd "/projects/${project}" + if [[ -d ".git" ]]; then + git config --global --add safe.directory "$(pwd)" + fi + project_type=$(cat "/projects/${project}/project_type") + case "${project_type}" in + "expo") + echo "Starting web app in ${project}" + while true; do npm run web; sleep 5; done + ;; + "flutter") + cat << EOF > ~/.flutter +{ + "firstRun": false, + "clientId": "b88ac7f3-4d6f-41e0-bef6-f377e4eaceb7", + "enabled": false +} +EOF + Xvnc ${DISPLAY} ${XVNC_ARGS} >/dev/null 2>&1 & + fluxbox >/dev/null 2>&1 & + echo "X11 VNC running on ${DISPLAY} with password '${CONTAINER}'" + if [[ ! -d "/opt/devices/pixel-6a" ]]; then + echo "Creating Pixel 6A with Android 35 and Google APIs" + avdmanager create avd --force --path /opt/devices/pixel-6a --name Pixel-6a -k "system-images;android-34;google_apis_playstore;x86_64" --device pixel_6a + fi + echo "Launching emulator. Connect via vnc to view." + emulator -avd Pixel-6a & + echo "Launching terminal in ${project}" + /bin/bash + ;; + esac + ;; +*bash|shell) + /bin/bash "${options[@]}" exit $? ;; *) - fail "Invalid command '${command}'" + if [[ "${CONTAINER_MODE}" == "android-sdk-manager" ]] && [[ "${command}" =~ .*sdkmanager$ ]]; then + echo "'${command}' detected. Updating container copy of Android SDK from the host." + cp -a /sdk/. $ANDROID_HOME/ + fi + ${command} "${@}" + if [[ "${CONTAINER_MODE}" == "android-sdk-manager" ]] && [[ "${command}" =~ .*sdkmanager$ ]]; then + echo "'${command}' detected. Updating host copy of Android SDK" + cp -a $ANDROID_HOME/. /sdk/ + fi + ;; esac diff --git a/scripts/seed-project b/scripts/seed-project index 10b1225..6ca6ee7 100755 --- a/scripts/seed-project +++ b/scripts/seed-project @@ -106,6 +106,8 @@ case "${project_type}" in if ! chown -R ${user}:${group} .; then fail "chown -R ${user}:${group} ." fi + + echo "${project_type}" > project_type ;; "flutter") ls -altr /usr/bin/flutter @@ -121,5 +123,14 @@ case "${project_type}" in fi fi git config --global --add safe.directory /usr/bin/flutter + flutter config --no-analytics + flutter config --disable-analytics + flutter doctor --android-licenses + cd "${project}" + if ! chown -R ${user}:${group} .; then + fail "chown -R ${user}:${group} ." + fi + echo "${project_type}" > project_type ;; -esac \ No newline at end of file +esac + diff --git a/seed.sh b/seed.sh index 3da365e..69d6234 100755 --- a/seed.sh +++ b/seed.sh @@ -8,7 +8,7 @@ declare project_type=$1 declare project=$2 if [[ "${project}" == "" ]] || [[ "${project_type}" == "" ]]; then - echo "usage: ./launch expo|flutter PROJECT" >&2 + echo "usage: $0 expo|flutter PROJECT" >&2 exit 1 fi diff --git a/shell.sh b/shell.sh index 1dac97e..c1341fa 100755 --- a/shell.sh +++ b/shell.sh @@ -4,24 +4,18 @@ fail() { exit 1 } -declare project_type=$1 -declare project=$2 +declare project=$1 -if [[ "${project}" == "" ]] || [[ "${project_type}" == "" ]]; then - echo "usage: ./launch expo|flutter PROJECT" >&2 +if [[ "${project}" == "" ]]; then + echo "usage: ${0} PROJECT" >&2 exit 1 fi -if [[ ! -d "projects/${project}" ]]; then - if ! mkdir -p "projects/${project}"; then - fail "mkdir -p projects/${project}" - fi -fi declare full_path=$(realpath "projects/${project}") if ! docker compose exec -it \ "${project}" \ shell; then - fail "Unable to launch shell in ${project}" + fail "Unable to launch shell in '${project}'. Make sure it is running via './launch.sh ${project}'." fi