dependabot-composer 0.128.2 → 0.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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} +10 -5
- 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 +2610 -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 +31 -2
- data/lib/dependabot/composer/native_helpers.rb +2 -2
- data/lib/dependabot/composer/update_checker/version_resolver.rb +13 -2
- metadata +28 -17
- 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
|
+
}
|
@@ -34,6 +34,7 @@ module Dependabot
|
|
34
34
|
(?<!with|for|by)\sext\-[^\s/]+\s.*?\s(?=->)|
|
35
35
|
(?<=requires\s)php(?:\-[^\s/]+)?\s.*?\s(?=->)
|
36
36
|
}x.freeze
|
37
|
+
MISSING_ENV_VAR_REGEX = /Environment variable '(?<env_var>.[^']+)' is not set/.freeze
|
37
38
|
|
38
39
|
def initialize(dependencies:, dependency_files:, credentials:)
|
39
40
|
@dependencies = dependencies
|
@@ -179,10 +180,21 @@ module Dependabot
|
|
179
180
|
raise GitDependenciesNotReachable, dependency_url
|
180
181
|
end
|
181
182
|
|
182
|
-
|
183
|
+
# NOTE: This matches an error message from composer plugins used to install ACF PRO
|
184
|
+
# https://github.com/PhilippBaschke/acf-pro-installer/blob/772cec99c6ef8bc67ba6768419014cc60d141b27/src/ACFProInstaller/Exceptions/MissingKeyException.php#L14
|
185
|
+
# https://github.com/pivvenit/acf-pro-installer/blob/f2d4812839ee2c333709b0ad4c6c134e4c25fd6d/src/Exceptions/MissingKeyException.php#L25
|
186
|
+
if error.message.start_with?("Could not find a key for ACF PRO") ||
|
187
|
+
error.message.start_with?("Could not find a license key for ACF PRO")
|
183
188
|
raise MissingEnvironmentVariable, "ACF_PRO_KEY"
|
184
189
|
end
|
185
190
|
|
191
|
+
# NOTE: This matches error output from a composer plugin (private-composer-installer):
|
192
|
+
# https://github.com/ffraenz/private-composer-installer/blob/8655e3da4e8f99203f13ccca33b9ab953ad30a31/src/Exception/MissingEnvException.php#L22
|
193
|
+
if error.message.match?(MISSING_ENV_VAR_REGEX)
|
194
|
+
env_var = error.message.match(MISSING_ENV_VAR_REGEX).named_captures.fetch("env_var")
|
195
|
+
raise MissingEnvironmentVariable, env_var
|
196
|
+
end
|
197
|
+
|
186
198
|
if error.message.start_with?("Unknown downloader type: npm-sign") ||
|
187
199
|
error.message.include?("file could not be downloaded") ||
|
188
200
|
error.message.include?("configuration does not allow connect")
|
@@ -197,6 +209,7 @@ module Dependabot
|
|
197
209
|
raise PrivateSourceAuthenticationFailure, source
|
198
210
|
end
|
199
211
|
|
212
|
+
# NOTE: This error is raised by composer v1
|
200
213
|
if error.message.include?("Argument 1 passed to Composer")
|
201
214
|
msg = "One of your Composer plugins is not compatible with the "\
|
202
215
|
"latest version of Composer. Please update Composer and "\
|
@@ -204,6 +217,12 @@ module Dependabot
|
|
204
217
|
raise DependencyFileNotResolvable, msg
|
205
218
|
end
|
206
219
|
|
220
|
+
# NOTE: This error is raised by composer v2 and includes helpful
|
221
|
+
# information about which plugins or dependencies are not compatible
|
222
|
+
if error.message.include?("Your requirements could not be resolved")
|
223
|
+
raise DependencyFileNotResolvable, error.message
|
224
|
+
end
|
225
|
+
|
207
226
|
raise error
|
208
227
|
end
|
209
228
|
# rubocop:enable Metrics/AbcSize
|
@@ -425,7 +444,17 @@ module Dependabot
|
|
425
444
|
end
|
426
445
|
|
427
446
|
def php_helper_path
|
428
|
-
NativeHelpers.composer_helper_path
|
447
|
+
NativeHelpers.composer_helper_path(composer_version: composer_version)
|
448
|
+
end
|
449
|
+
|
450
|
+
def composer_version
|
451
|
+
@composer_version ||=
|
452
|
+
begin
|
453
|
+
return "v2" unless parsed_lockfile["plugin-api-version"]
|
454
|
+
|
455
|
+
version = Version.new(parsed_lockfile["plugin-api-version"])
|
456
|
+
version.canonical_segments.first == 1 ? "v1" : "v2"
|
457
|
+
end
|
429
458
|
end
|
430
459
|
|
431
460
|
def credentials_env
|