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

move-elevator / composer-translation-validator / 18560103885

16 Oct 2025 11:42AM UTC coverage: 95.519%. Remained the same
18560103885

Pull #73

github

jackd248
refactor: remove unnecessary type hint from MismatchValidator
Pull Request #73: build: add php-cs-fixer-preset

206 of 210 new or added lines in 16 files covered. (98.1%)

91 existing lines in 20 files now uncovered.

2345 of 2455 relevant lines covered (95.52%)

7.73 hits per line

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

98.41
/src/Result/ValidationResultGitHubRenderer.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the "composer-translation-validator" Composer plugin.
7
 *
8
 * (c) 2025 Konrad Michalik <km@move-elevator.de>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13

14
namespace MoveElevator\ComposerTranslationValidator\Result;
15

16
use MoveElevator\ComposerTranslationValidator\Validator\ResultType;
17

18
use function sprintf;
19

20
/**
21
 * ValidationResultGitHubRenderer.
22
 *
23
 * @author Konrad Michalik <km@move-elevator.de>
24
 * @license GPL-3.0-or-later
25
 */
26
class ValidationResultGitHubRenderer extends AbstractValidationResultRenderer
27
{
28
    public function render(ValidationResult $validationResult): int
7✔
29
    {
30
        $exitCode = $this->calculateExitCode($validationResult);
7✔
31

32
        // Output GitHub Actions workflow commands for each issue
33
        $this->renderIssues($validationResult);
7✔
34

35
        // Output summary
36
        $this->renderSummary($validationResult, $exitCode);
7✔
37

38
        return $exitCode;
7✔
39
    }
40

41
    private function renderIssues(ValidationResult $validationResult): void
7✔
42
    {
43
        $groupedByFile = $this->groupIssuesByFile($validationResult);
7✔
44

45
        foreach ($groupedByFile as $filePath => $validators) {
7✔
46
            foreach ($validators as $validatorData) {
5✔
47
                $resultType = $validatorData['validator']->resultTypeOnValidationFailure();
5✔
48

49
                foreach ($validatorData['issues'] as $issue) {
5✔
50
                    $this->renderGitHubAnnotation($issue, $filePath, $resultType);
5✔
51
                }
52
            }
53
        }
54
    }
55

56
    private function renderGitHubAnnotation(Issue $issue, string $filePath, ResultType $resultType): void
5✔
57
    {
58
        $details = $issue->getDetails();
5✔
59
        $message = $details['message'] ?? 'Translation validation issue';
5✔
60
        $line = $details['line'] ?? null;
5✔
61
        $column = $details['column'] ?? null;
5✔
62

63
        $annotationType = match ($resultType) {
5✔
64
            ResultType::ERROR => 'error',
5✔
65
            ResultType::WARNING => 'warning',
2✔
UNCOV
66
            default => 'notice',
×
67
        };
5✔
68

69
        $params = ['file='.$this->escapeProperty($filePath)];
5✔
70

71
        if (null !== $line) {
5✔
72
            $params[] = 'line='.$line;
3✔
73
        }
74

75
        if (null !== $column) {
5✔
76
            $params[] = 'col='.$column;
1✔
77
        }
78

79
        if (isset($details['title'])) {
5✔
80
            $params[] = 'title='.$this->escapeProperty((string) $details['title']);
2✔
81
        }
82

83
        $paramString = implode(',', $params);
5✔
84
        $escapedMessage = $this->escapeData($message);
5✔
85

86
        $this->output->writeln("::{$annotationType} {$paramString}::{$escapedMessage}");
5✔
87
    }
88

89
    private function renderSummary(ValidationResult $validationResult, int $exitCode): void
7✔
90
    {
91
        $message = $this->generateMessage($validationResult);
7✔
92
        $resultType = $validationResult->getOverallResult();
7✔
93

94
        $summaryType = match (true) {
7✔
95
            0 === $exitCode => 'notice',
7✔
96
            ResultType::WARNING === $resultType && !$this->strict => 'notice',
3✔
97
            default => 'error',
3✔
98
        };
7✔
99

100
        $this->output->writeln("::{$summaryType}::{$message}");
7✔
101

102
        $statistics = $this->formatStatisticsForOutput($validationResult);
7✔
103
        if (!empty($statistics)) {
7✔
104
            $statsMessage = sprintf(
1✔
105
                'Validation completed in %s - Files: %d, Keys: %d, Validators: %d',
1✔
106
                $statistics['execution_time_formatted'] ?? 'unknown',
1✔
107
                $statistics['files_checked'] ?? 0,
1✔
108
                $statistics['keys_checked'] ?? 0,
1✔
109
                $statistics['validators_run'] ?? 0,
1✔
110
            );
1✔
111

112
            $this->output->writeln("::notice::{$statsMessage}");
1✔
113
        }
114
    }
115

116
    /**
117
     * Escape property values for GitHub Actions annotations
118
     * https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message.
119
     */
120
    private function escapeProperty(string $value): string
5✔
121
    {
122
        return str_replace(
5✔
123
            ['%', "\r", "\n", ':', ',', ' '],
5✔
124
            ['%25', '%0D', '%0A', '%3A', '%2C', '%20'],
5✔
125
            $value,
5✔
126
        );
5✔
127
    }
128

129
    /**
130
     * Escape data values for GitHub Actions annotations
131
     * https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message.
132
     */
133
    private function escapeData(string $value): string
5✔
134
    {
135
        return str_replace(
5✔
136
            ['%', "\r", "\n", ':'],
5✔
137
            ['%25', '%0D', '%0A', '%3A'],
5✔
138
            $value,
5✔
139
        );
5✔
140
    }
141
}
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