From a6b8efbae3c1ad858169e13828e92d0529654e8c Mon Sep 17 00:00:00 2001 From: Alexander Sosedkin Date: Sun, 12 Dec 2021 11:53:51 +0100 Subject: [PATCH] tests/fakedroid: emulate a nix-on-droid-like env for testing --- .github/workflows/fakedroid-odt.yml | 34 ++++++++ .gitignore | 1 + tests/fakedroid.sh | 115 ++++++++++++++++++++++++++++ tests/on-device/lib.bash | 13 ++-- tests/proot-test.nix | 35 +++++++++ 5 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/fakedroid-odt.yml create mode 100755 tests/fakedroid.sh create mode 100644 tests/proot-test.nix diff --git a/.github/workflows/fakedroid-odt.yml b/.github/workflows/fakedroid-odt.yml new file mode 100644 index 0000000..15a54e0 --- /dev/null +++ b/.github/workflows/fakedroid-odt.yml @@ -0,0 +1,34 @@ +name: 'run on-device-tests with fakedroid' +on: + pull_request: + push: +jobs: + fakedroid-odt: + runs-on: ubuntu-latest + + steps: + + - uses: actions/checkout@v2.4.0 + + - uses: cachix/install-nix-action@v15 + with: + nix_path: nixpkgs=channel:nixos-21.11 + extra_nix_config: "experimental-features = nix-command" + + - uses: cachix/cachix-action@v10 + with: + name: nix-on-droid + signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' + + - name: init + run: tests/fakedroid.sh echo INIT + + - name: test + run: | + tests/fakedroid.sh mkdir -p .cache/nix-on-droid-self-test + tests/fakedroid.sh touch .cache/nix-on-droid-self-test/confirmation-granted + tests/fakedroid.sh nix-on-droid on-device-test + + - name: upload + if: always() + run: tests/fakedroid.sh nix-shell -p cachix --run 'nix path-info --all | cachix push nix-on-droid' diff --git a/.gitignore b/.gitignore index cfa7b0d..cf7730b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ result result-* out flake.lock +.fakedroid diff --git a/tests/fakedroid.sh b/tests/fakedroid.sh new file mode 100755 index 0000000..0899fd6 --- /dev/null +++ b/tests/fakedroid.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +# This is a script to run "on-device" tests in CI, without the device. +# Takes the bootstrap aarch64 zipball, unpacks, proots into it. +# This won't catch all bugs, of course, but that's something. + +# Set up envvars, prepare directories: + +set -ueo pipefail + +QEMU_URL=https://github.com/multiarch/qemu-user-static/releases/download/v6.1.0-8/qemu-aarch64-static +QEMU=.fakedroid/inj/qemu-aarch64 +INSTALLATION_DIR=/data/data/com.termux.nix/files/usr +TARGET_HOME=/data/data/com.termux.nix/files/home + +mkdir -p .fakedroid/inj +mkdir -p .fakedroid/env/{$INSTALLATION_DIR,$TARGET_HOME,n-o-d} + +export PROOT_TMP_DIR=.fakedroid/env/$INSTALLATION_DIR/proot/tmp +export PROOT_L2S_DIR=.fakedroid/env/$INSTALLATION_DIR/proot/l2s +mkdir -p $PROOT_TMP_DIR +mkdir -p $PROOT_L2S_DIR + +PASSTHROUGH_VARS='' +PASSTHROUGH_VARS+=" PROOT_TMP_DIR=$PROOT_TMP_DIR" +PASSTHROUGH_VARS+=" PROOT_L2S_DIR=$PROOT_L2S_DIR" +PASSTHROUGH_VARS+=" TERM=$TERM" +PASSTHROUGH_VARS+=" HOME=$TARGET_HOME" +PASSTHROUGH_VARS+=" USER=$USER" +set +u +[[ -n "$CACHIX_SIGNING_KEY" ]] && \ + PASSTHROUGH_VARS+=" CACHIX_SIGNING_KEY=$CACHIX_SIGNING_KEY" +set -u + +PROOT_ARGS='' +PROOT_ARGS+=' -r .fakedroid/env' +PROOT_ARGS+=" -q $QEMU" +PROOT_ARGS+=" -w $TARGET_HOME" +PROOT_ARGS+=" -b .fakedroid/env/$INSTALLATION_DIR/nix:/nix" +PROOT_ARGS+=" -b .fakedroid/env/$INSTALLATION_DIR/bin:/bin" +PROOT_ARGS+=" -b .fakedroid/env/$INSTALLATION_DIR/etc:/etc" +PROOT_ARGS+=" -b .fakedroid/env/$INSTALLATION_DIR/tmp:/tmp" +PROOT_ARGS+=" -b .fakedroid/env/$INSTALLATION_DIR/usr:/usr" +PROOT_ARGS+=' -b /dev' +PROOT_ARGS+=' -b /proc' +PROOT_ARGS+=' -b /sys' +PROOT_ARGS+=' --link2symlink' + + +# Procure a static QEMU for user emulation and a proot with our patches: + +[[ -e .fakedroid/inj/qemu-aarch64 ]] || wget $QEMU_URL -O $QEMU +chmod +x $QEMU + +PROOT=$(nix-build tests/proot-test.nix)/bin/proot + + +# Do the first install if not installed yet: + +if [[ ! -e .fakedroid/env/$INSTALLATION_DIR/etc || + -e .fakedroid/env/$INSTALLATION_DIR/etc/UNINITIALIZED ]]; then + # Build a zipball: + nix build --show-trace -f pkgs \ + --argstr arch aarch64 \ + --argstr nixOnDroidChannelURL file:///n-o-d/archive.tar.gz \ + bootstrapZip -o .fakedroid/inj/nix-on-droid-aarch64 + ZIPBALL=$(realpath .fakedroid/inj/nix-on-droid-aarch64/bootstrap-aarch64.zip) + # Unpack the zipball the way the Android app does it: + pushd .fakedroid/env/$INSTALLATION_DIR + unzip "$ZIPBALL" + chmod -R u+rw . # unzip results in -r-xr-xr-x files and directories + while read e; do + SYM_TGT=${e%%←*} + SYM_SRC=${e##*←} + ln -sf "$SYM_TGT" "$SYM_SRC" + done < SYMLINKS.txt + while read e; do + chmod +x "$e" + done < EXECUTABLES.txt + rm SYMLINKS.txt EXECUTABLES.txt + popd +fi + + +# Inject nix-on-droid version under test into the environment. +# Uncommitted chages won't be picked up, just HEAD. +# /n-o-d/archive.tar.gz is used as a channel, /n-o-d/unpacked --- as a flake. + +rm -rf .fakedroid/env/n-o-d; mkdir -p .fakedroid/env/n-o-d/unpacked +git archive --format=tar --prefix n-o-d/ HEAD \ + > .fakedroid/env/n-o-d/archive.tar +tar --strip-components=1 -xf \ + .fakedroid/env/n-o-d/archive.tar -C .fakedroid/env/n-o-d/unpacked +gzip .fakedroid/env/n-o-d/archive.tar + + +# The 'first boot' proot invocation: + +SH=$(readlink .fakedroid/env/$INSTALLATION_DIR/bin/sh) + +# 'first boot' execs interactive bash unconditionally; +# makes sense on device, requires us to work around it here though +env -i $PASSTHROUGH_VARS $PROOT $PROOT_ARGS \ + $INSTALLATION_DIR/$SH ${INSTALLATION_DIR}/usr/lib/login-inner <<<'echo OK' + +# this is usually done by login on 'first reboot' +[[ -e .fakedroid/env/${INSTALLATION_DIR}/usr/lib/.login-inner.new ]] && + mv .fakedroid/env/${INSTALLATION_DIR}/usr/lib/.login-inner.new \ + .fakedroid/env/${INSTALLATION_DIR}/usr/lib/login-inner + + +# Actually execute something inside that fakedroid environment: + +exec env -i $PASSTHROUGH_VARS $PROOT $PROOT_ARGS \ + $INSTALLATION_DIR/$SH ${INSTALLATION_DIR}/usr/lib/login-inner "$@" diff --git a/tests/on-device/lib.bash b/tests/on-device/lib.bash index 64dd54a..e179e15 100644 --- a/tests/on-device/lib.bash +++ b/tests/on-device/lib.bash @@ -11,11 +11,14 @@ setup() { fi done < <(nix-channel --list) echo "parsing detected channel url: $CHANNEL_URL" - [[ "$CHANNEL_URL" =~ https://github.com/(.+)/(.+)/archive/(.+)\.tar\.gz ]] - REPO_USER=${BASH_REMATCH[1]} - REPO_NAME=${BASH_REMATCH[2]} - REPO_BRANCH=${BASH_REMATCH[3]} - FLAKE_URL=github:$REPO_USER/$REPO_NAME/$REPO_BRANCH + if [[ "$CHANNEL_URL" =~ https://github.com/(.+)/(.+)/archive/(.+)\.tar\.gz ]]; then + REPO_USER=${BASH_REMATCH[1]} + REPO_NAME=${BASH_REMATCH[2]} + REPO_BRANCH=${BASH_REMATCH[3]} + FLAKE_URL=github:$REPO_USER/$REPO_NAME/$REPO_BRANCH + elif [[ "$CHANNEL_URL" == file:///n-o-d/archive.tar.gz ]]; then + FLAKE_URL=/n-o-d/unpacked + fi echo "autodetected flake url: $FLAKE_URL" ON_DEVICE_TESTS_SETUP=1 diff --git a/tests/proot-test.nix b/tests/proot-test.nix new file mode 100644 index 0000000..629d21e --- /dev/null +++ b/tests/proot-test.nix @@ -0,0 +1,35 @@ +# Copyright (c) 2019-2021, see AUTHORS. Licensed under MIT License, see LICENSE. + +{ pkgs ? import { } }: + +let + #stdenv = pkgs.pkgsStatic.stdenvAdapters.makeStatic pkgs.stdenv; + stdenv = pkgs.stdenv; +in + +stdenv.mkDerivation { + pname = "proot-termux"; + version = "unstable-2021-11-21"; + + src = pkgs.fetchFromGitHub { + repo = "proot"; + owner = "termux"; + rev = "7d6bdd9f6cf31144e11ce65648dab2a1e495a7de"; + sha256 = "sha256-sbueMoqhOw0eChgp6KOZbhwRnSmDZhHq+jm06mGqxC4="; + + # 1 step behind 6f12fbee "Implement shmat", use if ashmem.h is missing + #rev = "ffd811ee726c62094477ed335de89fc107cadf17"; + #sha256 = "1zjblclsybvsrjmq2i0z6prhka268f0609w08dh9vdrbrng117f8"; + + }; + + buildInputs = [ pkgs.talloc ]; + + patches = [ ../pkgs/cross-compiling/proot-detranslate-empty.patch ]; + + makeFlags = [ "-Csrc" "V=1" "CFLAGS=-O3" ]; + + installPhase = '' + install -D -m 0755 src/proot $out/bin/proot + ''; +}