dependabot-composer 0.128.0 → 0.129.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/helpers/{.php_cs → v1/.php_cs} +0 -0
- data/helpers/{bin → v1/bin}/run +0 -0
- data/helpers/v1/build +20 -0
- data/helpers/{composer.json → v1/composer.json} +0 -0
- data/helpers/{composer.lock → v1/composer.lock} +63 -61
- data/helpers/{phpstan.neon → v1/phpstan.neon} +0 -0
- data/helpers/{src → v1/src}/DependabotInstallationManager.php +0 -0
- data/helpers/{src → v1/src}/DependabotPluginManager.php +0 -0
- data/helpers/{src → v1/src}/ExceptionIO.php +0 -0
- data/helpers/{src → v1/src}/Hasher.php +0 -0
- data/helpers/{src → v1/src}/UpdateChecker.php +0 -0
- data/helpers/{src → v1/src}/Updater.php +0 -0
- data/helpers/v2/.php_cs +32 -0
- data/helpers/v2/bin/run +86 -0
- data/helpers/{build → v2/build} +0 -0
- data/helpers/v2/composer.json +23 -0
- data/helpers/v2/composer.lock +2483 -0
- data/helpers/v2/phpstan.neon +5 -0
- data/helpers/v2/src/DependabotInstallationManager.php +67 -0
- data/helpers/v2/src/DependabotPluginManager.php +23 -0
- data/helpers/v2/src/ExceptionIO.php +25 -0
- data/helpers/v2/src/Hasher.php +28 -0
- data/helpers/v2/src/UpdateChecker.php +133 -0
- data/helpers/v2/src/Updater.php +99 -0
- data/lib/dependabot/composer/file_updater/lockfile_updater.rb +26 -2
- data/lib/dependabot/composer/helpers.rb +20 -0
- data/lib/dependabot/composer/native_helpers.rb +2 -2
- data/lib/dependabot/composer/update_checker/version_resolver.rb +10 -2
- metadata +33 -21
- data/helpers/setup.sh +0 -17
@@ -0,0 +1,67 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
declare(strict_types=1);
|
4
|
+
|
5
|
+
namespace Dependabot\Composer;
|
6
|
+
|
7
|
+
use Composer\DependencyResolver\Operation\InstallOperation;
|
8
|
+
use Composer\DependencyResolver\Operation\UninstallOperation;
|
9
|
+
use Composer\DependencyResolver\Operation\UpdateOperation;
|
10
|
+
use Composer\Installer\InstallationManager;
|
11
|
+
use Composer\Package\PackageInterface;
|
12
|
+
use Composer\Repository\RepositoryInterface;
|
13
|
+
|
14
|
+
final class DependabotInstallationManager extends InstallationManager
|
15
|
+
{
|
16
|
+
private array $installed = [];
|
17
|
+
private array $updated = [];
|
18
|
+
private array $uninstalled = [];
|
19
|
+
|
20
|
+
public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true): void
|
21
|
+
{
|
22
|
+
foreach ($operations as $operation) {
|
23
|
+
$method = $operation->getOperationType();
|
24
|
+
// NOTE: skipping download() step
|
25
|
+
$this->$method($repo, $operation);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
public function install(RepositoryInterface $repo, InstallOperation $operation): void
|
30
|
+
{
|
31
|
+
$this->installed[] = $operation->getPackage();
|
32
|
+
}
|
33
|
+
|
34
|
+
public function update(RepositoryInterface $repo, UpdateOperation $operation): void
|
35
|
+
{
|
36
|
+
$this->updated[] = [$operation->getInitialPackage(), $operation->getTargetPackage()];
|
37
|
+
}
|
38
|
+
|
39
|
+
public function uninstall(RepositoryInterface $repo, UninstallOperation $operation): void
|
40
|
+
{
|
41
|
+
$this->uninstalled[] = $operation->getPackage();
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* @return PackageInterface[]
|
46
|
+
*/
|
47
|
+
public function getInstalledPackages(): array
|
48
|
+
{
|
49
|
+
return $this->installed;
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* @return PackageInterface[]
|
54
|
+
*/
|
55
|
+
public function getUpdatedPackages(): array
|
56
|
+
{
|
57
|
+
return $this->updated;
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* @return PackageInterface[]
|
62
|
+
*/
|
63
|
+
public function getUninstalledPackages(): array
|
64
|
+
{
|
65
|
+
return $this->uninstalled;
|
66
|
+
}
|
67
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
declare(strict_types=1);
|
4
|
+
|
5
|
+
namespace Dependabot\Composer;
|
6
|
+
|
7
|
+
use Composer\Package\PackageInterface;
|
8
|
+
use Composer\Plugin\PluginManager;
|
9
|
+
|
10
|
+
final class DependabotPluginManager extends PluginManager
|
11
|
+
{
|
12
|
+
public function registerPackage(PackageInterface $package, $failOnMissingClasses = false, $isGlobalPlugin = false): void
|
13
|
+
{
|
14
|
+
// This package does some setup for PHP_CodeSniffer, but errors out the
|
15
|
+
// install if Symfony isn't installed (which it won't be for a lockfile
|
16
|
+
// only install run). Safe to ignore
|
17
|
+
if (strpos($package->getName(), 'phpcodesniffer') !== false) {
|
18
|
+
return;
|
19
|
+
}
|
20
|
+
|
21
|
+
parent::registerPackage($package, $failOnMissingClasses, $isGlobalPlugin);
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
declare(strict_types=1);
|
4
|
+
|
5
|
+
namespace Dependabot\Composer;
|
6
|
+
|
7
|
+
use Composer\IO\NullIO;
|
8
|
+
|
9
|
+
final class ExceptionIO extends NullIO
|
10
|
+
{
|
11
|
+
private bool $raise_next_error = false;
|
12
|
+
|
13
|
+
public function writeError($messages, $newline = true, $verbosity = self::NORMAL): void
|
14
|
+
{
|
15
|
+
if (is_array($messages)) {
|
16
|
+
return;
|
17
|
+
}
|
18
|
+
if ($this->raise_next_error) {
|
19
|
+
throw new \RuntimeException('Your requirements could not be resolved to an installable set of packages.' . $messages);
|
20
|
+
}
|
21
|
+
if (strpos($messages, 'Your requirements could not be resolved') !== false) {
|
22
|
+
$this->raise_next_error = true;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
declare(strict_types=1);
|
4
|
+
|
5
|
+
namespace Dependabot\Composer;
|
6
|
+
|
7
|
+
use Composer\Package\Locker;
|
8
|
+
|
9
|
+
final class Hasher
|
10
|
+
{
|
11
|
+
/**
|
12
|
+
* @throws \RuntimeException
|
13
|
+
*/
|
14
|
+
public static function getContentHash(array $args): string
|
15
|
+
{
|
16
|
+
[$workingDirectory] = $args;
|
17
|
+
|
18
|
+
$config = $workingDirectory . '/composer.json';
|
19
|
+
|
20
|
+
$contents = file_get_contents($config);
|
21
|
+
|
22
|
+
if (!is_string($contents)) {
|
23
|
+
throw new \RuntimeException(sprintf('Failed to load contents of "%s".', $config));
|
24
|
+
}
|
25
|
+
|
26
|
+
return Locker::getContentHash($contents);
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,133 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
declare(strict_types=1);
|
4
|
+
|
5
|
+
namespace Dependabot\Composer;
|
6
|
+
|
7
|
+
use Composer\DependencyResolver\Request;
|
8
|
+
use Composer\Factory;
|
9
|
+
use Composer\Installer;
|
10
|
+
use Composer\Package\PackageInterface;
|
11
|
+
use Composer\Util\Filesystem;
|
12
|
+
|
13
|
+
final class UpdateChecker
|
14
|
+
{
|
15
|
+
public static function getLatestResolvableVersion(array $args): ?string
|
16
|
+
{
|
17
|
+
[$workingDirectory, $dependencyName, $gitCredentials, $registryCredentials] = $args;
|
18
|
+
|
19
|
+
$httpBasicCredentials = [];
|
20
|
+
|
21
|
+
foreach ($gitCredentials as $credentials) {
|
22
|
+
$httpBasicCredentials[$credentials['host']] = [
|
23
|
+
'username' => $credentials['username'],
|
24
|
+
'password' => $credentials['password'],
|
25
|
+
];
|
26
|
+
}
|
27
|
+
|
28
|
+
foreach ($registryCredentials as $credentials) {
|
29
|
+
$httpBasicCredentials[$credentials['registry']] = [
|
30
|
+
'username' => $credentials['username'],
|
31
|
+
'password' => $credentials['password'],
|
32
|
+
];
|
33
|
+
}
|
34
|
+
|
35
|
+
$io = new ExceptionIO();
|
36
|
+
|
37
|
+
$composer = Factory::create($io, $workingDirectory . '/composer.json');
|
38
|
+
|
39
|
+
$config = $composer->getConfig();
|
40
|
+
|
41
|
+
if (0 < count($httpBasicCredentials)) {
|
42
|
+
$config->merge([
|
43
|
+
'config' => [
|
44
|
+
'http-basic' => $httpBasicCredentials,
|
45
|
+
],
|
46
|
+
]);
|
47
|
+
|
48
|
+
$io->loadConfiguration($config);
|
49
|
+
}
|
50
|
+
|
51
|
+
$installationManager = new DependabotInstallationManager($composer->getLoop(), $io);
|
52
|
+
|
53
|
+
$fs = new Filesystem(null);
|
54
|
+
$binaryInstaller = new Installer\BinaryInstaller($io, rtrim($composer->getConfig()->get('bin-dir'), '/'), $composer->getConfig()->get('bin-compat'), $fs);
|
55
|
+
|
56
|
+
$installationManager->addInstaller(new Installer\LibraryInstaller($io, $composer, null, $fs, $binaryInstaller));
|
57
|
+
$installationManager->addInstaller(new Installer\PluginInstaller($io, $composer, $fs, $binaryInstaller));
|
58
|
+
$installationManager->addInstaller(new Installer\MetapackageInstaller($io));
|
59
|
+
|
60
|
+
$install = new Installer(
|
61
|
+
$io,
|
62
|
+
$config,
|
63
|
+
$composer->getPackage(),
|
64
|
+
$composer->getDownloadManager(),
|
65
|
+
$composer->getRepositoryManager(),
|
66
|
+
$composer->getLocker(),
|
67
|
+
$installationManager,
|
68
|
+
$composer->getEventDispatcher(),
|
69
|
+
$composer->getAutoloadGenerator()
|
70
|
+
);
|
71
|
+
|
72
|
+
// For all potential options, see UpdateCommand in composer
|
73
|
+
$install
|
74
|
+
->setUpdate(true)
|
75
|
+
->setDevMode(true)
|
76
|
+
->setUpdateAllowTransitiveDependencies(Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS)
|
77
|
+
->setDumpAutoloader(false)
|
78
|
+
->setRunScripts(false)
|
79
|
+
->setIgnorePlatformRequirements(false);
|
80
|
+
|
81
|
+
// if no lock is present, we do not do a partial update as
|
82
|
+
// this is not supported by the Installer
|
83
|
+
if ($composer->getLocker()->isLocked()) {
|
84
|
+
$install->setUpdateAllowList([$dependencyName]);
|
85
|
+
}
|
86
|
+
|
87
|
+
$install->run();
|
88
|
+
|
89
|
+
$installedPackages = $installationManager->getInstalledPackages();
|
90
|
+
|
91
|
+
$updatedPackage = current(array_filter($installedPackages, static function (PackageInterface $package) use ($dependencyName): bool {
|
92
|
+
return $package->getName() === $dependencyName;
|
93
|
+
}));
|
94
|
+
|
95
|
+
// We found the package in the list of updated packages. Return its version.
|
96
|
+
if ($updatedPackage instanceof PackageInterface) {
|
97
|
+
return preg_replace('/^([v])/', '', $updatedPackage->getPrettyVersion());
|
98
|
+
}
|
99
|
+
|
100
|
+
// We didn't find the package in the list of updated packages. Check if
|
101
|
+
// it was replaced by another package (in which case we can ignore).
|
102
|
+
foreach ($composer->getPackage()->getReplaces() as $link) {
|
103
|
+
if ($link->getTarget() === $dependencyName) {
|
104
|
+
return null;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
foreach ($installedPackages as $package) {
|
109
|
+
foreach ($package->getReplaces() as $link) {
|
110
|
+
if ($link->getTarget() === $dependencyName) {
|
111
|
+
return null;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
// Similarly, check if the package was provided by any other package.
|
117
|
+
foreach ($composer->getPackage()->getProvides() as $link) {
|
118
|
+
if ($link->getTarget() === $dependencyName) {
|
119
|
+
return preg_replace('/^([v])/', '', $link->getPrettyConstraint());
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
foreach ($installedPackages as $package) {
|
124
|
+
foreach ($package->getProvides() as $link) {
|
125
|
+
if ($link->getTarget() === $dependencyName) {
|
126
|
+
return preg_replace('/^([v])/', '', $link->getPrettyConstraint());
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
throw new \RuntimeException('Package not found in updated packages!');
|
132
|
+
}
|
133
|
+
}
|
@@ -0,0 +1,99 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
declare(strict_types=1);
|
4
|
+
|
5
|
+
namespace Dependabot\Composer;
|
6
|
+
|
7
|
+
use Composer\DependencyResolver\Request;
|
8
|
+
use Composer\Factory;
|
9
|
+
use Composer\Installer;
|
10
|
+
|
11
|
+
final class Updater
|
12
|
+
{
|
13
|
+
/**
|
14
|
+
* @throws \RuntimeException
|
15
|
+
*/
|
16
|
+
public static function update(array $args): array
|
17
|
+
{
|
18
|
+
[$workingDirectory, $dependencyName, $dependencyVersion, $gitCredentials, $registryCredentials] = $args;
|
19
|
+
|
20
|
+
// Change working directory to the one provided, this ensures that we
|
21
|
+
// install dependencies into the working dir, rather than a vendor folder
|
22
|
+
// in the root of the project
|
23
|
+
$originalDir = getcwd();
|
24
|
+
|
25
|
+
if (!is_string($originalDir)) {
|
26
|
+
throw new \RuntimeException('Failed determining the current working directory.');
|
27
|
+
}
|
28
|
+
|
29
|
+
chdir($workingDirectory);
|
30
|
+
|
31
|
+
$io = new ExceptionIO();
|
32
|
+
$composer = Factory::create($io);
|
33
|
+
$config = $composer->getConfig();
|
34
|
+
$httpBasicCredentials = [];
|
35
|
+
|
36
|
+
$pm = new DependabotPluginManager($io, $composer, null, false);
|
37
|
+
$composer->setPluginManager($pm);
|
38
|
+
$pm->loadInstalledPlugins();
|
39
|
+
|
40
|
+
foreach ($gitCredentials as &$cred) {
|
41
|
+
$httpBasicCredentials[$cred['host']] = [
|
42
|
+
'username' => $cred['username'],
|
43
|
+
'password' => $cred['password'],
|
44
|
+
];
|
45
|
+
}
|
46
|
+
|
47
|
+
foreach ($registryCredentials as &$cred) {
|
48
|
+
$httpBasicCredentials[$cred['registry']] = [
|
49
|
+
'username' => $cred['username'],
|
50
|
+
'password' => $cred['password'],
|
51
|
+
];
|
52
|
+
}
|
53
|
+
|
54
|
+
if ($httpBasicCredentials) {
|
55
|
+
$config->merge(
|
56
|
+
[
|
57
|
+
'config' => [
|
58
|
+
'http-basic' => $httpBasicCredentials,
|
59
|
+
],
|
60
|
+
]
|
61
|
+
);
|
62
|
+
$io->loadConfiguration($config);
|
63
|
+
}
|
64
|
+
|
65
|
+
$install = new Installer(
|
66
|
+
$io,
|
67
|
+
$config,
|
68
|
+
$composer->getPackage(),
|
69
|
+
$composer->getDownloadManager(),
|
70
|
+
$composer->getRepositoryManager(),
|
71
|
+
$composer->getLocker(),
|
72
|
+
$composer->getInstallationManager(),
|
73
|
+
$composer->getEventDispatcher(),
|
74
|
+
$composer->getAutoloadGenerator()
|
75
|
+
);
|
76
|
+
|
77
|
+
// For all potential options, see UpdateCommand in composer
|
78
|
+
$install
|
79
|
+
->setWriteLock(true)
|
80
|
+
->setUpdate(true)
|
81
|
+
->setDevMode(true)
|
82
|
+
->setUpdateAllowList([$dependencyName])
|
83
|
+
->setUpdateAllowTransitiveDependencies(Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS)
|
84
|
+
->setExecuteOperations(true)
|
85
|
+
->setDumpAutoloader(false)
|
86
|
+
->setRunScripts(false)
|
87
|
+
->setIgnorePlatformRequirements(false);
|
88
|
+
|
89
|
+
$install->run();
|
90
|
+
|
91
|
+
$result = [
|
92
|
+
'composer.lock' => file_get_contents('composer.lock'),
|
93
|
+
];
|
94
|
+
|
95
|
+
chdir($originalDir);
|
96
|
+
|
97
|
+
return $result;
|
98
|
+
}
|
99
|
+
}
|
@@ -7,6 +7,7 @@ require "dependabot/composer/file_updater"
|
|
7
7
|
require "dependabot/composer/version"
|
8
8
|
require "dependabot/composer/requirement"
|
9
9
|
require "dependabot/composer/native_helpers"
|
10
|
+
require "dependabot/composer/helpers"
|
10
11
|
|
11
12
|
# rubocop:disable Metrics/ClassLength
|
12
13
|
module Dependabot
|
@@ -34,6 +35,7 @@ module Dependabot
|
|
34
35
|
(?<!with|for|by)\sext\-[^\s/]+\s.*?\s(?=->)|
|
35
36
|
(?<=requires\s)php(?:\-[^\s/]+)?\s.*?\s(?=->)
|
36
37
|
}x.freeze
|
38
|
+
MISSING_ENV_VAR_REGEX = /Environment variable '(?<env_var>.[^']+)' is not set/.freeze
|
37
39
|
|
38
40
|
def initialize(dependencies:, dependency_files:, credentials:)
|
39
41
|
@dependencies = dependencies
|
@@ -179,10 +181,21 @@ module Dependabot
|
|
179
181
|
raise GitDependenciesNotReachable, dependency_url
|
180
182
|
end
|
181
183
|
|
182
|
-
|
184
|
+
# NOTE: This matches an error message from composer plugins used to install ACF PRO
|
185
|
+
# https://github.com/PhilippBaschke/acf-pro-installer/blob/772cec99c6ef8bc67ba6768419014cc60d141b27/src/ACFProInstaller/Exceptions/MissingKeyException.php#L14
|
186
|
+
# https://github.com/pivvenit/acf-pro-installer/blob/f2d4812839ee2c333709b0ad4c6c134e4c25fd6d/src/Exceptions/MissingKeyException.php#L25
|
187
|
+
if error.message.start_with?("Could not find a key for ACF PRO") ||
|
188
|
+
error.message.start_with?("Could not find a license key for ACF PRO")
|
183
189
|
raise MissingEnvironmentVariable, "ACF_PRO_KEY"
|
184
190
|
end
|
185
191
|
|
192
|
+
# NOTE: This matches error output from a composer plugin (private-composer-installer):
|
193
|
+
# https://github.com/ffraenz/private-composer-installer/blob/8655e3da4e8f99203f13ccca33b9ab953ad30a31/src/Exception/MissingEnvException.php#L22
|
194
|
+
if error.message.match?(MISSING_ENV_VAR_REGEX)
|
195
|
+
env_var = error.message.match(MISSING_ENV_VAR_REGEX).named_captures.fetch("env_var")
|
196
|
+
raise MissingEnvironmentVariable, env_var
|
197
|
+
end
|
198
|
+
|
186
199
|
if error.message.start_with?("Unknown downloader type: npm-sign") ||
|
187
200
|
error.message.include?("file could not be downloaded") ||
|
188
201
|
error.message.include?("configuration does not allow connect")
|
@@ -197,6 +210,7 @@ module Dependabot
|
|
197
210
|
raise PrivateSourceAuthenticationFailure, source
|
198
211
|
end
|
199
212
|
|
213
|
+
# NOTE: This error is raised by composer v1
|
200
214
|
if error.message.include?("Argument 1 passed to Composer")
|
201
215
|
msg = "One of your Composer plugins is not compatible with the "\
|
202
216
|
"latest version of Composer. Please update Composer and "\
|
@@ -204,6 +218,12 @@ module Dependabot
|
|
204
218
|
raise DependencyFileNotResolvable, msg
|
205
219
|
end
|
206
220
|
|
221
|
+
# NOTE: This error is raised by composer v2 and includes helpful
|
222
|
+
# information about which plugins or dependencies are not compatible
|
223
|
+
if error.message.include?("Your requirements could not be resolved")
|
224
|
+
raise DependencyFileNotResolvable, error.message
|
225
|
+
end
|
226
|
+
|
207
227
|
raise error
|
208
228
|
end
|
209
229
|
# rubocop:enable Metrics/AbcSize
|
@@ -425,7 +445,11 @@ module Dependabot
|
|
425
445
|
end
|
426
446
|
|
427
447
|
def php_helper_path
|
428
|
-
NativeHelpers.composer_helper_path
|
448
|
+
NativeHelpers.composer_helper_path(composer_version: composer_version)
|
449
|
+
end
|
450
|
+
|
451
|
+
def composer_version
|
452
|
+
@composer_version ||= Helpers.composer_version(parsed_composer_json, parsed_lockfile)
|
429
453
|
end
|
430
454
|
|
431
455
|
def credentials_env
|