From b9d6b8398a3ae9c423cf005796241907d899a1c5 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 8 Apr 2026 12:26:10 +0930 Subject: [PATCH 01/44] chore(ci): run acceptance tests in GitHub workflows --- .github/workflows/acceptance.yml | 250 +++++++++++++++++++++++++++++++ .github/workflows/main.yml | 20 +++ Makefile | 5 + 3 files changed, 275 insertions(+) create mode 100644 .github/workflows/acceptance.yml diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml new file mode 100644 index 00000000..460ff685 --- /dev/null +++ b/.github/workflows/acceptance.yml @@ -0,0 +1,250 @@ +on: + workflow_call: + inputs: + app-name: + description: 'Name of the app' + required: true + type: string + php-versions: + description: JSON array of PHP versions + required: false + type: string + default: '["8.3"]' + databases: + description: JSON array of databases + required: false + type: string + default: '["mariadb:10.6"]' + test-suites: + description: JSON array of acceptance test suite names + required: false + type: string + default: '[""]' + app-repository: + description: 'Repository of the app (optional, defaults to the current repository)' + required: false + type: string + default: '' + do-api-tests: + description: 'Whether to run API acceptance tests' + required: false + type: boolean + default: false + do-cli-tests: + description: 'Whether to run CLI acceptance tests' + required: false + type: boolean + default: false + do-webui-tests: + description: 'Whether to run WebUI acceptance tests' + required: false + type: boolean + default: false + additional-app: + description: 'Additional app to enable for testing' + required: false + type: string + default: '' + additional-app-github-token: + description: 'GitHub PAT to access the additional app repo - required if private' + required: false + type: string + default: '' + additional-packages: + description: Additional Linux packages to be installed - space separated + required: false + type: string + default: '' + +jobs: + acceptance: + name: Acceptance tests + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: ${{ fromJSON(inputs.php-versions) }} + database: ${{ fromJSON(inputs.databases) }} + suite: ${{ fromJSON(inputs.test-suites) }} + + services: + mysql: + image: >- + ${{ (startsWith(matrix.database,'mysql:') || startsWith(matrix.database,'mariadb:')) && matrix.database || '' }} + env: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: owncloud + MYSQL_USER: owncloud + MYSQL_PASSWORD: owncloud + options: >- + --health-cmd="mysqladmin ping -u root -ppassword" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + ports: + - 3306:3306 + postgres: + image: ${{ startsWith(matrix.database,'postgres:') && matrix.database || '' }} + env: + POSTGRES_DB: owncloud + POSTGRES_USER: owncloud + POSTGRES_PASSWORD: owncloud + ports: + - 5432:5432 + + steps: + - name: Clone owncloud/core + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: owncloud/core + + - name: Checkout apps/${{ inputs.app-name }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: apps/${{ inputs.app-name }} + repository: ${{ inputs.app-repository }} + + - name: Checkout apps/${{ inputs.additional-app }} + if: ${{ inputs.additional-app != '' }} + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + path: apps/${{ inputs.additional-app }} + repository: owncloud/${{ inputs.additional-app }} + token: ${{ inputs.additional-app-github-token || github.token }} + + - name: Install Linux packages + if: ${{ inputs.additional-packages }} + run: | + sudo apt-get install ${{ inputs.additional-packages }} + + - name: Setup PHP + uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.37.0 + with: + php-version: ${{ matrix.php }} + extensions: apcu, ctype, curl, exif, fileinfo, gd, iconv, imagick, intl, json, mbstring, memcached, pdo, posix, simplexml, xml, zip, smbclient, ldap, krb5 + ini-values: "memory_limit=1024M" + env: + fail-fast: true + KRB5_LINUX_LIBS: libkrb5-dev + + - name: Install Apache + run: | + sudo apt-get update + sudo apt-get install -y libapache2-mod-php${{ matrix.php }} + sudo apt-get install -y apache2 + sudo a2enmod rewrite + sudo service apache2 restart + + - name: Check Apache server status + run: curl http://localhost:80 + + - name: Install owncloud/core + run: | + docker run -v /var/www/html:/var/www/html \ + --network=host \ + -e PLUGIN_VERSION=${VERSION} \ + -e PLUGIN_CORE_PATH=${CORE_PATH} \ + -e PLUGIN_DB_TYPE=${DB_TYPE} \ + -e PLUGIN_DB_NAME=${DB_NAME} \ + -e PLUGIN_DB_HOST=${DB_HOST} \ + -e PLUGIN_DB_USERNAME=${DB_USERNAME} \ + -e PLUGIN_DB_PASSWORD=${DB_PASSWORD} \ + owncloudci/core:php83 + env: + VERSION: 'daily-master-qa' + CORE_PATH: '/var/www/html/server' + DB_TYPE: 'mysql' + DB_NAME: 'owncloud' + DB_HOST: '127.0.0.1' + DB_USERNAME: 'owncloud' + DB_PASSWORD: 'owncloud' + + - name: Setup permissions for the backend of the Apache server + run: | + sudo chown -R www-data:www-data /var/www/html/server + sudo chmod -R 777 /var/www/html/server + + - name: Selenium + if: ${{ inputs.do-webui-tests }} + run: | + docker run -d --rm --network host \ + --name selenium-chrome \ + selenium/standalone-chrome-debug:3.141.59-oxygen + + - name: Make local core + run: | + make + + - name: Setup app ${{ inputs.app-name }} + run: | + cd apps/${{ inputs.app-name }} + make ci + sudo -u www-data mkdir /var/www/html/server/apps/${{ inputs.app-name }} + sudo -u www-data cp -r * /var/www/html/server/apps/${{ inputs.app-name }} + cd /var/www/html/server + sudo -u www-data php occ a:e ${{ inputs.app-name }} + + - name: Setup additional app ${{ inputs.app-name }} + if: ${{ inputs.additional-app != '' }} + run: | + cd apps/${{ inputs.additional-app }} + make ci + sudo -u www-data mkdir /var/www/html/server/apps/${{ inputs.additional-app }} + sudo -u www-data cp -r * /var/www/html/server/apps/${{ inputs.additional-app }} + cd /var/www/html/server + sudo -u www-data php occ a:e ${{ inputs.additional-app }} + + - name: Setup owncloud server settings + run: | + cd /var/www/html/server + sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost + sudo -u www-data php occ log:manage --level 2 + sudo -u www-data php occ config:system:set csrf.disabled --value=true + sudo -u www-data php occ a:e testing + sudo -u www-data php occ a:l -e + + - name: Check access to status + run: curl http://localhost:80/server/status.php + + - name: Install libxml2-utils to get xmllint used by acceptance test script + run: sudo apt install -y libxml2-utils + + - name: Run API acceptance tests + if: ${{ inputs.do-api-tests }} + env: + TEST_SERVER_URL: "http://localhost/server" + BEHAT_SUITE: ${{ matrix.suite }} + BEHAT_FILTER_TAGS: "" + run: | + cd apps/${{ inputs.app-name }} + make test-acceptance-api + + - name: Run CLI acceptance tests + if: ${{ inputs.do-cli-tests }} + env: + TEST_SERVER_URL: "http://localhost/server" + BEHAT_SUITE: ${{ matrix.suite }} + BEHAT_FILTER_TAGS: "" + run: | + cd apps/${{ inputs.app-name }} + make test-acceptance-cli + + - name: Run WebUI acceptance tests + if: ${{ inputs.do-webui-tests }} + env: + TEST_SERVER_URL: "http://localhost/server" + BROWSER: "chrome" + SELENIUM_HOST: "localhost" + SELENIUM_PORT: "4444" + BEHAT_SUITE: ${{ matrix.suite }} + BEHAT_FILTER_TAGS: "" + run: | + cd apps/${{ inputs.app-name }} + make test-acceptance-webui + + - name: Display log file + if: failure() + run: | + sudo -u www-data ls -l /var/www/html/server + sudo -u www-data ls -l /var/www/html/server/data + sudo -u www-data cat /var/www/html/server/data/owncloud.log diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e351d3b9..65962182 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,3 +64,23 @@ jobs: with: app-name: ${{ needs.get-vars.outputs.app-name }} php-version: '8.3' + + acceptance-api: + name: API acceptance tests + needs: + - get-vars + uses: ./.github/workflows/acceptance.yml + with: + app-name: ${{ needs.get-vars.outputs.app-name }} + do-api-tests: true + + acceptance-webui: + name: WebUI acceptance tests + needs: + - get-vars + uses: ./.github/workflows/acceptance.yml + with: + app-name: ${{ needs.get-vars.outputs.app-name }} + do-webui-tests: true + test-suites: "['webUIActivityComments', 'webUIActivityCreateUpdate', 'webUIActivityDeleteRestore', 'webUIActivityFileMoveAndRename', 'webUIActivitySharingInternal', 'webUIActivityTags']" + diff --git a/Makefile b/Makefile index 5b2f2f90..2216799f 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,11 @@ ifneq (,$(wildcard $(CURDIR)/js/package.json)) make npm endif +# Installs dependencies and does any build actions needed for the app to run in CI +.PHONY: ci +ci: vendor + @echo dependencies and build actions for CI are completed + # Installs and updates the composer dependencies. .PHONY: composer composer: From 1addb013e7847b4e32e0d85539db5a24db47e51d Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 8 Apr 2026 13:23:15 +0930 Subject: [PATCH 02/44] chore(ci): run core server in Apache top-level folder --- .github/workflows/acceptance.yml | 39 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 460ff685..e3539425 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -138,6 +138,9 @@ jobs: - name: Check Apache server status run: curl http://localhost:80 + - name: Delete default Apache server html files + run: sudo rm -Rf /var/www/html/* + - name: Install owncloud/core run: | docker run -v /var/www/html:/var/www/html \ @@ -152,7 +155,7 @@ jobs: owncloudci/core:php83 env: VERSION: 'daily-master-qa' - CORE_PATH: '/var/www/html/server' + CORE_PATH: '/var/www/html' DB_TYPE: 'mysql' DB_NAME: 'owncloud' DB_HOST: '127.0.0.1' @@ -161,8 +164,8 @@ jobs: - name: Setup permissions for the backend of the Apache server run: | - sudo chown -R www-data:www-data /var/www/html/server - sudo chmod -R 777 /var/www/html/server + sudo chown -R www-data:www-data /var/www/html + sudo chmod -R 777 /var/www/html - name: Selenium if: ${{ inputs.do-webui-tests }} @@ -179,24 +182,24 @@ jobs: run: | cd apps/${{ inputs.app-name }} make ci - sudo -u www-data mkdir /var/www/html/server/apps/${{ inputs.app-name }} - sudo -u www-data cp -r * /var/www/html/server/apps/${{ inputs.app-name }} - cd /var/www/html/server + sudo -u www-data mkdir /var/www/html/apps/${{ inputs.app-name }} + sudo -u www-data cp -r * /var/www/html/apps/${{ inputs.app-name }} + cd /var/www/html sudo -u www-data php occ a:e ${{ inputs.app-name }} - - name: Setup additional app ${{ inputs.app-name }} + - name: Setup additional app ${{ inputs.additional-app }} if: ${{ inputs.additional-app != '' }} run: | cd apps/${{ inputs.additional-app }} make ci - sudo -u www-data mkdir /var/www/html/server/apps/${{ inputs.additional-app }} - sudo -u www-data cp -r * /var/www/html/server/apps/${{ inputs.additional-app }} - cd /var/www/html/server + sudo -u www-data mkdir /var/www/html/apps/${{ inputs.additional-app }} + sudo -u www-data cp -r * /var/www/html/apps/${{ inputs.additional-app }} + cd /var/www/html sudo -u www-data php occ a:e ${{ inputs.additional-app }} - name: Setup owncloud server settings run: | - cd /var/www/html/server + cd /var/www/html sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -204,7 +207,7 @@ jobs: sudo -u www-data php occ a:l -e - name: Check access to status - run: curl http://localhost:80/server/status.php + run: curl http://localhost:80/status.php - name: Install libxml2-utils to get xmllint used by acceptance test script run: sudo apt install -y libxml2-utils @@ -212,7 +215,7 @@ jobs: - name: Run API acceptance tests if: ${{ inputs.do-api-tests }} env: - TEST_SERVER_URL: "http://localhost/server" + TEST_SERVER_URL: "http://localhost" BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | @@ -222,7 +225,7 @@ jobs: - name: Run CLI acceptance tests if: ${{ inputs.do-cli-tests }} env: - TEST_SERVER_URL: "http://localhost/server" + TEST_SERVER_URL: "http://localhost" BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | @@ -232,7 +235,7 @@ jobs: - name: Run WebUI acceptance tests if: ${{ inputs.do-webui-tests }} env: - TEST_SERVER_URL: "http://localhost/server" + TEST_SERVER_URL: "http://localhost" BROWSER: "chrome" SELENIUM_HOST: "localhost" SELENIUM_PORT: "4444" @@ -245,6 +248,6 @@ jobs: - name: Display log file if: failure() run: | - sudo -u www-data ls -l /var/www/html/server - sudo -u www-data ls -l /var/www/html/server/data - sudo -u www-data cat /var/www/html/server/data/owncloud.log + sudo -u www-data ls -l /var/www/html + sudo -u www-data ls -l /var/www/html/data + sudo -u www-data cat /var/www/html/data/owncloud.log From f85a93b79444d7c488714e816e612b4ba4f35b91 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 8 Apr 2026 14:43:50 +0930 Subject: [PATCH 03/44] chore(ci): allowing specifying server-root of the core server installation --- .github/workflows/acceptance.yml | 41 ++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index e3539425..3563ca61 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -25,6 +25,11 @@ on: required: false type: string default: '' + server-root: + description: 'The folder under the Apache root in which to install owncloud core' + required: false + type: string + default: '' do-api-tests: description: 'Whether to run API acceptance tests' required: false @@ -143,7 +148,7 @@ jobs: - name: Install owncloud/core run: | - docker run -v /var/www/html:/var/www/html \ + docker run -v /var/www/html${{ inputs.server-root }}:/var/www/html${{ inputs.server-root }} \ --network=host \ -e PLUGIN_VERSION=${VERSION} \ -e PLUGIN_CORE_PATH=${CORE_PATH} \ @@ -155,7 +160,7 @@ jobs: owncloudci/core:php83 env: VERSION: 'daily-master-qa' - CORE_PATH: '/var/www/html' + CORE_PATH: /var/www/html${{ inputs.server-root }} DB_TYPE: 'mysql' DB_NAME: 'owncloud' DB_HOST: '127.0.0.1' @@ -164,8 +169,8 @@ jobs: - name: Setup permissions for the backend of the Apache server run: | - sudo chown -R www-data:www-data /var/www/html - sudo chmod -R 777 /var/www/html + sudo chown -R www-data:www-data /var/www/html${{ inputs.server-root }} + sudo chmod -R 777 /var/www/html${{ inputs.server-root }} - name: Selenium if: ${{ inputs.do-webui-tests }} @@ -182,9 +187,9 @@ jobs: run: | cd apps/${{ inputs.app-name }} make ci - sudo -u www-data mkdir /var/www/html/apps/${{ inputs.app-name }} - sudo -u www-data cp -r * /var/www/html/apps/${{ inputs.app-name }} - cd /var/www/html + sudo -u www-data mkdir /var/www/html${{ inputs.server-root }}/apps/${{ inputs.app-name }} + sudo -u www-data cp -r * /var/www/html${{ inputs.server-root }}/apps/${{ inputs.app-name }} + cd /var/www/html${{ inputs.server-root }} sudo -u www-data php occ a:e ${{ inputs.app-name }} - name: Setup additional app ${{ inputs.additional-app }} @@ -192,14 +197,14 @@ jobs: run: | cd apps/${{ inputs.additional-app }} make ci - sudo -u www-data mkdir /var/www/html/apps/${{ inputs.additional-app }} - sudo -u www-data cp -r * /var/www/html/apps/${{ inputs.additional-app }} - cd /var/www/html + sudo -u www-data mkdir /var/www/html${{ inputs.server-root }}/apps/${{ inputs.additional-app }} + sudo -u www-data cp -r * /var/www/html${{ inputs.server-root }}/apps/${{ inputs.additional-app }} + cd /var/www/html${{ inputs.server-root }} sudo -u www-data php occ a:e ${{ inputs.additional-app }} - name: Setup owncloud server settings run: | - cd /var/www/html + cd /var/www/html${{ inputs.server-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -207,7 +212,7 @@ jobs: sudo -u www-data php occ a:l -e - name: Check access to status - run: curl http://localhost:80/status.php + run: curl http://localhost:80${{ inputs.server-root }}/status.php - name: Install libxml2-utils to get xmllint used by acceptance test script run: sudo apt install -y libxml2-utils @@ -215,7 +220,7 @@ jobs: - name: Run API acceptance tests if: ${{ inputs.do-api-tests }} env: - TEST_SERVER_URL: "http://localhost" + TEST_SERVER_URL: http://localhost${{ inputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | @@ -225,7 +230,7 @@ jobs: - name: Run CLI acceptance tests if: ${{ inputs.do-cli-tests }} env: - TEST_SERVER_URL: "http://localhost" + TEST_SERVER_URL: http://localhost${{ inputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | @@ -235,7 +240,7 @@ jobs: - name: Run WebUI acceptance tests if: ${{ inputs.do-webui-tests }} env: - TEST_SERVER_URL: "http://localhost" + TEST_SERVER_URL: http://localhost${{ inputs.server-root }} BROWSER: "chrome" SELENIUM_HOST: "localhost" SELENIUM_PORT: "4444" @@ -248,6 +253,6 @@ jobs: - name: Display log file if: failure() run: | - sudo -u www-data ls -l /var/www/html - sudo -u www-data ls -l /var/www/html/data - sudo -u www-data cat /var/www/html/data/owncloud.log + sudo -u www-data ls -l /var/www/html${{ inputs.server-root }} + sudo -u www-data ls -l /var/www/html${{ inputs.server-root }}/data + sudo -u www-data cat /var/www/html${{ inputs.server-root }}/data/owncloud.log From 3e15372638985049888372a1ca2a52722c5d0f57 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 8 Apr 2026 18:10:00 +0930 Subject: [PATCH 04/44] chore(ci): support a federated server in acceptance tests --- .github/workflows/acceptance.yml | 59 ++++++++++++++++++++++++++++++++ .github/workflows/main.yml | 2 ++ 2 files changed, 61 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 3563ca61..60d58331 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -30,6 +30,11 @@ on: required: false type: string default: '' + federated-root: + description: 'The folder under the Apache root in which to install a federated server' + required: false + type: string + default: '' do-api-tests: description: 'Whether to run API acceptance tests' required: false @@ -167,11 +172,45 @@ jobs: DB_USERNAME: 'owncloud' DB_PASSWORD: 'owncloud' + - name: Create database for federated server + if: ${{ inputs.federated-root != '' }} + run: | + mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "CREATE DATABASE federated;" + mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "GRANT ALL PRIVILEGES ON federated.* TO 'owncloud'@'%';" + + - name: Install federated owncloud/core + if: ${{ inputs.federated-root != '' }} + run: | + docker run -v /var/www/html${{ inputs.federated-root }}:/var/www/html${{ inputs.federated-root }} \ + --network=host \ + -e PLUGIN_VERSION=${VERSION} \ + -e PLUGIN_CORE_PATH=${CORE_PATH} \ + -e PLUGIN_DB_TYPE=${DB_TYPE} \ + -e PLUGIN_DB_NAME=${DB_NAME} \ + -e PLUGIN_DB_HOST=${DB_HOST} \ + -e PLUGIN_DB_USERNAME=${DB_USERNAME} \ + -e PLUGIN_DB_PASSWORD=${DB_PASSWORD} \ + owncloudci/core:php83 + env: + VERSION: 'daily-master-qa' + CORE_PATH: /var/www/html${{ inputs.federated-root }} + DB_TYPE: 'mysql' + DB_NAME: federated + DB_HOST: '127.0.0.1' + DB_USERNAME: 'owncloud' + DB_PASSWORD: 'owncloud' + - name: Setup permissions for the backend of the Apache server run: | sudo chown -R www-data:www-data /var/www/html${{ inputs.server-root }} sudo chmod -R 777 /var/www/html${{ inputs.server-root }} + - name: Setup permissions for the backend of the federated Apache server + if: ${{ inputs.federated-root != '' }} + run: | + sudo chown -R www-data:www-data /var/www/html${{ inputs.federated-root }} + sudo chmod -R 777 /var/www/html${{ inputs.federated-root }} + - name: Selenium if: ${{ inputs.do-webui-tests }} run: | @@ -210,10 +249,30 @@ jobs: sudo -u www-data php occ config:system:set csrf.disabled --value=true sudo -u www-data php occ a:e testing sudo -u www-data php occ a:l -e + sudo -u www-data php occ config:list + + - name: Setup owncloud federated server settings + if: ${{ inputs.federated-root != '' }} + run: | + cd /var/www/html${{ inputs.federated-root }} + sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost + sudo -u www-data php occ log:manage --level 2 + sudo -u www-data php occ config:system:set csrf.disabled --value=true + sudo -u www-data php occ a:e testing + sudo -u www-data php occ a:l -e + sudo -u www-data php occ config:list - name: Check access to status run: curl http://localhost:80${{ inputs.server-root }}/status.php + - name: Check access to federated status + if: ${{ inputs.federated-root != '' }} + run: curl http://localhost:80${{ inputs.federated-root }}/status.php + + - name: Define environment variable pointing to the federated server + if: ${{ inputs.federated-root != '' }} + run: echo "TEST_SERVER_FED_URL=http://localhost${{ inputs.federated-root }}" >> $GITHUB_ENV + - name: Install libxml2-utils to get xmllint used by acceptance test script run: sudo apt install -y libxml2-utils diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 65962182..9e1002ca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -73,6 +73,8 @@ jobs: with: app-name: ${{ needs.get-vars.outputs.app-name }} do-api-tests: true + server-root: '/server' + federated-root: '/federated' acceptance-webui: name: WebUI acceptance tests From 394a015122c07a3b4de7b7b598f86fa5febb519c Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 9 Apr 2026 13:46:57 +0930 Subject: [PATCH 05/44] test: enhance error processing for JSON returned in sendActivityGetRequest --- .../features/bootstrap/ActivityContext.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/features/bootstrap/ActivityContext.php b/tests/acceptance/features/bootstrap/ActivityContext.php index e51ef4c9..5a06a4bc 100644 --- a/tests/acceptance/features/bootstrap/ActivityContext.php +++ b/tests/acceptance/features/bootstrap/ActivityContext.php @@ -207,6 +207,7 @@ public function getActivitiesForFileAsUser( * * @return array * @throws GuzzleException + * @throws Exception */ private function sendActivityGetRequest(string $url, string $user):array { $fullUrl = $this->featureContext->getBaseUrl() . $url; @@ -220,10 +221,23 @@ private function sendActivityGetRequest(string $url, string $user):array { 200, $response->getStatusCode() ); - return \json_decode( - $response->getBody()->getContents(), + $contents = $response->getBody()->getContents(); + if ($contents === "") { + // If the empty string is returned then there are no activity entries. + // Return an empty array because this function should always return an array. + return []; + } + + $decoded_json = \json_decode( + $contents, true ); + if ($decoded_json === null) { + throw new Exception( + "JSON returned by sendActivityGetRequest to $fullUrl is not valid: '$contents'" + ); + } + return $decoded_json; } /** From f05df8b025329123dd38152ef59a905820fa122a Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 9 Apr 2026 14:48:52 +0930 Subject: [PATCH 06/44] chore(ci): setup app-under-test on federated server --- .github/workflows/acceptance.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 60d58331..d8ff74b3 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -231,6 +231,15 @@ jobs: cd /var/www/html${{ inputs.server-root }} sudo -u www-data php occ a:e ${{ inputs.app-name }} + - name: Setup app ${{ inputs.app-name }} on federated server + if: ${{ inputs.federated-root != '' }} + run: | + cd apps/${{ inputs.app-name }} + sudo -u www-data mkdir /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.app-name }} + sudo -u www-data cp -r * /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.app-name }} + cd /var/www/html${{ inputs.federated-root }} + sudo -u www-data php occ a:e ${{ inputs.app-name }} + - name: Setup additional app ${{ inputs.additional-app }} if: ${{ inputs.additional-app != '' }} run: | @@ -241,6 +250,15 @@ jobs: cd /var/www/html${{ inputs.server-root }} sudo -u www-data php occ a:e ${{ inputs.additional-app }} + - name: Setup additional app ${{ inputs.additional-app }} on federated server + if: ${{ inputs.additional-app != '' && inputs.federated-root != '' }} + run: | + cd apps/${{ inputs.additional-app }} + sudo -u www-data mkdir /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.additional-app }} + sudo -u www-data cp -r * /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.additional-app }} + cd /var/www/html${{ inputs.federated-root }} + sudo -u www-data php occ a:e ${{ inputs.additional-app }} + - name: Setup owncloud server settings run: | cd /var/www/html${{ inputs.server-root }} From e51378008ebfe5dbb3389e0024db9f15305f5f54 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 9 Apr 2026 19:00:47 +0930 Subject: [PATCH 07/44] chore: see what is returned by the login endpoint --- .github/workflows/acceptance.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index d8ff74b3..5905c6b0 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -283,6 +283,9 @@ jobs: - name: Check access to status run: curl http://localhost:80${{ inputs.server-root }}/status.php + - name: Check access to login endpoint + run: curl http://localhost:80${{ inputs.server-root }}/login + - name: Check access to federated status if: ${{ inputs.federated-root != '' }} run: curl http://localhost:80${{ inputs.federated-root }}/status.php From e7218feb920004a6ccb76e98f4ed92c125486b99 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 9 Apr 2026 19:38:06 +0930 Subject: [PATCH 08/44] chore(ci): Check content of server files --- .github/workflows/acceptance.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 5905c6b0..d91b4089 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -286,6 +286,11 @@ jobs: - name: Check access to login endpoint run: curl http://localhost:80${{ inputs.server-root }}/login + - name: Check content of server files + run: | + sudo -u www-data cat /var/www/html${{ inputs.server-root }}/.htaccess + sudo -u www-data cat /var/www/html${{ inputs.server-root }}/config/config.php + - name: Check access to federated status if: ${{ inputs.federated-root != '' }} run: curl http://localhost:80${{ inputs.federated-root }}/status.php From 75cd1223ac89628adc57176d6d3eaf592faed544 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 9 Apr 2026 20:19:25 +0930 Subject: [PATCH 09/44] chore: dispaly more about server settings --- .github/workflows/acceptance.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index d91b4089..b1ff661d 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -290,6 +290,10 @@ jobs: run: | sudo -u www-data cat /var/www/html${{ inputs.server-root }}/.htaccess sudo -u www-data cat /var/www/html${{ inputs.server-root }}/config/config.php + sudo cat /etc/apache2/apache2.conf + sudo cat /etc/apache2/ports.conf + sudo ls -l /etc/apache2/sites-available + sudo ls -l /etc/apache2/sites-enabled - name: Check access to federated status if: ${{ inputs.federated-root != '' }} From 9740b441bae1e65c167d168e24bf69022aa43b01 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 9 Apr 2026 22:24:52 +0930 Subject: [PATCH 10/44] chore: display 000-default.conf --- .github/workflows/acceptance.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index b1ff661d..0db53919 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -294,6 +294,7 @@ jobs: sudo cat /etc/apache2/ports.conf sudo ls -l /etc/apache2/sites-available sudo ls -l /etc/apache2/sites-enabled + sudo cat /etc/apache2/sites-enabled/000-default.conf - name: Check access to federated status if: ${{ inputs.federated-root != '' }} From 1237b562d5300d2d86cd3eddab1fb5f48c17d034 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 10 Apr 2026 13:27:51 +0930 Subject: [PATCH 11/44] chore(ci): provide Apache site configs for server and federated This allows settings like: AllowOverride All to happen. The final way to do this needs to be thought about, bwecause we want to put the potential site conf files somewhere that will be a common place for all app CI to get them. And there should be flexibility about the names "server" and "federated". --- .github/workflows/acceptance.yml | 13 +++++++++++++ tests/acceptance/federated.conf | 12 ++++++++++++ tests/acceptance/server.conf | 12 ++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/acceptance/federated.conf create mode 100644 tests/acceptance/server.conf diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 0db53919..a15e22f5 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -145,6 +145,19 @@ jobs: sudo a2enmod rewrite sudo service apache2 restart + - name: Configure Apache for server + run: | + sudo cp apps/${{ inputs.app-name }}/tests/acceptance/server.conf /etc/apache2/sites-available + sudo a2ensite server + sudo service apache2 restart + + - name: Configure Apache for federated + if: ${{ inputs.federated-root != '' }} + run: | + sudo cp apps/${{ inputs.app-name }}/tests/acceptance/federated.conf /etc/apache2/sites-available + sudo a2ensite federated + sudo service apache2 restart + - name: Check Apache server status run: curl http://localhost:80 diff --git a/tests/acceptance/federated.conf b/tests/acceptance/federated.conf new file mode 100644 index 00000000..806e5c30 --- /dev/null +++ b/tests/acceptance/federated.conf @@ -0,0 +1,12 @@ + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + ErrorLog /dev/stdout + + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Require all granted + + diff --git a/tests/acceptance/server.conf b/tests/acceptance/server.conf new file mode 100644 index 00000000..27579d7c --- /dev/null +++ b/tests/acceptance/server.conf @@ -0,0 +1,12 @@ + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + ErrorLog /dev/stdout + + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Require all granted + + From 9e264489e7707ff44688ca2c454bdf5b6e888d3c Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 10 Apr 2026 13:43:19 +0930 Subject: [PATCH 12/44] chore(ci): Reload Apache to get new settings --- .github/workflows/acceptance.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index a15e22f5..fd3c4406 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -149,14 +149,16 @@ jobs: run: | sudo cp apps/${{ inputs.app-name }}/tests/acceptance/server.conf /etc/apache2/sites-available sudo a2ensite server - sudo service apache2 restart - name: Configure Apache for federated if: ${{ inputs.federated-root != '' }} run: | sudo cp apps/${{ inputs.app-name }}/tests/acceptance/federated.conf /etc/apache2/sites-available sudo a2ensite federated - sudo service apache2 restart + + - name: Reload Apache to get new settings + run: | + sudo systemctl reload apache2 - name: Check Apache server status run: curl http://localhost:80 From 71265b430f9a1a427bca60168cb13ef1c691545d Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 10 Apr 2026 13:58:33 +0930 Subject: [PATCH 13/44] chore(ci): fixup Apache configs --- .github/workflows/acceptance.yml | 1 + tests/acceptance/federated.conf | 2 +- tests/acceptance/server.conf | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index fd3c4406..1574a603 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -310,6 +310,7 @@ jobs: sudo ls -l /etc/apache2/sites-available sudo ls -l /etc/apache2/sites-enabled sudo cat /etc/apache2/sites-enabled/000-default.conf + sudo cat /etc/apache2/sites-enabled/server.conf - name: Check access to federated status if: ${{ inputs.federated-root != '' }} diff --git a/tests/acceptance/federated.conf b/tests/acceptance/federated.conf index 806e5c30..e22e2836 100644 --- a/tests/acceptance/federated.conf +++ b/tests/acceptance/federated.conf @@ -1,6 +1,6 @@ ServerAdmin webmaster@localhost - DocumentRoot /var/www/html + DocumentRoot /var/www/html/federated ErrorLog /dev/stdout diff --git a/tests/acceptance/server.conf b/tests/acceptance/server.conf index 27579d7c..2a377c30 100644 --- a/tests/acceptance/server.conf +++ b/tests/acceptance/server.conf @@ -1,6 +1,6 @@ ServerAdmin webmaster@localhost - DocumentRoot /var/www/html + DocumentRoot /var/www/html/server ErrorLog /dev/stdout From 3922d95dafc03110f6880b5aaff83192b642e9ee Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 10 Apr 2026 22:50:07 +0930 Subject: [PATCH 14/44] test: make imtermittent activity message step more reliable When deleting files, the activity message about the delete is not always at gthe top of the list. So the existing check sometimes fails, but passes when the test scenario is run again. So check for the expected activity message anywhere in the list, it is not essential that it be right at the top. --- .../bootstrap/WebUIActivityContext.php | 23 +++++++++++++++++++ .../webUIActivityDeleteRestore/delete.feature | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/features/bootstrap/WebUIActivityContext.php b/tests/acceptance/features/bootstrap/WebUIActivityContext.php index ae57fc6d..50c73e92 100644 --- a/tests/acceptance/features/bootstrap/WebUIActivityContext.php +++ b/tests/acceptance/features/bootstrap/WebUIActivityContext.php @@ -246,6 +246,29 @@ public function theActivityNumberShouldHaveMessageInTheActivityPage( PHPUnit\Framework\Assert::assertEquals($message, $latestActivityMessage); } + /** + * @Then the activity page/tab should have message :message + * + * @param string $message + * + * @return void + */ + public function theActivityPageShouldHaveMessage( + string $message + ): void { + $message = $this->featureContext->substituteInLineCodes($message); + $activityMessages = $this->activityPage->getAllActivityMessageLists(); + $found = false; + $messagesLine = ''; + foreach ($activityMessages as $activityMessage) { + $messagesLine .= $activityMessage . "\n"; + if ($message === $activityMessage) { + $found = true; + } + } + PHPUnit\Framework\Assert::assertTrue($found, "Message $message not found in\n $messagesLine activity messages"); + } + /** * @Then the activity number :index should have a message saying that you have shared file/folder :entry with user :user * diff --git a/tests/acceptance/features/webUIActivityDeleteRestore/delete.feature b/tests/acceptance/features/webUIActivityDeleteRestore/delete.feature index eb74ff80..79d7596a 100644 --- a/tests/acceptance/features/webUIActivityDeleteRestore/delete.feature +++ b/tests/acceptance/features/webUIActivityDeleteRestore/delete.feature @@ -104,7 +104,7 @@ Feature: Deleted files/folders activities And user "Alice" has deleted folder "folder with space/simple-empty-folder" When the user browses to the activity page And the user filters activity list by "" - Then the activity number 1 should have message "You deleted simple-empty-folder, textfile0.txt, 'single'quotes, testavatar.png and 0" in the activity page + Then the activity page should have message "You deleted simple-empty-folder, textfile0.txt, 'single'quotes, testavatar.png and 0" Examples: | filter | | All Activities | From 530a008aab9a903cfe5ff12c139486e31bb1899d Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 11:27:43 +0930 Subject: [PATCH 15/44] chore(ci): Allow Http fallback for federation --- .github/workflows/acceptance.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 1574a603..432c2776 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -278,17 +278,28 @@ jobs: run: | cd /var/www/html${{ inputs.server-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=server + sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true sudo -u www-data php occ a:e testing sudo -u www-data php occ a:l -e sudo -u www-data php occ config:list + - name: Allow Http fallback for federation to local server + if: ${{ inputs.federated-root != '' }} + run: | + cd /var/www/html${{ inputs.server-root }} + sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean + - name: Setup owncloud federated server settings if: ${{ inputs.federated-root != '' }} run: | cd /var/www/html${{ inputs.federated-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost + sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=federated + sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true sudo -u www-data php occ a:e testing From 1d1bf4ab9e8f2d375c4f03dbd50bb4b325d672eb Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 13:21:43 +0930 Subject: [PATCH 16/44] chore: refactor to pass in server-folder and federated-folder --- .github/workflows/acceptance.yml | 113 ++++++++++++++++++------------- .github/workflows/main.yml | 4 +- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 432c2776..3f69cc6c 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -25,12 +25,12 @@ on: required: false type: string default: '' - server-root: + server-folder: description: 'The folder under the Apache root in which to install owncloud core' required: false type: string default: '' - federated-root: + federated-folder: description: 'The folder under the Apache root in which to install a federated server' required: false type: string @@ -103,6 +103,23 @@ jobs: - 5432:5432 steps: + - name: Prepend folder names with slash + id: prepend + env: + SERVER_FOLDER: ${{ inputs.server-folder }} + FEDERATED_FOLDER: ${{ inputs.federated-folder }} + run: | + if [ -n "$SERVER_FOLDER" ]; then + echo "server-root=/$SERVER_FOLDER" >> $GITHUB_OUTPUT + else + echo "server-root=" >> $GITHUB_OUTPUT + fi + if [ -n "$FEDERATED_FOLDER" ]; then + echo "federated-root=/$FEDERATED_FOLDER" >> $GITHUB_OUTPUT + else + echo "federated-root=" >> $GITHUB_OUTPUT + fi + - name: Clone owncloud/core uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -151,7 +168,7 @@ jobs: sudo a2ensite server - name: Configure Apache for federated - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | sudo cp apps/${{ inputs.app-name }}/tests/acceptance/federated.conf /etc/apache2/sites-available sudo a2ensite federated @@ -168,7 +185,7 @@ jobs: - name: Install owncloud/core run: | - docker run -v /var/www/html${{ inputs.server-root }}:/var/www/html${{ inputs.server-root }} \ + docker run -v /var/www/html${{ steps.prepend.outputs.server-root }}:/var/www/html${{ steps.prepend.outputs.server-root }} \ --network=host \ -e PLUGIN_VERSION=${VERSION} \ -e PLUGIN_CORE_PATH=${CORE_PATH} \ @@ -180,7 +197,7 @@ jobs: owncloudci/core:php83 env: VERSION: 'daily-master-qa' - CORE_PATH: /var/www/html${{ inputs.server-root }} + CORE_PATH: /var/www/html${{ steps.prepend.outputs.server-root }} DB_TYPE: 'mysql' DB_NAME: 'owncloud' DB_HOST: '127.0.0.1' @@ -188,15 +205,15 @@ jobs: DB_PASSWORD: 'owncloud' - name: Create database for federated server - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "CREATE DATABASE federated;" mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "GRANT ALL PRIVILEGES ON federated.* TO 'owncloud'@'%';" - name: Install federated owncloud/core - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | - docker run -v /var/www/html${{ inputs.federated-root }}:/var/www/html${{ inputs.federated-root }} \ + docker run -v /var/www/html${{ steps.prepend.outputs.federated-root }}:/var/www/html${{ steps.prepend.outputs.federated-root }} \ --network=host \ -e PLUGIN_VERSION=${VERSION} \ -e PLUGIN_CORE_PATH=${CORE_PATH} \ @@ -208,7 +225,7 @@ jobs: owncloudci/core:php83 env: VERSION: 'daily-master-qa' - CORE_PATH: /var/www/html${{ inputs.federated-root }} + CORE_PATH: /var/www/html${{ steps.prepend.outputs.federated-root }} DB_TYPE: 'mysql' DB_NAME: federated DB_HOST: '127.0.0.1' @@ -217,14 +234,14 @@ jobs: - name: Setup permissions for the backend of the Apache server run: | - sudo chown -R www-data:www-data /var/www/html${{ inputs.server-root }} - sudo chmod -R 777 /var/www/html${{ inputs.server-root }} + sudo chown -R www-data:www-data /var/www/html${{ steps.prepend.outputs.server-root }} + sudo chmod -R 777 /var/www/html${{ steps.prepend.outputs.server-root }} - name: Setup permissions for the backend of the federated Apache server - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | - sudo chown -R www-data:www-data /var/www/html${{ inputs.federated-root }} - sudo chmod -R 777 /var/www/html${{ inputs.federated-root }} + sudo chown -R www-data:www-data /var/www/html${{ steps.prepend.outputs.federated-root }} + sudo chmod -R 777 /var/www/html${{ steps.prepend.outputs.federated-root }} - name: Selenium if: ${{ inputs.do-webui-tests }} @@ -241,18 +258,18 @@ jobs: run: | cd apps/${{ inputs.app-name }} make ci - sudo -u www-data mkdir /var/www/html${{ inputs.server-root }}/apps/${{ inputs.app-name }} - sudo -u www-data cp -r * /var/www/html${{ inputs.server-root }}/apps/${{ inputs.app-name }} - cd /var/www/html${{ inputs.server-root }} + sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.app-name }} + sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.app-name }} + cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ a:e ${{ inputs.app-name }} - name: Setup app ${{ inputs.app-name }} on federated server - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | cd apps/${{ inputs.app-name }} - sudo -u www-data mkdir /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.app-name }} - sudo -u www-data cp -r * /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.app-name }} - cd /var/www/html${{ inputs.federated-root }} + sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.app-name }} + sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.app-name }} + cd /var/www/html${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ a:e ${{ inputs.app-name }} - name: Setup additional app ${{ inputs.additional-app }} @@ -260,25 +277,25 @@ jobs: run: | cd apps/${{ inputs.additional-app }} make ci - sudo -u www-data mkdir /var/www/html${{ inputs.server-root }}/apps/${{ inputs.additional-app }} - sudo -u www-data cp -r * /var/www/html${{ inputs.server-root }}/apps/${{ inputs.additional-app }} - cd /var/www/html${{ inputs.server-root }} + sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.additional-app }} + sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.additional-app }} + cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ a:e ${{ inputs.additional-app }} - name: Setup additional app ${{ inputs.additional-app }} on federated server - if: ${{ inputs.additional-app != '' && inputs.federated-root != '' }} + if: ${{ inputs.additional-app != '' && inputs.federated-folder != '' }} run: | cd apps/${{ inputs.additional-app }} - sudo -u www-data mkdir /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.additional-app }} - sudo -u www-data cp -r * /var/www/html${{ inputs.federated-root }}/apps/${{ inputs.additional-app }} - cd /var/www/html${{ inputs.federated-root }} + sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.additional-app }} + sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.additional-app }} + cd /var/www/html${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ a:e ${{ inputs.additional-app }} - name: Setup owncloud server settings run: | - cd /var/www/html${{ inputs.server-root }} + cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=server + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.server-root }}/ sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -287,18 +304,18 @@ jobs: sudo -u www-data php occ config:list - name: Allow Http fallback for federation to local server - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | - cd /var/www/html${{ inputs.server-root }} + cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - name: Setup owncloud federated server settings - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: | - cd /var/www/html${{ inputs.federated-root }} + cd /var/www/html${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=federated + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.federated-root }}/ sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -307,15 +324,15 @@ jobs: sudo -u www-data php occ config:list - name: Check access to status - run: curl http://localhost:80${{ inputs.server-root }}/status.php + run: curl http://localhost:80${{ steps.prepend.outputs.server-root }}/status.php - name: Check access to login endpoint - run: curl http://localhost:80${{ inputs.server-root }}/login + run: curl http://localhost:80${{ steps.prepend.outputs.server-root }}/login - name: Check content of server files run: | - sudo -u www-data cat /var/www/html${{ inputs.server-root }}/.htaccess - sudo -u www-data cat /var/www/html${{ inputs.server-root }}/config/config.php + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/.htaccess + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/config/config.php sudo cat /etc/apache2/apache2.conf sudo cat /etc/apache2/ports.conf sudo ls -l /etc/apache2/sites-available @@ -324,12 +341,12 @@ jobs: sudo cat /etc/apache2/sites-enabled/server.conf - name: Check access to federated status - if: ${{ inputs.federated-root != '' }} - run: curl http://localhost:80${{ inputs.federated-root }}/status.php + if: ${{ inputs.federated-folder != '' }} + run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/status.php - name: Define environment variable pointing to the federated server if: ${{ inputs.federated-root != '' }} - run: echo "TEST_SERVER_FED_URL=http://localhost${{ inputs.federated-root }}" >> $GITHUB_ENV + run: echo "TEST_SERVER_FED_URL=http://localhost${{ steps.prepend.outputs.federated-root }}" >> $GITHUB_ENV - name: Install libxml2-utils to get xmllint used by acceptance test script run: sudo apt install -y libxml2-utils @@ -337,7 +354,7 @@ jobs: - name: Run API acceptance tests if: ${{ inputs.do-api-tests }} env: - TEST_SERVER_URL: http://localhost${{ inputs.server-root }} + TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | @@ -347,7 +364,7 @@ jobs: - name: Run CLI acceptance tests if: ${{ inputs.do-cli-tests }} env: - TEST_SERVER_URL: http://localhost${{ inputs.server-root }} + TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | @@ -357,7 +374,7 @@ jobs: - name: Run WebUI acceptance tests if: ${{ inputs.do-webui-tests }} env: - TEST_SERVER_URL: http://localhost${{ inputs.server-root }} + TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BROWSER: "chrome" SELENIUM_HOST: "localhost" SELENIUM_PORT: "4444" @@ -370,6 +387,6 @@ jobs: - name: Display log file if: failure() run: | - sudo -u www-data ls -l /var/www/html${{ inputs.server-root }} - sudo -u www-data ls -l /var/www/html${{ inputs.server-root }}/data - sudo -u www-data cat /var/www/html${{ inputs.server-root }}/data/owncloud.log + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }} + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }}/data + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/data/owncloud.log diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9e1002ca..1d4793ba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -73,8 +73,8 @@ jobs: with: app-name: ${{ needs.get-vars.outputs.app-name }} do-api-tests: true - server-root: '/server' - federated-root: '/federated' + server-folder: 'server' + federated-folder: 'federated' acceptance-webui: name: WebUI acceptance tests From 3809db063045f6143043e951552bcf43219fda5d Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 15:26:55 +0930 Subject: [PATCH 17/44] chore(ci): enable Apache env module --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 3f69cc6c..125f2fcf 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -159,7 +159,7 @@ jobs: sudo apt-get update sudo apt-get install -y libapache2-mod-php${{ matrix.php }} sudo apt-get install -y apache2 - sudo a2enmod rewrite + sudo a2enmod rewrite env sudo service apache2 restart - name: Configure Apache for server From 352ae84c6b58812101f57f45d29204968d235342 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 15:53:07 +0930 Subject: [PATCH 18/44] chore(ci): always set RewriteBase to slash --- .github/workflows/acceptance.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 125f2fcf..eb61688c 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -295,7 +295,7 @@ jobs: run: | cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.server-root }}/ + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/ sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -315,7 +315,7 @@ jobs: cd /var/www/html${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.federated-root }}/ + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/ sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true From f3b134e8fbf9659b12fbeb50d204e8cc072488b2 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 16:46:20 +0930 Subject: [PATCH 19/44] test: try to fix the trashbin restore This is code from core for processing a trashbin restore. It did not account for when the server is installed in a subfolder, and the href of items has the subfolder at the front. "/server/remote.php/dav/trash-bin/Alice/" The code here is copied from core TrashbinContext and adjusted to expect the subfolder at the front of the href. If it works, then it will need to go in the core code. --- .../features/apiActivity/list.feature | 2 +- .../features/bootstrap/ActivityContext.php | 275 ++++++++++++++++++ 2 files changed, 276 insertions(+), 1 deletion(-) diff --git a/tests/acceptance/features/apiActivity/list.feature b/tests/acceptance/features/apiActivity/list.feature index d5381712..7fd7a6cb 100644 --- a/tests/acceptance/features/apiActivity/list.feature +++ b/tests/acceptance/features/apiActivity/list.feature @@ -641,7 +641,7 @@ Feature: List activity And user "Alice" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" And user "Alice" has shared file "/lorem.txt" with user "Brian" When user "Alice" deletes file "/lorem.txt" using the WebDAV API - And user "Alice" restores the file with original path "lorem.txt" using the trashbin API + And user "Alice" restores the file with original path "lorem.txt" using the activity test code Then the activity number 1 of user "Alice" should match these properties: | type | /^file_restored$/ | | user | /^Alice$/ | diff --git a/tests/acceptance/features/bootstrap/ActivityContext.php b/tests/acceptance/features/bootstrap/ActivityContext.php index 5a06a4bc..34a5995b 100644 --- a/tests/acceptance/features/bootstrap/ActivityContext.php +++ b/tests/acceptance/features/bootstrap/ActivityContext.php @@ -27,6 +27,7 @@ use PHPUnit\Framework\Assert; use Psr\Http\Message\ResponseInterface; use TestHelpers\HttpRequestHelper; +use TestHelpers\WebDavHelper; require_once 'bootstrap.php'; @@ -317,4 +318,278 @@ public function userHasAddedTheLastPublicLinkShareToRemoteServer(string $user):v ) ); } + + /** + * @When /^user "([^"]*)" restores the (?:file|folder|entry) with original path "([^"]*)" using the activity test code$/ + * + * @param string|null $user + * @param string $originalPath + * + * @return void + * @throws JsonException + * @throws Exception + */ + public function elementInTrashIsRestoredUsingActivityTestCode(?string $user, string $originalPath):void { + $user = $this->featureContext->getActualUsername($user); + $this->restoreElement($user, $originalPath); + } + + /** + * @param string $user + * @param string $originalPath + * @param string|null $destinationPath + * @param bool $throwExceptionIfNotFound + * @param string|null $asUser - To send request as another user + * @param string|null $password + * + * @return void + * @throws Exception + */ + private function restoreElement(string $user, string $originalPath, ?string $destinationPath = null, bool $throwExceptionIfNotFound = true, ?string $asUser = null, ?string $password = null):void { + $asUser = $asUser ?? $user; + $listing = $this->listTrashbinFolder($user); + $originalPath = \trim($originalPath, '/'); + if ($destinationPath === null) { + $destinationPath = $originalPath; + } + foreach ($listing as $entry) { + if ($entry['original-location'] === $originalPath) { + $this->sendUndeleteRequest( + $user, + $entry['href'], + $destinationPath, + $asUser, + $password + ); + return; + } + } + // The requested element to restore was not even in the trashbin. + // Throw an exception, because there was not any API call, and so there + // is also no up-to-date response to examine in later test steps. + if ($throwExceptionIfNotFound) { + throw new \Exception( + __METHOD__ + . " cannot restore from trashbin because no element was found for user $user at original path $originalPath" + ); + } + } + + /** + * List trashbin folder + * + * @param string|null $user user + * @param string $depth + * + * @return array of all the items in the trashbin of the user + * @throws Exception + */ + public function listTrashbinFolder(?string $user, string $depth = "1"):array { + return $this->listTrashbinFolderCollection( + $user, + "", + $depth + ); + } + + /** + * List a collection in the trashbin + * + * @param string|null $user user + * @param string|null $collectionPath the string of ids of the folder and sub-folders + * @param string $depth + * @param int $level + * + * @return array response + * @throws Exception + */ + public function listTrashbinFolderCollection(?string $user, ?string $collectionPath = "", string $depth = "1", int $level = 1):array { + // $collectionPath should be some list of file-ids like 2147497661/2147497662 + // or the empty string, which will list the whole trashbin from the top. + $collectionPath = \trim($collectionPath, "/"); + $password = $this->featureContext->getPasswordForUser($user); + $davPathVersion = $this->featureContext->getDavPathVersion(); + $response = WebDavHelper::listFolder( + $this->featureContext->getBaseUrl(), + $user, + $password, + $collectionPath, + $depth, + $this->featureContext->getStepLineRef(), + [ + 'oc:trashbin-original-filename', + 'oc:trashbin-original-location', + 'oc:trashbin-delete-timestamp', + 'd:resourcetype', + 'd:getlastmodified' + ], + 'trash-bin', + $davPathVersion + ); + $responseXml = HttpRequestHelper::getResponseXml( + $response, + __METHOD__ . " $collectionPath" + ); + + $subfolder = parse_url($this->featureContext->getBaseUrl(), PHP_URL_PATH); + $subfolderWithSlashAtEnd = \trim($subfolder, "/") . "/"; + if ($subfolder === null) { + $subfolder = ""; + $subfolderWithSlashAtEnd = ""; + } + $files = $this->getTrashbinContentFromResponseXml($responseXml); + // filter out the collection itself, we only want to return the members + $files = \array_filter( + $files, + static function ($element) use ($user, $collectionPath, $subfolder) { + $path = $collectionPath; + if ($path !== "") { + $path = $path . "/"; + } + return ($element['href'] !== "$subfolder/remote.php/dav/trash-bin/$user/$path"); + } + ); + + foreach ($files as $file) { + // check for unexpected/invalid href values and fail early in order to + // avoid "common" situations that could cause infinite recursion. + $trashbinRef = $file["href"]; + $trimmedTrashbinRef = \trim($trashbinRef, "/"); + $expectedStart = "{$subfolderWithSlashAtEnd}remote.php/dav/trash-bin/$user"; + $expectedStartLength = \strlen($expectedStart); + if ((\substr($trimmedTrashbinRef, 0, $expectedStartLength) !== $expectedStart) + || (\strlen($trimmedTrashbinRef) === $expectedStartLength) + ) { + // A top href (maybe without even the username) has been returned + // in the response. That should never happen, or have been filtered out + // by code above. + throw new Exception( + __METHOD__ . " Error: unexpected href in trashbin propfind at level $level: '$trashbinRef'" + ); + } + if ($file["collection"]) { + $trimmedHref = \trim($trashbinRef, "/"); + $explodedHref = \explode("/", $trimmedHref); + $trashbinId = $collectionPath . "/" . end($explodedHref); + $nextFiles = $this->listTrashbinFolderCollection( + $user, + $trashbinId, + $depth, + $level + 1 + ); + // filter the collection element. We only want the members. + $nextFiles = \array_filter( + $nextFiles, + static function ($element) use ($user, $trashbinRef) { + return ($element['href'] !== $trashbinRef); + } + ); + \array_push($files, ...$nextFiles); + } + } + return $files; + } + + /** + * Get files list from the response from trashbin api + * + * @param SimpleXMLElement|null $responseXml + * + * @return array + */ + public function getTrashbinContentFromResponseXml(?SimpleXMLElement $responseXml): array { + $xmlElements = $responseXml->xpath('//d:response'); + $files = \array_map( + static function (SimpleXMLElement $element) { + $href = $element->xpath('./d:href')[0]; + + $propStats = $element->xpath('./d:propstat'); + $successPropStat = \array_filter( + $propStats, + static function (SimpleXMLElement $propStat) { + $status = $propStat->xpath('./d:status'); + return (string) $status[0] === 'HTTP/1.1 200 OK'; + } + ); + if (isset($successPropStat[0])) { + $successPropStat = $successPropStat[0]; + + $name = $successPropStat->xpath('./d:prop/oc:trashbin-original-filename'); + $mtime = $successPropStat->xpath('./d:prop/oc:trashbin-delete-timestamp'); + $resourcetype = $successPropStat->xpath('./d:prop/d:resourcetype'); + if (\array_key_exists(0, $resourcetype) && ($resourcetype[0]->asXML() === "")) { + $collection[0] = true; + } else { + $collection[0] = false; + } + $originalLocation = $successPropStat->xpath('./d:prop/oc:trashbin-original-location'); + } else { + $name = []; + $mtime = []; + $collection = []; + $originalLocation = []; + } + + return [ + 'href' => (string) $href, + 'name' => isset($name[0]) ? (string) $name[0] : null, + 'mtime' => isset($mtime[0]) ? (string) $mtime[0] : null, + 'collection' => isset($collection[0]) ? $collection[0] : false, + 'original-location' => isset($originalLocation[0]) ? (string) $originalLocation[0] : null + ]; + }, + $xmlElements + ); + + return $files; + } + + /** + * @param string $user + * @param string $trashItemHRef + * @param string $destinationPath + * @param string|null $asUser - To send request as another user + * @param string|null $password + * + * @return ResponseInterface + */ + private function sendUndeleteRequest(string $user, string $trashItemHRef, string $destinationPath, ?string $asUser = null, ?string $password = null):ResponseInterface { + $asUser = $asUser ?? $user; + $destinationPath = \trim($destinationPath, '/'); + $destinationValue = $this->featureContext->getBaseUrl() . "/remote.php/dav/files/$user/$destinationPath"; + + $trashItemHRef = $this->convertTrashbinHref($trashItemHRef); + $headers['Destination'] = $destinationValue; + $response = $this->featureContext->makeDavRequest( + $asUser, + 'MOVE', + $trashItemHRef, + $headers, + null, + 'trash-bin', + '2', + false, + $password, + [], + $user + ); + $this->featureContext->setResponse($response); + return $response; + } + + /** + * converts the trashItemHRef from //remote.php/dav/trash-bin/// to /trash-bin// + * + * @param string $href + * + * @return string + */ + private function convertTrashbinHref(string $href):string { + $trashItemHRef = \trim($href, '/'); + $trashItemHRef = \strstr($trashItemHRef, '/trash-bin'); + $trashItemHRef = \trim($trashItemHRef, '/'); + $parts = \explode('/', $trashItemHRef); + $decodedParts = \array_slice($parts, 2); + return '/' . \join('/', $decodedParts); + } } From 09c6b80d4c06a4ab622ac66174c3b3ece11ec660 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 19:57:15 +0930 Subject: [PATCH 20/44] chore(ci): use core-branch-for-test-code fix-trashbin-ref to get the fix for the trashbin restore acceptance test. --- .github/workflows/acceptance.yml | 6 ++++++ .github/workflows/main.yml | 1 + tests/acceptance/features/apiActivity/list.feature | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index eb61688c..cf9ee702 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -65,6 +65,11 @@ on: required: false type: string default: '' + core-branch-for-test-code: + description: the branch of core to be used for the common acceptance test code + required: false + type: string + default: 'master' jobs: acceptance: @@ -124,6 +129,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: owncloud/core + ref: ${{ inputs.core-branch-for-test-code }} - name: Checkout apps/${{ inputs.app-name }} uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d4793ba..545d5d10 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,6 +75,7 @@ jobs: do-api-tests: true server-folder: 'server' federated-folder: 'federated' + core-branch-for-test-code: fix-trashbin-ref acceptance-webui: name: WebUI acceptance tests diff --git a/tests/acceptance/features/apiActivity/list.feature b/tests/acceptance/features/apiActivity/list.feature index 7fd7a6cb..d5381712 100644 --- a/tests/acceptance/features/apiActivity/list.feature +++ b/tests/acceptance/features/apiActivity/list.feature @@ -641,7 +641,7 @@ Feature: List activity And user "Alice" has uploaded file "filesForUpload/lorem.txt" to "/lorem.txt" And user "Alice" has shared file "/lorem.txt" with user "Brian" When user "Alice" deletes file "/lorem.txt" using the WebDAV API - And user "Alice" restores the file with original path "lorem.txt" using the activity test code + And user "Alice" restores the file with original path "lorem.txt" using the trashbin API Then the activity number 1 of user "Alice" should match these properties: | type | /^file_restored$/ | | user | /^Alice$/ | From e448f2dd01045f054770921033fb9717dc4ccf66 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 20:32:53 +0930 Subject: [PATCH 21/44] test: delete temporary test code --- .../features/bootstrap/ActivityContext.php | 274 ------------------ 1 file changed, 274 deletions(-) diff --git a/tests/acceptance/features/bootstrap/ActivityContext.php b/tests/acceptance/features/bootstrap/ActivityContext.php index 34a5995b..3c7738c8 100644 --- a/tests/acceptance/features/bootstrap/ActivityContext.php +++ b/tests/acceptance/features/bootstrap/ActivityContext.php @@ -318,278 +318,4 @@ public function userHasAddedTheLastPublicLinkShareToRemoteServer(string $user):v ) ); } - - /** - * @When /^user "([^"]*)" restores the (?:file|folder|entry) with original path "([^"]*)" using the activity test code$/ - * - * @param string|null $user - * @param string $originalPath - * - * @return void - * @throws JsonException - * @throws Exception - */ - public function elementInTrashIsRestoredUsingActivityTestCode(?string $user, string $originalPath):void { - $user = $this->featureContext->getActualUsername($user); - $this->restoreElement($user, $originalPath); - } - - /** - * @param string $user - * @param string $originalPath - * @param string|null $destinationPath - * @param bool $throwExceptionIfNotFound - * @param string|null $asUser - To send request as another user - * @param string|null $password - * - * @return void - * @throws Exception - */ - private function restoreElement(string $user, string $originalPath, ?string $destinationPath = null, bool $throwExceptionIfNotFound = true, ?string $asUser = null, ?string $password = null):void { - $asUser = $asUser ?? $user; - $listing = $this->listTrashbinFolder($user); - $originalPath = \trim($originalPath, '/'); - if ($destinationPath === null) { - $destinationPath = $originalPath; - } - foreach ($listing as $entry) { - if ($entry['original-location'] === $originalPath) { - $this->sendUndeleteRequest( - $user, - $entry['href'], - $destinationPath, - $asUser, - $password - ); - return; - } - } - // The requested element to restore was not even in the trashbin. - // Throw an exception, because there was not any API call, and so there - // is also no up-to-date response to examine in later test steps. - if ($throwExceptionIfNotFound) { - throw new \Exception( - __METHOD__ - . " cannot restore from trashbin because no element was found for user $user at original path $originalPath" - ); - } - } - - /** - * List trashbin folder - * - * @param string|null $user user - * @param string $depth - * - * @return array of all the items in the trashbin of the user - * @throws Exception - */ - public function listTrashbinFolder(?string $user, string $depth = "1"):array { - return $this->listTrashbinFolderCollection( - $user, - "", - $depth - ); - } - - /** - * List a collection in the trashbin - * - * @param string|null $user user - * @param string|null $collectionPath the string of ids of the folder and sub-folders - * @param string $depth - * @param int $level - * - * @return array response - * @throws Exception - */ - public function listTrashbinFolderCollection(?string $user, ?string $collectionPath = "", string $depth = "1", int $level = 1):array { - // $collectionPath should be some list of file-ids like 2147497661/2147497662 - // or the empty string, which will list the whole trashbin from the top. - $collectionPath = \trim($collectionPath, "/"); - $password = $this->featureContext->getPasswordForUser($user); - $davPathVersion = $this->featureContext->getDavPathVersion(); - $response = WebDavHelper::listFolder( - $this->featureContext->getBaseUrl(), - $user, - $password, - $collectionPath, - $depth, - $this->featureContext->getStepLineRef(), - [ - 'oc:trashbin-original-filename', - 'oc:trashbin-original-location', - 'oc:trashbin-delete-timestamp', - 'd:resourcetype', - 'd:getlastmodified' - ], - 'trash-bin', - $davPathVersion - ); - $responseXml = HttpRequestHelper::getResponseXml( - $response, - __METHOD__ . " $collectionPath" - ); - - $subfolder = parse_url($this->featureContext->getBaseUrl(), PHP_URL_PATH); - $subfolderWithSlashAtEnd = \trim($subfolder, "/") . "/"; - if ($subfolder === null) { - $subfolder = ""; - $subfolderWithSlashAtEnd = ""; - } - $files = $this->getTrashbinContentFromResponseXml($responseXml); - // filter out the collection itself, we only want to return the members - $files = \array_filter( - $files, - static function ($element) use ($user, $collectionPath, $subfolder) { - $path = $collectionPath; - if ($path !== "") { - $path = $path . "/"; - } - return ($element['href'] !== "$subfolder/remote.php/dav/trash-bin/$user/$path"); - } - ); - - foreach ($files as $file) { - // check for unexpected/invalid href values and fail early in order to - // avoid "common" situations that could cause infinite recursion. - $trashbinRef = $file["href"]; - $trimmedTrashbinRef = \trim($trashbinRef, "/"); - $expectedStart = "{$subfolderWithSlashAtEnd}remote.php/dav/trash-bin/$user"; - $expectedStartLength = \strlen($expectedStart); - if ((\substr($trimmedTrashbinRef, 0, $expectedStartLength) !== $expectedStart) - || (\strlen($trimmedTrashbinRef) === $expectedStartLength) - ) { - // A top href (maybe without even the username) has been returned - // in the response. That should never happen, or have been filtered out - // by code above. - throw new Exception( - __METHOD__ . " Error: unexpected href in trashbin propfind at level $level: '$trashbinRef'" - ); - } - if ($file["collection"]) { - $trimmedHref = \trim($trashbinRef, "/"); - $explodedHref = \explode("/", $trimmedHref); - $trashbinId = $collectionPath . "/" . end($explodedHref); - $nextFiles = $this->listTrashbinFolderCollection( - $user, - $trashbinId, - $depth, - $level + 1 - ); - // filter the collection element. We only want the members. - $nextFiles = \array_filter( - $nextFiles, - static function ($element) use ($user, $trashbinRef) { - return ($element['href'] !== $trashbinRef); - } - ); - \array_push($files, ...$nextFiles); - } - } - return $files; - } - - /** - * Get files list from the response from trashbin api - * - * @param SimpleXMLElement|null $responseXml - * - * @return array - */ - public function getTrashbinContentFromResponseXml(?SimpleXMLElement $responseXml): array { - $xmlElements = $responseXml->xpath('//d:response'); - $files = \array_map( - static function (SimpleXMLElement $element) { - $href = $element->xpath('./d:href')[0]; - - $propStats = $element->xpath('./d:propstat'); - $successPropStat = \array_filter( - $propStats, - static function (SimpleXMLElement $propStat) { - $status = $propStat->xpath('./d:status'); - return (string) $status[0] === 'HTTP/1.1 200 OK'; - } - ); - if (isset($successPropStat[0])) { - $successPropStat = $successPropStat[0]; - - $name = $successPropStat->xpath('./d:prop/oc:trashbin-original-filename'); - $mtime = $successPropStat->xpath('./d:prop/oc:trashbin-delete-timestamp'); - $resourcetype = $successPropStat->xpath('./d:prop/d:resourcetype'); - if (\array_key_exists(0, $resourcetype) && ($resourcetype[0]->asXML() === "")) { - $collection[0] = true; - } else { - $collection[0] = false; - } - $originalLocation = $successPropStat->xpath('./d:prop/oc:trashbin-original-location'); - } else { - $name = []; - $mtime = []; - $collection = []; - $originalLocation = []; - } - - return [ - 'href' => (string) $href, - 'name' => isset($name[0]) ? (string) $name[0] : null, - 'mtime' => isset($mtime[0]) ? (string) $mtime[0] : null, - 'collection' => isset($collection[0]) ? $collection[0] : false, - 'original-location' => isset($originalLocation[0]) ? (string) $originalLocation[0] : null - ]; - }, - $xmlElements - ); - - return $files; - } - - /** - * @param string $user - * @param string $trashItemHRef - * @param string $destinationPath - * @param string|null $asUser - To send request as another user - * @param string|null $password - * - * @return ResponseInterface - */ - private function sendUndeleteRequest(string $user, string $trashItemHRef, string $destinationPath, ?string $asUser = null, ?string $password = null):ResponseInterface { - $asUser = $asUser ?? $user; - $destinationPath = \trim($destinationPath, '/'); - $destinationValue = $this->featureContext->getBaseUrl() . "/remote.php/dav/files/$user/$destinationPath"; - - $trashItemHRef = $this->convertTrashbinHref($trashItemHRef); - $headers['Destination'] = $destinationValue; - $response = $this->featureContext->makeDavRequest( - $asUser, - 'MOVE', - $trashItemHRef, - $headers, - null, - 'trash-bin', - '2', - false, - $password, - [], - $user - ); - $this->featureContext->setResponse($response); - return $response; - } - - /** - * converts the trashItemHRef from //remote.php/dav/trash-bin/// to /trash-bin// - * - * @param string $href - * - * @return string - */ - private function convertTrashbinHref(string $href):string { - $trashItemHRef = \trim($href, '/'); - $trashItemHRef = \strstr($trashItemHRef, '/trash-bin'); - $trashItemHRef = \trim($trashItemHRef, '/'); - $parts = \explode('/', $trashItemHRef); - $decodedParts = \array_slice($parts, 2); - return '/' . \join('/', $decodedParts); - } } From 1568667625bacef76affb2a48f9ebcc10b53cef7 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 20:33:58 +0930 Subject: [PATCH 22/44] chore(ci): add test to clone core name --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index cf9ee702..a451b63e 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -125,7 +125,7 @@ jobs: echo "federated-root=" >> $GITHUB_OUTPUT fi - - name: Clone owncloud/core + - name: Clone owncloud/core ${{ inputs.core-branch-for-test-code }} branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: owncloud/core From 863d628c900bb2ab9051f82367f28fe532d5d492 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 13 Apr 2026 22:19:10 +0930 Subject: [PATCH 23/44] chore(ci): display federated log file on failure --- .github/workflows/acceptance.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index a451b63e..8e7c3c1f 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -396,3 +396,6 @@ jobs: sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }}/data sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/data/owncloud.log + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }} + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }}/data + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/data/owncloud.log From 59b77fa935cab42e38f39009d8f2800dc10e155a Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 14 Apr 2026 14:17:24 +0930 Subject: [PATCH 24/44] chore(ci): fix if federated-folder check --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 8e7c3c1f..4a2ca290 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -351,7 +351,7 @@ jobs: run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/status.php - name: Define environment variable pointing to the federated server - if: ${{ inputs.federated-root != '' }} + if: ${{ inputs.federated-folder != '' }} run: echo "TEST_SERVER_FED_URL=http://localhost${{ steps.prepend.outputs.federated-root }}" >> $GITHUB_ENV - name: Install libxml2-utils to get xmllint used by acceptance test script From 39df5eb9acec5169e20bac737542652206f5e60f Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 14 Apr 2026 16:03:28 +0930 Subject: [PATCH 25/44] chore(ci): enable more Apache2 modules the same as was done in owncloud-ci/php --- .github/workflows/acceptance.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 4a2ca290..97467a89 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -165,7 +165,7 @@ jobs: sudo apt-get update sudo apt-get install -y libapache2-mod-php${{ matrix.php }} sudo apt-get install -y apache2 - sudo a2enmod rewrite env + sudo a2enmod rewrite headers env dir mime ssl expires dav dav_fs sudo service apache2 restart - name: Configure Apache for server From 8c379118e2872a446cc040b9c13c39b1703c4117 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 14 Apr 2026 16:44:56 +0930 Subject: [PATCH 26/44] chore(ci): Display content of server files after tests are finished --- .github/workflows/acceptance.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 97467a89..4bd901db 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -390,6 +390,30 @@ jobs: cd apps/${{ inputs.app-name }} make test-acceptance-webui + - name: Display content of server files after tests are finished + if: failure() + run: | + echo "server .htaccess and config.php" + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/.htaccess + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/config/config.php + echo "federated .htaccess and config.php" + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/.htaccess + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/config/config.php + echo "apache2.conf" + sudo cat /etc/apache2/apache2.conf + echo "ports.conf" + sudo cat /etc/apache2/ports.conf + echo "ls sites-available" + sudo ls -l /etc/apache2/sites-available + echo "ls sites-enabled" + sudo ls -l /etc/apache2/sites-enabled + echo "Cat 000-default.conf" + sudo cat /etc/apache2/sites-enabled/000-default.conf + echo "Cat server.conf" + sudo cat /etc/apache2/sites-enabled/server.conf + echo "Cat federated.conf" + sudo cat /etc/apache2/sites-enabled/federated.conf + - name: Display log file if: failure() run: | From 524a3562bc2bdda514049151f6a7e05b9663709e Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 15 Apr 2026 10:57:06 +0930 Subject: [PATCH 27/44] chore(ci): define Alias in Apache conf files to explicitly tell where the server and federated installs should be served. --- tests/acceptance/federated.conf | 2 +- tests/acceptance/server.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/acceptance/federated.conf b/tests/acceptance/federated.conf index e22e2836..d511f81a 100644 --- a/tests/acceptance/federated.conf +++ b/tests/acceptance/federated.conf @@ -1,7 +1,7 @@ ServerAdmin webmaster@localhost DocumentRoot /var/www/html/federated - + Alias /federated /var/www/html/federated ErrorLog /dev/stdout diff --git a/tests/acceptance/server.conf b/tests/acceptance/server.conf index 2a377c30..168c6fcc 100644 --- a/tests/acceptance/server.conf +++ b/tests/acceptance/server.conf @@ -1,7 +1,7 @@ ServerAdmin webmaster@localhost DocumentRoot /var/www/html/server - + Alias /server /var/www/html/server ErrorLog /dev/stdout From 1e8e4a079eecbfca3690741cce149d22d6f46cd8 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 15 Apr 2026 13:49:33 +0930 Subject: [PATCH 28/44] chore(ci): provide Apache conf for server and server-federated --- .github/workflows/acceptance.yml | 31 +++++++++++++++----------- tests/acceptance/server-federated.conf | 17 ++++++++++++++ tests/acceptance/server.conf | 6 ++--- 3 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 tests/acceptance/server-federated.conf diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 4bd901db..262b8979 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -168,16 +168,19 @@ jobs: sudo a2enmod rewrite headers env dir mime ssl expires dav dav_fs sudo service apache2 restart - - name: Configure Apache for server + - name: Configure Apache for server only (no federated) + if: ${{ inputs.federated-folder == '' }} run: | sudo cp apps/${{ inputs.app-name }}/tests/acceptance/server.conf /etc/apache2/sites-available + sudo a2dissite 000-default sudo a2ensite server - - name: Configure Apache for federated + - name: Configure Apache for server and federated if: ${{ inputs.federated-folder != '' }} run: | - sudo cp apps/${{ inputs.app-name }}/tests/acceptance/federated.conf /etc/apache2/sites-available - sudo a2ensite federated + sudo cp apps/${{ inputs.app-name }}/tests/acceptance/server-federated.conf /etc/apache2/sites-available + sudo a2dissite 000-default + sudo a2ensite server-federated - name: Reload Apache to get new settings run: | @@ -301,7 +304,7 @@ jobs: run: | cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/ + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/server sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -321,7 +324,7 @@ jobs: cd /var/www/html${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/ + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/federated sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -343,9 +346,17 @@ jobs: sudo cat /etc/apache2/ports.conf sudo ls -l /etc/apache2/sites-available sudo ls -l /etc/apache2/sites-enabled - sudo cat /etc/apache2/sites-enabled/000-default.conf + + - name: Display server.conf + if: ${{ inputs.federated-folder == '' }} + run: | sudo cat /etc/apache2/sites-enabled/server.conf + - name: Display server-federated.conf + if: ${{ inputs.federated-folder != '' }} + run: | + sudo cat /etc/apache2/sites-enabled/server-federated.conf + - name: Check access to federated status if: ${{ inputs.federated-folder != '' }} run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/status.php @@ -407,12 +418,6 @@ jobs: sudo ls -l /etc/apache2/sites-available echo "ls sites-enabled" sudo ls -l /etc/apache2/sites-enabled - echo "Cat 000-default.conf" - sudo cat /etc/apache2/sites-enabled/000-default.conf - echo "Cat server.conf" - sudo cat /etc/apache2/sites-enabled/server.conf - echo "Cat federated.conf" - sudo cat /etc/apache2/sites-enabled/federated.conf - name: Display log file if: failure() diff --git a/tests/acceptance/server-federated.conf b/tests/acceptance/server-federated.conf new file mode 100644 index 00000000..cae45b78 --- /dev/null +++ b/tests/acceptance/server-federated.conf @@ -0,0 +1,17 @@ + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Require all granted + + + Options Indexes FollowSymLinks MultiViews + AllowOverride All + Require all granted + + diff --git a/tests/acceptance/server.conf b/tests/acceptance/server.conf index 168c6fcc..d879b432 100644 --- a/tests/acceptance/server.conf +++ b/tests/acceptance/server.conf @@ -1,8 +1,8 @@ ServerAdmin webmaster@localhost - DocumentRoot /var/www/html/server - Alias /server /var/www/html/server - ErrorLog /dev/stdout + DocumentRoot /var/www/html + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined Options Indexes FollowSymLinks MultiViews From 5139e6490e3fb958dd5802e16c1e37dd90927ebf Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 15 Apr 2026 19:57:21 +0930 Subject: [PATCH 29/44] chore: run webUIActivitySharingExternal and minor cleanup --- .github/workflows/acceptance.yml | 19 +++++++++++-------- .github/workflows/main.yml | 11 +++++++++++ .../features/bootstrap/ActivityContext.php | 1 - tests/acceptance/federated.conf | 12 ------------ 4 files changed, 22 insertions(+), 21 deletions(-) delete mode 100644 tests/acceptance/federated.conf diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 262b8979..e504125a 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -36,22 +36,22 @@ on: type: string default: '' do-api-tests: - description: 'Whether to run API acceptance tests' + description: 'Run API acceptance tests' required: false type: boolean default: false do-cli-tests: - description: 'Whether to run CLI acceptance tests' + description: 'Run CLI acceptance tests' required: false type: boolean default: false do-webui-tests: - description: 'Whether to run WebUI acceptance tests' + description: 'Run WebUI acceptance tests' required: false type: boolean default: false additional-app: - description: 'Additional app to enable for testing' + description: 'Additional app to install and enable (if needed for testing)' required: false type: string default: '' @@ -332,7 +332,7 @@ jobs: sudo -u www-data php occ a:l -e sudo -u www-data php occ config:list - - name: Check access to status + - name: Check access to status endpoint run: curl http://localhost:80${{ steps.prepend.outputs.server-root }}/status.php - name: Check access to login endpoint @@ -357,10 +357,13 @@ jobs: run: | sudo cat /etc/apache2/sites-enabled/server-federated.conf - - name: Check access to federated status + - name: Check access to federated status endpoint if: ${{ inputs.federated-folder != '' }} run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/status.php + - name: Check access to federated login endpoint + run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/login + - name: Define environment variable pointing to the federated server if: ${{ inputs.federated-folder != '' }} run: echo "TEST_SERVER_FED_URL=http://localhost${{ steps.prepend.outputs.federated-root }}" >> $GITHUB_ENV @@ -414,9 +417,9 @@ jobs: sudo cat /etc/apache2/apache2.conf echo "ports.conf" sudo cat /etc/apache2/ports.conf - echo "ls sites-available" + echo "sites-available" sudo ls -l /etc/apache2/sites-available - echo "ls sites-enabled" + echo "sites-enabled" sudo ls -l /etc/apache2/sites-enabled - name: Display log file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 545d5d10..fb808332 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -87,3 +87,14 @@ jobs: do-webui-tests: true test-suites: "['webUIActivityComments', 'webUIActivityCreateUpdate', 'webUIActivityDeleteRestore', 'webUIActivityFileMoveAndRename', 'webUIActivitySharingInternal', 'webUIActivityTags']" + acceptance-webui-federated: + name: WebUI acceptance tests that need a federated server + needs: + - get-vars + uses: ./.github/workflows/acceptance.yml + with: + app-name: ${{ needs.get-vars.outputs.app-name }} + do-webui-tests: true + server-folder: 'server' + federated-folder: 'federated' + test-suites: "['webUIActivitySharingExternal']" diff --git a/tests/acceptance/features/bootstrap/ActivityContext.php b/tests/acceptance/features/bootstrap/ActivityContext.php index 3c7738c8..5a06a4bc 100644 --- a/tests/acceptance/features/bootstrap/ActivityContext.php +++ b/tests/acceptance/features/bootstrap/ActivityContext.php @@ -27,7 +27,6 @@ use PHPUnit\Framework\Assert; use Psr\Http\Message\ResponseInterface; use TestHelpers\HttpRequestHelper; -use TestHelpers\WebDavHelper; require_once 'bootstrap.php'; diff --git a/tests/acceptance/federated.conf b/tests/acceptance/federated.conf deleted file mode 100644 index d511f81a..00000000 --- a/tests/acceptance/federated.conf +++ /dev/null @@ -1,12 +0,0 @@ - - ServerAdmin webmaster@localhost - DocumentRoot /var/www/html/federated - Alias /federated /var/www/html/federated - ErrorLog /dev/stdout - - - Options Indexes FollowSymLinks MultiViews - AllowOverride All - Require all granted - - From eb8a887317669f7a89f1a13b65fbb17061e6a1d5 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Wed, 15 Apr 2026 21:00:05 +0930 Subject: [PATCH 30/44] chore(ci): provide server and federated subdirs in envvars --- .github/workflows/acceptance.yml | 44 +++++++++++-------- ...server-federated.conf => dual-server.conf} | 4 +- .../{server.conf => single-server.conf} | 2 +- 3 files changed, 28 insertions(+), 22 deletions(-) rename tests/acceptance/{server-federated.conf => dual-server.conf} (80%) rename tests/acceptance/{server.conf => single-server.conf} (86%) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index e504125a..5c30fdbb 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -168,23 +168,26 @@ jobs: sudo a2enmod rewrite headers env dir mime ssl expires dav dav_fs sudo service apache2 restart - - name: Configure Apache for server only (no federated) - if: ${{ inputs.federated-folder == '' }} + - name: Configure Apache for single server only (no federated) + if: ${{ inputs.server-folder != '' && inputs.federated-folder == '' }} run: | - sudo cp apps/${{ inputs.app-name }}/tests/acceptance/server.conf /etc/apache2/sites-available + echo "export SERVER_SUBDIR=${{ inputs.server-folder }}" | sudo tee -a /etc/apache2/envvars + sudo cp apps/${{ inputs.app-name }}/tests/acceptance/single-server.conf /etc/apache2/sites-available sudo a2dissite 000-default - sudo a2ensite server + sudo a2ensite single-server - - name: Configure Apache for server and federated + - name: Configure Apache for dual server (federated) if: ${{ inputs.federated-folder != '' }} run: | - sudo cp apps/${{ inputs.app-name }}/tests/acceptance/server-federated.conf /etc/apache2/sites-available + echo "export SERVER_SUBDIR=${{ inputs.server-folder }}" | sudo tee -a /etc/apache2/envvars + echo "export FEDERATED_SUBDIR=${{ inputs.federated-folder }}" | sudo tee -a /etc/apache2/envvars + sudo cp apps/${{ inputs.app-name }}/tests/acceptance/dual-server.conf /etc/apache2/sites-available sudo a2dissite 000-default - sudo a2ensite server-federated + sudo a2ensite dual-server - - name: Reload Apache to get new settings + - name: Restart Apache to get new settings run: | - sudo systemctl reload apache2 + sudo systemctl restart apache2 - name: Check Apache server status run: curl http://localhost:80 @@ -216,8 +219,8 @@ jobs: - name: Create database for federated server if: ${{ inputs.federated-folder != '' }} run: | - mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "CREATE DATABASE federated;" - mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "GRANT ALL PRIVILEGES ON federated.* TO 'owncloud'@'%';" + mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "CREATE DATABASE federateddb;" + mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "GRANT ALL PRIVILEGES ON federateddb.* TO 'owncloud'@'%';" - name: Install federated owncloud/core if: ${{ inputs.federated-folder != '' }} @@ -236,7 +239,7 @@ jobs: VERSION: 'daily-master-qa' CORE_PATH: /var/www/html${{ steps.prepend.outputs.federated-root }} DB_TYPE: 'mysql' - DB_NAME: federated + DB_NAME: federateddb DB_HOST: '127.0.0.1' DB_USERNAME: 'owncloud' DB_PASSWORD: 'owncloud' @@ -304,7 +307,7 @@ jobs: run: | cd /var/www/html${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/server + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.server-root }} sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -324,7 +327,7 @@ jobs: cd /var/www/html${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=/federated + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.federated-root }} sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -344,18 +347,19 @@ jobs: sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/config/config.php sudo cat /etc/apache2/apache2.conf sudo cat /etc/apache2/ports.conf + sudo cat /etc/apache2/envvars sudo ls -l /etc/apache2/sites-available sudo ls -l /etc/apache2/sites-enabled - - name: Display server.conf - if: ${{ inputs.federated-folder == '' }} + - name: Display single server site config + if: ${{ inputs.server-folder != '' && inputs.federated-folder == '' }} run: | - sudo cat /etc/apache2/sites-enabled/server.conf + sudo cat /etc/apache2/sites-enabled/single-server.conf - - name: Display server-federated.conf + - name: Display dual server site config if: ${{ inputs.federated-folder != '' }} run: | - sudo cat /etc/apache2/sites-enabled/server-federated.conf + sudo cat /etc/apache2/sites-enabled/dual-server.conf - name: Check access to federated status endpoint if: ${{ inputs.federated-folder != '' }} @@ -417,6 +421,8 @@ jobs: sudo cat /etc/apache2/apache2.conf echo "ports.conf" sudo cat /etc/apache2/ports.conf + echo "envvars" + sudo cat /etc/apache2/envvars echo "sites-available" sudo ls -l /etc/apache2/sites-available echo "sites-enabled" diff --git a/tests/acceptance/server-federated.conf b/tests/acceptance/dual-server.conf similarity index 80% rename from tests/acceptance/server-federated.conf rename to tests/acceptance/dual-server.conf index cae45b78..1af10ef6 100644 --- a/tests/acceptance/server-federated.conf +++ b/tests/acceptance/dual-server.conf @@ -4,12 +4,12 @@ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined - + Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted - + Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted diff --git a/tests/acceptance/server.conf b/tests/acceptance/single-server.conf similarity index 86% rename from tests/acceptance/server.conf rename to tests/acceptance/single-server.conf index d879b432..a57cd5b6 100644 --- a/tests/acceptance/server.conf +++ b/tests/acceptance/single-server.conf @@ -4,7 +4,7 @@ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined - + Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted From f2d5ef4d2b574d9cdf67e559418c1ed2eed1d1fc Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Thu, 16 Apr 2026 22:22:29 +0930 Subject: [PATCH 31/44] chore(ci): use Apache single-server.conf when server is in top-level dir --- .github/workflows/acceptance.yml | 16 ++++++++++++---- tests/acceptance/dual-server.conf | 4 ++-- tests/acceptance/single-server.conf | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 5c30fdbb..9eafdb49 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -169,9 +169,9 @@ jobs: sudo service apache2 restart - name: Configure Apache for single server only (no federated) - if: ${{ inputs.server-folder != '' && inputs.federated-folder == '' }} + if: ${{ inputs.federated-folder == '' }} run: | - echo "export SERVER_SUBDIR=${{ inputs.server-folder }}" | sudo tee -a /etc/apache2/envvars + echo "export SERVER_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars sudo cp apps/${{ inputs.app-name }}/tests/acceptance/single-server.conf /etc/apache2/sites-available sudo a2dissite 000-default sudo a2ensite single-server @@ -179,8 +179,8 @@ jobs: - name: Configure Apache for dual server (federated) if: ${{ inputs.federated-folder != '' }} run: | - echo "export SERVER_SUBDIR=${{ inputs.server-folder }}" | sudo tee -a /etc/apache2/envvars - echo "export FEDERATED_SUBDIR=${{ inputs.federated-folder }}" | sudo tee -a /etc/apache2/envvars + echo "export SERVER_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars + echo "export FEDERATED_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars sudo cp apps/${{ inputs.app-name }}/tests/acceptance/dual-server.conf /etc/apache2/sites-available sudo a2dissite 000-default sudo a2ensite dual-server @@ -428,6 +428,14 @@ jobs: echo "sites-enabled" sudo ls -l /etc/apache2/sites-enabled + - name: Display Apache logs + if: failure() + run: | + echo "access log" + sudo cat /var/log/apache2/access.log + echo "error log" + sudo cat /var/log/apache2/error.log + - name: Display log file if: failure() run: | diff --git a/tests/acceptance/dual-server.conf b/tests/acceptance/dual-server.conf index 1af10ef6..4b8e0a96 100644 --- a/tests/acceptance/dual-server.conf +++ b/tests/acceptance/dual-server.conf @@ -4,12 +4,12 @@ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined - + Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted - + Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted diff --git a/tests/acceptance/single-server.conf b/tests/acceptance/single-server.conf index a57cd5b6..76b330bb 100644 --- a/tests/acceptance/single-server.conf +++ b/tests/acceptance/single-server.conf @@ -4,7 +4,7 @@ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined - + Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted From 7fb7795f5cb3b275d3c05509c9e533bfa5da1c32 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 17 Apr 2026 13:25:57 +0930 Subject: [PATCH 32/44] chore: get Apache site conf files from core The Apache site conf files are now stored in core tests/acceptance/setupApache And sort out the display of settings and log on failure() --- .github/workflows/acceptance.yml | 46 ++++++++++++++++------------- tests/acceptance/dual-server.conf | 17 ----------- tests/acceptance/single-server.conf | 12 -------- 3 files changed, 26 insertions(+), 49 deletions(-) delete mode 100644 tests/acceptance/dual-server.conf delete mode 100644 tests/acceptance/single-server.conf diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 9eafdb49..8318b048 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -172,7 +172,7 @@ jobs: if: ${{ inputs.federated-folder == '' }} run: | echo "export SERVER_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars - sudo cp apps/${{ inputs.app-name }}/tests/acceptance/single-server.conf /etc/apache2/sites-available + sudo cp tests/acceptance/setupApache/single-server.conf /etc/apache2/sites-available sudo a2dissite 000-default sudo a2ensite single-server @@ -181,7 +181,7 @@ jobs: run: | echo "export SERVER_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars echo "export FEDERATED_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars - sudo cp apps/${{ inputs.app-name }}/tests/acceptance/dual-server.conf /etc/apache2/sites-available + sudo cp tests/acceptance/setupApache/dual-server.conf /etc/apache2/sites-available sudo a2dissite 000-default sudo a2ensite dual-server @@ -352,7 +352,7 @@ jobs: sudo ls -l /etc/apache2/sites-enabled - name: Display single server site config - if: ${{ inputs.server-folder != '' && inputs.federated-folder == '' }} + if: ${{ inputs.federated-folder == '' }} run: | sudo cat /etc/apache2/sites-enabled/single-server.conf @@ -408,15 +408,35 @@ jobs: cd apps/${{ inputs.app-name }} make test-acceptance-webui - - name: Display content of server files after tests are finished + - name: Display content of server settings and logs if tests failed if: failure() run: | - echo "server .htaccess and config.php" + echo "server .htaccess" sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/.htaccess + echo "server config.php" sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/config/config.php - echo "federated .htaccess and config.php" + echo "server directory contents" + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }} + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }}/data + echo "server owncloud.log" + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/data/owncloud.log + + - name: Display content of federated settings and logs if tests failed + if: failure() && inputs.federated-folder != '' + run: | + echo "federated .htaccess" sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/.htaccess + echo "federated config.php" sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/config/config.php + echo "federated directory contents" + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }} + sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }}/data + echo "federated owncloud.log" + sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/data/owncloud.log + + - name: Display Apache config and logs if tests failed + if: failure() + run: | echo "apache2.conf" sudo cat /etc/apache2/apache2.conf echo "ports.conf" @@ -427,21 +447,7 @@ jobs: sudo ls -l /etc/apache2/sites-available echo "sites-enabled" sudo ls -l /etc/apache2/sites-enabled - - - name: Display Apache logs - if: failure() - run: | echo "access log" sudo cat /var/log/apache2/access.log echo "error log" sudo cat /var/log/apache2/error.log - - - name: Display log file - if: failure() - run: | - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }} - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }}/data - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/data/owncloud.log - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }} - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }}/data - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/data/owncloud.log diff --git a/tests/acceptance/dual-server.conf b/tests/acceptance/dual-server.conf deleted file mode 100644 index 4b8e0a96..00000000 --- a/tests/acceptance/dual-server.conf +++ /dev/null @@ -1,17 +0,0 @@ - - ServerAdmin webmaster@localhost - DocumentRoot /var/www/html - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - - - Options Indexes FollowSymLinks MultiViews - AllowOverride All - Require all granted - - - Options Indexes FollowSymLinks MultiViews - AllowOverride All - Require all granted - - diff --git a/tests/acceptance/single-server.conf b/tests/acceptance/single-server.conf deleted file mode 100644 index 76b330bb..00000000 --- a/tests/acceptance/single-server.conf +++ /dev/null @@ -1,12 +0,0 @@ - - ServerAdmin webmaster@localhost - DocumentRoot /var/www/html - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - - - Options Indexes FollowSymLinks MultiViews - AllowOverride All - Require all granted - - From 07948db3dc20413bf8e34c12e59502e0e1d8ee18 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 17 Apr 2026 15:51:12 +0930 Subject: [PATCH 33/44] chore(ci): use core fix-trashbin-ref branch in webUI tests --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fb808332..2b20bef9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -86,6 +86,7 @@ jobs: app-name: ${{ needs.get-vars.outputs.app-name }} do-webui-tests: true test-suites: "['webUIActivityComments', 'webUIActivityCreateUpdate', 'webUIActivityDeleteRestore', 'webUIActivityFileMoveAndRename', 'webUIActivitySharingInternal', 'webUIActivityTags']" + core-branch-for-test-code: fix-trashbin-ref acceptance-webui-federated: name: WebUI acceptance tests that need a federated server @@ -98,3 +99,4 @@ jobs: server-folder: 'server' federated-folder: 'federated' test-suites: "['webUIActivitySharingExternal']" + core-branch-for-test-code: fix-trashbin-ref From 019597503bda1bd1a27a7e48539bddb2a0a8fb9e Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Fri, 17 Apr 2026 19:28:56 +0930 Subject: [PATCH 34/44] chore(ci): prevent command injection and set permissions and validate app-repository --- .github/workflows/acceptance.yml | 230 ++++++++++++++++++++----------- 1 file changed, 148 insertions(+), 82 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 8318b048..61bf00f5 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -71,10 +71,15 @@ on: type: string default: 'master' +permissions: + contents: read + jobs: acceptance: name: Acceptance tests runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: true matrix: @@ -114,30 +119,40 @@ jobs: SERVER_FOLDER: ${{ inputs.server-folder }} FEDERATED_FOLDER: ${{ inputs.federated-folder }} run: | - if [ -n "$SERVER_FOLDER" ]; then - echo "server-root=/$SERVER_FOLDER" >> $GITHUB_OUTPUT + if [ -n "${SERVER_FOLDER}" ]; then + echo "server-root=/${SERVER_FOLDER}" >> $GITHUB_OUTPUT else echo "server-root=" >> $GITHUB_OUTPUT fi - if [ -n "$FEDERATED_FOLDER" ]; then - echo "federated-root=/$FEDERATED_FOLDER" >> $GITHUB_OUTPUT + if [ -n "${FEDERATED_FOLDER}" ]; then + echo "federated-root=/${FEDERATED_FOLDER}" >> $GITHUB_OUTPUT else echo "federated-root=" >> $GITHUB_OUTPUT fi + - name: Validate app-repository + if: ${{ inputs.app-repository != '' }} + env: + APP_REPO: ${{ inputs.app-repository }} + run: | + if ! echo "$APP_REPO" | grep -qE '^owncloud/[a-zA-Z0-9._-]+$'; then + echo "Error: app-repository must be within the owncloud/ namespace, got: $APP_REPO" + exit 1 + fi + - name: Clone owncloud/core ${{ inputs.core-branch-for-test-code }} branch uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: owncloud/core ref: ${{ inputs.core-branch-for-test-code }} - - name: Checkout apps/${{ inputs.app-name }} + - name: Checkout app uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: apps/${{ inputs.app-name }} repository: ${{ inputs.app-repository }} - - name: Checkout apps/${{ inputs.additional-app }} + - name: Checkout additional app if: ${{ inputs.additional-app != '' }} uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -147,8 +162,10 @@ jobs: - name: Install Linux packages if: ${{ inputs.additional-packages }} + env: + ADDITIONAL_PACKAGES: ${{ inputs.additional-packages }} run: | - sudo apt-get install ${{ inputs.additional-packages }} + sudo apt-get install $ADDITIONAL_PACKAGES - name: Setup PHP uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.37.0 @@ -161,26 +178,32 @@ jobs: KRB5_LINUX_LIBS: libkrb5-dev - name: Install Apache + env: + PHP_VERSION: ${{ matrix.php }} run: | sudo apt-get update - sudo apt-get install -y libapache2-mod-php${{ matrix.php }} + sudo apt-get install -y "libapache2-mod-php${PHP_VERSION}" sudo apt-get install -y apache2 sudo a2enmod rewrite headers env dir mime ssl expires dav dav_fs sudo service apache2 restart - name: Configure Apache for single server only (no federated) if: ${{ inputs.federated-folder == '' }} + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - echo "export SERVER_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars + echo "export SERVER_SUBDIR=${SERVER_ROOT}" | sudo tee -a /etc/apache2/envvars sudo cp tests/acceptance/setupApache/single-server.conf /etc/apache2/sites-available sudo a2dissite 000-default sudo a2ensite single-server - name: Configure Apache for dual server (federated) if: ${{ inputs.federated-folder != '' }} + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - echo "export SERVER_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars - echo "export FEDERATED_SUBDIR=${{ steps.prepend.outputs.server-root }}" | sudo tee -a /etc/apache2/envvars + echo "export SERVER_SUBDIR=${SERVER_ROOT}" | sudo tee -a /etc/apache2/envvars + echo "export FEDERATED_SUBDIR=${SERVER_ROOT}" | sudo tee -a /etc/apache2/envvars sudo cp tests/acceptance/setupApache/dual-server.conf /etc/apache2/sites-available sudo a2dissite 000-default sudo a2ensite dual-server @@ -196,8 +219,17 @@ jobs: run: sudo rm -Rf /var/www/html/* - name: Install owncloud/core + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} + VERSION: 'daily-master-qa' + CORE_PATH: /var/www/html${{ steps.prepend.outputs.server-root }} + DB_TYPE: 'mysql' + DB_NAME: 'owncloud' + DB_HOST: '127.0.0.1' + DB_USERNAME: 'owncloud' + DB_PASSWORD: 'owncloud' run: | - docker run -v /var/www/html${{ steps.prepend.outputs.server-root }}:/var/www/html${{ steps.prepend.outputs.server-root }} \ + docker run -v /var/www/html${SERVER_ROOT}:/var/www/html${SERVER_ROOT} \ --network=host \ -e PLUGIN_VERSION=${VERSION} \ -e PLUGIN_CORE_PATH=${CORE_PATH} \ @@ -207,14 +239,6 @@ jobs: -e PLUGIN_DB_USERNAME=${DB_USERNAME} \ -e PLUGIN_DB_PASSWORD=${DB_PASSWORD} \ owncloudci/core:php83 - env: - VERSION: 'daily-master-qa' - CORE_PATH: /var/www/html${{ steps.prepend.outputs.server-root }} - DB_TYPE: 'mysql' - DB_NAME: 'owncloud' - DB_HOST: '127.0.0.1' - DB_USERNAME: 'owncloud' - DB_PASSWORD: 'owncloud' - name: Create database for federated server if: ${{ inputs.federated-folder != '' }} @@ -224,8 +248,17 @@ jobs: - name: Install federated owncloud/core if: ${{ inputs.federated-folder != '' }} + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} + VERSION: 'daily-master-qa' + CORE_PATH: /var/www/html${{ steps.prepend.outputs.federated-root }} + DB_TYPE: 'mysql' + DB_NAME: federateddb + DB_HOST: '127.0.0.1' + DB_USERNAME: 'owncloud' + DB_PASSWORD: 'owncloud' run: | - docker run -v /var/www/html${{ steps.prepend.outputs.federated-root }}:/var/www/html${{ steps.prepend.outputs.federated-root }} \ + docker run -v /var/www/html${FEDERATED_ROOT}:/var/www/html${FEDERATED_ROOT} \ --network=host \ -e PLUGIN_VERSION=${VERSION} \ -e PLUGIN_CORE_PATH=${CORE_PATH} \ @@ -235,25 +268,21 @@ jobs: -e PLUGIN_DB_USERNAME=${DB_USERNAME} \ -e PLUGIN_DB_PASSWORD=${DB_PASSWORD} \ owncloudci/core:php83 - env: - VERSION: 'daily-master-qa' - CORE_PATH: /var/www/html${{ steps.prepend.outputs.federated-root }} - DB_TYPE: 'mysql' - DB_NAME: federateddb - DB_HOST: '127.0.0.1' - DB_USERNAME: 'owncloud' - DB_PASSWORD: 'owncloud' - name: Setup permissions for the backend of the Apache server + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - sudo chown -R www-data:www-data /var/www/html${{ steps.prepend.outputs.server-root }} - sudo chmod -R 777 /var/www/html${{ steps.prepend.outputs.server-root }} + sudo chown -R www-data:www-data /var/www/html${SERVER_ROOT} + sudo chmod -R 777 /var/www/html${SERVER_ROOT} - name: Setup permissions for the backend of the federated Apache server if: ${{ inputs.federated-folder != '' }} + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} run: | - sudo chown -R www-data:www-data /var/www/html${{ steps.prepend.outputs.federated-root }} - sudo chmod -R 777 /var/www/html${{ steps.prepend.outputs.federated-root }} + sudo chown -R www-data:www-data /var/www/html${FEDERATED_ROOT} + sudo chmod -R 777 /var/www/html${FEDERATED_ROOT} - name: Selenium if: ${{ inputs.do-webui-tests }} @@ -266,48 +295,62 @@ jobs: run: | make - - name: Setup app ${{ inputs.app-name }} + - name: Setup app + env: + APP_NAME: ${{ inputs.app-name }} + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - cd apps/${{ inputs.app-name }} + cd "apps/${APP_NAME}" make ci - sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.app-name }} - sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.app-name }} - cd /var/www/html${{ steps.prepend.outputs.server-root }} - sudo -u www-data php occ a:e ${{ inputs.app-name }} + sudo -u www-data mkdir /var/www/html${SERVER_ROOT}/apps/${APP_NAME} + sudo -u www-data cp -r * /var/www/html${SERVER_ROOT}/apps/${APP_NAME} + cd "/var/www/html${SERVER_ROOT}" + sudo -u www-data php occ a:e "${APP_NAME}" - - name: Setup app ${{ inputs.app-name }} on federated server + - name: Setup app on federated server if: ${{ inputs.federated-folder != '' }} + env: + APP_NAME: ${{ inputs.app-name }} + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} run: | - cd apps/${{ inputs.app-name }} - sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.app-name }} - sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.app-name }} - cd /var/www/html${{ steps.prepend.outputs.federated-root }} - sudo -u www-data php occ a:e ${{ inputs.app-name }} + cd "apps/${APP_NAME}" + sudo -u www-data mkdir /var/www/html${FEDERATED_ROOT}/apps/${APP_NAME} + sudo -u www-data cp -r * /var/www/html${FEDERATED_ROOT}/apps/${APP_NAME} + cd "/var/www/html${FEDERATED_ROOT}" + sudo -u www-data php occ a:e "${APP_NAME}" - - name: Setup additional app ${{ inputs.additional-app }} + - name: Setup additional app if: ${{ inputs.additional-app != '' }} + env: + ADDITIONAL_APP: ${{ inputs.additional-app }} + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - cd apps/${{ inputs.additional-app }} + cd "apps/${ADDITIONAL_APP}" make ci - sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.additional-app }} - sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.server-root }}/apps/${{ inputs.additional-app }} - cd /var/www/html${{ steps.prepend.outputs.server-root }} - sudo -u www-data php occ a:e ${{ inputs.additional-app }} + sudo -u www-data mkdir /var/www/html${SERVER_ROOT}/apps/${ADDITIONAL_APP} + sudo -u www-data cp -r * /var/www/html${SERVER_ROOT}/apps/${ADDITIONAL_APP} + cd "/var/www/html${SERVER_ROOT}" + sudo -u www-data php occ a:e "${ADDITIONAL_APP}" - - name: Setup additional app ${{ inputs.additional-app }} on federated server + - name: Setup additional app on federated server if: ${{ inputs.additional-app != '' && inputs.federated-folder != '' }} + env: + ADDITIONAL_APP: ${{ inputs.additional-app }} + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} run: | - cd apps/${{ inputs.additional-app }} - sudo -u www-data mkdir /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.additional-app }} - sudo -u www-data cp -r * /var/www/html${{ steps.prepend.outputs.federated-root }}/apps/${{ inputs.additional-app }} - cd /var/www/html${{ steps.prepend.outputs.federated-root }} - sudo -u www-data php occ a:e ${{ inputs.additional-app }} + cd "apps/${ADDITIONAL_APP}" + sudo -u www-data mkdir /var/www/html${FEDERATED_ROOT}/apps/${ADDITIONAL_APP} + sudo -u www-data cp -r * /var/www/html${FEDERATED_ROOT}/apps/${ADDITIONAL_APP} + cd "/var/www/html${FEDERATED_ROOT}" + sudo -u www-data php occ a:e "${ADDITIONAL_APP}" - name: Setup owncloud server settings + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - cd /var/www/html${{ steps.prepend.outputs.server-root }} + cd "/var/www/html${SERVER_ROOT}" sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.server-root }} + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${SERVER_ROOT} sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -317,17 +360,21 @@ jobs: - name: Allow Http fallback for federation to local server if: ${{ inputs.federated-folder != '' }} + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - cd /var/www/html${{ steps.prepend.outputs.server-root }} + cd "/var/www/html${SERVER_ROOT}" sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - name: Setup owncloud federated server settings if: ${{ inputs.federated-folder != '' }} + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} run: | - cd /var/www/html${{ steps.prepend.outputs.federated-root }} + cd "/var/www/html${FEDERATED_ROOT}" sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${{ steps.prepend.outputs.federated-root }} + sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${FEDERATED_ROOT} sudo -u www-data php occ maintenance:update:htaccess sudo -u www-data php occ log:manage --level 2 sudo -u www-data php occ config:system:set csrf.disabled --value=true @@ -336,15 +383,21 @@ jobs: sudo -u www-data php occ config:list - name: Check access to status endpoint - run: curl http://localhost:80${{ steps.prepend.outputs.server-root }}/status.php + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} + run: curl http://localhost:80${SERVER_ROOT}/status.php - name: Check access to login endpoint - run: curl http://localhost:80${{ steps.prepend.outputs.server-root }}/login + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} + run: curl http://localhost:80${SERVER_ROOT}/login - name: Check content of server files + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/.htaccess - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/config/config.php + sudo -u www-data cat /var/www/html${SERVER_ROOT}/.htaccess + sudo -u www-data cat /var/www/html${SERVER_ROOT}/config/config.php sudo cat /etc/apache2/apache2.conf sudo cat /etc/apache2/ports.conf sudo cat /etc/apache2/envvars @@ -363,14 +416,20 @@ jobs: - name: Check access to federated status endpoint if: ${{ inputs.federated-folder != '' }} - run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/status.php + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} + run: curl http://localhost:80${FEDERATED_ROOT}/status.php - name: Check access to federated login endpoint - run: curl http://localhost:80${{ steps.prepend.outputs.federated-root }}/login + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} + run: curl http://localhost:80${FEDERATED_ROOT}/login - name: Define environment variable pointing to the federated server if: ${{ inputs.federated-folder != '' }} - run: echo "TEST_SERVER_FED_URL=http://localhost${{ steps.prepend.outputs.federated-root }}" >> $GITHUB_ENV + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} + run: echo "TEST_SERVER_FED_URL=http://localhost${FEDERATED_ROOT}" >> $GITHUB_ENV - name: Install libxml2-utils to get xmllint used by acceptance test script run: sudo apt install -y libxml2-utils @@ -378,26 +437,29 @@ jobs: - name: Run API acceptance tests if: ${{ inputs.do-api-tests }} env: + APP_NAME: ${{ inputs.app-name }} TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | - cd apps/${{ inputs.app-name }} + cd "apps/${APP_NAME}" make test-acceptance-api - name: Run CLI acceptance tests if: ${{ inputs.do-cli-tests }} env: + APP_NAME: ${{ inputs.app-name }} TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | - cd apps/${{ inputs.app-name }} + cd "apps/${APP_NAME}" make test-acceptance-cli - name: Run WebUI acceptance tests if: ${{ inputs.do-webui-tests }} env: + APP_NAME: ${{ inputs.app-name }} TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BROWSER: "chrome" SELENIUM_HOST: "localhost" @@ -405,34 +467,38 @@ jobs: BEHAT_SUITE: ${{ matrix.suite }} BEHAT_FILTER_TAGS: "" run: | - cd apps/${{ inputs.app-name }} + cd "apps/${APP_NAME}" make test-acceptance-webui - name: Display content of server settings and logs if tests failed if: failure() + env: + SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} run: | echo "server .htaccess" - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/.htaccess + sudo -u www-data cat /var/www/html${SERVER_ROOT}/.htaccess echo "server config.php" - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/config/config.php + sudo -u www-data cat /var/www/html${SERVER_ROOT}/config/config.php echo "server directory contents" - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }} - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.server-root }}/data + sudo -u www-data ls -l /var/www/html${SERVER_ROOT} + sudo -u www-data ls -l /var/www/html${SERVER_ROOT}/data echo "server owncloud.log" - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.server-root }}/data/owncloud.log + sudo -u www-data cat /var/www/html${SERVER_ROOT}/data/owncloud.log - name: Display content of federated settings and logs if tests failed if: failure() && inputs.federated-folder != '' + env: + FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} run: | echo "federated .htaccess" - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/.htaccess + sudo -u www-data cat /var/www/html${FEDERATED_ROOT}/.htaccess echo "federated config.php" - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/config/config.php + sudo -u www-data cat /var/www/html${FEDERATED_ROOT}/config/config.php echo "federated directory contents" - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }} - sudo -u www-data ls -l /var/www/html${{ steps.prepend.outputs.federated-root }}/data + sudo -u www-data ls -l /var/www/html${FEDERATED_ROOT} + sudo -u www-data ls -l /var/www/html${FEDERATED_ROOT}/data echo "federated owncloud.log" - sudo -u www-data cat /var/www/html${{ steps.prepend.outputs.federated-root }}/data/owncloud.log + sudo -u www-data cat /var/www/html${FEDERATED_ROOT}/data/owncloud.log - name: Display Apache config and logs if tests failed if: failure() From 9e5ee5985f29cd68bca6236e6c667007254f358c Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Sun, 19 Apr 2026 21:02:36 +0930 Subject: [PATCH 35/44] chore(ci): use default core master --- .github/workflows/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2b20bef9..98409ccc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,7 +75,6 @@ jobs: do-api-tests: true server-folder: 'server' federated-folder: 'federated' - core-branch-for-test-code: fix-trashbin-ref acceptance-webui: name: WebUI acceptance tests @@ -86,7 +85,6 @@ jobs: app-name: ${{ needs.get-vars.outputs.app-name }} do-webui-tests: true test-suites: "['webUIActivityComments', 'webUIActivityCreateUpdate', 'webUIActivityDeleteRestore', 'webUIActivityFileMoveAndRename', 'webUIActivitySharingInternal', 'webUIActivityTags']" - core-branch-for-test-code: fix-trashbin-ref acceptance-webui-federated: name: WebUI acceptance tests that need a federated server @@ -99,4 +97,3 @@ jobs: server-folder: 'server' federated-folder: 'federated' test-suites: "['webUIActivitySharingExternal']" - core-branch-for-test-code: fix-trashbin-ref From bdc5823f4b240b47ad177d1cf08af3fb0d6a727c Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 20 Apr 2026 11:29:44 +0930 Subject: [PATCH 36/44] chore(ci): add filter-tags option to acceptance test workflow --- .github/workflows/acceptance.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml index 61bf00f5..dd4dc0e1 100644 --- a/.github/workflows/acceptance.yml +++ b/.github/workflows/acceptance.yml @@ -70,6 +70,11 @@ on: required: false type: string default: 'master' + filter-tags: + description: test tags to run (or not run) + required: false + type: string + default: '' permissions: contents: read @@ -440,7 +445,7 @@ jobs: APP_NAME: ${{ inputs.app-name }} TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} - BEHAT_FILTER_TAGS: "" + BEHAT_FILTER_TAGS: "${{ inputs.filter-tags }}" run: | cd "apps/${APP_NAME}" make test-acceptance-api @@ -451,7 +456,7 @@ jobs: APP_NAME: ${{ inputs.app-name }} TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} BEHAT_SUITE: ${{ matrix.suite }} - BEHAT_FILTER_TAGS: "" + BEHAT_FILTER_TAGS: "${{ inputs.filter-tags }}" run: | cd "apps/${APP_NAME}" make test-acceptance-cli @@ -465,7 +470,7 @@ jobs: SELENIUM_HOST: "localhost" SELENIUM_PORT: "4444" BEHAT_SUITE: ${{ matrix.suite }} - BEHAT_FILTER_TAGS: "" + BEHAT_FILTER_TAGS: "${{ inputs.filter-tags }}" run: | cd "apps/${APP_NAME}" make test-acceptance-webui From 1e8d10248dfcf3da72b2de53bce0b036ebc4e261 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 20 Apr 2026 19:17:53 +0930 Subject: [PATCH 37/44] chore(ci): use reusable-workflows acceptance workflow --- .github/workflows/acceptance.yml | 524 ------------------------------- .github/workflows/main.yml | 6 +- 2 files changed, 3 insertions(+), 527 deletions(-) delete mode 100644 .github/workflows/acceptance.yml diff --git a/.github/workflows/acceptance.yml b/.github/workflows/acceptance.yml deleted file mode 100644 index dd4dc0e1..00000000 --- a/.github/workflows/acceptance.yml +++ /dev/null @@ -1,524 +0,0 @@ -on: - workflow_call: - inputs: - app-name: - description: 'Name of the app' - required: true - type: string - php-versions: - description: JSON array of PHP versions - required: false - type: string - default: '["8.3"]' - databases: - description: JSON array of databases - required: false - type: string - default: '["mariadb:10.6"]' - test-suites: - description: JSON array of acceptance test suite names - required: false - type: string - default: '[""]' - app-repository: - description: 'Repository of the app (optional, defaults to the current repository)' - required: false - type: string - default: '' - server-folder: - description: 'The folder under the Apache root in which to install owncloud core' - required: false - type: string - default: '' - federated-folder: - description: 'The folder under the Apache root in which to install a federated server' - required: false - type: string - default: '' - do-api-tests: - description: 'Run API acceptance tests' - required: false - type: boolean - default: false - do-cli-tests: - description: 'Run CLI acceptance tests' - required: false - type: boolean - default: false - do-webui-tests: - description: 'Run WebUI acceptance tests' - required: false - type: boolean - default: false - additional-app: - description: 'Additional app to install and enable (if needed for testing)' - required: false - type: string - default: '' - additional-app-github-token: - description: 'GitHub PAT to access the additional app repo - required if private' - required: false - type: string - default: '' - additional-packages: - description: Additional Linux packages to be installed - space separated - required: false - type: string - default: '' - core-branch-for-test-code: - description: the branch of core to be used for the common acceptance test code - required: false - type: string - default: 'master' - filter-tags: - description: test tags to run (or not run) - required: false - type: string - default: '' - -permissions: - contents: read - -jobs: - acceptance: - name: Acceptance tests - runs-on: ubuntu-latest - permissions: - contents: read - strategy: - fail-fast: true - matrix: - php: ${{ fromJSON(inputs.php-versions) }} - database: ${{ fromJSON(inputs.databases) }} - suite: ${{ fromJSON(inputs.test-suites) }} - - services: - mysql: - image: >- - ${{ (startsWith(matrix.database,'mysql:') || startsWith(matrix.database,'mariadb:')) && matrix.database || '' }} - env: - MYSQL_ROOT_PASSWORD: password - MYSQL_DATABASE: owncloud - MYSQL_USER: owncloud - MYSQL_PASSWORD: owncloud - options: >- - --health-cmd="mysqladmin ping -u root -ppassword" - --health-interval=10s - --health-timeout=5s - --health-retries=3 - ports: - - 3306:3306 - postgres: - image: ${{ startsWith(matrix.database,'postgres:') && matrix.database || '' }} - env: - POSTGRES_DB: owncloud - POSTGRES_USER: owncloud - POSTGRES_PASSWORD: owncloud - ports: - - 5432:5432 - - steps: - - name: Prepend folder names with slash - id: prepend - env: - SERVER_FOLDER: ${{ inputs.server-folder }} - FEDERATED_FOLDER: ${{ inputs.federated-folder }} - run: | - if [ -n "${SERVER_FOLDER}" ]; then - echo "server-root=/${SERVER_FOLDER}" >> $GITHUB_OUTPUT - else - echo "server-root=" >> $GITHUB_OUTPUT - fi - if [ -n "${FEDERATED_FOLDER}" ]; then - echo "federated-root=/${FEDERATED_FOLDER}" >> $GITHUB_OUTPUT - else - echo "federated-root=" >> $GITHUB_OUTPUT - fi - - - name: Validate app-repository - if: ${{ inputs.app-repository != '' }} - env: - APP_REPO: ${{ inputs.app-repository }} - run: | - if ! echo "$APP_REPO" | grep -qE '^owncloud/[a-zA-Z0-9._-]+$'; then - echo "Error: app-repository must be within the owncloud/ namespace, got: $APP_REPO" - exit 1 - fi - - - name: Clone owncloud/core ${{ inputs.core-branch-for-test-code }} branch - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - repository: owncloud/core - ref: ${{ inputs.core-branch-for-test-code }} - - - name: Checkout app - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - path: apps/${{ inputs.app-name }} - repository: ${{ inputs.app-repository }} - - - name: Checkout additional app - if: ${{ inputs.additional-app != '' }} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - path: apps/${{ inputs.additional-app }} - repository: owncloud/${{ inputs.additional-app }} - token: ${{ inputs.additional-app-github-token || github.token }} - - - name: Install Linux packages - if: ${{ inputs.additional-packages }} - env: - ADDITIONAL_PACKAGES: ${{ inputs.additional-packages }} - run: | - sudo apt-get install $ADDITIONAL_PACKAGES - - - name: Setup PHP - uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.37.0 - with: - php-version: ${{ matrix.php }} - extensions: apcu, ctype, curl, exif, fileinfo, gd, iconv, imagick, intl, json, mbstring, memcached, pdo, posix, simplexml, xml, zip, smbclient, ldap, krb5 - ini-values: "memory_limit=1024M" - env: - fail-fast: true - KRB5_LINUX_LIBS: libkrb5-dev - - - name: Install Apache - env: - PHP_VERSION: ${{ matrix.php }} - run: | - sudo apt-get update - sudo apt-get install -y "libapache2-mod-php${PHP_VERSION}" - sudo apt-get install -y apache2 - sudo a2enmod rewrite headers env dir mime ssl expires dav dav_fs - sudo service apache2 restart - - - name: Configure Apache for single server only (no federated) - if: ${{ inputs.federated-folder == '' }} - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - echo "export SERVER_SUBDIR=${SERVER_ROOT}" | sudo tee -a /etc/apache2/envvars - sudo cp tests/acceptance/setupApache/single-server.conf /etc/apache2/sites-available - sudo a2dissite 000-default - sudo a2ensite single-server - - - name: Configure Apache for dual server (federated) - if: ${{ inputs.federated-folder != '' }} - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - echo "export SERVER_SUBDIR=${SERVER_ROOT}" | sudo tee -a /etc/apache2/envvars - echo "export FEDERATED_SUBDIR=${SERVER_ROOT}" | sudo tee -a /etc/apache2/envvars - sudo cp tests/acceptance/setupApache/dual-server.conf /etc/apache2/sites-available - sudo a2dissite 000-default - sudo a2ensite dual-server - - - name: Restart Apache to get new settings - run: | - sudo systemctl restart apache2 - - - name: Check Apache server status - run: curl http://localhost:80 - - - name: Delete default Apache server html files - run: sudo rm -Rf /var/www/html/* - - - name: Install owncloud/core - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - VERSION: 'daily-master-qa' - CORE_PATH: /var/www/html${{ steps.prepend.outputs.server-root }} - DB_TYPE: 'mysql' - DB_NAME: 'owncloud' - DB_HOST: '127.0.0.1' - DB_USERNAME: 'owncloud' - DB_PASSWORD: 'owncloud' - run: | - docker run -v /var/www/html${SERVER_ROOT}:/var/www/html${SERVER_ROOT} \ - --network=host \ - -e PLUGIN_VERSION=${VERSION} \ - -e PLUGIN_CORE_PATH=${CORE_PATH} \ - -e PLUGIN_DB_TYPE=${DB_TYPE} \ - -e PLUGIN_DB_NAME=${DB_NAME} \ - -e PLUGIN_DB_HOST=${DB_HOST} \ - -e PLUGIN_DB_USERNAME=${DB_USERNAME} \ - -e PLUGIN_DB_PASSWORD=${DB_PASSWORD} \ - owncloudci/core:php83 - - - name: Create database for federated server - if: ${{ inputs.federated-folder != '' }} - run: | - mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "CREATE DATABASE federateddb;" - mysql --host 127.0.0.1 --port 3306 -uroot -ppassword -e "GRANT ALL PRIVILEGES ON federateddb.* TO 'owncloud'@'%';" - - - name: Install federated owncloud/core - if: ${{ inputs.federated-folder != '' }} - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - VERSION: 'daily-master-qa' - CORE_PATH: /var/www/html${{ steps.prepend.outputs.federated-root }} - DB_TYPE: 'mysql' - DB_NAME: federateddb - DB_HOST: '127.0.0.1' - DB_USERNAME: 'owncloud' - DB_PASSWORD: 'owncloud' - run: | - docker run -v /var/www/html${FEDERATED_ROOT}:/var/www/html${FEDERATED_ROOT} \ - --network=host \ - -e PLUGIN_VERSION=${VERSION} \ - -e PLUGIN_CORE_PATH=${CORE_PATH} \ - -e PLUGIN_DB_TYPE=${DB_TYPE} \ - -e PLUGIN_DB_NAME=${DB_NAME} \ - -e PLUGIN_DB_HOST=${DB_HOST} \ - -e PLUGIN_DB_USERNAME=${DB_USERNAME} \ - -e PLUGIN_DB_PASSWORD=${DB_PASSWORD} \ - owncloudci/core:php83 - - - name: Setup permissions for the backend of the Apache server - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - sudo chown -R www-data:www-data /var/www/html${SERVER_ROOT} - sudo chmod -R 777 /var/www/html${SERVER_ROOT} - - - name: Setup permissions for the backend of the federated Apache server - if: ${{ inputs.federated-folder != '' }} - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: | - sudo chown -R www-data:www-data /var/www/html${FEDERATED_ROOT} - sudo chmod -R 777 /var/www/html${FEDERATED_ROOT} - - - name: Selenium - if: ${{ inputs.do-webui-tests }} - run: | - docker run -d --rm --network host \ - --name selenium-chrome \ - selenium/standalone-chrome-debug:3.141.59-oxygen - - - name: Make local core - run: | - make - - - name: Setup app - env: - APP_NAME: ${{ inputs.app-name }} - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - cd "apps/${APP_NAME}" - make ci - sudo -u www-data mkdir /var/www/html${SERVER_ROOT}/apps/${APP_NAME} - sudo -u www-data cp -r * /var/www/html${SERVER_ROOT}/apps/${APP_NAME} - cd "/var/www/html${SERVER_ROOT}" - sudo -u www-data php occ a:e "${APP_NAME}" - - - name: Setup app on federated server - if: ${{ inputs.federated-folder != '' }} - env: - APP_NAME: ${{ inputs.app-name }} - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: | - cd "apps/${APP_NAME}" - sudo -u www-data mkdir /var/www/html${FEDERATED_ROOT}/apps/${APP_NAME} - sudo -u www-data cp -r * /var/www/html${FEDERATED_ROOT}/apps/${APP_NAME} - cd "/var/www/html${FEDERATED_ROOT}" - sudo -u www-data php occ a:e "${APP_NAME}" - - - name: Setup additional app - if: ${{ inputs.additional-app != '' }} - env: - ADDITIONAL_APP: ${{ inputs.additional-app }} - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - cd "apps/${ADDITIONAL_APP}" - make ci - sudo -u www-data mkdir /var/www/html${SERVER_ROOT}/apps/${ADDITIONAL_APP} - sudo -u www-data cp -r * /var/www/html${SERVER_ROOT}/apps/${ADDITIONAL_APP} - cd "/var/www/html${SERVER_ROOT}" - sudo -u www-data php occ a:e "${ADDITIONAL_APP}" - - - name: Setup additional app on federated server - if: ${{ inputs.additional-app != '' && inputs.federated-folder != '' }} - env: - ADDITIONAL_APP: ${{ inputs.additional-app }} - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: | - cd "apps/${ADDITIONAL_APP}" - sudo -u www-data mkdir /var/www/html${FEDERATED_ROOT}/apps/${ADDITIONAL_APP} - sudo -u www-data cp -r * /var/www/html${FEDERATED_ROOT}/apps/${ADDITIONAL_APP} - cd "/var/www/html${FEDERATED_ROOT}" - sudo -u www-data php occ a:e "${ADDITIONAL_APP}" - - - name: Setup owncloud server settings - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - cd "/var/www/html${SERVER_ROOT}" - sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${SERVER_ROOT} - sudo -u www-data php occ maintenance:update:htaccess - sudo -u www-data php occ log:manage --level 2 - sudo -u www-data php occ config:system:set csrf.disabled --value=true - sudo -u www-data php occ a:e testing - sudo -u www-data php occ a:l -e - sudo -u www-data php occ config:list - - - name: Allow Http fallback for federation to local server - if: ${{ inputs.federated-folder != '' }} - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - cd "/var/www/html${SERVER_ROOT}" - sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - - - name: Setup owncloud federated server settings - if: ${{ inputs.federated-folder != '' }} - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: | - cd "/var/www/html${FEDERATED_ROOT}" - sudo -u www-data php occ config:system:set trusted_domains 1 --value=localhost - sudo -u www-data php occ config:system:set sharing.federation.allowHttpFallback --value="true" --type=boolean - sudo -u www-data php occ config:system:set htaccess.RewriteBase --value=${FEDERATED_ROOT} - sudo -u www-data php occ maintenance:update:htaccess - sudo -u www-data php occ log:manage --level 2 - sudo -u www-data php occ config:system:set csrf.disabled --value=true - sudo -u www-data php occ a:e testing - sudo -u www-data php occ a:l -e - sudo -u www-data php occ config:list - - - name: Check access to status endpoint - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: curl http://localhost:80${SERVER_ROOT}/status.php - - - name: Check access to login endpoint - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: curl http://localhost:80${SERVER_ROOT}/login - - - name: Check content of server files - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - sudo -u www-data cat /var/www/html${SERVER_ROOT}/.htaccess - sudo -u www-data cat /var/www/html${SERVER_ROOT}/config/config.php - sudo cat /etc/apache2/apache2.conf - sudo cat /etc/apache2/ports.conf - sudo cat /etc/apache2/envvars - sudo ls -l /etc/apache2/sites-available - sudo ls -l /etc/apache2/sites-enabled - - - name: Display single server site config - if: ${{ inputs.federated-folder == '' }} - run: | - sudo cat /etc/apache2/sites-enabled/single-server.conf - - - name: Display dual server site config - if: ${{ inputs.federated-folder != '' }} - run: | - sudo cat /etc/apache2/sites-enabled/dual-server.conf - - - name: Check access to federated status endpoint - if: ${{ inputs.federated-folder != '' }} - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: curl http://localhost:80${FEDERATED_ROOT}/status.php - - - name: Check access to federated login endpoint - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: curl http://localhost:80${FEDERATED_ROOT}/login - - - name: Define environment variable pointing to the federated server - if: ${{ inputs.federated-folder != '' }} - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: echo "TEST_SERVER_FED_URL=http://localhost${FEDERATED_ROOT}" >> $GITHUB_ENV - - - name: Install libxml2-utils to get xmllint used by acceptance test script - run: sudo apt install -y libxml2-utils - - - name: Run API acceptance tests - if: ${{ inputs.do-api-tests }} - env: - APP_NAME: ${{ inputs.app-name }} - TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} - BEHAT_SUITE: ${{ matrix.suite }} - BEHAT_FILTER_TAGS: "${{ inputs.filter-tags }}" - run: | - cd "apps/${APP_NAME}" - make test-acceptance-api - - - name: Run CLI acceptance tests - if: ${{ inputs.do-cli-tests }} - env: - APP_NAME: ${{ inputs.app-name }} - TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} - BEHAT_SUITE: ${{ matrix.suite }} - BEHAT_FILTER_TAGS: "${{ inputs.filter-tags }}" - run: | - cd "apps/${APP_NAME}" - make test-acceptance-cli - - - name: Run WebUI acceptance tests - if: ${{ inputs.do-webui-tests }} - env: - APP_NAME: ${{ inputs.app-name }} - TEST_SERVER_URL: http://localhost${{ steps.prepend.outputs.server-root }} - BROWSER: "chrome" - SELENIUM_HOST: "localhost" - SELENIUM_PORT: "4444" - BEHAT_SUITE: ${{ matrix.suite }} - BEHAT_FILTER_TAGS: "${{ inputs.filter-tags }}" - run: | - cd "apps/${APP_NAME}" - make test-acceptance-webui - - - name: Display content of server settings and logs if tests failed - if: failure() - env: - SERVER_ROOT: ${{ steps.prepend.outputs.server-root }} - run: | - echo "server .htaccess" - sudo -u www-data cat /var/www/html${SERVER_ROOT}/.htaccess - echo "server config.php" - sudo -u www-data cat /var/www/html${SERVER_ROOT}/config/config.php - echo "server directory contents" - sudo -u www-data ls -l /var/www/html${SERVER_ROOT} - sudo -u www-data ls -l /var/www/html${SERVER_ROOT}/data - echo "server owncloud.log" - sudo -u www-data cat /var/www/html${SERVER_ROOT}/data/owncloud.log - - - name: Display content of federated settings and logs if tests failed - if: failure() && inputs.federated-folder != '' - env: - FEDERATED_ROOT: ${{ steps.prepend.outputs.federated-root }} - run: | - echo "federated .htaccess" - sudo -u www-data cat /var/www/html${FEDERATED_ROOT}/.htaccess - echo "federated config.php" - sudo -u www-data cat /var/www/html${FEDERATED_ROOT}/config/config.php - echo "federated directory contents" - sudo -u www-data ls -l /var/www/html${FEDERATED_ROOT} - sudo -u www-data ls -l /var/www/html${FEDERATED_ROOT}/data - echo "federated owncloud.log" - sudo -u www-data cat /var/www/html${FEDERATED_ROOT}/data/owncloud.log - - - name: Display Apache config and logs if tests failed - if: failure() - run: | - echo "apache2.conf" - sudo cat /etc/apache2/apache2.conf - echo "ports.conf" - sudo cat /etc/apache2/ports.conf - echo "envvars" - sudo cat /etc/apache2/envvars - echo "sites-available" - sudo ls -l /etc/apache2/sites-available - echo "sites-enabled" - sudo ls -l /etc/apache2/sites-enabled - echo "access log" - sudo cat /var/log/apache2/access.log - echo "error log" - sudo cat /var/log/apache2/error.log diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 98409ccc..10120a01 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -69,7 +69,7 @@ jobs: name: API acceptance tests needs: - get-vars - uses: ./.github/workflows/acceptance.yml + uses: owncloud/reusable-workflows/.github/workflows/acceptance.yml@main with: app-name: ${{ needs.get-vars.outputs.app-name }} do-api-tests: true @@ -80,7 +80,7 @@ jobs: name: WebUI acceptance tests needs: - get-vars - uses: ./.github/workflows/acceptance.yml + uses: owncloud/reusable-workflows/.github/workflows/acceptance.yml@main with: app-name: ${{ needs.get-vars.outputs.app-name }} do-webui-tests: true @@ -90,7 +90,7 @@ jobs: name: WebUI acceptance tests that need a federated server needs: - get-vars - uses: ./.github/workflows/acceptance.yml + uses: owncloud/reusable-workflows/.github/workflows/acceptance.yml@main with: app-name: ${{ needs.get-vars.outputs.app-name }} do-webui-tests: true From ade53de69c91634b99580b1dac1d1ebc05b45594 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Mon, 20 Apr 2026 20:50:51 +0930 Subject: [PATCH 38/44] fix: format parameter must be a string --- lib/Formatter/BaseFormatter.php | 2 +- lib/Formatter/CloudIDFormatter.php | 2 +- lib/Formatter/FileFormatter.php | 2 +- lib/Formatter/GroupFormatter.php | 2 +- lib/Formatter/IFormatter.php | 2 +- lib/Formatter/UrlFormatter.php | 2 +- lib/Formatter/UserFormatter.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/Formatter/BaseFormatter.php b/lib/Formatter/BaseFormatter.php index 848b0833..675676d5 100644 --- a/lib/Formatter/BaseFormatter.php +++ b/lib/Formatter/BaseFormatter.php @@ -30,7 +30,7 @@ class BaseFormatter implements IFormatter { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, $parameter) { + public function format(IEvent $event, string $parameter) { return '' . Util::sanitizeHTML($parameter) . ''; } } diff --git a/lib/Formatter/CloudIDFormatter.php b/lib/Formatter/CloudIDFormatter.php index de2839c8..95263b09 100644 --- a/lib/Formatter/CloudIDFormatter.php +++ b/lib/Formatter/CloudIDFormatter.php @@ -46,7 +46,7 @@ public function __construct(IManager $contactsManager) { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, $parameter) { + public function format(IEvent $event, string $parameter) { $displayName = $parameter; try { list($user, $server) = Helper::splitUserRemote($parameter); diff --git a/lib/Formatter/FileFormatter.php b/lib/Formatter/FileFormatter.php index dea332b5..37296879 100644 --- a/lib/Formatter/FileFormatter.php +++ b/lib/Formatter/FileFormatter.php @@ -55,7 +55,7 @@ public function __construct(ViewInfoCache $infoCache, IURLGenerator $urlGenerato * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, $parameter) { + public function format(IEvent $event, string $parameter) { $param = $this->fixLegacyFilename($parameter); // If the activity is about the very same file, we use the current path diff --git a/lib/Formatter/GroupFormatter.php b/lib/Formatter/GroupFormatter.php index 958270a1..d93b7422 100644 --- a/lib/Formatter/GroupFormatter.php +++ b/lib/Formatter/GroupFormatter.php @@ -38,7 +38,7 @@ public function __construct(IGroupManager $groupManager) { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, $parameter) { + public function format(IEvent $event, string $parameter) { $group = $this->groupManager->get($parameter); $displayName = $parameter; if ($group !== null) { diff --git a/lib/Formatter/IFormatter.php b/lib/Formatter/IFormatter.php index c7b9f927..f5fa3981 100644 --- a/lib/Formatter/IFormatter.php +++ b/lib/Formatter/IFormatter.php @@ -29,5 +29,5 @@ interface IFormatter { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, $parameter); + public function format(IEvent $event, string $parameter); } diff --git a/lib/Formatter/UrlFormatter.php b/lib/Formatter/UrlFormatter.php index 6e6e3464..bdc88e80 100644 --- a/lib/Formatter/UrlFormatter.php +++ b/lib/Formatter/UrlFormatter.php @@ -39,7 +39,7 @@ class UrlFormatter implements IFormatter { * * @return string The formatted parameter */ - public function format(IEvent $event, $parameter) { + public function format(IEvent $event, string $parameter) { $params = \json_decode($parameter, true); if (!isset($params['url'])) { // we can't work without a url diff --git a/lib/Formatter/UserFormatter.php b/lib/Formatter/UserFormatter.php index ee5f8955..6591b192 100644 --- a/lib/Formatter/UserFormatter.php +++ b/lib/Formatter/UserFormatter.php @@ -46,7 +46,7 @@ public function __construct(IUserManager $userManager, IL10N $l) { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, $parameter) { + public function format(IEvent $event, string $parameter) { // If the username is empty, the action has been performed by a remote // user, or via a public share. We don't know the username in that case if ($parameter === '') { From da0966702284e7dc120d12e26dd7877c428c5ae1 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 21 Apr 2026 11:07:34 +0930 Subject: [PATCH 39/44] fix: handle Collection parameters that are arrays --- lib/Parameter/Collection.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Parameter/Collection.php b/lib/Parameter/Collection.php index d4ed582d..e4e5b83d 100644 --- a/lib/Parameter/Collection.php +++ b/lib/Parameter/Collection.php @@ -27,7 +27,7 @@ class Collection implements IParameter { /** @var IL10N */ protected $l; - /** @var IParameter[] */ + /** @var IParameter[]|IParameter[][] */ protected $parameters; /** @var string */ @@ -84,7 +84,13 @@ public function format() { $parameterList = $plainParameterList = []; foreach ($this->parameters as $parameter) { - $parameterList[] = $parameter->format(); + if (\is_array($parameter)) { + foreach ($parameter as $parameterValue) { + $parameterList[] = $parameterValue->format(); + } + } else { + $parameterList[] = $parameter->format(); + } } return '' . \implode('', $parameterList) . ''; From d0bbea66b4b92751a07b9f439459cdf40cef1dec Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 21 Apr 2026 12:02:56 +0930 Subject: [PATCH 40/44] fix: FileFormatter format parameter can be an array --- lib/Formatter/FileFormatter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Formatter/FileFormatter.php b/lib/Formatter/FileFormatter.php index 37296879..219cf733 100644 --- a/lib/Formatter/FileFormatter.php +++ b/lib/Formatter/FileFormatter.php @@ -52,10 +52,10 @@ public function __construct(ViewInfoCache $infoCache, IURLGenerator $urlGenerato /** * @param IEvent $event - * @param string $parameter The parameter to be formatted + * @param string|array $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, string $parameter) { + public function format(IEvent $event, string|array $parameter) { $param = $this->fixLegacyFilename($parameter); // If the activity is about the very same file, we use the current path From 21c44fe08821b3cdd1908ba75c473a3941294b83 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 21 Apr 2026 12:37:52 +0930 Subject: [PATCH 41/44] fix: handle array passed to BaseFormatter format --- lib/Formatter/BaseFormatter.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Formatter/BaseFormatter.php b/lib/Formatter/BaseFormatter.php index 675676d5..74e0cd94 100644 --- a/lib/Formatter/BaseFormatter.php +++ b/lib/Formatter/BaseFormatter.php @@ -28,9 +28,13 @@ class BaseFormatter implements IFormatter { /** * @param IEvent $event * @param string $parameter The parameter to be formatted - * @return string The formatted parameter + * @return string|array The formatted parameter */ - public function format(IEvent $event, string $parameter) { - return '' . Util::sanitizeHTML($parameter) . ''; + public function format(IEvent $event, string|array $parameter) { + $sanitizedParameter = Util::sanitizeHTML($parameter); + if (\is_array($sanitizedParameter)) { + $sanitizedParameter = \implode("", $sanitizedParameter); + } + return '' . $sanitizedParameter . ''; } } From bce6bfcf4bd485420b16d0f6eef34b0e958d1912 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 21 Apr 2026 15:37:03 +0930 Subject: [PATCH 42/44] fix: specify return type string for format method --- lib/Formatter/BaseFormatter.php | 6 +++--- lib/Formatter/CloudIDFormatter.php | 2 +- lib/Formatter/FileFormatter.php | 2 +- lib/Formatter/GroupFormatter.php | 2 +- lib/Formatter/IFormatter.php | 2 +- lib/Formatter/UrlFormatter.php | 2 +- lib/Formatter/UserFormatter.php | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/Formatter/BaseFormatter.php b/lib/Formatter/BaseFormatter.php index 74e0cd94..d11f8c76 100644 --- a/lib/Formatter/BaseFormatter.php +++ b/lib/Formatter/BaseFormatter.php @@ -27,10 +27,10 @@ class BaseFormatter implements IFormatter { /** * @param IEvent $event - * @param string $parameter The parameter to be formatted - * @return string|array The formatted parameter + * @param string|array $parameter The parameter to be formatted + * @return string The formatted parameter */ - public function format(IEvent $event, string|array $parameter) { + public function format(IEvent $event, string|array $parameter): string { $sanitizedParameter = Util::sanitizeHTML($parameter); if (\is_array($sanitizedParameter)) { $sanitizedParameter = \implode("", $sanitizedParameter); diff --git a/lib/Formatter/CloudIDFormatter.php b/lib/Formatter/CloudIDFormatter.php index 95263b09..bd60685f 100644 --- a/lib/Formatter/CloudIDFormatter.php +++ b/lib/Formatter/CloudIDFormatter.php @@ -46,7 +46,7 @@ public function __construct(IManager $contactsManager) { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, string $parameter) { + public function format(IEvent $event, string $parameter): string { $displayName = $parameter; try { list($user, $server) = Helper::splitUserRemote($parameter); diff --git a/lib/Formatter/FileFormatter.php b/lib/Formatter/FileFormatter.php index 219cf733..5b65ead1 100644 --- a/lib/Formatter/FileFormatter.php +++ b/lib/Formatter/FileFormatter.php @@ -55,7 +55,7 @@ public function __construct(ViewInfoCache $infoCache, IURLGenerator $urlGenerato * @param string|array $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, string|array $parameter) { + public function format(IEvent $event, string|array $parameter): string { $param = $this->fixLegacyFilename($parameter); // If the activity is about the very same file, we use the current path diff --git a/lib/Formatter/GroupFormatter.php b/lib/Formatter/GroupFormatter.php index d93b7422..dc2e9460 100644 --- a/lib/Formatter/GroupFormatter.php +++ b/lib/Formatter/GroupFormatter.php @@ -38,7 +38,7 @@ public function __construct(IGroupManager $groupManager) { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, string $parameter) { + public function format(IEvent $event, string $parameter): string { $group = $this->groupManager->get($parameter); $displayName = $parameter; if ($group !== null) { diff --git a/lib/Formatter/IFormatter.php b/lib/Formatter/IFormatter.php index f5fa3981..62ac4664 100644 --- a/lib/Formatter/IFormatter.php +++ b/lib/Formatter/IFormatter.php @@ -29,5 +29,5 @@ interface IFormatter { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, string $parameter); + public function format(IEvent $event, string $parameter): string; } diff --git a/lib/Formatter/UrlFormatter.php b/lib/Formatter/UrlFormatter.php index bdc88e80..5785323b 100644 --- a/lib/Formatter/UrlFormatter.php +++ b/lib/Formatter/UrlFormatter.php @@ -39,7 +39,7 @@ class UrlFormatter implements IFormatter { * * @return string The formatted parameter */ - public function format(IEvent $event, string $parameter) { + public function format(IEvent $event, string $parameter): string { $params = \json_decode($parameter, true); if (!isset($params['url'])) { // we can't work without a url diff --git a/lib/Formatter/UserFormatter.php b/lib/Formatter/UserFormatter.php index 6591b192..76a83139 100644 --- a/lib/Formatter/UserFormatter.php +++ b/lib/Formatter/UserFormatter.php @@ -46,7 +46,7 @@ public function __construct(IUserManager $userManager, IL10N $l) { * @param string $parameter The parameter to be formatted * @return string The formatted parameter */ - public function format(IEvent $event, string $parameter) { + public function format(IEvent $event, string $parameter): string { // If the username is empty, the action has been performed by a remote // user, or via a public share. We don't know the username in that case if ($parameter === '') { From 442892724511f09063b9a88b4736a67cba83924b Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 21 Apr 2026 15:54:11 +0930 Subject: [PATCH 43/44] fix: revert changes to Collection.php --- lib/Parameter/Collection.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/Parameter/Collection.php b/lib/Parameter/Collection.php index e4e5b83d..d4ed582d 100644 --- a/lib/Parameter/Collection.php +++ b/lib/Parameter/Collection.php @@ -27,7 +27,7 @@ class Collection implements IParameter { /** @var IL10N */ protected $l; - /** @var IParameter[]|IParameter[][] */ + /** @var IParameter[] */ protected $parameters; /** @var string */ @@ -84,13 +84,7 @@ public function format() { $parameterList = $plainParameterList = []; foreach ($this->parameters as $parameter) { - if (\is_array($parameter)) { - foreach ($parameter as $parameterValue) { - $parameterList[] = $parameterValue->format(); - } - } else { - $parameterList[] = $parameter->format(); - } + $parameterList[] = $parameter->format(); } return '' . \implode('', $parameterList) . ''; From 7d45f5b9caf85f9079bdbed49ccd5613a30ca258 Mon Sep 17 00:00:00 2001 From: Phillip Davis Date: Tue, 21 Apr 2026 16:34:37 +0930 Subject: [PATCH 44/44] chore: debug failing webUI test --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 10120a01..740c2e5a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -97,3 +97,4 @@ jobs: server-folder: 'server' federated-folder: 'federated' test-suites: "['webUIActivitySharingExternal']" + core-branch-for-test-code: "debug-login-as"