Completed
Pull Request — master (#1)
by Marco
02:10
created

Verify::activate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\ComposerGpgVerify;
6
7
use Composer\Composer;
8
use Composer\Config;
9
use Composer\EventDispatcher\EventSubscriberInterface;
10
use Composer\Installer\InstallationManager;
11
use Composer\IO\IOInterface;
12
use Composer\Package\PackageInterface;
13
use Composer\Plugin\PluginInterface;
14
use Composer\Script\Event;
15
use Composer\Script\ScriptEvents;
16
use Roave\ComposerGpgVerify\Exception\PackagesTrustCheckFailed;
17
use Roave\ComposerGpgVerify\Package\Git\GitSignatureCheck;
18
use Roave\ComposerGpgVerify\Package\GitPackage;
19
use Roave\ComposerGpgVerify\Package\PackageVerification;
20
use Roave\ComposerGpgVerify\Package\UnknownPackageFormat;
21
22
final class Verify implements PluginInterface, EventSubscriberInterface
23
{
24
    /**
25
     * {@inheritDoc}
26
     */
27
    public static function getSubscribedEvents() : array
28
    {
29
        return [
30
            ScriptEvents::PRE_AUTOLOAD_DUMP => 'verify',
31
        ];
32
    }
33
34
    /**
35
     * {@inheritDoc}
36
     *
37
     * @codeCoverageIgnore
38
     */
39
    public function activate(Composer $composer, IOInterface $io) : void
40
    {
41
        // Nothing to do here, as all features are provided through event listeners
42
    }
43
44
    /**
45
     * @param Event $composerEvent
46
     *
47
     * @throws \Roave\ComposerGpgVerify\Exception\PackagesTrustCheckFailed
48
     * @throws \LogicException
49
     */
50
    public static function verify(Event $composerEvent) : void
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
51
    {
52
        $originalLanguage = getenv('LANGUAGE');
53
        $composer         = $composerEvent->getComposer();
54
        $config           = $composer->getConfig();
55
56
        self::assertSourceInstallation($config);
57
58
        // prevent output changes caused by locale settings on the system where this script is running
59
        putenv(sprintf('LANGUAGE=%s', 'en_US'));
60
61
        $installationManager = $composer->getInstallationManager();
62
        /* @var $checkedPackages PackageVerification[] */
63
        $checkedPackages     = array_map(
64
            function (PackageInterface $package) use ($installationManager) : PackageVerification {
65
                return self::verifyPackage($installationManager, $package);
66
            },
67
            $composer->getRepositoryManager()->getLocalRepository()->getPackages()
68
        );
69
70
        putenv(sprintf('LANGUAGE=%s', (string) $originalLanguage));
71
72
        $escapes = array_filter(
73
            $checkedPackages,
74
            function (PackageVerification $verification) : bool {
75
                return ! $verification->isVerified();
76
            }
77
        );
78
79
        if (! $escapes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $escapes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
80
            return;
81
        }
82
83
        throw PackagesTrustCheckFailed::fromFailedPackageVerifications(...$escapes);
84
    }
85
86
    private static function verifyPackage(
87
        InstallationManager $installationManager,
88
        PackageInterface $package
89
    ) : PackageVerification {
90
        $gitDirectory = $installationManager->getInstallPath($package) . '/.git';
91
92
        if (! is_dir($gitDirectory)) {
93
            return UnknownPackageFormat::fromNonGitPackage($package);
94
        }
95
96
        return GitPackage::fromPackageAndSignatureChecks(
97
            $package,
98
            self::checkCurrentCommitSignature($gitDirectory, $package),
99
            ...self::checkTagSignatures($gitDirectory, $package, ...self::getTagsForCurrentCommit($gitDirectory))
100
        );
101
    }
102
103 View Code Duplication
    private static function checkCurrentCommitSignature(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
104
        string $gitDirectory,
105
        PackageInterface $package
106
    ) : GitSignatureCheck {
107
        $command = sprintf(
108
            'git --git-dir %s verify-commit --verbose HEAD 2>&1',
109
            escapeshellarg($gitDirectory)
110
        );
111
112
        exec($command, $output, $exitCode);
113
114
        return GitSignatureCheck::fromGitCommitCheck($package, $command, $exitCode, implode("\n", $output));
115
    }
116
117
    /**
118
     * @param string $gitDirectory
119
     *
120
     * @return string[]
121
     */
122
    private static function getTagsForCurrentCommit(string $gitDirectory) : array
123
    {
124
        exec(
125
            sprintf(
126
                'git --git-dir %s tag --points-at HEAD 2>&1',
127
                escapeshellarg($gitDirectory)
128
            ),
129
            $tags
130
        );
131
132
        return array_values(array_filter($tags));
133
    }
134
135
136
    /**
137
     * @param string           $gitDirectory
138
     * @param PackageInterface $package
139
     * @param string[]         $tags
140
     *
141
     * @return GitSignatureCheck[]
142
     */
143
    private static function checkTagSignatures(string $gitDirectory, PackageInterface $package, string ...$tags) : array
144
    {
145
        return array_map(
146 View Code Duplication
            function (string $tag) use ($gitDirectory, $package) : GitSignatureCheck {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
                $command = sprintf(
148
                    'git --git-dir %s tag -v %s 2>&1',
149
                    escapeshellarg($gitDirectory),
150
                    escapeshellarg($tag)
151
                );
152
153
                exec($command, $tagSignatureOutput, $exitCode);
154
155
                return GitSignatureCheck::fromGitTagCheck(
156
                    $package,
157
                    $command,
158
                    $exitCode,
159
                    implode("\n", $tagSignatureOutput)
160
                );
161
            },
162
            $tags
163
        );
164
    }
165
166
    /**
167
     * @param Config $config
168
     *
169
     *
170
     * @throws \LogicException
171
     * @throws \RuntimeException
172
     */
173
    private static function assertSourceInstallation(Config $config) : void
174
    {
175
        $preferredInstall = $config->get('preferred-install');
176
177
        if ('source' !== $preferredInstall) {
178
            throw new \LogicException(sprintf(
179
                'Expected installation "preferred-install" to be "source", found "%s" instead',
180
                (string) $preferredInstall
181
            ));
182
        }
183
    }
184
}
185