Skip to content
Open
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
66 changes: 66 additions & 0 deletions .github/workflows/phpunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

name: PHPUnit

on:
pull_request:
push:
branches:
- main
- master
- stable*

permissions:
contents: read

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

jobs:
phpunit:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ["8.2", "8.3", "8.4"]

name: PHPUnit PHP ${{ matrix.php-versions }}

steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false

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

- name: Install dependencies
run: |
composer i
composer bin phpunit install

- name: PHPUnit
run: composer run test:unit

summary:
permissions:
contents: none
runs-on: ubuntu-latest-low
needs: phpunit

if: always()

name: phpunit-summary

steps:
- name: Summary status
run: if ${{ needs.phpunit.result != 'success' && needs.phpunit.result != 'skipped' }}; then exit 1; fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
/vendor/symfony/service-contracts/Test
/vendor-bin/**/vendor
.php-cs-fixer.cache
.phpunit.result.cache
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ index.php: lib/UpdateException.php lib/LogException.php lib/Updater.php index.we
test/vendor:
composer bin tests install

vendor/bin/phpunit:
composer bin phpunit install

test: updater.phar test/vendor
cd tests && ../vendor/bin/behat

test-unit: vendor/bin/phpunit
composer run test:unit

test-cli: updater.phar test/vendor
cd tests && ../vendor/bin/behat features/cli.feature

Expand Down
2 changes: 1 addition & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ SPDX-FileCopyrightText = "2020 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "MIT"

[[annotations]]
path = ["vendor-bin/box/**", "vendor-bin/psalm/**", "vendor-bin/rector/**"]
path = ["vendor-bin/box/**", "vendor-bin/phpunit/**", "vendor-bin/psalm/**", "vendor-bin/rector/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "2023 Nextcloud GmbH and Nextcloud contributors"
SPDX-License-Identifier = "AGPL-3.0-or-later"
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"psalm": "psalm --threads=$(nproc)",
"psalm:ci": "psalm --threads=1",
"psalm:fix": "- --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType",
"rector": "rector && composer run cs:fix"
"rector": "rector && composer run cs:fix",
"test:unit": "phpunit -c phpunit.xml"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8"
Expand Down
61 changes: 36 additions & 25 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public function __construct(
throw new \Exception('Could not read data directory from config.php.');
}

$versionFileName = $this->nextcloudDir . '/version.php';
$versionFileName = $this->buildPath('version.php');
if (!file_exists($versionFileName)) {
// fallback to version in config.php
$version = $this->getConfigOptionString('version');
Expand Down Expand Up @@ -111,6 +111,17 @@ public function __construct(
$this->buildTime = $buildTime;
}

/**
* Builds an absolute path by joining the Nextcloud root directory with the provided relative path.
* Handles leading and trailing slashes to prevent double-slash issues.
*
* @param string $suffix Relative path to append (with or without leading slash)
* @return string The absolute path
*/
public function buildPath(string $suffix): string {
return rtrim($this->nextcloudDir, '/') . '/' . ltrim($suffix, '/');
}

/**
* @return array{array, string}
*/
Expand All @@ -121,7 +132,7 @@ private function readConfigFile(): array {
throw new \Exception('Configuration not found in ' . $dir);
}
} else {
$configFileName = $this->nextcloudDir . '/config/config.php';
$configFileName = $this->buildPath('config/config.php');
}

if (!file_exists($configFileName)) {
Expand Down Expand Up @@ -335,8 +346,8 @@ private function getAppDirectories(): array {
throw new \Exception('Invalid configuration in apps_paths configuration key');
}

if (str_starts_with($appsPath['path'], $this->nextcloudDir . '/')) {
$relativePath = substr($appsPath['path'], strlen($this->nextcloudDir . '/'));
if (str_starts_with($appsPath['path'], $this->buildPath(''))) {
$relativePath = substr($appsPath['path'], strlen($this->buildPath('')));
if ($relativePath !== 'apps') {
$expected[] = $relativePath;
}
Expand Down Expand Up @@ -436,13 +447,13 @@ public function checkWritePermissions(): void {
}

// Special handling for included default theme
foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir . '/themes/example', $excludedElements) as $fileInfo) {
foreach ($this->getRecursiveDirectoryIterator($this->buildPath('themes/example'), $excludedElements) as $fileInfo) {
if (!$fileInfo->isWritable()) {
$notWritablePaths[] = $fileInfo->getFilename();
}
}

$themesReadmeFileInfo = new \SplFileInfo($this->nextcloudDir . '/themes/README');
$themesReadmeFileInfo = new \SplFileInfo($this->buildPath('themes/README'));
if (!$themesReadmeFileInfo->isWritable()) {
$notWritablePaths[] = $themesReadmeFileInfo->getFilename();
}
Expand Down Expand Up @@ -502,14 +513,14 @@ public function createBackup(): void {
}

foreach ($this->getRecursiveDirectoryIterator($this->nextcloudDir, $excludedElements) as $absolutePath => $fileInfo) {
$relativePath = explode($this->nextcloudDir, $absolutePath)[1];
$relativePath = ltrim(substr($absolutePath, strlen($this->nextcloudDir)), '/');
$relativeDirectory = dirname($relativePath);

// Create folder if it doesn't exist
if (!file_exists($backupFolderLocation . '/' . $relativeDirectory)) {
$state = mkdir($backupFolderLocation . '/' . $relativeDirectory, 0750, true);
if (!file_exists($backupFolderLocation . $relativeDirectory)) {
$state = mkdir($backupFolderLocation . $relativeDirectory, 0750, true);
if ($state === false) {
throw new \Exception('Could not create folder: ' . $backupFolderLocation . '/' . $relativeDirectory);
throw new \Exception('Could not create folder: ' . $backupFolderLocation . $relativeDirectory);
}
}

Expand Down Expand Up @@ -949,7 +960,7 @@ public function extractDownload(): void {

// Ensure that the downloaded version is not lower
$downloadedVersion = $this->getVersionByVersionFile(dirname($downloadedFilePath) . '/nextcloud/version.php');
$currentVersion = $this->getVersionByVersionFile($this->nextcloudDir . '/version.php');
$currentVersion = $this->getVersionByVersionFile($this->buildPath('version.php'));
if (version_compare($downloadedVersion, $currentVersion, '<')) {
throw new \Exception('Downloaded version is lower than installed version');
}
Expand Down Expand Up @@ -977,15 +988,15 @@ public function replaceEntryPoints(): void {
$content = "<?php\nhttp_response_code(503);\ndie('Update in process.');";
foreach ($filesToReplace as $file) {
$this->silentLog('[info] replace ' . $file);
$parentDir = dirname($this->nextcloudDir . '/' . $file);
$parentDir = dirname($this->buildPath($file));
if (!file_exists($parentDir)) {
$r = mkdir($parentDir);
if (!$r) {
throw new \Exception("Can't create parent directory for entry point: " . $file);
}
}

$state = file_put_contents($this->nextcloudDir . '/' . $file, $content);
$state = file_put_contents($this->buildPath($file), $content);
if ($state === false) {
throw new \Exception("Can't replace entry point: " . $file);
}
Expand Down Expand Up @@ -1028,7 +1039,7 @@ private function recursiveDelete(string $folder): void {
public function deleteOldFiles(): void {
$this->silentLog('[info] deleteOldFiles()');

$shippedAppsFile = $this->nextcloudDir . '/core/shipped.json';
$shippedAppsFile = $this->buildPath('core/shipped.json');
$shippedAppsFileContent = file_get_contents($shippedAppsFile);
if ($shippedAppsFileContent === false) {
throw new \Exception('core/shipped.json is not available');
Expand Down Expand Up @@ -1056,10 +1067,10 @@ public function deleteOldFiles(): void {
$shippedApps = array_merge($shippedApps, $newShippedApps);
/** @var string $app */
foreach ($shippedApps as $app) {
$this->recursiveDelete($this->nextcloudDir . '/apps/' . $app);
$this->recursiveDelete($this->buildPath('apps/' . $app));
}

$configSampleFile = $this->nextcloudDir . '/config/config.sample.php';
$configSampleFile = $this->buildPath('config/config.sample.php');
if (file_exists($configSampleFile)) {
$this->silentLog('[info] config sample exists');

Expand All @@ -1070,7 +1081,7 @@ public function deleteOldFiles(): void {
}
}

$themesReadme = $this->nextcloudDir . '/themes/README';
$themesReadme = $this->buildPath('themes/README');
if (file_exists($themesReadme)) {
$this->silentLog('[info] themes README exists');

Expand All @@ -1081,7 +1092,7 @@ public function deleteOldFiles(): void {
}
}

$this->recursiveDelete($this->nextcloudDir . '/themes/example/');
$this->recursiveDelete($this->buildPath('themes/example/'));

// Delete the rest
$excludedElements = [
Expand Down Expand Up @@ -1128,23 +1139,23 @@ private function moveWithExclusions(string $dataLocation, array $excludedElement

}

$fileName = explode($dataLocation, $path)[1];
$fileName = substr($path, strlen($dataLocation));

if ($fileInfo->isFile()) {
if (!file_exists($this->nextcloudDir . '/' . dirname($fileName))) {
$state = mkdir($this->nextcloudDir . '/' . dirname($fileName), 0755, true);
if (!file_exists($this->buildPath(dirname($fileName)))) {
$state = mkdir($this->buildPath(dirname($fileName)), 0755, true);
if ($state === false) {
throw new \Exception('Could not mkdir ' . $this->nextcloudDir . '/' . dirname($fileName));
throw new \Exception('Could not mkdir ' . $this->buildPath(dirname($fileName)));
}
}

$state = @rename($path, $this->nextcloudDir . '/' . $fileName);
$state = @rename($path, $this->buildPath($fileName));
if ($state === false) {
throw new \Exception(
sprintf(
'Could not rename %s to %s',
$path,
$this->nextcloudDir . '/' . $fileName
$this->buildPath($fileName)
)
);
}
Expand Down Expand Up @@ -1217,7 +1228,7 @@ public function finalize(): void {
$user_ini_additional_lines = implode(PHP_EOL, $user_ini_additional_lines);
}

$result = file_put_contents($this->nextcloudDir . '/.user.ini', PHP_EOL . '; Additional settings from config.php:' . PHP_EOL . $user_ini_additional_lines . PHP_EOL, FILE_APPEND);
$result = file_put_contents($this->buildPath('.user.ini'), PHP_EOL . '; Additional settings from config.php:' . PHP_EOL . $user_ini_additional_lines . PHP_EOL, FILE_APPEND);
if ($result === false) {
throw new \Exception('Could not append to .user.ini');
}
Expand Down
Loading
Loading