Verified Commit 688a5c36 authored by Elias Häußler's avatar Elias Häußler 🐛
Browse files

[!!!][FEATURE] Send root package name when reporting outdated packages

parent 82477805
......@@ -94,14 +94,28 @@ class Reporter
private function buildServicesFromConfiguration(): array
{
$services = [];
// Resolve project name from root composer.json
$projectName = $this->composer->getPackage()->getName();
if ('__root__' === $projectName) {
$projectName = null;
}
/** @var ServiceInterface $registeredService */
foreach ($this->registeredServices as $registeredService) {
if ($registeredService::isEnabled($this->configuration)) {
$service = $registeredService::fromConfiguration($this->configuration);
$service->setBehavior($this->behavior);
$service->setOptions($this->options);
$services[] = $service;
if (!$registeredService::isEnabled($this->configuration)) {
continue;
}
$service = $registeredService::fromConfiguration($this->configuration);
$service->setBehavior($this->behavior);
$service->setOptions($this->options);
if (!empty($projectName)) {
$service->setProjectName($projectName);
}
$services[] = $service;
}
return $services;
......
......@@ -53,6 +53,11 @@ abstract class AbstractService implements ServiceInterface
*/
protected $options;
/**
* @var string|null
*/
protected $projectName;
/**
* @param array<string, mixed> $configuration
*/
......@@ -133,6 +138,13 @@ abstract class AbstractService implements ServiceInterface
return $this;
}
public function setProjectName(string $projectName): ServiceInterface
{
$this->projectName = $projectName;
return $this;
}
private function getDefaultBehavior(): OutputBehavior
{
return new OutputBehavior(
......
......@@ -96,6 +96,9 @@ class Email extends AbstractService
// Set subject
$count = count($outdatedPackages);
$subject = sprintf('%d outdated package%s', $count, 1 !== $count ? 's' : '');
if (null !== $this->projectName) {
$subject .= sprintf(' @ %s', $this->projectName);
}
// Set plain text body and html content
$body = $this->parsePlainBody($outdatedPackages);
......@@ -122,6 +125,12 @@ class Email extends AbstractService
private function parsePlainBody(array $outdatedPackages): string
{
$textParts = [];
if (null !== $this->projectName) {
$textParts[] = sprintf('Project: "%s"', $this->projectName);
$textParts[] = '';
}
foreach ($outdatedPackages as $outdatedPackage) {
$insecure = '';
if ($outdatedPackage->isInsecure()) {
......@@ -145,12 +154,19 @@ class Email extends AbstractService
private function parseHtmlBody(array $outdatedPackages): string
{
$html = [];
if (null !== $this->projectName) {
$html[] = sprintf('<p>Project: <strong>%s</strong></p>', $this->projectName);
$html[] = '<hr>';
}
$html[] = '<table>';
$html[] = '<tr>';
$html[] = '<th>Package name</th>';
$html[] = '<th>Outdated version</th>';
$html[] = '<th>New version</th>';
$html[] = '</tr>';
foreach ($outdatedPackages as $outdatedPackage) {
$insecure = '';
if ($outdatedPackage->isInsecure()) {
......
......@@ -89,10 +89,17 @@ class GitLab extends AbstractService
{
$outdatedPackages = $result->getOutdatedPackages();
// Build JSON payload
// Build title
$count = count($outdatedPackages);
$title = sprintf('%d outdated package%s', $count, 1 !== $count ? 's' : '');
if (null !== $this->projectName) {
$title .= sprintf(' @ %s', $this->projectName);
}
// Build JSON payload
$payload = array_merge([
'title' => sprintf('%d outdated package%s', $count, 1 !== $count ? 's' : ''),
'title' => $title,
], $this->getPackagesPayload($outdatedPackages));
// Send report
......
......@@ -97,8 +97,10 @@ class Mattermost extends AbstractService
$outdatedPackages = $result->getOutdatedPackages();
// Build JSON payload
$count = count($outdatedPackages);
$payload = [
'channel' => $this->channelName,
'text' => sprintf('#### :rotating_light: %d outdated package%s', $count, 1 !== $count ? 's' : ''),
'attachments' => [
[
'color' => '#EE0000',
......@@ -124,23 +126,22 @@ class Mattermost extends AbstractService
*/
private function renderText(array $outdatedPackages): string
{
$count = count($outdatedPackages);
$textParts = [
sprintf('#### :rotating_light: %d outdated package%s', $count, 1 !== $count ? 's' : ''),
'| Package | Current version | New version |',
'|:------- |:--------------- |:----------- |',
];
$textParts = [];
if (null !== $this->projectName) {
$textParts[] = sprintf('##### %s', $this->projectName);
}
$textParts[] = '| Package | Current version | New version |';
$textParts[] = '|:------- |:--------------- |:----------- |';
foreach ($outdatedPackages as $outdatedPackage) {
$insecure = '';
if ($outdatedPackage->isInsecure()) {
$insecure = ' :warning: **`insecure`**';
}
$textParts[] = sprintf(
'| [%s](%s) | %s%s | **%s** |',
$outdatedPackage->getName(),
$outdatedPackage->getProviderLink(),
$outdatedPackage->getOutdatedVersion(),
$insecure,
$outdatedPackage->isInsecure() ? ' :warning: **`insecure`**' : '',
$outdatedPackage->getNewVersion()
);
}
......
......@@ -52,13 +52,9 @@ interface ServiceInterface
public function report(UpdateCheckResult $result): bool;
/**
* @return static
*/
public function setBehavior(OutputBehavior $behavior): self;
/**
* @return static
*/
public function setOptions(Options $options): self;
public function setProjectName(string $projectName): self;
}
......@@ -104,6 +104,8 @@ class Slack extends AbstractService
{
$hasInsecurePackages = false;
$count = count($outdatedPackages);
$remainingPackages = $count;
$maxBlocks = 50;
// Calculate longest version numbers of all outdated packages
$outdatedVersionNumberLength = 0;
......@@ -125,16 +127,17 @@ class Slack extends AbstractService
'text' => sprintf('%d outdated package%s', $count, 1 !== $count ? 's' : ''),
],
],
[
];
if (null !== $this->projectName) {
$blocks[] = [
'type' => 'section',
'text' => [
'type' => 'plain_text',
'text' => 1 !== $count
? 'The following packages are outdated and need to be updated:'
: 'The following package is outdated and needs to be updated:',
'type' => 'mrkdwn',
'text' => sprintf('Project: *%s*', $this->projectName),
],
],
];
];
}
foreach ($outdatedPackages as $outdatedPackage) {
if ($outdatedPackage->isInsecure()) {
......@@ -166,8 +169,7 @@ class Slack extends AbstractService
// Slack allows only a limited number of blocks, therefore
// we have to omit the remaining packages and show a message instead
$remainingPackages = $count - (count($blocks) - 2);
if (count($blocks) >= 48 && $remainingPackages > 0) {
if (count($blocks) >= ($maxBlocks - 2) && --$remainingPackages > 0) {
$blocks[] = [
'type' => 'section',
'text' => [
......
......@@ -90,6 +90,10 @@ class Teams extends AbstractService
'sections' => $this->renderSections($outdatedPackages),
];
if (null !== $this->projectName) {
$payload['title'] .= sprintf(' @ %s', $this->projectName);
}
// Send report
if (!$this->behavior->style->isJson()) {
$this->behavior->io->write(Emoji::rocket().' Sending report to MS Teams...');
......
{
"name": "foo/baz",
"description": "Test application",
"require-dev": {
"eliashaeussler/composer-update-check": "^1.0@dev",
"phpunit/phpunit": "^5.0"
......
......@@ -62,6 +62,11 @@ class DummyService extends AbstractService
*/
public static $customOptions;
/**
* @var string|null
*/
public static $customProjectName;
public static function getIdentifier(): string
{
return 'dummy';
......@@ -84,6 +89,7 @@ class DummyService extends AbstractService
static::$reportWasExecuted = false;
static::$customBehavior = null;
static::$customOptions = null;
static::$customProjectName = null;
}
public static function isEnabled(array $configuration): bool
......@@ -136,4 +142,16 @@ class DummyService extends AbstractService
{
return $this->options;
}
public function setProjectName(string $projectName): ServiceInterface
{
static::$customProjectName = $projectName;
return parent::setProjectName($projectName);
}
public function getProjectName(): ?string
{
return $this->projectName;
}
}
......@@ -105,7 +105,7 @@ class ReporterTest extends AbstractTestCase
/**
* @test
*/
public function setBehaviorForwardsBehaviorToServices(): void
public function reportForwardsBehaviorToServices(): void
{
$behavior = new OutputBehavior(new Style(), new Verbosity(), new NullIO());
......@@ -118,7 +118,7 @@ class ReporterTest extends AbstractTestCase
/**
* @test
*/
public function setOptionsForwardsOptionsToServices(): void
public function reportForwardsOptionsToServices(): void
{
DummyService::$enabled = true;
......@@ -130,6 +130,16 @@ class ReporterTest extends AbstractTestCase
static::assertSame($options, DummyService::$customOptions);
}
/**
* @test
*/
public function reportForwardsProjectNameToServices(): void
{
$this->subject->report(new UpdateCheckResult([]));
static::assertSame('foo/baz', DummyService::$customProjectName);
}
/**
* @test
*/
......
......@@ -295,6 +295,40 @@ class EmailTest extends AbstractTestCase
static::assertSame($expected, $message->body);
}
/**
* @test
*/
public function reportIncludesProjectNameInSubjectAndBody(): void
{
$this->subject->setProjectName('foo/baz');
$result = new UpdateCheckResult([
new OutdatedPackage('foo/foo', '1.0.0', '1.0.5'),
]);
$this->subject->report($result);
$message = $this->mailhog->getLastMessage();
static::assertSame('1 outdated package @ foo/baz', $message->subject);
$expected = implode("\r\n", [
'<p>Project: <strong>foo/baz</strong></p>',
'<hr>',
'<table>',
'<tr>',
'<th>Package name</th>',
'<th>Outdated version</th>',
'<th>New version</th>',
'</tr>',
'<tr>',
'<td><a href="https://packagist.org/packages/foo/foo#1.0.5">foo/foo</a></td>',
'<td>1.0.0</td>',
'<td><strong>1.0.5</strong></td>',
'</tr>',
'</table>',
]);
static::assertSame($expected, $message->body);
}
/**
* @return array<string, array>
*/
......
......@@ -189,6 +189,27 @@ class GitLabTest extends AbstractTestCase
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
}
/**
* @test
*/
public function reportIncludesProjectNameInTitle(): void
{
$result = new UpdateCheckResult([
new OutdatedPackage('foo/foo', '1.0.0', '1.0.5'),
]);
$this->subject->setProjectName('foo/baz');
$this->subject->setClient($this->getClient());
$this->mockedResponse = new MockResponse();
static::assertTrue($this->subject->report($result));
$expectedPayloadSubset = [
'title' => '1 outdated package @ foo/baz',
];
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
}
/**
* @return array<string, array>
*/
......
......@@ -189,6 +189,7 @@ class MattermostTest extends AbstractTestCase
$expectedPayloadSubset = [
'channel' => 'foo',
'text' => '#### :rotating_light: 1 outdated package',
'attachments' => [
[
'color' => '#EE0000',
......@@ -199,11 +200,31 @@ class MattermostTest extends AbstractTestCase
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
$payload = $this->getPayloadOfLastRequest();
$text = $payload['attachments'][0]['text'] ?? null;
$text = $payload['attachments'][0]['text'] ?? '';
$expected = '[foo/foo](https://packagist.org/packages/foo/foo#1.0.5) | 1.0.0'.$expectedSecurityNotice.' | **1.0.5**';
static::assertStringContainsString($expected, $text);
}
/**
* @test
*/
public function reportIncludesProjectNameInMessageAttachment(): void
{
$result = new UpdateCheckResult([
new OutdatedPackage('foo/foo', '1.0.0', '1.0.5'),
]);
$this->subject->setProjectName('foo/baz');
$this->subject->setClient($this->getClient());
$this->mockedResponse = new MockResponse();
static::assertTrue($this->subject->report($result));
$payload = $this->getPayloadOfLastRequest();
$text = $payload['attachments'][0]['text'] ?? '';
static::assertStringContainsString('##### foo/baz', $text);
}
/**
* @return array<string, array>
*/
......
......@@ -151,13 +151,6 @@ class SlackTest extends AbstractTestCase
'text' => '1 outdated package',
],
],
[
'type' => 'section',
'text' => [
'type' => 'plain_text',
'text' => 'The following package is outdated and needs to be updated:',
],
],
[
'type' => 'section',
'fields' => [
......@@ -175,7 +168,7 @@ class SlackTest extends AbstractTestCase
];
if ($insecure) {
$expectedPayloadSubset['blocks'][2]['fields'][1]['text'] .= ' :rotating_light:';
$expectedPayloadSubset['blocks'][1]['fields'][1]['text'] .= ' :rotating_light:';
$expectedPayloadSubset['blocks'][] = [
'type' => 'context',
'elements' => [
......@@ -214,15 +207,8 @@ class SlackTest extends AbstractTestCase
'text' => '1 outdated package',
],
],
[
'type' => 'section',
'text' => [
'type' => 'plain_text',
'text' => 'The following package is outdated and needs to be updated:',
],
],
],
array_fill(0, 46, [
array_fill(0, 47, [
'type' => 'section',
'fields' => [
[
......@@ -240,7 +226,7 @@ class SlackTest extends AbstractTestCase
'type' => 'section',
'text' => [
'type' => 'plain_text',
'text' => '... and 4 more',
'text' => '... and 3 more',
],
],
]
......@@ -256,6 +242,35 @@ class SlackTest extends AbstractTestCase
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
}
/**
* @test
*/
public function reportIncludesProjectNameInMessage(): void
{
$result = new UpdateCheckResult([
new OutdatedPackage('foo/foo', '1.0.0', '1.0.5'),
]);
$this->subject->setProjectName('foo/baz');
$this->subject->setClient($this->getClient());
$this->mockedResponse = new MockResponse();
static::assertTrue($this->subject->report($result));
$expectedPayloadSubset = [
'blocks' => [
[
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => 'Project: *foo/baz*',
],
],
],
];
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
}
/**
* @return array<string, array>
*/
......
......@@ -162,6 +162,25 @@ class TeamsTest extends AbstractTestCase
static::assertSame($expected, $text);
}
/**
* @test
*/
public function reportIncludesProjectNameInTitle(): void
{
$result = new UpdateCheckResult([
new OutdatedPackage('foo/foo', '1.0.0', '1.0.5'),
]);
$this->subject->setProjectName('foo/baz');
$this->subject->setClient($this->getClient());
$this->mockedResponse = new MockResponse();
static::assertTrue($this->subject->report($result));
$payload = $this->getPayloadOfLastRequest();
static::assertSame(sprintf('%s 1 outdated package @ foo/baz', Emoji::policeCarLight()), $payload['title']);
}
/**
* @return array<string, array>
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment