diff --git a/.github/run-package-tests.sh b/.github/run-package-tests.sh index 1f365e68147e..18b50282fdf3 100644 --- a/.github/run-package-tests.sh +++ b/.github/run-package-tests.sh @@ -37,12 +37,15 @@ for DIR in ${DIRS}; do { fi done - echo "Running $DIR Unit Tests" - composer -q --no-interaction --no-ansi --no-progress update -d ${DIR}; + echo "Installing composer in $DIR" + COMPOSER_ROOT_VERSION=$(cat $DIR/VERSION) composer -q --no-interaction --no-ansi --no-progress update -d ${DIR}; if [ $? != 0 ]; then echo "$DIR: composer install failed" >> "${FAILED_FILE}" + # run again but without "-q" so we can see the error + COMPOSER_ROOT_VERSION=$(cat $DIR/VERSION) composer --no-interaction --no-ansi --no-progress update -d ${DIR}; continue fi + echo "Running $DIR Unit Tests" ${DIR}/vendor/bin/phpunit -c ${DIR}/phpunit.xml.dist; if [ $? != 0 ]; then echo "$DIR: failed" >> "${FAILED_FILE}" diff --git a/ContainerAnalysis/composer.json b/ContainerAnalysis/composer.json index 8cc8c1da0f5d..899863008d75 100644 --- a/ContainerAnalysis/composer.json +++ b/ContainerAnalysis/composer.json @@ -25,7 +25,7 @@ "require": { "php": ">=7.4", "google/gax": "^1.24.0", - "google/grafeas": "^0.8.0" + "google/grafeas": "^0.8.0" }, "require-dev": { "phpunit/phpunit": "^9.0" diff --git a/Debugger/composer.json b/Debugger/composer.json index 2b5b577e59d4..2ecbf79454df 100644 --- a/Debugger/composer.json +++ b/Debugger/composer.json @@ -46,7 +46,9 @@ } }, "archive": { - "exclude": ["/ext"] + "exclude": [ + "/ext" + ] }, "bin": [ "bin/google-cloud-debugger" diff --git a/ShoppingMerchantReports/composer.json b/ShoppingMerchantReports/composer.json index 7ac3a4174d40..9592610ca922 100644 --- a/ShoppingMerchantReports/composer.json +++ b/ShoppingMerchantReports/composer.json @@ -19,7 +19,7 @@ "require": { "php": ">=7.4", "google/gax": "^1.24.0", - "google/shopping-common-protos": "^0.1.0" + "google/shopping-common-protos": "^0.1.0" }, "require-dev": { "phpunit/phpunit": "^9.0" diff --git a/Trace/composer.json b/Trace/composer.json index 2ef1c5aaef87..a87dd2dc8013 100644 --- a/Trace/composer.json +++ b/Trace/composer.json @@ -9,7 +9,7 @@ "ramsey/uuid": "^3.0|^4.0", "google/gax": "^1.24.0" }, - "require-dev": { + "require-dev": { "phpunit/phpunit": "^9.0", "phpspec/prophecy-phpunit": "^2.0", "squizlabs/php_codesniffer": "2.*", diff --git a/Translate/composer.json b/Translate/composer.json index 4aa2c59ebc3f..385bff3b14e1 100644 --- a/Translate/composer.json +++ b/Translate/composer.json @@ -8,7 +8,7 @@ "google/cloud-core": "^1.52.7", "google/gax": "^1.24.0" }, - "require-dev": { + "require-dev": { "phpunit/phpunit": "^9.0", "phpspec/prophecy-phpunit": "^2.0", "squizlabs/php_codesniffer": "2.*", diff --git a/dev/google-cloud b/dev/google-cloud index cd90c718a38e..4683cd760b98 100755 --- a/dev/google-cloud +++ b/dev/google-cloud @@ -28,6 +28,7 @@ use Google\Cloud\Dev\Command\DocFxCommand; use Google\Cloud\Dev\Command\RepoInfoCommand; use Google\Cloud\Dev\Command\ReleaseInfoCommand; use Google\Cloud\Dev\Command\SplitCommand; +use Google\Cloud\Dev\Command\UpdateDepsCommand; use Symfony\Component\Console\Application; if (!class_exists(Application::class)) { @@ -47,4 +48,5 @@ $app->add(new DocFxCommand()); $app->add(new RepoInfoCommand()); $app->add(new ReleaseInfoCommand()); $app->add(new SplitCommand($rootDirectory)); +$app->add(new UpdateDepsCommand()); $app->run(); diff --git a/dev/sh/bump-dep.sh b/dev/sh/bump-dep.sh deleted file mode 100755 index e5c0d25e23b0..000000000000 --- a/dev/sh/bump-dep.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -ev - -# This script expects to be run from the google-cloud-php root. -# It finds all composer.json files and updates the google/gax -# version to the specified command line argument. -# -# Usage (to update gax version to e.g. 0.1.2): -# $ ./dev/sh/bump-dep.sh 'google/gax' 0.1.2 - -dep=$(echo $1 | sed 's/\//\\\//g') - -find . -maxdepth 2 -name composer.json \ - -not -path "./vendor/*" \ - -exec sed -i "s/$dep[\"']: [\"']\^[0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?[\"']/$dep\": \"\^$2\"/" "{}" \; diff --git a/dev/src/Command/ComponentInfoCommand.php b/dev/src/Command/ComponentInfoCommand.php index a78a558a2dd8..46d6f8c234f1 100644 --- a/dev/src/Command/ComponentInfoCommand.php +++ b/dev/src/Command/ComponentInfoCommand.php @@ -64,7 +64,7 @@ protected function configure() { $this->setName('component-info') ->setDescription('list info of a component or the whole library') - ->addOption('component', 'c', InputOption::VALUE_REQUIRED, 'Generate docs only for a single component.', '') + ->addOption('component', 'c', InputOption::VALUE_REQUIRED, 'get info for a single component', '') ->addOption('csv', '', InputOption::VALUE_REQUIRED, 'export findings to csv.') ->addOption('fields', 'f', InputOption::VALUE_REQUIRED, sprintf( "Comma-separated list of fields. The following fields are available: \n - %s\n" . diff --git a/dev/src/Command/UpdateDepsCommand.php b/dev/src/Command/UpdateDepsCommand.php new file mode 100644 index 000000000000..2c423cc1519a --- /dev/null +++ b/dev/src/Command/UpdateDepsCommand.php @@ -0,0 +1,115 @@ +setName('update-deps') + ->setDescription('update a dependency across all components') + ->addArgument('package', InputArgument::REQUIRED, 'Package name to update, e.g. "google/gax"') + ->addArgument('version', InputArgument::OPTIONAL, 'Package version to update to, e.g. "1.4.0"', '') + ->addOption('component', 'c', InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'bumps deps for the specified component/file') + ->addOption('bump', '', InputOption::VALUE_NONE, 'Bump to latest version of the package') + ->addOption('add', '', InputOption::VALUE_OPTIONAL, 'Adds the dep if it doesn\'t exist (--add=dev for require-dev)', false) + ->addOption('local', '', InputOption::VALUE_NONE, 'Add a link to the local component') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $package = $input->getArgument('package'); + if (!$version = $input->getArgument('version')) { + if (!$input->getOption('bump')) { + throw new \InvalidArgumentException('You must either supply a package version or the --bump flag'); + } + if (!$version = Composer::getLatestVersion($package)) { + throw new \InvalidArgumentException('Could not find a version for ' . $package); + } + } elseif ($input->getOption('bump')) { + throw new \InvalidArgumentException('You cannot supply both a package version and the --bump flag'); + } + + $projectRoot = realpath(__DIR__ . '/../../../'); + $result = (new Finder())->files()->in($projectRoot)->depth('<= 1')->name('composer.json'); + $paths = array_map(fn ($file) => $file->getPathname(), iterator_to_array($result)); + sort($paths); + + $componentPath = $input->getOption('local') ? $this->getComponentName($paths, $package) : null; + $updateCount = 0; + foreach ($input->getOption('component') ?: $paths as $jsonFile) { + if (is_dir($jsonFile) && file_exists($jsonFile . '/composer.json')) { + $jsonFile .= '/composer.json'; + } + $composerJson = json_decode(file_get_contents($jsonFile), true); + $require = 'require'; + if (!isset($composerJson['require'][$package])) { + if (isset($composerJson['require-dev'][$package])) { + $require = 'require-dev'; + } elseif (false === $input->getOption('add')) { + continue; + } elseif ('dev' === $input->getOption('add')) { + $require = 'require-dev'; + } + } + $composerJson[$require][$package] = $version; + if ($input->getOption('local')) { + $composerJson['repositories'] ??= []; + $composerJson['repositories'][] = [ + 'type' => 'path', + 'url' => '../' . $componentPath, + 'options' => [ + 'versions' => [$package => $version], + ] + ]; + } + $output->writeln(sprintf('Updated %s', basename(dirname($jsonFile)))); + file_put_contents($jsonFile, json_encode($composerJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"); + $updateCount++; + } + $output->writeln("Updated $updateCount packages to use $package: $version"); + + return 0; + } + + private function getComponentName(array $paths, string $package): string + { + foreach ($paths as $path) { + $composerJson = json_decode(file_get_contents($path), true); + if (isset($composerJson['name']) && $composerJson['name'] === $package) { + return basename(dirname($path)); + } + } + + throw new \InvalidArgumentException('Component not found for package ' . $package); + } +} diff --git a/dev/tests/Unit/Command/UpdateDepsCommandTest.php b/dev/tests/Unit/Command/UpdateDepsCommandTest.php new file mode 100644 index 000000000000..17067647c798 --- /dev/null +++ b/dev/tests/Unit/Command/UpdateDepsCommandTest.php @@ -0,0 +1,138 @@ +execute($cmdOptions); + + $this->assertEquals($expectedJson, json_decode(file_get_contents($tmpFile), true)); + } + + public function testBumpWithVersionThrowsException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('You cannot supply both a package version and the --bump flag'); + + $commandTester = new CommandTester(new UpdateDepsCommand()); + $commandTester->execute([ + 'package' => 'google/gax', + 'version' => '1.2.3', + '--bump' => true, + ]); + } + + public function testNoBumpOrVersionThrowsException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('You must either supply a package version or the --bump flag'); + + $commandTester = new CommandTester(new UpdateDepsCommand()); + $commandTester->execute([ + 'package' => 'google/gax', + ]); + } + + public function testInvalidComponentWithLocalThrowsException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Component not found for package google/foo'); + + $commandTester = new CommandTester(new UpdateDepsCommand()); + $commandTester->execute([ + 'package' => 'google/foo', + 'version' => '1.2.3', + '--local' => true, + ]); + } + + public function provideUpdateDeps() + { + return [ + // Update package (require) + [ + ['package' => 'google/gax', 'version' => '4.5.6'], + ['require' => ['google/gax' => '1.2.3']], + ['require' => ['google/gax' => '4.5.6']], + ], + // Update package (require-dev) + [ + ['package' => 'google/gax', 'version' => '4.5.6'], + ['require-dev' => ['google/gax' => '1.2.3']], + ['require-dev' => ['google/gax' => '4.5.6']], + ], + // Update package (doesn't exist) + [ + ['package' => 'google/gax', 'version' => '4.5.6'], + [], + [], + ], + // Update package with add (require) + [ + ['package' => 'google/gax', 'version' => '4.5.6', '--add' => true], + [], + ['require' => ['google/gax' => '4.5.6']], + ], + // Update package with add (require-dev) + [ + ['package' => 'google/gax', 'version' => '4.5.6', '--add' => 'dev'], + [], + ['require-dev' => ['google/gax' => '4.5.6']], + ], + // Update package with bump (require-dev) + [ + ['package' => 'google/gax', '--bump' => true], + ['require' => ['google/gax' => '1.2.3']], + ['require' => ['google/gax' => Composer::getLatestVersion('google/gax')]], + ], + // Update package with local + [ + ['package' => 'google/cloud-core', 'version' => '1.100', '--local' => true], + ['require' => ['google/cloud-core' => '1.2.3']], + [ + 'require' => ['google/cloud-core' => '1.100'], + 'repositories' => [ + [ + 'type' => 'path', + 'url' => '../Core', + 'options' => ['versions' => ['google/cloud-core' => '1.100']], + ], + ], + ], + ], + ]; + } +}