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

JBZoo / Codestyle / 29770057002

20 Jul 2026 06:01AM UTC coverage: 81.195% (+0.2%) from 80.952%
29770057002

push

github

SmetDenis
docs: refresh PHP floor and dependency versions for the 8.0 line

557 of 686 relevant lines covered (81.2%)

10.78 hits per line

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

91.89
/src/PHPUnit/TraitGithubActions.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - Codestyle.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/Codestyle
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\Codestyle\PHPUnit;
18

19
use function JBZoo\Data\yml;
20
use function JBZoo\PHPUnit\isSame;
21
use function JBZoo\PHPUnit\isTrue;
22

23
trait TraitGithubActions
24
{
25
    public static function testGithubActionsWorkflow(): void
26
    {
27
        $mailYmlPath = PROJECT_ROOT . '/.github/workflows/main.yml';
6✔
28
        $actual      = yml($mailYmlPath)->getArrayCopy();
6✔
29

30
        isSame(120, $actual['env']['COLUMNS']);
6✔
31
        isSame('Hyper', $actual['env']['TERM_PROGRAM']);
6✔
32
        unset($actual['env']);
6✔
33

34
        // General Parameters
35
        $expectedOs = 'ubuntu-latest';
6✔
36

37
        // Expected
38
        $expected = [
6✔
39
            'name'        => 'CI',
6✔
40
            'permissions' => [
6✔
41
                'contents' => 'read',
6✔
42
            ],
6✔
43
            'on' => [
6✔
44
                'pull_request' => ['branches' => ['*']],
6✔
45
                'push'         => ['branches' => ['master']],
6✔
46
            ],
6✔
47

48
            'jobs' => [
6✔
49
                'phpunit' => [
6✔
50
                    'name'     => 'PHPUnit',
6✔
51
                    'runs-on'  => $expectedOs,
6✔
52
                    'env'      => ['JBZOO_COMPOSER_UPDATE_FLAGS' => '${{ matrix.composer_flags }}'],
6✔
53
                    'strategy' => [
6✔
54
                        'matrix' => [
6✔
55
                            'php-version'    => static::phpVersions(),
6✔
56
                            'coverage'       => ['xdebug', 'none'],
6✔
57
                            'composer_flags' => ['--prefer-lowest', ''],
6✔
58
                        ],
6✔
59
                    ],
6✔
60
                    'steps' => [
6✔
61
                        static::checkoutStep('phpunit'),
6✔
62
                        static::setupPhpStep('phpunit'),
6✔
63
                        static::buildStep('phpunit'),
6✔
64
                        static::stepBeforeTests(),
6✔
65
                        [
6✔
66
                            'name' => '๐Ÿงช PHPUnit Tests',
6✔
67
                            'run'  => 'make test --no-print-directory',
6✔
68
                        ],
6✔
69
                        static::stepAfterTests(),
6✔
70
                        [
6✔
71
                            'name'              => 'Uploading coverage to coveralls',
6✔
72
                            'if'                => '${{ matrix.coverage == \'xdebug\' }}',
6✔
73
                            'continue-on-error' => true,
6✔
74
                            'env'               => ['COVERALLS_REPO_TOKEN' => '${{ secrets.GITHUB_TOKEN }}'],
6✔
75
                            'run'               => 'make report-coveralls --no-print-directory || true',
6✔
76
                        ],
6✔
77
                        static::uploadArtifactsStep('PHPUnit - ${{ matrix.php-version }} - ${{ matrix.coverage }}'),
6✔
78
                    ],
6✔
79
                ],
6✔
80
                'linters' => [
6✔
81
                    'name'     => 'Linters',
6✔
82
                    'runs-on'  => $expectedOs,
6✔
83
                    'strategy' => ['matrix' => ['php-version' => static::phpVersions()]],
6✔
84
                    'steps'    => [
6✔
85
                        static::checkoutStep('linters'),
6✔
86
                        static::setupPhpStep('linters'),
6✔
87
                        static::buildStep('linters'),
6✔
88
                        [
6✔
89
                            'name' => '๐Ÿ‘ Code Quality',
6✔
90
                            'run'  => 'make codestyle --no-print-directory',
6✔
91
                        ],
6✔
92
                        static::uploadArtifactsStep('Linters - ${{ matrix.php-version }}'),
6✔
93
                    ],
6✔
94
                ],
6✔
95
                'report' => [
6✔
96
                    'name'     => 'Reports',
6✔
97
                    'runs-on'  => $expectedOs,
6✔
98
                    'strategy' => ['matrix' => ['php-version' => static::phpVersions()]],
6✔
99
                    'steps'    => [
6✔
100
                        static::checkoutStep('report'),
6✔
101
                        static::setupPhpStep('report'),
6✔
102
                        static::buildStep('report'),
6✔
103
                        [
6✔
104
                            'name' => '๐Ÿ“ Build Reports',
6✔
105
                            'run'  => 'make report-all --no-print-directory',
6✔
106
                        ],
6✔
107
                        static::uploadArtifactsStep('Reports - ${{ matrix.php-version }}'),
6✔
108
                    ],
6✔
109
                ],
6✔
110
            ],
6✔
111
        ];
6✔
112

113
        // #3 Canary (major 8.0): the workflow MUST define a schedule with at least one non-blank string
114
        // cron and allow workflow_dispatch (null or a config map), but NOT a specific cron literal
115
        // (per-repo cadence is allowed โ€” e.g. toolbox keeps its tighter '30 */8 * * *'). Verified as
116
        // presence, then dropped before the structural compare below.
117
        $triggers = (array)($actual['on'] ?? []);
6✔
118
        $schedule = $triggers['schedule'] ?? [];
6✔
119
        $crons    = \is_array($schedule)
6✔
120
            ? \array_filter(
6✔
121
                $schedule,
6✔
122
                static fn (mixed $entry): bool => \is_array($entry)
6✔
123
                    && \is_string($entry['cron'] ?? null)
6✔
124
                    && $entry['cron'] !== '',
6✔
125
            )
6✔
126
            : [];
×
127
        isTrue($crons !== [], 'The workflow must define a schedule with at least one non-blank cron (canary).');
6✔
128

129
        $dispatchOk = \array_key_exists('workflow_dispatch', $triggers)
6✔
130
            && ($triggers['workflow_dispatch'] === null || \is_array($triggers['workflow_dispatch']));
6✔
131
        isTrue($dispatchOk, 'The workflow must allow workflow_dispatch (null or a config map) for the dispatcher.');
6✔
132
        unset($actual['on']['schedule'], $actual['on']['workflow_dispatch']);
6✔
133

134
        $expectedYaml = self::toYaml($expected);
6✔
135
        $actualYaml   = self::toYaml($actual);
6✔
136

137
        if (!\str_contains($actualYaml, $expectedYaml)) {
6✔
138
            isSame(
×
139
                $expectedYaml,
×
140
                $actualYaml,
×
141
                \implode("\n", [
×
142
                    'Expected Yaml file:',
×
143
                    "See: {$mailYmlPath}",
×
144
                    '----------------------------------------',
×
145
                    $expectedYaml,
×
146
                    '----------------------------------------',
×
147
                ]),
×
148
            );
×
149
        }
150
    }
151

152
    protected static function stepBeforeTests(): ?array
153
    {
154
        return null;
6✔
155
    }
156

157
    protected static function stepAfterTests(): ?array
158
    {
159
        return null;
6✔
160
    }
161

162
    /**
163
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
164
     * @phan-suppress PhanUnusedProtectedNoOverrideMethodParameter
165
     * @psalm-suppress PossiblyUnusedParam
166
     */
167
    protected static function checkoutStep(string $jobName): array
168
    {
169
        return [
6✔
170
            'name' => 'Checkout code',
6✔
171
            'uses' => 'actions/checkout@v7',
6✔
172
            'with' => ['fetch-depth' => 0],
6✔
173
        ];
6✔
174
    }
175

176
    protected static function phpVersions(): array
177
    {
178
        return [8.3, 8.4, 8.5];
6✔
179
    }
180

181
    /**
182
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
183
     * @phan-suppress PhanUnusedProtectedNoOverrideMethodParameter
184
     * @psalm-suppress PossiblyUnusedParam
185
     */
186
    protected static function buildStep(string $jobName): array
187
    {
188
        return [
6✔
189
            'name' => 'Build the Project',
6✔
190
            'run'  => 'make update --no-print-directory',
6✔
191
        ];
6✔
192
    }
193

194
    protected static function setupPhpStep(string $jobName): array
195
    {
196
        $coverage = 'none';
6✔
197
        if ($jobName === 'phpunit') {
6✔
198
            $coverage = '${{ matrix.coverage }}';
6✔
199
        } elseif ($jobName === 'report') {
6✔
200
            $coverage = 'xdebug';
6✔
201
        }
202

203
        return [
6✔
204
            'name' => 'Setup PHP',
6✔
205
            'uses' => 'shivammathur/setup-php@v2',
6✔
206
            'with' => [
6✔
207
                'php-version' => '${{ matrix.php-version }}',
6✔
208
                'coverage'    => $coverage,
6✔
209
                'tools'       => 'composer',
6✔
210
                'extensions'  => 'ast',
6✔
211
            ],
6✔
212
        ];
6✔
213
    }
214

215
    protected static function uploadArtifactsStep(string $stepName): array
216
    {
217
        return [
6✔
218
            'name'              => 'Upload Artifacts',
6✔
219
            'uses'              => 'actions/upload-artifact@v7',
6✔
220
            'continue-on-error' => true,
6✔
221
            'with'              => ['name' => $stepName, 'path' => 'build/', 'overwrite' => true],
6✔
222
        ];
6✔
223
    }
224

225
    private static function normalizeData(array $data): array
226
    {
227
        foreach ($data['jobs'] as $jobName => $job) {
6✔
228
            foreach ($job['steps'] as $stepIndex => $step) {
6✔
229
                if ($step === null) {
6✔
230
                    unset($data['jobs'][$jobName]['steps'][$stepIndex]);
6✔
231
                }
232
            }
233

234
            $data['jobs'][$jobName]['steps'] = \array_values($data['jobs'][$jobName]['steps']);
6✔
235
        }
236

237
        return $data;
6✔
238
    }
239

240
    private static function toYaml(array $data): string
241
    {
242
        return (string)yml(self::normalizeData($data));
6✔
243
    }
244
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc