Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT
name: PHPUnit

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: phpunit-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
phpunit:
runs-on: ubuntu-22.04
name: PHPUnit • PHP ${{ matrix.php-version }} • ${{ matrix.server-version }}
strategy:
fail-fast: false
matrix:
php-version: ['8.2', '8.3']
server-version: ['master']

steps:
- name: Set app env
run: echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV

- name: Checkout server
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
submodules: true
repository: nextcloud/server
ref: ${{ matrix.server-version }}

- name: Checkout AppAPI
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
path: apps/${{ env.APP_NAME }}

- name: Set up PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2
with:
php-version: ${{ matrix.php-version }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite3, pdo_sqlite
coverage: none
ini-file: development
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Install app dependencies
working-directory: apps/${{ env.APP_NAME }}
run: composer i

- name: Set up Nextcloud
run: |
mkdir data
./occ maintenance:install --verbose --database=sqlite --admin-user admin --admin-pass admin
./occ app:enable --force ${{ env.APP_NAME }}

- name: Run PHPUnit
working-directory: apps/${{ env.APP_NAME }}
run: composer run test:unit

phpunit-summary:
permissions:
contents: none
runs-on: ubuntu-22.04
needs: [phpunit]
name: PHPUnit-OK
steps:
- run: echo "PHPUnit tests passed successfully"
28 changes: 26 additions & 2 deletions lib/Service/AppAPIService.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ public function validateExAppRequestToNC(IRequest $request, bool $isDav = false)
$this->logger->error(sprintf('Error getting path info. Error: %s', $e->getMessage()), ['exception' => $e]);
return false;
}
if (($this->sanitizeOcsRoute($path) !== '/apps/app_api/ex-app/state') && !$exApp->getEnabled()) {
if (!$exApp->getEnabled() && !$this->isExemptFromEnabledCheck($path, $exApp)) {
$this->logger->error(sprintf('ExApp with appId %s is disabled (%s)', $request->getHeader('EX-APP-ID'), $request->getRequestUri()));
return false;
}
Expand Down Expand Up @@ -368,6 +368,26 @@ private function sanitizeOcsRoute(string $route): string {
return $route;
}

/**
* Check if the given path is exempt from the ExApp enabled check.
* /ex-app/state is always exempt (query enabled state).
* Init status endpoints are only exempt while the ExApp is actively being
* installed or updated (status type set server-side by Register/Update commands),
* preventing a disabled ExApp from re-enabling itself via set_init_status(100).
*/
private function isExemptFromEnabledCheck(string $path, ExApp $exApp): bool {
$sanitizedPath = $this->sanitizeOcsRoute($path);
if ($sanitizedPath === '/apps/app_api/ex-app/state') {
return true;
}
$status = $exApp->getStatus();
$isInitializing = in_array($status['type'] ?? '', ['install', 'update'], true);
if ($isInitializing && $sanitizedPath === '/apps/app_api/ex-app/status') {
return true;
}
return false;
}

private function getCustomLogger(string $name): LoggerInterface {
$path = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $name;
return $this->logFactory->getCustomPsrLogger($path);
Expand Down Expand Up @@ -411,7 +431,11 @@ public function dispatchExAppInitInternal(ExApp $exApp): void {
}

$this->setAppInitProgress($exApp, 0);
$this->exAppService->enableExAppInternal($exApp);
if (!$this->exAppService->enableExAppInternal($exApp)) {
$this->logger->error(sprintf('Failed to enable ExApp %s before init dispatch', $exApp->getAppid()));
$this->setAppInitProgress($exApp, 0, 'Failed to enable ExApp before init');
return;
}
try {
$this->client->post($initUrl, $options);
} catch (\Exception $e) {
Expand Down
Loading