diff --git a/index.php b/index.php index c95d25f6..ccba6572 100644 --- a/index.php +++ b/index.php @@ -784,7 +784,8 @@ private function downloadArchive(string $fromUrl, string $toLocation): bool { } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if ($httpCode !== 200 && $httpCode !== 206) { + $isFileUrl = parse_url($fromUrl, PHP_URL_SCHEME) === 'file'; + if (!$isFileUrl && $httpCode !== 200 && $httpCode !== 206) { fclose($fp); unlink($toLocation); $this->silentLog('[warn] fail to download archive from ' . $fromUrl . '. Error: ' . $httpCode . ' ' . curl_error($ch)); diff --git a/lib/Updater.php b/lib/Updater.php index aeded0ef..b4bd947b 100644 --- a/lib/Updater.php +++ b/lib/Updater.php @@ -768,7 +768,8 @@ private function downloadArchive(string $fromUrl, string $toLocation): bool { } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if ($httpCode !== 200 && $httpCode !== 206) { + $isFileUrl = parse_url($fromUrl, PHP_URL_SCHEME) === 'file'; + if (!$isFileUrl && $httpCode !== 200 && $httpCode !== 206) { fclose($fp); unlink($toLocation); $this->silentLog('[warn] fail to download archive from ' . $fromUrl . '. Error: ' . $httpCode . ' ' . curl_error($ch)); diff --git a/tests/features/bootstrap/FeatureContext.php b/tests/features/bootstrap/FeatureContext.php index 86878d47..fc0428a8 100644 --- a/tests/features/bootstrap/FeatureContext.php +++ b/tests/features/bootstrap/FeatureContext.php @@ -212,6 +212,84 @@ public function theCliUpdaterIsRun() { $this->CLIReturnCode = $returnCode; } + /** + * @Given the archive for version :version is available locally + */ + public function theArchiveForVersionIsAvailableLocally(string $version): void { + if ($this->skipIt) { + return; + } + + $filename = 'nextcloud-' . $version . '.zip'; + if (!file_exists($this->tmpDownloadDir . $filename)) { + $fp = fopen($this->tmpDownloadDir . $filename, 'w+'); + $url = $this->downloadURL . $filename; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_USERAGENT, 'Nextcloud Updater'); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); + if (curl_exec($ch) === false) { + throw new \Exception('Curl error: ' . curl_error($ch)); + } + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if ($httpCode !== 200) { + throw new \Exception('Download failed for ' . $url . ' - HTTP code: ' . $httpCode); + } + curl_close($ch); + fclose($fp); + } + } + + /** + * @When the CLI updater is run with local file URL for version :version and --no-verify successfully + */ + public function theCliUpdaterIsRunWithLocalFileUrlAndNoVerifySuccessfully(string $version): void { + if ($this->skipIt) { + return; + } + + $this->runUpdaterWithLocalFileUrl($version, noVerify: true); + + if ($this->CLIReturnCode !== 0) { + throw new Exception('updater failed' . PHP_EOL . implode(PHP_EOL, $this->CLIOutput)); + } + } + + /** + * @When the CLI updater is run with local file URL for version :version + */ + public function theCliUpdaterIsRunWithLocalFileUrl(string $version): void { + if ($this->skipIt) { + return; + } + + $this->runUpdaterWithLocalFileUrl($version, noVerify: false); + } + + private function runUpdaterWithLocalFileUrl(string $version, bool $noVerify): void { + $filename = 'nextcloud-' . $version . '.zip'; + $fileUrl = 'file://' . $this->tmpDownloadDir . $filename; + + if (!file_exists($this->buildDir . 'updater.phar')) { + throw new Exception('updater.phar not available - please build it in advance via "box build -c box.json"'); + } + + copy($this->buildDir . 'updater.phar', $this->serverDir . 'nextcloud/updater/updater'); + chdir($this->serverDir . 'nextcloud/updater'); + chmod($this->serverDir . 'nextcloud/updater/updater', 0755); + + $args = '-n --url ' . escapeshellarg($fileUrl); + if ($noVerify) { + $args .= ' --no-verify'; + } + exec('./updater ' . $args . ' 2>&1', $output, $returnCode); + + // sleep to let the opcache do it's work and invalidate the status.php + sleep(5); + $this->CLIOutput = $output; + $this->CLIReturnCode = $returnCode; + } + /** * @param $version */ diff --git a/tests/features/cli.feature b/tests/features/cli.feature index 9cd1db17..6480bc3a 100644 --- a/tests/features/cli.feature +++ b/tests/features/cli.feature @@ -114,3 +114,21 @@ Feature: CLI updater And the installed version should be 26.0.0 And maintenance mode should be off And upgrade is not required + + Scenario: Update via local file URL with --no-verify - 26.0.0 to 26.0.13 + Given the current installed version is 26.0.0 + And the archive for version 26.0.13 is available locally + When the CLI updater is run with local file URL for version 26.0.13 and --no-verify successfully + Then the installed version should be 26.0.1 + And maintenance mode should be off + And upgrade is not required + + Scenario: Update via local file URL without --no-verify fails - 26.0.0 to 26.0.13 + Given the current installed version is 26.0.0 + And the archive for version 26.0.13 is available locally + When the CLI updater is run with local file URL for version 26.0.13 + Then the return code should not be 0 + And the output should contain "You need to provide a signature with --signature or skip integrity check with --no-verify." + And the installed version should be 26.0.0 + And maintenance mode should be off + And upgrade is not required diff --git a/updater.phar b/updater.phar index 11bd0f4b..ed220978 100755 Binary files a/updater.phar and b/updater.phar differ diff --git a/vendor/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml b/vendor/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml deleted file mode 100644 index ebb57672..00000000 --- a/vendor/bamarni/composer-bin-plugin/.github/workflows/phpstan.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: "Static analysis" - -on: - push: - branches: - - "main" - - "master" - pull_request: null - -jobs: - static-analysis: - runs-on: "ubuntu-latest" - name: "PHPStan on PHP ${{ matrix.php }}" - strategy: - fail-fast: false - matrix: - php: - - "8.1" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v2" - - - name: "Setup PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "${{ matrix.php }}" - tools: "composer" - - - name: "Install Composer dependencies" - uses: "ramsey/composer-install@v2" - with: - dependency-versions: "highest" - - - name: "Perform static analysis" - run: "make phpstan" diff --git a/vendor/bamarni/composer-bin-plugin/.github/workflows/tests.yaml b/vendor/bamarni/composer-bin-plugin/.github/workflows/tests.yaml deleted file mode 100644 index 5bec38a4..00000000 --- a/vendor/bamarni/composer-bin-plugin/.github/workflows/tests.yaml +++ /dev/null @@ -1,80 +0,0 @@ -name: "Tests" - -on: - push: - branches: - - "main" - - "master" - pull_request: null - -jobs: - unit-tests: - runs-on: "ubuntu-latest" - name: "Unit Tests on PHP ${{ matrix.php }} and ${{ matrix.tools }}" - strategy: - fail-fast: false - matrix: - php: - - "7.2" - - "7.3" - - "7.4" - - "8.0" - - "8.1" - tools: [ "composer" ] - dependency-versions: [ "highest" ] - include: - - php: "7.2" - tools: "composer:v2.0" - dependency-versions: "lowest" - - steps: - - name: "Check out repository code" - uses: "actions/checkout@v2" - - - name: "Setup PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "${{ matrix.php }}" - tools: "${{ matrix.tools }}" - - - name: "Install Composer dependencies" - uses: "ramsey/composer-install@v2" - with: - dependency-versions: "${{ matrix.dependency-versions }}" - - - name: "Validate composer.json" - run: "composer validate --strict --no-check-lock" - - - name: "Run tests" - run: "vendor/bin/phpunit --group default" - - e2e-tests: - runs-on: "ubuntu-latest" - name: "E2E Tests on PHP ${{ matrix.php }}" - strategy: - fail-fast: false - matrix: - php: - - "8.1" - - steps: - - name: "Check out repository code" - uses: "actions/checkout@v2" - - - name: "Setup PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "${{ matrix.php }}" - tools: "composer" - - - name: "Correct bin plugin version for e2e scenarios (PR-only)" - if: github.event_name == 'pull_request' - run: find e2e -maxdepth 1 -mindepth 1 -type d -exec bash -c "cd {} && composer require --dev bamarni/composer-bin-plugin:dev-${GITHUB_SHA} --no-update" \; - - - name: "Install Composer dependencies" - uses: "ramsey/composer-install@v2" - with: - dependency-versions: "highest" - - - name: "Run tests" - run: "vendor/bin/phpunit --group e2e" diff --git a/vendor/bamarni/composer-bin-plugin/.makefile/touch.sh b/vendor/bamarni/composer-bin-plugin/.makefile/touch.sh deleted file mode 100755 index 5c0a7759..00000000 --- a/vendor/bamarni/composer-bin-plugin/.makefile/touch.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -# -# Takes a given string, e.g. 'bin/console' or 'docker-compose exec php bin/console' -# and split it by words. For each words, if the target is a file, it is touched. -# -# This allows to implement a similar rule to: -# -# ```Makefile -# bin/php-cs-fixer: vendor -# touch $@ -# ``` -# -# Indeed when the rule `bin/php-cs-fixer` is replaced with a docker-compose -# equivalent, it will not play out as nicely. -# -# Arguments: -# $1 - {string} Command potentially containing a file -# - -set -Eeuo pipefail; - - -readonly ERROR_COLOR="\e[41m"; -readonly NO_COLOR="\e[0m"; - - -if [ $# -ne 1 ]; then - printf "${ERROR_COLOR}Illegal number of parameters.${NO_COLOR}\n"; - - exit 1; -fi - - -readonly FILES="$1"; - - -####################################### -# Touch the given file path if the target is a file and do not create the file -# if does not exist. -# -# Globals: -# None -# -# Arguments: -# $1 - {string} File path -# -# Returns: -# None -####################################### -touch_file() { - local file="$1"; - - if [ -e ${file} ]; then - touch -c ${file}; - fi -} - -for file in ${FILES} -do - touch_file ${file}; -done diff --git a/vendor/bamarni/composer-bin-plugin/.phive/phars.xml b/vendor/bamarni/composer-bin-plugin/.phive/phars.xml deleted file mode 100644 index 335086e3..00000000 --- a/vendor/bamarni/composer-bin-plugin/.phive/phars.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/vendor/bamarni/composer-bin-plugin/composer.json b/vendor/bamarni/composer-bin-plugin/composer.json index 5b3809ba..4b76ff1b 100644 --- a/vendor/bamarni/composer-bin-plugin/composer.json +++ b/vendor/bamarni/composer-bin-plugin/composer.json @@ -17,11 +17,11 @@ }, "require-dev": { "ext-json": "*", - "composer/composer": "^2.0", + "composer/composer": "^2.2.26", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.5", + "phpstan/phpstan": "^1.8 || ^2.0", + "phpstan/phpstan-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.0", "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" @@ -38,9 +38,9 @@ }, "config": { "allow-plugins": { - "phpstan/extension-installer": true, "ergebnis/composer-normalize": true, - "infection/extension-installer": true + "infection/extension-installer": true, + "phpstan/extension-installer": true }, "sort-packages": true }, diff --git a/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php b/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php index 1d21c93b..19e36e0a 100644 --- a/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php +++ b/vendor/bamarni/composer-bin-plugin/src/BamarniBinPlugin.php @@ -24,6 +24,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Throwable; + use function count; use function in_array; use function sprintf; diff --git a/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php b/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php index 9edb4c3e..69c4aa99 100644 --- a/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php +++ b/vendor/bamarni/composer-bin-plugin/src/Command/BinCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Throwable; + use function chdir; use function count; use function file_exists; @@ -30,6 +31,7 @@ use function mkdir; use function putenv; use function sprintf; + use const GLOB_ONLYDIR; /** @@ -96,6 +98,7 @@ public function isProxyCommand(): bool public function execute(InputInterface $input, OutputInterface $output): int { // Switch to requireComposer() once Composer 2.3 is set as the minimum + // @phpstan-ignore function.alreadyNarrowedType $composer = method_exists($this, 'requireComposer') ? $this->requireComposer() : $this->getComposer(); diff --git a/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php b/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php index 33145078..8985a7cd 100644 --- a/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php +++ b/vendor/bamarni/composer-bin-plugin/src/Command/CouldNotCreateNamespaceDir.php @@ -5,6 +5,7 @@ namespace Bamarni\Composer\Bin\Command; use RuntimeException; + use function sprintf; final class CouldNotCreateNamespaceDir extends RuntimeException diff --git a/vendor/bamarni/composer-bin-plugin/src/Config/Config.php b/vendor/bamarni/composer-bin-plugin/src/Config/Config.php index fa62b3a1..629ab868 100644 --- a/vendor/bamarni/composer-bin-plugin/src/Config/Config.php +++ b/vendor/bamarni/composer-bin-plugin/src/Config/Config.php @@ -5,6 +5,7 @@ namespace Bamarni\Composer\Bin\Config; use Composer\Composer; + use function array_key_exists; use function array_merge; use function function_exists; diff --git a/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php b/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php index dab96f68..105afae9 100644 --- a/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php +++ b/vendor/bamarni/composer-bin-plugin/src/Input/BinInputFactory.php @@ -6,6 +6,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\StringInput; + use function array_filter; use function array_map; use function implode; @@ -61,7 +62,7 @@ public static function createInput( public static function createNamespaceInput(InputInterface $previousInput): InputInterface { $matchResult = preg_match( - '/^(.+?\s?)(--(?: .+)?)?$/', + '/^([^\s]+)(.*?\s?)(--(?: .+)?)?$/', $previousInput->__toString(), $matches ); @@ -74,9 +75,10 @@ public static function createNamespaceInput(InputInterface $previousInput): Inpu array_map( 'trim', [ - $matches[1], '--working-dir=.', - $matches[2] ?? '', + $matches[1], + $matches[2], + $matches[3] ?? '', ] ) ); diff --git a/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php b/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php index 2418d9c8..7f44ff5e 100644 --- a/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php +++ b/vendor/bamarni/composer-bin-plugin/src/Input/InvalidBinInput.php @@ -6,6 +6,7 @@ use RuntimeException; use Symfony\Component\Console\Input\InputInterface; + use function sprintf; final class InvalidBinInput extends RuntimeException diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 55853f22..d44e57cc 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2,17 +2,17 @@ "packages": [ { "name": "bamarni/composer-bin-plugin", - "version": "1.8.2", - "version_normalized": "1.8.2.0", + "version": "1.9.1", + "version_normalized": "1.9.1.0", "source": { "type": "git", "url": "https://github.com/bamarni/composer-bin-plugin.git", - "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880" + "reference": "641d0663f5ac270b1aeec4337b7856f76204df47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", - "reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880", + "url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/641d0663f5ac270b1aeec4337b7856f76204df47", + "reference": "641d0663f5ac270b1aeec4337b7856f76204df47", "shasum": "" }, "require": { @@ -20,17 +20,17 @@ "php": "^7.2.5 || ^8.0" }, "require-dev": { - "composer/composer": "^2.0", + "composer/composer": "^2.2.26", "ext-json": "*", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.5", + "phpstan/phpstan": "^1.8 || ^2.0", + "phpstan/phpstan-phpunit": "^1.1 || ^2.0", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.0", "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0", "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0" }, - "time": "2022-10-31T08:38:03+00:00", + "time": "2026-02-04T10:18:12+00:00", "type": "composer-plugin", "extra": { "class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin" @@ -56,7 +56,7 @@ ], "support": { "issues": "https://github.com/bamarni/composer-bin-plugin/issues", - "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2" + "source": "https://github.com/bamarni/composer-bin-plugin/tree/1.9.1" }, "install-path": "../bamarni/composer-bin-plugin" }, diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index a121f657..5bfe9212 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' => '0deb35c1f4fd23fea55c03db40f07c72603adc43', + 'reference' => 'ef7c1a2fde3c86416f5ca4805dd9f0cbf30e4385', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,16 +13,16 @@ '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '0deb35c1f4fd23fea55c03db40f07c72603adc43', + 'reference' => 'ef7c1a2fde3c86416f5ca4805dd9f0cbf30e4385', 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => false, ), 'bamarni/composer-bin-plugin' => array( - 'pretty_version' => '1.8.2', - 'version' => '1.8.2.0', - 'reference' => '92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880', + 'pretty_version' => '1.9.1', + 'version' => '1.9.1.0', + 'reference' => '641d0663f5ac270b1aeec4337b7856f76204df47', 'type' => 'composer-plugin', 'install_path' => __DIR__ . '/../bamarni/composer-bin-plugin', 'aliases' => array(),