diff --git a/.github/workflows/package_waydroid.yml b/.github/workflows/package_waydroid.yml new file mode 100644 index 0000000000..4ccd1e3f56 --- /dev/null +++ b/.github/workflows/package_waydroid.yml @@ -0,0 +1,288 @@ +# Based on https://github.com/termux/termux-packages/blob/595969f581655e8cbf65182cf84bd5ffb2cf7b89/.github/workflows/packages.yml +## +## Copyright 2020 Termux +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## + +name: Packages-TUR-Waydroid + +on: + push: + branches: + - master + - dev + - 'dev/**' + paths: + - 'tur-waydroid/**' + pull_request: + paths: + - 'tur-waydroid/**' + workflow_dispatch: + inputs: + packages: + description: "A space-separated names of packages selected for rebuilding" + required: true + +jobs: + build: + runs-on: ubuntu-24.04 + env: + ANDROID_HOME: "/opt/termux/android-sdk" + NDK: "/opt/termux/android-ndk" + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + strategy: + matrix: + target_arch: [i686, x86_64] + # target_arch: [aarch64, arm, i686, x86_64] + fail-fast: false + steps: + - name: Install DKMS and missing modules + run: | + sudo apt update + sudo apt install dkms -y + git clone https://github.com/licy183/anbox-modules -b 6.8.x-ubuntu2404 + cd anbox-modules && ./INSTALL.sh + - name: Install wayland + run: | + sudo apt update + sudo apt install xwayland mutter pulseaudio -y + - name: Install waydroid + run: | + sudo apt update + sudo apt install curl ca-certificates -y + curl https://repo.waydro.id | sudo bash + sudo apt update + sudo apt install waydroid -y + sudo waydroid init + - name: Solve network issues for waydroid + run: | + # https://unix.stackexchange.com/a/743946 + sudo sed -i~ -E 's/=.\$\(command -v (nft|ip6?tables-legacy).*/=/g' \ + /usr/lib/waydroid/data/scripts/waydroid-net.sh + + - name: Clone repository + uses: actions/checkout@v4 + with: + fetch-depth: 1000 + submodules: true + + - name: Merge repos + run: ./setup-environment.sh + + - name: Gather build summary + run: | + if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then + BASE_COMMIT=$(jq --raw-output .pull_request.base.sha "$GITHUB_EVENT_PATH") + OLD_COMMIT=$(jq --raw-output .commits[0].id "$GITHUB_EVENT_PATH") + HEAD_COMMIT=$(jq --raw-output .commits[-1].id "$GITHUB_EVENT_PATH") + if [ "$BASE_COMMIT" = "null" ]; then + if [ "$OLD_COMMIT" = "$HEAD_COMMIT" ]; then + # Single-commit push. + echo "Processing commit: ${HEAD_COMMIT}" + CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r "${HEAD_COMMIT}") + else + # Multi-commit push. + OLD_COMMIT="${OLD_COMMIT}~1" + echo "Processing commit range: ${OLD_COMMIT}..${HEAD_COMMIT}" + CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r "${OLD_COMMIT}" "${HEAD_COMMIT}") + fi + else + # Pull requests. + echo "Processing pull request #$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH"): ${BASE_COMMIT}..HEAD" + CHANGED_FILES=$(git diff-tree --no-commit-id --name-only -r "${BASE_COMMIT}" "HEAD") + fi + fi + mkdir -p ./artifacts ./debs + touch ./debs/.placeholder + if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then + # Process tag '%ci:no-build' that may be added as line to commit message. + # Forces CI to cancel current build with status 'passed' + if grep -qiP '^\s*%ci:no-build\s*$' <(git log --format="%B" -n 1 "HEAD"); then + tar cf artifacts/debs-${{ matrix.target_arch }}.tar debs + echo "[!] Force exiting as tag '%ci:no-build' was applied to HEAD commit message." + exit 0 + fi + # XXX: TUR uses the termux-builder directly and may add custom builder image later. + # Build local Docker image if setup scripts were changed. + # Useful for pull requests submitting changes for both build environment and packages. + # if grep -qP '^scripts/(Dockerfile|setup-android-sdk\.sh|setup-ubuntu\.sh)$' <<< "$CHANGED_FILES"; then + # echo "Detected changes for environment setup scripts. Building custom Docker image now." + # cd ./scripts + # docker build -t termux/package-builder:latest . + # cd .. + # fi + for repo_path in $(jq --raw-output 'keys | .[]' repo.json); do + repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) + # Parse changed files and identify new packages and deleted packages. + # Create lists of those packages that will be passed to upload job for + # further processing. + while read -r file; do + if ! [[ $file == ${repo_path}/* ]]; then + # This file does not belong to a package, so ignore it + continue + fi + if [[ $file =~ ^${repo_path}/([.a-z0-9+-]*)/([.a-z0-9+-]*).subpackage.sh$ ]]; then + # A subpackage was modified, check if it was deleted or just updated + pkg=${BASH_REMATCH[1]} + subpkg=${BASH_REMATCH[2]} + if [ ! -f "${repo_path}/${pkg}/${subpkg}.subpackage.sh" ]; then + echo "$subpkg" >> ./deleted_${repo}_packages.txt + fi + elif [[ $file =~ ^${repo_path}/([.a-z0-9+-]*)/.*$ ]]; then + # package, check if it was deleted or updated + pkg=${BASH_REMATCH[1]} + if [ -d "${repo_path}/${pkg}" ]; then + echo "$pkg" >> ./built_${repo}_packages.txt + # If there are subpackages we want to create a list of those + # as well + for file in $(find "${repo_path}/${pkg}/" -maxdepth 1 -type f -name \*.subpackage.sh | sort); do + echo "$(basename "${file%%.subpackage.sh}")" >> ./built_${repo}_subpackages.txt + done + else + echo "$pkg" >> ./deleted_${repo}_packages + fi + fi + done<<<${CHANGED_FILES} + done + else + for pkg in ${{ github.event.inputs.packages }}; do + repo_paths=$(jq --raw-output 'keys | .[]' repo.json) + found=false + for repo_path in $repo_paths; do + repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) + if [ -d "${repo_path}/${pkg}" ]; then + found=true + echo "$pkg" >> ./built_${repo}_packages.txt + for subpkg in $(find "${repo_path}/${pkg}/" -maxdepth 1 -type f -name \*.subpackage.sh | sort); do + echo "$(basename "${subpkg%%.subpackage.sh}")" >> ./built_${repo}_subpackages.txt + done + fi + done + if [ "$found" != true ]; then + echo "Package '${pkg}' not found in any of the repo" + exit 1 + fi + done + fi + for repo in $(jq --raw-output '.[].name' repo.json); do + # Fix so that lists do not contain duplicates + if [ -f ./built_${repo}_packages.txt ]; then + uniq ./built_${repo}_packages.txt > ./built_${repo}_packages.txt.tmp + mv ./built_${repo}_packages.txt.tmp ./built_${repo}_packages.txt + fi + if [ -f ./built_${repo}_subpackages.txt ]; then + uniq ./built_${repo}_subpackages.txt > ./built_${repo}_subpackages.txt.tmp + mv ./built_${repo}_subpackages.txt.tmp ./built_${repo}_subpackages.txt + fi + if [ -f ./deleted_${repo}_packages.txt ]; then + uniq ./deleted_${repo}_packages.txt > ./deleted_${repo}_packages.txt.tmp + mv ./deleted_${repo}_packages.txt.tmp ./deleted_${repo}_packages.txt + fi + done + - name: Free additional disk space (if necessary) + run: | + if grep -q '^code-server$\|^demo-package2$\$' ./built_tur_packages.txt; then + sudo apt purge -yq $(dpkg -l | grep '^ii' | awk '{ print $2 }' | grep -P '(cabal-|dotnet-|ghc-|libmono|php|aspnetcore)') \ + mono-runtime-common monodoc-manual ruby + sudo apt autoremove -yq + sudo rm -rf /opt/hostedtoolcache /usr/local /usr/share/dotnet /usr/share/swift + fi + - name: Lint packages + run: | + declare -a package_recipes + for repo_path in $(jq --raw-output 'keys | .[]' repo.json); do + repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) + if [ -f ./built_${repo}_packages.txt ]; then + package_recipes="$package_recipes $(cat ./built_${repo}_packages.txt | repo_path=${repo_path} awk '{print ENVIRON["repo_path"]"/"$1"/build.sh"}')" + fi + done + if [ ! -z "$package_recipes" ]; then + ./scripts/lint-packages.sh $package_recipes + fi + - name: Build packages + run: | + declare -a packages + for repo_path in $(jq --raw-output 'keys | .[]' repo.json); do + repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) + if [ -f ./built_${repo}_packages.txt ]; then + packages="$packages $(cat ./built_${repo}_packages.txt)" + fi + done + if [ ! -z "$packages" ]; then + PACKAGE_TO_BUILD="$packages" bash -x ./waydroid-build-wrapper.sh + fi + - name: Generate build artifacts + if: always() + run: | + test -d tur/output && mv tur/output/* ./output/ + for repo in $(jq --raw-output '.[].name' repo.json); do + # Put package lists into directory with *.deb files so they will be transferred to + # upload job. + test -f ./built_${repo}_packages.txt && mv ./built_${repo}_packages.txt ./debs/ + test -f ./built_${repo}_subpackages.txt && cat ./built_${repo}_subpackages.txt >> ./debs/built_${repo}_packages.txt \ + && rm ./built_${repo}_subpackages.txt + test -f ./deleted_${repo}_packages.txt && mv ./deleted_${repo}_packages.txt ./debs/ + # Move only debs from built_packages into debs/ folder before + # creating an archive. + while read -r pkg; do + # Match both $pkg.deb and $pkg-static.deb. + find output \( -name "$pkg_*.deb" -o -name "$pkg-static_*.deb" \) -type f -print0 | xargs -0r mv -t debs/ + done < <(cat ./debs/built_${repo}_packages.txt) + done + # Files containing certain symbols (e.g. ":") will cause failure in actions/upload-artifact. + # Archiving *.deb files in a tarball to avoid issues with uploading. + tar cf artifacts/debs-${{ matrix.target_arch }}-${{ github.sha }}.tar debs + - name: Checksums for built *.deb files + if: always() + run: | + find debs -type f -name "*.deb" -exec sha256sum "{}" \; | sort -k2 + - name: Store *.deb files + if: always() + uses: actions/upload-artifact@v4 + with: + name: debs-${{ matrix.target_arch }}-${{ github.sha }} + path: ./artifacts + include-hidden-files: true + overwrite: true + compression-level: 0 + + upload: + if: github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + steps: + - name: Clone repository + uses: actions/checkout@v4 + - name: Get *.deb files + uses: actions/download-artifact@v4 + with: + path: ./ + - name: Upload to a temporary release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + run: | + GITHUB_SHA=${{ github.sha }} + # for archive in debs-*/debs-{aarch64,arm,i686,x86_64}-${{ github.sha }}.tar; do + for archive in debs-*/debs-{arm,i686,x86_64}-${{ github.sha }}.tar; do + gh release upload -R https://github.com/termux-user-repository/tur "0.1" $archive + echo "$archive uploaded" + done + - name: Trigger workflow in dists repository + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + EVENT: "from_tur" + ORG: "termux-user-repository" + REPO: "dists" + run: | + curl -d "{\"event_type\": \"${EVENT}\"}" -H "Content-Type: application/json" -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.everest-preview+json" "https://api.github.com/repos/${ORG}/${REPO}/dispatches" diff --git a/common-files/run-as-termux.sh b/common-files/run-as-termux.sh new file mode 100755 index 0000000000..b27de87fbd --- /dev/null +++ b/common-files/run-as-termux.sh @@ -0,0 +1,18 @@ +#!/system/bin/sh +BASE_DIR="/data/data/com.termux/files" + +if [ $(id -u) = 0 ]; then + su $(stat -c %u $BASE_DIR) "$(realpath $0)" "$@" + exit 0 +fi + +export PREFIX="$BASE_DIR/usr" +export HOME="$BASE_DIR/home" +export TMPDIR="$BASE_DIR/usr/tmp" +export LD_LIBRARY_PATH="$PREFIX/lib:$LD_LIBRARY_PATH" +export PATH="$PREFIX/bin:$PATH" +export TZ="UTC" +export LANG="en_US.UTF-8" +export SHELL="$PREFIX/bin/bash" + +"$SHELL" "$@" diff --git a/repo.json b/repo.json index 863cab4857..d1b1ce6923 100644 --- a/repo.json +++ b/repo.json @@ -17,6 +17,12 @@ "component": "tur-on-device", "url": "https://tur.kcubeterm.com" }, + "tur-waydroid": { + "name": "tur-waydroid", + "distribution": "tur-packages", + "component": "tur-waydroid", + "url": "https://tur.kcubeterm.com" + }, "tur-hacking": { "name": "tur-hacking", "distribution": "tur-packages", diff --git a/tur-waydroid/hello-tur-waydroid/build.sh b/tur-waydroid/hello-tur-waydroid/build.sh new file mode 100644 index 0000000000..f54e0fe694 --- /dev/null +++ b/tur-waydroid/hello-tur-waydroid/build.sh @@ -0,0 +1,15 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/termux-user-repository/tur +TERMUX_PKG_DESCRIPTION="Dummy test for TUR Waydroid" +TERMUX_PKG_LICENSE="GPL-2.0" +TERMUX_PKG_MAINTAINER="@termux-user-repository" +TERMUX_PKG_VERSION=0.1 +TERMUX_PKG_SKIP_SRC_EXTRACT=true +TERMUX_PKG_BUILD_IN_SRC=true + +termux_step_make() { + $CC $CFLAGS $CPPFLAGS $TERMUX_PKG_BUILDER_DIR/main.c -o hello-tur-waydroid +} + +termux_step_make_install() { + install -Dm700 hello-tur-waydroid $TERMUX_PREFIX/bin/hello-tur-waydroid +} diff --git a/tur-waydroid/hello-tur-waydroid/main.c b/tur-waydroid/hello-tur-waydroid/main.c new file mode 100644 index 0000000000..fb9bbf5fa3 --- /dev/null +++ b/tur-waydroid/hello-tur-waydroid/main.c @@ -0,0 +1,4 @@ +#include +int main() { + puts("Hello TUR Waydroid."); +} \ No newline at end of file diff --git a/waydroid-build-wrapper.sh b/waydroid-build-wrapper.sh new file mode 100755 index 0000000000..65305ea2be --- /dev/null +++ b/waydroid-build-wrapper.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +set -e -u -o pipefail + +REPO_DIR=$(cd "$(realpath "$(dirname "$0")")"; pwd) +TERMUX_BASE_DIR="/data/data/com.termux/files" +TERMUX_HOME="$TERMUX_BASE_DIR/home" +TERMUX_PREFIX="$TERMUX_BASE_DIR/usr" + +: ${TERMUX_ARCH:=x86_64} +: ${TERMUX_VERSION:=0.118.1} + +# Test if PACKAGE_TO_BUILD is set or not +echo "Package(s) to build: $PACKAGE_TO_BUILD" + +# Enter workspace dir +export __log_dir="$(mktemp -d)" +cd "$__log_dir" + +__start_waydroid() { + echo "Starting mutter..." + mutter --wayland --headless > mutter.out 2> mutter.err & + echo "Starting pulseaudio..." + pulseaudio --start --exit-idle-time=-1 + echo "Starting waydroid..." + sudo sed -i 's|^\(\[properties\]\)$|\1\nro.hardware.gralloc=default|g' /var/lib/waydroid/waydroid.cfg + sudo sed -i 's|^\(\[properties\]\)$|\1\nro.hardware.egl=swiftshader|g' /var/lib/waydroid/waydroid.cfg + sudo sed -i 's|^\(\[properties\]\)$|\1\npersist.waydroid.suspend=false|g' /var/lib/waydroid/waydroid.cfg + sudo waydroid upgrade -o + waydroid session start > waydroid-session-start.out 2> waydroid-session-start.err & + waydroid show-full-ui + echo "Sleep 30s..." + sleep 30 + echo "Test waydroid network" + sudo waydroid shell -- curl -v https://www.google.com +} + +__install_termux() { + local version="$TERMUX_VERSION" + local abi= + case $TERMUX_ARCH in + aarch64) + abi="arm64-v8a" + ;; + arm) + abi="armeabi-v7a" + ;; + i686) + abi="x86" + ;; + x86_64) + abi="x86_64" + ;; + *) + echo "Invalid arch." + exit 1 + ;; + esac + local url="https://github.com/termux/termux-app/releases/download/v$version/termux-app_v$version+github-debug_$abi.apk" + wget $url + waydroid app install $__log_dir/$(basename $url) + sleep 10 + rm -f $__log_dir/$(basename $url) +} + +__check_file_exists() { + local path="$1" + local counter=0 + local maxcnt=100 + while ! [ $(sudo waydroid shell -- run-as com.termux sh -c '[ -e "$1" ]; echo $?' - "$path") = 0 ]; do + sleep 10s + counter=$[counter+1] + echo "Wait $counter time(s) for $path exists" + if [ "$counter" = "$maxcnt" ]; then + echo "Max counter reached" + exit 1 + fi + done +} + +__prepare_sshd() { + # Start Termux + waydroid app launch com.termux + # Check until $PREFIX exists + __check_file_exists "$TERMUX_PREFIX" + # OK. Now we have Termux bootstrap installed. Kill Termux now + sudo waydroid shell -- am force-stop com.termux + sleep 5 + # Install openssh in Termux + sudo waydroid shell -- run-as com.termux sh -c "echo 'apt update && touch 1 && apt dist-upgrade -o Dpkg::Options::=--force-confnew -y && touch 2 && apt update && touch 3 && apt install openssh -yqq && touch 4' > $TERMUX_HOME/.bashrc" + sudo waydroid shell -- am start -n com.termux/com.termux.app.TermuxActivity + __check_file_exists $TERMUX_HOME/1 + __check_file_exists $TERMUX_HOME/2 + __check_file_exists $TERMUX_HOME/3 + __check_file_exists $TERMUX_HOME/4 + # Generate ssh-key for Termux + ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa + public_key="$(cat ~/.ssh/id_rsa.pub)" + # Add ssh-key to Termux's authorized_keys + sudo waydroid shell -- run-as com.termux sh -c "echo '$public_key' >> $TERMUX_HOME/.ssh/authorized_keys" + # Start sshd in Termux + sudo waydroid shell -- run-as com.termux sh -c "echo 'sshd' > $TERMUX_HOME/.bashrc" + sudo waydroid shell -- am force-stop com.termux + sleep 5 + sudo waydroid shell -- am start -n com.termux/com.termux.app.TermuxActivity + sleep 5 +} + +__build_package() { + # Get IP address of Waydroid container + waydroid_ip="$(waydroid status | grep -oP 'IP address:\s+\K[\d.]+')" + # Execute `ls -al` with ssh for testing + ssh -o StrictHostKeyChecking=no "$waydroid_ip" -p 8022 -- ls -al + # Connect to Waydroid connect with adb + scp -r -o StrictHostKeyChecking=no -P 8022 $REPO_DIR/ "$waydroid_ip":$TERMUX_HOME/repo + + # Build packages + ssh -o StrictHostKeyChecking=no "$waydroid_ip" -p 8022 -- "cd $TERMUX_HOME/repo && ./scripts/setup-termux.sh" + ssh -o StrictHostKeyChecking=no "$waydroid_ip" -p 8022 -- "cd $TERMUX_HOME/repo && ./build-package.sh -I $PACKAGE_TO_BUILD" + + # Pull result + rm -rf ./output + scp -r -o StrictHostKeyChecking=no $TERMUX_HOME/repo/output "$waydroid_ip":$REPO_DIR/ -p 8022 +} + +__start_waydroid +__install_termux +__prepare_sshd +__build_package