--- title: "Flutter Integration Test Server in Debian 11 Nspawn Container" date: 2021-09-24 draft: false tags: ["debian", "nspawn", "android", "flutter"] authors: ["trent"] post: 22 --- date: 2021-09-24 ## Introduction ### Performance Your Debian Server is way more powerful than your laptop or desktop and flutter integration_tests suck. ### Ergonomics You have an Android Emulator (or a real device) connected to the machine that you are sitting in front of for reference, and now you can run integration_tests on a different device without having to juggle adb connections on the same machine. ### Nspawn Tho? Because containers unlike virtual machines access the full power of the host, but nspawn containers are peristent like virtual machines, sparing you the cognitive overhead of dealing with the ephemerality of docker containers and/or of herding cats. And you already have nspawn, it's build into systemd. Even including the (virtual) network interfaces. ### Documentation Let's face it: setting up an Android Development Environment is a nightmare. So don't just follow this guide; follow this guide a repetition of three times, building your own step-by-step guide for yourself as you go. Your brain will thank you. ## Host Preparation (Debian 11) 1. install `systemd-container` and `debootstrap` 2. enable unprivileged user namespaces * `echo 'kernel.unprivileged_userns_clone=1' >/etc/sysctl.d/nspawn.conf` * `systemctl restart systemd-sysctl.service` 3. you might as well allow debootstrap to user your apt-cacher-ng proxy * `export http_proxy=http://:3142` ### br0 bridge describe br0 bridge in `/etc/systemd/nspawn/ftest.nspawn` (optional). ```cfg # /etc/systemd/nspawn/ftest.nspawn [Network] VirtualEthernet=yes Bridge=br0 ``` ## ZFS mountpoint This is optional, obviously; you might not even use zfs. * `zfs create vm_pool/nspawn/ftest` * `zfs set mountpoint=/var/lib/machines/ftest vm_pool/nspawn/ftest` * sanity check `zfs list -r vm_pool/nspawn` ## bootstrap container ```shell # for apt-cacher-ng proxy export http_proxy=http://:3142 debootstrap --include=systemd-container stable /var/list/machines/ftest ``` ## preboot config 1. delete container's package cache 2. copy `/etc/apt/apt.conf` to container 2. copy `/root/.bashrc` to container 2. copy `/root/.inputrc` to container 2. edit `/etc/hostname` in container 2. [write nspawn file on host](#br0-bridge) 2. copy `/etc/locale.gen` to `/etc/locale.gen.bak` on container ## first interactive boot 1. `systemd-nspawn -D /var/lib/machines/ftest -U --machine ftest` 2. set passwd: `passwd` 2. stop container: `logout` ## run as service 1. `systemctl start systemd-nspawn@ftest` 2. login: `machinectl login ftest` 2. start/enable network `systemctl enable --now systemd-networkd` 2. add regular user `useradd ` ## install applications ### locale * install locales * edit `/etc/locale.gen` to taste and then run the command `locale-gen` ### essential apps ```shell apt-get install openssh-server git unzip wget sudo curl file rsync ``` ### add regular user to sudo group `usermod -a -G sudo ` ### other apps ```shell apt-get install mosh htop haveged byobu needrestart tree bash-completion ``` ### install openjdk-8 from stretch repo 1. add following to `/etc/apt/sources.list` ```cfg deb http://security.debian.org/debian-security stretch/updates main ``` 2. `apt-get update && apt-get install openjdk-8-jdk-headless` ## user environment You can now ssh into your container. scp your favorite environment files over to the container * ~/.byobu/ * ~/.bashrc * ~/.bash_aliases * ~/.inputrc ## install flutter Pick a location to taste; I prefer `~/.local/` ```shell cd ; cd .local git clone https://github.com/flutter/flutter.git ``` ### downgrade flutter if needed: ```shell cd ~/.local/flutter git checkout 2.2.3 ``` ## install command-line-tools The schuck and jive here is absurd, but here goes. Now is the time to decide where ANDROID_HOME and ANDROID_SDK_ROOT are going to be; I prefer `~/.local/share/Android/Sdk/` ```shell mkdir -p ~/.local/share/Android/Sdk ``` ### temporary installation of cmdline-tools [Command line tools only Scroll half way down](https://developer.android.com/studio){target="_blank"} ```shell cd ~/.local/share/Android/Sdk wget https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip unzip commandlinetools-linux-7583922_latest.zip mkdir 5.0 mv cmdline-tools/* 5.0/ mv 5.0 cmdline-tools/ ``` ## flutter and sdk environment add the following to `~/.bashrc` ```cfg function addToPATH { case ":$PATH:" in *":$1:"*) :;; # already there *) PATH="$PATH:$1";; # or PATH="$PATH:$1" esac } addToPATH ~/.local/flutter/bin addToPATH ~/.local/share/Android/Sdk/cmdline-tools/latest/bin addToPATH ~/.local/share/Android/Sdk/platform-tools # temporary path to temporary version of cmdline-tools addToPATH ~/.local/share/Android/Sdk/cmdline-tools/5.0/bin ``` add the following to `~/.bash_aliases` ```cfg alias sdkmanager='sdkmanager --sdk_root=~/.local/share/Android/Sdk' ``` Confirm by logging out and then back in and: ```shell which flutter ; which sdkmanager ; alias ``` ### now install cmdline-tools for real `sdkmanager --install "cmdline-tools;latest"` and then logout and log back in #### cleanup At this point I think you can remove or comment the temporary PATH statement from `~/.bashrc` for the temporary location of cmdline-tools ## install Android SDK review your options ```shell sdkmanager --list ``` and then install them (platform-tools: adb and fastboot will be pulled in automatically) ```shell sdkmanager --install "platforms;android-30" \ "build-tools;31.0.0" "build-tools;30.0.3" ``` ## confirm flutter installation `flutter doctor` ## run tests At this point you shoud be able to rsync a flutter app over to the container, connect to a device using network adb, and run something like: ```shell flutter drive --driver integration_test/driver.dart \ --target integration_test/app_test.dart --profile ```