Completed
Push — master ( da1a6c...6bb072 )
by Marco
02:08
created

Verify::getTagsForCurrentCommit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 1
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\Exception\PreferredInstallIsNotSource;
18
use Roave\ComposerGpgVerify\Package\Git\GitSignatureCheck;
19
use Roave\ComposerGpgVerify\Package\GitPackage;
20
use Roave\ComposerGpgVerify\Package\PackageVerification;
21
use Roave\ComposerGpgVerify\Package\UnknownPackageFormat;
22
23
final class Verify implements PluginInterface, EventSubscriberInterface
24
{
25
    /**
26
     * {@inheritDoc}
27
     */
28
    public static function getSubscribedEvents() : array
29
    {
30
        return [
31
            ScriptEvents::PRE_AUTOLOAD_DUMP => 'verify',
32
        ];
33
    }
34
35
    /**
36
     * {@inheritDoc}
37
     *
38
     * @codeCoverageIgnore
39
     */
40
    public function activate(Composer $composer, IOInterface $io) : void
41
    {
42
        // Nothing to do here, as all features are provided through event listeners
43
    }
44
45
    /**
46
     * @param Event $composerEvent
47
     *
48
     * @throws \Roave\ComposerGpgVerify\Exception\PackagesTrustCheckFailed
49
     * @throws \RuntimeException
50
     * @throws \Roave\ComposerGpgVerify\Exception\PreferredInstallIsNotSource
51
     */
52
    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...
53
    {
54
        $originalLanguage = getenv('LANGUAGE');
55
        $io               = $composerEvent->getIO();
56
        $composer         = $composerEvent->getComposer();
57
        $config           = $composer->getConfig();
58
59
        $io->write('<info>roave/composer-gpg-verify:</info>  Analysing downloaded packages...');
60
61
        self::assertSourceInstallation($config);
62
63
        // prevent output changes caused by locale settings on the system where this script is running
64
        putenv(sprintf('LANGUAGE=%s', 'en_US'));
65
66
        $installationManager = $composer->getInstallationManager();
67
        /* @var $checkedPackages PackageVerification[] */
68
        $checkedPackages     = array_map(
69
            function (PackageInterface $package) use ($installationManager) : PackageVerification {
70
                return self::verifyPackage($installationManager, $package);
71
            },
72
            $composer->getRepositoryManager()->getLocalRepository()->getPackages()
73
        );
74
75
        putenv(sprintf('LANGUAGE=%s', (string) $originalLanguage));
76
77
        $escapes = array_filter(
78
            $checkedPackages,
79
            function (PackageVerification $verification) : bool {
80
                return ! $verification->isVerified();
81
            }
82
        );
83
84
        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...
85
            $io->write('<info>roave/composer-gpg-verify:</info>  All installed packages passed GPG validation!');
86
87
            return;
88
        }
89
90
        throw PackagesTrustCheckFailed::fromFailedPackageVerifications(...$escapes);
91
    }
92
93
    private static function verifyPackage(
94
        InstallationManager $installationManager,
95
        PackageInterface $package
96
    ) : PackageVerification {
97
        $gitDirectory = $installationManager->getInstallPath($package) . '/.git';
98
99
        if (! is_dir($gitDirectory)) {
100
            return UnknownPackageFormat::fromNonGitPackage($package);
101
        }
102
103
        return GitPackage::fromPackageAndSignatureChecks(
104
            $package,
105
            self::checkCurrentCommitSignature($gitDirectory, $package),
106
            ...self::checkTagSignatures($gitDirectory, $package, ...self::getTagsForCurrentCommit($gitDirectory))
107
        );
108
    }
109
110 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...
111
        string $gitDirectory,
112
        PackageInterface $package
113
    ) : GitSignatureCheck {
114
        $command = sprintf(
115
            'git --git-dir %s verify-commit --verbose HEAD 2>&1',
116
            escapeshellarg($gitDirectory)
117
        );
118
119
        exec($command, $output, $exitCode);
120
121
        return GitSignatureCheck::fromGitCommitCheck($package, $command, $exitCode, implode("\n", $output));
122
    }
123
124
    /**
125
     * @param string $gitDirectory
126
     *
127
     * @return string[]
128
     */
129
    private static function getTagsForCurrentCommit(string $gitDirectory) : array
130
    {
131
        exec(
132
            sprintf(
133
                'git --git-dir %s tag --points-at HEAD 2>&1',
134
                escapeshellarg($gitDirectory)
135
            ),
136
            $tags
137
        );
138
139
        return array_values(array_filter($tags));
140
    }
141
142
143
    /**
144
     * @param string           $gitDirectory
145
     * @param PackageInterface $package
146
     * @param string[]         $tags
147
     *
148
     * @return GitSignatureCheck[]
149
     */
150
    private static function checkTagSignatures(string $gitDirectory, PackageInterface $package, string ...$tags) : array
151
    {
152
        return array_map(
153 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...
154
                $command = sprintf(
155
                    'git --git-dir %s tag -v %s 2>&1',
156
                    escapeshellarg($gitDirectory),
157
                    escapeshellarg($tag)
158
                );
159
160
                exec($command, $tagSignatureOutput, $exitCode);
161
162
                return GitSignatureCheck::fromGitTagCheck(
163
                    $package,
164
                    $command,
165
                    $exitCode,
166
                    implode("\n", $tagSignatureOutput)
167
                );
168
            },
169
            $tags
170
        );
171
    }
172
173
    /**
174
     * @param Config $config
175
     *
176
     *
177
     * @throws \RuntimeException
178
     * @throws \Roave\ComposerGpgVerify\Exception\PreferredInstallIsNotSource
179
     */
180
    private static function assertSourceInstallation(Config $config) : void
181
    {
182
        $preferredInstall = $config->get('preferred-install');
183
184
        if ('source' !== $preferredInstall) {
185
            throw PreferredInstallIsNotSource::fromPreferredInstall($preferredInstall);
186
        }
187
    }
188
}
189