dependabot-composer 0.128.0 → 0.129.2
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} +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
|