diff --git a/.drone.yml b/.drone.yml_
similarity index 100%
rename from .drone.yml
rename to .drone.yml_
diff --git a/.github/workflows/configNC.sh b/.github/workflows/configNC.sh
new file mode 100644
index 000000000000..8446e69faa16
--- /dev/null
+++ b/.github/workflows/configNC.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+# Nextcloud Android Library
+#
+# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: MIT
+#
+
+if [ $1 = "master" ]; then
+ SERVER_VERSION_MAIN="main"
+ SERVER_VERSION_MASTER="master"
+else
+ SERVER_VERSION_MAIN=$1
+ SERVER_VERSION_MASTER=$1
+fi
+
+curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash
+export NVM_DIR="$HOME/.nvm"
+[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
+
+. ~/.bashrc; nvm install node
+
+php /var/www/html/occ log:manage --level warning
+
+OC_PASS=user1 php /var/www/html/occ user:add --password-from-env --display-name='User One' user1
+OC_PASS=user2 php /var/www/html/occ user:add --password-from-env --display-name='User Two' user2
+OC_PASS=user3 php /var/www/html/occ user:add --password-from-env --display-name='User Three' user3
+OC_PASS=test php /var/www/html/occ user:add --password-from-env --display-name='Test@Test' test@test
+OC_PASS=test php /var/www/html/occ user:add --password-from-env --display-name='Test Spaces' 'test test'
+php /var/www/html/occ user:setting user2 files quota 1G
+php /var/www/html/occ group:add users
+php /var/www/html/occ group:adduser users user1
+php /var/www/html/occ group:adduser users user2
+php /var/www/html/occ group:adduser users test
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/activity.git /var/www/html/apps/activity/
+php /var/www/html/occ app:enable -f activity
+
+git clone --depth=1 -b $SERVER_VERSION_MAIN https://github.com/nextcloud/text.git /var/www/html/apps/text/
+php /var/www/html/occ app:enable -f text
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/end_to_end_encryption.git /var/www/html/apps/end_to_end_encryption/
+php /var/www/html/occ app:enable -f end_to_end_encryption
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/password_policy.git /var/www/html/apps/password_policy/
+php /var/www/html/occ app:enable -f password_policy
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/external.git /var/www/html/apps/external/
+cd /var/www/html/apps/external; composer install --no-dev
+php /var/www/html/occ app:enable -f external
+php /var/www/html/occ config:app:set external sites --value="{\"1\":{\"id\":1,\"name\":\"Nextcloud\",\"url\":\"https:\/\/www.nextcloud.com\",\"lang\":\"\",\"type\":\"link\",\"device\":\"\",\"icon\":\"external.svg\",\"groups\":[],\"redirect\":false},\"2\":{\"id\":2,\"name\":\"Forum\",\"url\":\"https:\/\/help.nextcloud.com\",\"lang\":\"\",\"type\":\"link\",\"device\":\"\",\"icon\":\"external.svg\",\"groups\":[],\"redirect\":false}}"
+
+git clone --depth=1 -b $SERVER_VERSION_MAIN https://github.com/nextcloud/files_lock.git /var/www/html/apps/files_lock/
+php /var/www/html/occ app:enable -f files_lock
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/groupfolders.git /var/www/html/apps/groupfolders/
+php /var/www/html/occ app:enable -f groupfolders
+php /var/www/html/occ groupfolders:create groupfolder
+php /var/www/html/occ groupfolders:group 1 users
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/notifications.git /var/www/html/apps/notifications/
+cd /var/www/html/apps/notifications; composer install --no-dev
+php /var/www/html/occ app:enable -f notifications
+
+if [ $1 = 'stable22' ]; then
+ php /var/www/html/occ notification:generate test test
+else
+ php /var/www/html/occ notification:generate test -d test
+fi
+
+git clone --depth=1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/photos.git /var/www/html/apps/photos/
+cd /var/www/html/apps/photos; composer install --no-dev
+php /var/www/html/occ app:enable -f photos
+
+git clone --depth=1 -b $SERVER_VERSION_MAIN https://github.com/nextcloud/assistant.git /var/www/html/apps/assistant/
+cd /var/www/html/apps/assistant; . ~/.bashrc; make
+php /var/www/html/occ app:enable -f assistant
+
+php /var/www/html/occ app:enable -f testing
+
+git clone --depth 1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/files_downloadlimit.git /var/www/html/apps/files_downloadlimit/
+php /var/www/html/occ app:enable -f files_downloadlimit
+
+git clone --depth 1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/recommendations.git /var/www/html/apps/recommendations/
+cd /var/www/html/apps/recommendations; composer install --no-dev
+php /var/www/html/occ app:enable -f recommendations
+
+git clone --depth 1 -b $SERVER_VERSION_MASTER https://github.com/nextcloud/viewer.git /var/www/html/apps/viewer/
+php /var/www/html/occ app:enable -f viewer
+
+php /var/www/html/occ config:system:set ratelimit.protection.enabled --value false --type bool
diff --git a/.github/workflows/configServer.sh b/.github/workflows/configServer.sh
new file mode 100644
index 000000000000..e87cbbf0f35e
--- /dev/null
+++ b/.github/workflows/configServer.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Nextcloud Android Library
+#
+# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: MIT
+#
+
+wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
+apt-get update && apt-get install -y composer
+mkdir /var/www/.nvm /var/www/.npm
+mkdir /var/www/.cache/
+touch /var/www/.bashrc
+chown -R 33:33 /var/www/.nvm /var/www/.npm /var/www/.bashrc /var/www/.cache
+
+cd /var/www/html/
+rm data -rf
+rm config/config.php
+su www-data -c "git reset --hard"
+BRANCH="$1" /usr/local/bin/initnc.sh
diff --git a/.github/workflows/garm.yml b/.github/workflows/garm.yml
new file mode 100644
index 000000000000..21117cb9ae79
--- /dev/null
+++ b/.github/workflows/garm.yml
@@ -0,0 +1,118 @@
+# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
+# SPDX-License-Identifier: MIT
+
+name: "Garm"
+
+on:
+ pull_request:
+ branches: [ master, stable-* ]
+
+permissions:
+ contents: read
+ pull-requests: write
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ runs-on: ubuntu-24.04
+ services:
+ server:
+ image: ${{ matrix.server == 'stable22' && 'ghcr.io/nextcloud/continuous-integration-shallow-server-php8.0:1' || 'ghcr.io/nextcloud/continuous-integration-shallow-server-php8.2:1' }}
+ options: --name server
+ ports:
+ - 80:80
+ strategy:
+ fail-fast: false
+ matrix:
+ server: [ stable22, stable33, master ]
+ api-level: [ 29 ] # [ 21, 36 ]
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+
+ - name: configure server
+ run: |
+ sudo apt-get update && sudo apt-get install unzip cpu-checker qemu-kvm --yes
+ sudo chmod 666 /dev/kvm
+ docker cp .github/workflows/configServer.sh server:/tmp/
+ docker exec server chmod +x /tmp/configServer.sh
+ docker exec server /tmp/configServer.sh ${{ matrix.server }}
+ docker cp .github/workflows/configNC.sh server:/tmp/
+ docker exec server chmod +x /tmp/configNC.sh
+ docker exec --user www-data server /tmp/configNC.sh ${{ matrix.server }}
+ docker exec server /usr/local/bin/run.sh
+
+ - name: set up JDK 21
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
+ with:
+ distribution: "temurin"
+ java-version: 21
+
+ - name: Enable KVM group perms
+ run: |
+ echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
+ sudo udevadm control --reload-rules
+ sudo udevadm trigger --name-match=kvm
+
+
+ - name: create AVD and generate snapshot for caching
+ uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
+ with:
+ api-level: ${{ matrix.api-level }}
+ force-avd-creation: false
+ arch: x86
+ sdcard-path-or-size: 100M
+ target: google_apis
+ emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -skin 500x833
+ script: echo "Generated AVD snapshot for caching."
+
+ - name: Configure gradle daemon
+ run: |
+ mkdir -p $HOME/.gradle
+ echo "org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g" >> $HOME/.gradle/gradle.properties
+ echo "org.gradle.caching=true" >> $HOME/.gradle/gradle.properties
+ echo "org.gradle.parallel=true" >> $HOME/.gradle/gradle.properties
+ echo "org.gradle.configureondemand=true" >> $HOME/.gradle/gradle.properties
+
+ - name: Build gplay
+ run: |
+ sdkmanager "cmake;4.1.2"
+ sed -i s#https://server#http://10.0.2.2# gradle.properties
+ sed -i s'#false#true#'g app/src/main/res/values/setup.xml
+ ./gradlew assembleDebug
+
+ - name: wait and ping server
+ run: |
+ while ! curl http://localhost/status.php 2>&1 | grep installed; do
+ echo "wait…"
+ sleep 5
+ done
+
+ - name: gplay
+ uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
+ with:
+ api-level: ${{ matrix.api-level }}
+ force-avd-creation: false
+ arch: x86
+ sdcard-path-or-size: 100M
+ target: google_apis
+ emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -skin 500x833
+ script: scripts/runCombinedTest.sh ${{github.event.number}} ${{ secrets.LOG_USERNAME }}" "${{ secrets.LOG_PASSWORD }}" ${{github.event.number}} ${{ matrix.server }}
+
+ - name: upload failing results
+ if: ${{ failure() }}
+ env:
+ LOG_USERNAME: ${{ secrets.LOG_USERNAME }}
+ LOG_PASSWORD: ${{ secrets.LOG_PASSWORD }}
+ GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
+ GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
+ run: scripts/uploadReport.sh "${{ secrets.LOG_USERNAME }}" "${{ secrets.LOG_PASSWORD }}" ${{github.event.number}} ${{ matrix.server }} "IT" ${{github.event.number}} "${{ secrets.GIT_USERNAME }}" "${{ secrets.GIT_TOKEN }}"
+ - name: Archive Espresso results
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
+ if: ${{ always() }}
+ with:
+ name: Report-${{ matrix.server }}-${{ matrix.api-level }}
+ path: app/build/reports
+ retention-days: 4
diff --git a/scripts/runCombinedTest.sh b/scripts/runCombinedTest.sh
index e588938f68df..a907f9a07ce7 100755
--- a/scripts/runCombinedTest.sh
+++ b/scripts/runCombinedTest.sh
@@ -8,6 +8,7 @@ DRONE_PULL_REQUEST=$1
LOG_USERNAME=$2
LOG_PASSWORD=$3
DRONE_BUILD_NUMBER=$4
+BRANCH=$5
function upload_logcat() {
log_filename="${DRONE_PULL_REQUEST}_logcat.txt.xz"
@@ -19,7 +20,7 @@ function upload_logcat() {
echo >&2 "Uploaded logcat to https://www.kaminsky.me/nc-dev/android-logcat/$log_filename"
}
-scripts/deleteOldComments.sh "master" "IT" "$DRONE_PULL_REQUEST"
+scripts/deleteOldComments.sh "$BRANCH" "IT" "$DRONE_PULL_REQUEST"
./gradlew assembleGplayDebugAndroidTest
@@ -42,7 +43,7 @@ kill $LOGCAT_PID
if [ ! $stat -eq 0 ]; then
upload_logcat
- bash scripts/uploadReport.sh "$LOG_USERNAME" "$LOG_PASSWORD" "$DRONE_BUILD_NUMBER" "master" "IT" "$DRONE_PULL_REQUEST"
+ bash scripts/uploadReport.sh "$LOG_USERNAME" "$LOG_PASSWORD" "$DRONE_BUILD_NUMBER" "$BRANCH" "IT" "$DRONE_PULL_REQUEST"
fi
curl -Os https://uploader.codecov.io/latest/linux/codecov