Skip to content

Commit

Permalink
feat(dev): add command to update composer deps (#6773)
Browse files Browse the repository at this point in the history
  • Loading branch information
bshaffer authored Nov 21, 2023
1 parent ec79f5e commit 8c36f3c
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 24 deletions.
7 changes: 5 additions & 2 deletions .github/run-package-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
2 changes: 1 addition & 1 deletion ContainerAnalysis/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 3 additions & 1 deletion Debugger/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
}
},
"archive": {
"exclude": ["/ext"]
"exclude": [
"/ext"
]
},
"bin": [
"bin/google-cloud-debugger"
Expand Down
2 changes: 1 addition & 1 deletion ShoppingMerchantReports/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion Trace/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.*",
Expand Down
2 changes: 1 addition & 1 deletion Translate/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.*",
Expand Down
2 changes: 2 additions & 0 deletions dev/google-cloud
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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();
16 changes: 0 additions & 16 deletions dev/sh/bump-dep.sh

This file was deleted.

2 changes: 1 addition & 1 deletion dev/src/Command/ComponentInfoCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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" .
Expand Down
115 changes: 115 additions & 0 deletions dev/src/Command/UpdateDepsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php
/**
* Copyright 2023 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Dev\Command;

use Google\Cloud\Dev\Composer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;

/**
* Update package dependencies
*
* @internal
*/
class UpdateDepsCommand extends Command
{
protected function configure()
{
$this->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 <info>%s</>', basename(dirname($jsonFile))));
file_put_contents($jsonFile, json_encode($composerJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");
$updateCount++;
}
$output->writeln("Updated <fg=white>$updateCount packages</> to use <comment>$package</>: <info>$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);
}
}
138 changes: 138 additions & 0 deletions dev/tests/Unit/Command/UpdateDepsCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Google\Cloud\Dev\Tests\Unit\Command;

use Google\Cloud\Dev\Command\UpdateDepsCommand;
use Google\Cloud\Dev\Composer;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Tester\CommandTester;

/**
* @group dev
*/
class UpdateDepsCommandTest extends TestCase
{
/**
* @dataProvider provideUpdateDeps
*/
public function testUpdateDeps(array $cmdOptions, array $json, array $expectedJson)
{
$tmpFile = sys_get_temp_dir() . '/composer.json';
file_put_contents($tmpFile, json_encode($json));
$cmdOptions['--component'] = [$tmpFile];
$commandTester = new CommandTester(new UpdateDepsCommand());
$commandTester->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']],
],
],
],
],
];
}
}

0 comments on commit 8c36f3c

Please sign in to comment.