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

eliashaeussler / composer-update-check / 10698213278

04 Sep 2024 08:42AM UTC coverage: 25.95%. First build
10698213278

Pull #130

github

web-flow
Merge pull request #138 from eliashaeussler/task/root-package

[TASK] Store root package information in `UpdateCheckResult`
Pull Request #130: [!!!][FEATURE] Modernize plugin

336 of 1369 new or added lines in 47 files covered. (24.54%)

362 of 1395 relevant lines covered (25.95%)

1.37 hits per line

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

0.0
/src/Entity/Report/SlackReport.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\Entity\Report;
25

26
use EliasHaeussler\ComposerUpdateCheck\Entity;
27
use JsonSerializable;
28

29
use function count;
30
use function implode;
31
use function sprintf;
32

33
/**
34
 * SlackReport.
35
 *
36
 * @author Elias Häußler <elias@haeussler.dev>
37
 * @license GPL-3.0-or-later
38
 */
39
final class SlackReport implements JsonSerializable
40
{
41
    private const MAX_BLOCKS = 45;
42

43
    /**
44
     * @param list<Dto\SlackBlock> $blocks
45
     */
NEW
46
    private function __construct(
×
47
        public readonly array $blocks,
NEW
48
    ) {}
×
49

NEW
50
    public static function create(Entity\Result\UpdateCheckResult $result): self
×
51
    {
NEW
52
        $rootPackageName = $result->getRootPackage()?->getName();
×
NEW
53
        $remainingBlocks = self::MAX_BLOCKS;
×
NEW
54
        $remainingPackages = count($result->getOutdatedPackages());
×
55

56
        // Create header block
NEW
57
        $blocks = [
×
NEW
58
            self::createHeaderBlock($result),
×
NEW
59
        ];
×
60

61
        // Create package name block
NEW
62
        if (null !== $rootPackageName) {
×
NEW
63
            $blocks[] = self::createPackageNameBlock($rootPackageName);
×
64
        }
65

66
        // Create outdated package blocks
NEW
67
        foreach ($result->getOutdatedPackages() as $outdatedPackage) {
×
NEW
68
            if (--$remainingBlocks > 0) {
×
NEW
69
                $blocks[] = self::createOutdatedPackageBlock($outdatedPackage);
×
70
            } else {
71
                // Slack allows only a limited number of blocks, therefore
72
                // we have to omit the remaining packages and show a message instead
NEW
73
                $blocks[] = self::createMoreBlock($remainingPackages);
×
74
            }
75

NEW
76
            --$remainingPackages;
×
77
        }
78

79
        // Create security advisories block
NEW
80
        if ($remainingBlocks > 4) {
×
NEW
81
            $remainingSecurityAdvisories = count($result->getSecurityAdvisories());
×
NEW
82
            $remainingBlocks -= 2;
×
NEW
83
            $blocks[] = Dto\SlackBlock::header(
×
NEW
84
                Dto\SlackBlockElement::plainText('Security advisories'),
×
NEW
85
            );
×
86

NEW
87
            foreach ($result->getSecurityAdvisories() as $securityAdvisory) {
×
NEW
88
                if (--$remainingBlocks > 1) {
×
NEW
89
                    $blocks[] = self::createSecurityAdvisoryBlock($securityAdvisory);
×
90
                } else {
91
                    // Slack allows only a limited number of blocks, therefore
92
                    // we have to omit the remaining security advisories and show a message instead
NEW
93
                    $blocks[] = self::createMoreBlock($remainingSecurityAdvisories);
×
94
                }
95

NEW
96
                --$remainingSecurityAdvisories;
×
97
            }
98
        }
99

NEW
100
        return new self($blocks);
×
101
    }
102

NEW
103
    private static function createHeaderBlock(Entity\Result\UpdateCheckResult $result): Dto\SlackBlock
×
104
    {
NEW
105
        $numberOfOutdatedPackages = 0;
×
NEW
106
        $numberOfInsecurePackages = 0;
×
107

108
        // Count outdated and insecure packages
NEW
109
        foreach ($result->getOutdatedPackages() as $outdatedPackage) {
×
NEW
110
            ++$numberOfOutdatedPackages;
×
111

NEW
112
            if ($outdatedPackage->isInsecure()) {
×
NEW
113
                ++$numberOfInsecurePackages;
×
114
            }
115
        }
116

NEW
117
        return Dto\SlackBlock::header(
×
NEW
118
            Dto\SlackBlockElement::plainText(
×
NEW
119
                sprintf(
×
NEW
120
                    '%d outdated%s package%s',
×
NEW
121
                    $numberOfOutdatedPackages,
×
NEW
122
                    $numberOfInsecurePackages > 0 ? sprintf(' (%d insecure)', $numberOfInsecurePackages) : '',
×
NEW
123
                    1 !== $numberOfOutdatedPackages ? 's' : '',
×
NEW
124
                ),
×
NEW
125
            ),
×
NEW
126
        );
×
127
    }
128

NEW
129
    private static function createPackageNameBlock(string $rootPackageName): Dto\SlackBlock
×
130
    {
NEW
131
        return Dto\SlackBlock::section(
×
NEW
132
            Dto\SlackBlockElement::markdown(
×
NEW
133
                sprintf('Project: *%s*', $rootPackageName),
×
NEW
134
            ),
×
NEW
135
        );
×
136
    }
137

NEW
138
    private static function createOutdatedPackageBlock(Entity\Package\OutdatedPackage $outdatedPackage): Dto\SlackBlock
×
139
    {
NEW
140
        $highestSeverityLevel = $outdatedPackage->getHighestSeverityLevel();
×
141

NEW
142
        return Dto\SlackBlock::section(
×
NEW
143
            fields: [
×
NEW
144
                Dto\SlackBlockElement::markdown(
×
NEW
145
                    sprintf(
×
NEW
146
                        '<%s|%s>%s',
×
NEW
147
                        $outdatedPackage->getProviderLink(),
×
NEW
148
                        $outdatedPackage->getName(),
×
NEW
149
                        null !== $highestSeverityLevel
×
NEW
150
                            ? sprintf(
×
NEW
151
                                "\n%s `%s`",
×
NEW
152
                                self::getEmojiForSeverityLevel($highestSeverityLevel),
×
NEW
153
                                $highestSeverityLevel->value,
×
NEW
154
                            ) : '',
×
NEW
155
                    ),
×
NEW
156
                ),
×
NEW
157
                Dto\SlackBlockElement::markdown(
×
NEW
158
                    sprintf(
×
NEW
159
                        "*Current version:* %s\n*New version:* %s",
×
NEW
160
                        $outdatedPackage->getOutdatedVersion()->toString(),
×
NEW
161
                        $outdatedPackage->getNewVersion()->toString(),
×
NEW
162
                    ),
×
NEW
163
                ),
×
NEW
164
            ],
×
NEW
165
        );
×
166
    }
167

NEW
168
    private static function createSecurityAdvisoryBlock(
×
169
        Entity\Security\SecurityAdvisory $securityAdvisory,
170
    ): Dto\SlackBlock {
NEW
171
        $textParts = [
×
NEW
172
            sprintf('*%s*', $securityAdvisory->getSanitizedTitle()),
×
NEW
173
            sprintf('• Package: `%s`', $securityAdvisory->getPackageName()),
×
NEW
174
            sprintf(
×
NEW
175
                '• Reported at: `<!date^%d^{date}|%s>`',
×
NEW
176
                $securityAdvisory->getReportedAt()->getTimestamp(),
×
NEW
177
                $securityAdvisory->getReportedAt()->format('Y-m-d'),
×
NEW
178
            ),
×
NEW
179
        ];
×
180

NEW
181
        if (null !== $securityAdvisory->getCVE()) {
×
NEW
182
            $textParts[] = sprintf('• CVE: `%s`', $securityAdvisory->getCVE());
×
183
        }
184

NEW
185
        if (null !== $securityAdvisory->getLink()) {
×
NEW
186
            $textParts[] = sprintf('<%s|Read more>', $securityAdvisory->getLink());
×
187
        }
188

NEW
189
        return Dto\SlackBlock::section(
×
NEW
190
            Dto\SlackBlockElement::markdown(
×
NEW
191
                implode(PHP_EOL, $textParts),
×
NEW
192
            ),
×
NEW
193
        );
×
194
    }
195

NEW
196
    private static function createMoreBlock(int $remaining): Dto\SlackBlock
×
197
    {
NEW
198
        return Dto\SlackBlock::section(
×
NEW
199
            Dto\SlackBlockElement::markdown(
×
NEW
200
                sprintf('_... and %d more_', $remaining),
×
NEW
201
            ),
×
NEW
202
        );
×
203
    }
204

NEW
205
    private static function getEmojiForSeverityLevel(Entity\Security\SeverityLevel $severityLevel): string
×
206
    {
NEW
207
        return match ($severityLevel) {
×
NEW
208
            Entity\Security\SeverityLevel::Low => ':white_circle:',
×
NEW
209
            Entity\Security\SeverityLevel::Medium => ':large_yellow_circle:',
×
NEW
210
            Entity\Security\SeverityLevel::High => ':large_orange_circle:',
×
NEW
211
            Entity\Security\SeverityLevel::Critical => ':red_circle:',
×
NEW
212
        };
×
213
    }
214

215
    /**
216
     * @return array{
217
     *     blocks: list<Dto\SlackBlock>,
218
     * }
219
     */
NEW
220
    public function jsonSerialize(): array
×
221
    {
NEW
222
        return [
×
NEW
223
            'blocks' => $this->blocks,
×
NEW
224
        ];
×
225
    }
226
}
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

© 2026 Coveralls, Inc