From c6b1edd40615ca8bb1df90a0de104dd4c9f37a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6=20=28skjnldsv=29?= Date: Wed, 20 Aug 2025 10:28:25 +0200 Subject: [PATCH 1/2] chore: update psalm workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ (skjnldsv) [skip ci] --- .github/workflows/psalm.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 7be48f26..3d29b70e 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -2,6 +2,9 @@ # # https://github.com/nextcloud/.github # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization +# +# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: MIT name: Static analysis @@ -17,6 +20,9 @@ concurrency: group: psalm-${{ github.head_ref || github.run_id }} cancel-in-progress: true +permissions: + contents: read + jobs: static-analysis: runs-on: ubuntu-latest @@ -32,6 +38,8 @@ jobs: php-version: 8.1 coverage: none ini-file: development + # Temporary workaround for missing pcntl_* in PHP 8.3 + ini-values: disable_functions= env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -39,4 +47,4 @@ jobs: run: composer i - name: Run coding standards check - run: composer run psalm + run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github From d41d3b8ae9a12cb8f5549f9c5a05227adbb2fa07 Mon Sep 17 00:00:00 2001 From: skjnldsv Date: Thu, 10 Jul 2025 10:48:29 +0200 Subject: [PATCH 2/2] feat: allow to provide manual URL Signed-off-by: skjnldsv --- index.php | 35 ++++++++++++++++---- lib/UpdateCommand.php | 26 +++++++++++---- lib/Updater.php | 35 ++++++++++++++++---- updater.phar | Bin 763202 -> 765587 bytes vendor/autoload.php | 5 +-- vendor/composer/InstalledVersions.php | 45 +++++++++++++++++++++++--- vendor/composer/installed.php | 4 +-- vendor/composer/platform_check.php | 5 ++- 8 files changed, 121 insertions(+), 34 deletions(-) diff --git a/index.php b/index.php index 24aad88c..4a16553e 100644 --- a/index.php +++ b/index.php @@ -553,11 +553,9 @@ private function getUpdateServerResponse() { * * @throws \Exception */ - public function downloadUpdate() { + public function downloadUpdate(?string $url = null) { $this->silentLog('[info] downloadUpdate()'); - $response = $this->getUpdateServerResponse(); - $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; if (file_exists($storageLocation)) { $this->silentLog('[info] storage location exists'); @@ -568,8 +566,26 @@ public function downloadUpdate() { throw new \Exception('Could not mkdir storage location'); } - $fp = fopen($storageLocation . basename($response['url']), 'w+'); - $ch = curl_init($response['url']); + $downloadURL = ''; + if ($url) { + // If a URL is provided, use it directly + $downloadURL = $url; + } else { + // Otherwise, get the download URLs from the update server + $response = $this->getUpdateServerResponse(); + + if (!isset($response['url']) || !is_string($response['url'])) { + throw new \Exception('Response from update server is missing url'); + } + $downloadURL = $response['url']; + } + + if (!$downloadURL) { + throw new \Exception('No download URL provided or available from update server'); + } + + $fp = fopen($storageLocation . basename($downloadURL), 'w+'); + $ch = curl_init($downloadURL); curl_setopt_array($ch, [ CURLOPT_FILE => $fp, CURLOPT_USERAGENT => 'Nextcloud Updater', @@ -611,7 +627,7 @@ public function downloadUpdate() { $message .= ' - curl error message: ' . $curlErrorMessage; } - $message .= ' - URL: ' . htmlentities($response['url']); + $message .= ' - URL: ' . htmlentities($downloadURL); throw new \Exception($message); } @@ -645,7 +661,7 @@ private function getDownloadedFilePath() { * * @throws \Exception */ - public function verifyIntegrity() { + public function verifyIntegrity(?string $urlOverride = null): void { $this->silentLog('[info] verifyIntegrity()'); if ($this->getCurrentReleaseChannel() === 'daily') { @@ -653,6 +669,11 @@ public function verifyIntegrity() { return; } + if ($urlOverride) { + $this->silentLog('[info] custom download url provided, cannot verify signature'); + return; + } + $response = $this->getUpdateServerResponse(); if (empty($response['signature'])) { throw new \Exception('No signature specified for defined update'); diff --git a/lib/UpdateCommand.php b/lib/UpdateCommand.php index c3b0c381..b56bc809 100644 --- a/lib/UpdateCommand.php +++ b/lib/UpdateCommand.php @@ -38,6 +38,9 @@ class UpdateCommand extends Command { /** @var bool */ protected $skipBackup = false; + /** @var string URL override for update server */ + protected $urlOverride = ''; + /** @var array strings of text for stages of updater */ protected $checkTexts = [ 0 => '', @@ -60,7 +63,8 @@ protected function configure() { ->setName('update') ->setDescription('Updates the code of an Nextcloud instance') ->setHelp("This command fetches the latest code that is announced via the updater server and safely replaces the existing code with the new one.") - ->addOption('no-backup', null, InputOption::VALUE_NONE, 'Skip backup of current Nextcloud version'); + ->addOption('no-backup', null, InputOption::VALUE_NONE, 'Skip backup of current Nextcloud version') + ->addOption('url', null, InputOption::VALUE_OPTIONAL, 'The URL of the Nextcloud release to download'); } public static function getUpdaterVersion(): string { @@ -74,6 +78,7 @@ public static function getUpdaterVersion(): string { protected function execute(InputInterface $input, OutputInterface $output) { $this->skipBackup = $input->getOption('no-backup'); + $this->urlOverride = (string)$input->getOption('url'); $version = static::getUpdaterVersion(); $output->writeln('Nextcloud Updater - version: ' . $version); @@ -146,7 +151,12 @@ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln('Current version is ' . $this->updater->getCurrentVersion() . '.'); // needs to be called that early because otherwise updateAvailable() returns false - $updateString = $this->updater->checkForUpdate(); + if ($this->urlOverride) { + $this->updater->log('[info] Using URL override: ' . $this->urlOverride); + $updateString = 'Update check forced with URL override: ' . $this->urlOverride; + } else { + $updateString = $this->updater->checkForUpdate(); + } $output->writeln(''); @@ -159,9 +169,11 @@ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln(''); - if (!$this->updater->updateAvailable() && $stepNumber === 0) { - $output->writeln('Nothing to do.'); - return 0; + if (!$this->urlOverride) { + if (!$this->updater->updateAvailable() && $stepNumber === 0) { + $output->writeln('Nothing to do.'); + return 0; + } } $questionText = 'Start update'; @@ -362,10 +374,10 @@ protected function executeStep($step) { } break; case 4: - $this->updater->downloadUpdate(); + $this->updater->downloadUpdate($this->urlOverride); break; case 5: - $this->updater->verifyIntegrity(); + $this->updater->verifyIntegrity($this->urlOverride); break; case 6: $this->updater->extractDownload(); diff --git a/lib/Updater.php b/lib/Updater.php index 06f65e8b..16e3a721 100644 --- a/lib/Updater.php +++ b/lib/Updater.php @@ -517,11 +517,9 @@ private function getUpdateServerResponse() { * * @throws \Exception */ - public function downloadUpdate() { + public function downloadUpdate(?string $url = null) { $this->silentLog('[info] downloadUpdate()'); - $response = $this->getUpdateServerResponse(); - $storageLocation = $this->getUpdateDirectoryLocation() . '/updater-'.$this->getConfigOption('instanceid') . '/downloads/'; if (file_exists($storageLocation)) { $this->silentLog('[info] storage location exists'); @@ -532,8 +530,26 @@ public function downloadUpdate() { throw new \Exception('Could not mkdir storage location'); } - $fp = fopen($storageLocation . basename($response['url']), 'w+'); - $ch = curl_init($response['url']); + $downloadURL = ''; + if ($url) { + // If a URL is provided, use it directly + $downloadURL = $url; + } else { + // Otherwise, get the download URLs from the update server + $response = $this->getUpdateServerResponse(); + + if (!isset($response['url']) || !is_string($response['url'])) { + throw new \Exception('Response from update server is missing url'); + } + $downloadURL = $response['url']; + } + + if (!$downloadURL) { + throw new \Exception('No download URL provided or available from update server'); + } + + $fp = fopen($storageLocation . basename($downloadURL), 'w+'); + $ch = curl_init($downloadURL); curl_setopt_array($ch, [ CURLOPT_FILE => $fp, CURLOPT_USERAGENT => 'Nextcloud Updater', @@ -575,7 +591,7 @@ public function downloadUpdate() { $message .= ' - curl error message: ' . $curlErrorMessage; } - $message .= ' - URL: ' . htmlentities($response['url']); + $message .= ' - URL: ' . htmlentities($downloadURL); throw new \Exception($message); } @@ -609,7 +625,7 @@ private function getDownloadedFilePath() { * * @throws \Exception */ - public function verifyIntegrity() { + public function verifyIntegrity(?string $urlOverride = null): void { $this->silentLog('[info] verifyIntegrity()'); if ($this->getCurrentReleaseChannel() === 'daily') { @@ -617,6 +633,11 @@ public function verifyIntegrity() { return; } + if ($urlOverride) { + $this->silentLog('[info] custom download url provided, cannot verify signature'); + return; + } + $response = $this->getUpdateServerResponse(); if (empty($response['signature'])) { throw new \Exception('No signature specified for defined update'); diff --git a/updater.phar b/updater.phar index 53a9e46af79b3334c7f690902b5ca30fa2deaa59..dfb9a535afaa4051be270b9d9dcfaba68df2b445 100755 GIT binary patch delta 7866 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 delta 6208 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 diff --git a/vendor/autoload.php b/vendor/autoload.php index e97f2aed..f52071e9 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -14,10 +14,7 @@ echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 51e734a7..2052022f 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -26,12 +26,23 @@ */ class InstalledVersions { + /** + * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to + * @internal + */ + private static $selfDir = null; + /** * @var mixed[]|null * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null */ private static $installed; + /** + * @var bool + */ + private static $installedIsLocalDir; + /** * @var bool|null */ @@ -309,6 +320,24 @@ public static function reload($data) { self::$installed = $data; self::$installedByVendor = array(); + + // when using reload, we disable the duplicate protection to ensure that self::$installed data is + // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, + // so we have to assume it does not, and that may result in duplicate data being returned when listing + // all installed packages for example + self::$installedIsLocalDir = false; + } + + /** + * @return string + */ + private static function getSelfDir() + { + if (self::$selfDir === null) { + self::$selfDir = strtr(__DIR__, '\\', '/'); + } + + return self::$selfDir; } /** @@ -322,19 +351,27 @@ private static function getInstalled() } $installed = array(); + $copiedLocalDir = false; if (self::$canGetVendors) { + $selfDir = self::getSelfDir(); foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + $vendorDir = strtr($vendorDir, '\\', '/'); if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; } elseif (is_file($vendorDir.'/composer/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require $vendorDir.'/composer/installed.php'; - $installed[] = self::$installedByVendor[$vendorDir] = $required; - if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { - self::$installed = $installed[count($installed) - 1]; + self::$installedByVendor[$vendorDir] = $required; + $installed[] = $required; + if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { + self::$installed = $required; + self::$installedIsLocalDir = true; } } + if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { + $copiedLocalDir = true; + } } } @@ -350,7 +387,7 @@ private static function getInstalled() } } - if (self::$installed !== array()) { + if (self::$installed !== array() && !$copiedLocalDir) { $installed[] = self::$installed; } diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index f488cf6a..636b6a5c 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '2df6d29bfc56049682a84b2a4671b95e82314ae4', + 'reference' => '0fc0ad02ddc01813943b8ab4a54928c8f3dafaf4', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '2df6d29bfc56049682a84b2a4671b95e82314ae4', + 'reference' => '0fc0ad02ddc01813943b8ab4a54928c8f3dafaf4', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php index 580fa960..d2225c7d 100644 --- a/vendor/composer/platform_check.php +++ b/vendor/composer/platform_check.php @@ -19,8 +19,7 @@ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } - trigger_error( - 'Composer detected issues in your platform: ' . implode(' ', $issues), - E_USER_ERROR + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) ); }