• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / composer-update-check / 12451471868

22 Dec 2024 01:21AM UTC coverage: 20.538%. First build
12451471868

Pull #130

github

web-flow
[TASK] Update all dependencies
Pull Request #130: [!!!][FEATURE] Modernize plugin

356 of 1832 new or added lines in 57 files covered. (19.43%)

382 of 1860 relevant lines covered (20.54%)

1.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.77
/src/Composer/Installer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Composer package "eliashaeussler/composer-update-check".
7
 *
8
 * Copyright (C) 2020-2024 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace EliasHaeussler\ComposerUpdateCheck\Composer;
25

26
use Composer\Composer;
27
use Composer\DependencyResolver;
28
use Composer\Factory;
29
use Composer\Installer as ComposerInstaller;
30
use Composer\IO;
31
use Composer\Package;
32
use Composer\Repository;
33
use EliasHaeussler\ComposerUpdateCheck\Entity;
34

35
use function array_map;
36
use function array_values;
37
use function method_exists;
38

39
/**
40
 * Installer.
41
 *
42
 * @author Elias Häußler <elias@haeussler.dev>
43
 * @license GPL-3.0-or-later
44
 *
45
 * @internal
46
 */
47
final class Installer
48
{
49
    public function __construct(
4✔
50
        private readonly Composer $composer,
51
    ) {}
4✔
52

53
    public function runInstall(?IO\IOInterface $io = null): int
4✔
54
    {
55
        $io ??= new IO\NullIO();
4✔
56

57
        $composer = $this->buildComposer($io);
4✔
58
        $preferredInstall = $composer->getConfig()->get('preferred-install');
4✔
59
        $installer = ComposerInstaller::create($io, $composer)
4✔
60
            ->setPreferSource('source' === $preferredInstall)
4✔
61
            ->setPreferDist('dist' === $preferredInstall)
4✔
62
            ->setDevMode()
4✔
63
        ;
4✔
64

65
        if (method_exists($installer, 'setAudit')) {
4✔
66
            $installer->setAudit(false);
4✔
67
        }
68

69
        return $installer->run();
4✔
70
    }
71

72
    /**
73
     * @param list<Entity\Package\Package> $packages
74
     */
75
    public function runUpdate(array $packages, ?IO\IOInterface $io = null): Entity\Result\ComposerUpdateResult
3✔
76
    {
77
        $io ??= new IO\NullIO();
3✔
78

79
        $outdatedPackages = [];
3✔
80
        $allowedPackageNames = array_map(
3✔
81
            static fn (Entity\Package\Package $package) => $package->getName(),
3✔
82
            $packages,
3✔
83
        );
3✔
84

85
        // Inject installer logger
86
        $composer = $this->buildComposer($io);
3✔
87
        $composer->getInstallationManager()->addInstaller(
3✔
88
            $this->mockInstaller($allowedPackageNames, $outdatedPackages),
3✔
89
        );
3✔
90

91
        // Disable writing of package updates in local repository
92
        $composer->getRepositoryManager()->setLocalRepository(
3✔
93
            $this->mockLocalRepository($composer->getRepositoryManager()->getLocalRepository()),
3✔
94
        );
3✔
95

96
        // Disable lock file management
97
        $composer->getConfig()->merge([
3✔
98
            'config' => [
3✔
99
                'lock' => false,
3✔
100
            ],
3✔
101
        ]);
3✔
102

103
        $preferredInstall = $composer->getConfig()->get('preferred-install');
3✔
104
        $installer = ComposerInstaller::create($io, $composer)
3✔
105
            ->setPreferSource('source' === $preferredInstall)
3✔
106
            ->setPreferDist('dist' === $preferredInstall)
3✔
107
            ->setDevMode()
3✔
108
            ->setUpdate(true)
3✔
109
            ->setDumpAutoloader(false)
3✔
110
            ->setUpdateAllowList($allowedPackageNames)
3✔
111
            ->setUpdateAllowTransitiveDependencies(DependencyResolver\Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS)
3✔
112
        ;
3✔
113

114
        if (method_exists($installer, 'setAudit')) {
3✔
115
            $installer->setAudit(false);
3✔
116
        }
117

118
        return new Entity\Result\ComposerUpdateResult(
3✔
119
            $installer->run(),
3✔
120
            array_values($outdatedPackages),
3✔
121
        );
3✔
122
    }
123

124
    /**
125
     * @param list<non-empty-string>                                  $allowedPackageNames
126
     * @param array<non-empty-string, Entity\Package\OutdatedPackage> $outdatedPackages
127
     */
128
    private function mockInstaller(
3✔
129
        array $allowedPackageNames,
130
        array &$outdatedPackages,
131
    ): ComposerInstaller\InstallerInterface {
132
        return new class($allowedPackageNames, $outdatedPackages) extends ComposerInstaller\NoopInstaller {
3✔
133
            /**
134
             * @param list<non-empty-string>                                  $allowedPackageNames
135
             * @param array<non-empty-string, Entity\Package\OutdatedPackage> $outdatedPackages
136
             */
137
            public function __construct(
138
                private readonly array $allowedPackageNames,
139
                private array &$outdatedPackages,
140
            ) {}
3✔
141

142
            public function update(
143
                Repository\InstalledRepositoryInterface $repo,
144
                Package\PackageInterface $initial,
145
                Package\PackageInterface $target,
146
            ) {
147
                $promise = parent::update($repo, $initial, $target);
3✔
148
                /** @var non-empty-string $name */
149
                $name = $initial->getName();
3✔
150

151
                if ($this->isSuitableUpdate($initial, $target)) {
3✔
152
                    $this->outdatedPackages[$name] = new Entity\Package\OutdatedPackage(
2✔
153
                        $name,
2✔
154
                        new Entity\Version($initial->getPrettyVersion()),
2✔
155
                        new Entity\Version($target->getPrettyVersion()),
2✔
156
                    );
2✔
157
                }
158

159
                return $promise;
3✔
160
            }
161

162
            private function isSuitableUpdate(
163
                Package\PackageInterface $initial,
164
                Package\PackageInterface $target,
165
            ): bool {
166
                $name = $initial->getName();
3✔
167

168
                if (!in_array($name, $this->allowedPackageNames, true)) {
3✔
169
                    return false;
3✔
170
                }
171

172
                if (array_key_exists($name, $this->outdatedPackages)) {
2✔
NEW
173
                    return false;
×
174
                }
175

176
                return $initial->getPrettyVersion() !== $target->getPrettyVersion();
2✔
177
            }
178
        };
3✔
179
    }
180

181
    private function mockLocalRepository(
3✔
182
        Repository\InstalledRepositoryInterface $repository,
183
    ): Repository\InstalledRepositoryInterface {
184
        $newRepository = new Repository\InstalledArrayRepository();
3✔
185
        $newRepository->setDevPackageNames($repository->getDevPackageNames());
3✔
186

187
        foreach ($repository->getPackages() as $package) {
3✔
188
            $newPackage = clone $package;
3✔
189
            $newPackage->setId($package->getId());
3✔
190
            $newRepository->addPackage($newPackage);
3✔
191
        }
192

193
        return $newRepository;
3✔
194
    }
195

196
    private function buildComposer(IO\IOInterface $io): Composer
4✔
197
    {
198
        $composer = Factory::create($io, $this->composer->getConfig()->getConfigSource()->getName(), true);
4✔
199
        $composer->getEventDispatcher()->setRunScripts(false);
4✔
200
        $composer->getAutoloadGenerator()->setRunScripts(false);
4✔
201

202
        return $composer;
4✔
203
    }
204
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc