Issues (186)

Build/update-changelog.php (2 issues)

1
<?php
2
3
/*
4
 * Copyright (C)
5
 * Nathan Boiron <[email protected]>
6
 * Romain Canon <[email protected]>
7
 *
8
 * This file is part of the TYPO3 NotiZ project.
9
 * It is free software; you can redistribute it and/or modify it
10
 * under the terms of the GNU General Public License, either
11
 * version 3 of the License, or any later version.
12
 *
13
 * For the full copyright and license information, see:
14
 * http://www.gnu.org/licenses/gpl-3.0.html
15
 */
16
17
/**
18
 * Run the following command in shell:
19
 *
20
 * $ php Build/update-changelog.php x.y.z
21
 *
22
 * Then update the `CHANGELOG.md` file, do modifications if needed, then commit.
23
 */
24
class UpdateChangelog
25
{
26
    const CHANGELOG_FILE = 'CHANGELOG.md';
27
28
    protected $version;
29
    protected $currentDate;
30
    protected $lastGitTag;
31
32
    /**
33
     * Must have the new version number as parameter.
34
     *
35
     * @param string $version
36
     */
37
    public function __construct($version)
38
    {
39
        $this->version = $version;
40
41
        // Format "02 February 2018"
42
        $this->currentDate = date('d F Y');
43
44
        // Fetches last tag that was added in git
45
        $this->lastGitTag = trim(shell_exec('git describe --tags --abbrev=0'));
46
    }
47
48
    /**
49
     * Will update the changelog file with the latest commits, ordered as
50
     * follow:
51
     *
52
     * - New features
53
     * - Bugs fixed
54
     * - Important/breaking changes
55
     * - Other less important commits
56
     */
57
    public function run()
58
    {
59
        $features = $this->getLogs('FEATURE');
60
        $bugfix = $this->getLogs('BUGFIX');
61
        $important = $this->getLogs('!!!');
62
        $others = $this->getInvertedLogs('FEATURE', 'BUGFIX', '!!!');
63
64
        $currentChangelog = file_get_contents(self::CHANGELOG_FILE);
65
66
        $changelog = $this->getChangelog($features, $bugfix, $important, $others);
67
        $changelog = preg_replace('/\n/', "\n$changelog", $currentChangelog, 1);
68
69
        file_put_contents(self::CHANGELOG_FILE, $changelog);
70
    }
71
72
    /**
73
     * @param string $features
74
     * @param string $bugfix
75
     * @param string $important
76
     * @param string $others
77
     * @return string
78
     */
79
    protected function getChangelog($features, $bugfix, $important, $others)
80
    {
81
        $changelog = "
82
## v$this->version - $this->currentDate
83
84
> ℹ️ *Click on a changelog entry to see more details.*";
85
86
        if ($features) {
87
            $changelog .= "
88
89
### New features
90
$features";
91
        }
92
93
        if ($bugfix) {
94
            $changelog .= "
95
### Bugs fixed
96
$bugfix";
97
        }
98
99
        if ($important) {
100
            $changelog .= "
101
### Important
102
103
**⚠ Please pay attention to the changes below as they might break your TYPO3 installation:**
104
$important";
105
        }
106
107
        if ($others) {
108
            $changelog .= "
109
### Others
110
$others";
111
        }
112
113
        return $changelog;
114
    }
115
116
    /**
117
     * @param string[] $types
118
     * @return string
119
     */
120
    protected function getLogs(...$types)
121
    {
122
        $command = $this->getLogsCommand(...$types);
123
124
        return $this->formatLogs($command);
125
    }
126
127
    /**
128
     * @param string[] $types
129
     * @return string
130
     */
131
    protected function getInvertedLogs(...$types)
132
    {
133
        $command = $this->getLogsCommand(...$types);
134
        $command .= ' --invert-grep';
135
136
        return $this->formatLogs($command);
137
    }
138
139
    /**
140
     * @param string[] $types
141
     * @return string
142
     */
143
    protected function getLogsCommand(...$types)
144
    {
145
        $command = "git log HEAD...$this->lastGitTag" .
146
            ' --date=format:"%d %b %Y"';
147
148
        foreach ($types as $type) {
149
            $command .= ' --grep="^\[' . $type . '\]"';
150
        }
151
152
        return $command;
153
    }
154
155
    /**
156
     * @param $command
157
     * @return string
158
     */
159
    protected function formatLogs($command)
160
    {
161
        $title = $this->getGitLog($command, '%s');
162
        $revisionShort = $this->getGitLog($command, '%h');
163
        $revision = $this->getGitLog($command, '%H');
164
        $body = $this->getGitLog($command, '%b');
165
        $author = $this->getGitLog($command, '%an');
166
        $authorEmail = $this->getGitLog($command, '%ae');
167
        $date = $this->getGitLog($command, '%ad');
168
169
        $count = count($title);
170
171
        if ($count === 0) {
172
            return '';
173
        }
174
175
        $result = '';
176
177
        for ($i = 0; $i < count($title); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
178
            $detailedTitle = $this->replaceCodeSections($title[$i]);
179
            $pullRequest = null;
180
181
            $detailedBody = preg_replace('/\n/', "\n> ", $body[$i]);
182
            $detailedBody = $this->addLinkToGitHubIssues($detailedBody);
183
184
            // Add a link to detected GitHub issues number.
185
            if (preg_match('/#([0-9]+)/', $title[$i], $pullRequestResult)) {
186
                $detailedTitle = preg_replace('/ *\(#([0-9]+)\)/', '', $detailedTitle);
187
                $detailedTitle = preg_replace('/ *#([0-9]+)/', '', $detailedTitle);
188
                $pullRequest = ' / [#' . $pullRequestResult[1] . '](https://github.com/CuyZ/NotiZ/issues/' . $pullRequestResult[1] . ')';
189
            }
190
191
            $result .= <<<HTML
192
193
<details>
194
<summary>$detailedTitle</summary>
195
196
> *by [$author[$i]](mailto:$authorEmail[$i])* on *$date[$i] / [$revisionShort[$i]](https://github.com/CuyZ/NotiZ/commit/$revision[$i])$pullRequest*
197
198
> $detailedBody
199
</details>
200
201
HTML;
202
        }
203
204
        return $result;
205
    }
206
207
    /**
208
     * @param string $command
209
     * @param string $format
210
     * @return array
211
     */
212
    protected function getGitLog($command, $format)
213
    {
214
        $result = shell_exec($command . '  --pretty=tformat:"' . $format . '>>>NEXT<<<"');
215
        $result = explode('>>>NEXT<<<', $result);
216
        $result = array_map('trim', $result);
217
        array_pop($result);
218
        $result = array_map([$this, 'sanitizeLog'], $result);
219
220
        return $result;
221
    }
222
223
    /**
224
     * @param string $text
225
     * @return string
226
     */
227
    protected function sanitizeLog($text)
228
    {
229
        // Replace redundant line breaks.
230
        $text = preg_replace('/\n\n\n+/m', "\n\n", $text);
231
232
        // Removes the commit prefix.
233
        $text = preg_replace('/^\[!!!\](.*)$/', '$1', $text);
234
        $text = preg_replace('/^\[[^ \]]+\] (.*)$/', '$1', $text);
235
236
        return $text;
237
    }
238
239
    /**
240
     * Add a link to all detected GitHub issues number.
241
     *
242
     * @param string $text
243
     * @return string
244
     */
245
    protected function addLinkToGitHubIssues($text)
246
    {
247
        return preg_replace('/#([0-9]+)/', '[#$1](https:\/\/github.com\/CuyZ\/NotiZ\/issues\/$1)', $text);
248
    }
249
250
    /**
251
     * @param string $text
252
     * @return string
253
     */
254
    protected function replaceCodeSections($text)
255
    {
256
        return preg_replace('/`([^`]*)`/', '<code>$1</code>', $text);
257
    }
258
}
259
260
unset($argv[0]);
261
(new UpdateChangelog(...$argv))->run();
0 ignored issues
show
$argv is expanded, but the parameter $version of UpdateChangelog::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

261
(new UpdateChangelog(/** @scrutinizer ignore-type */ ...$argv))->run();
Loading history...
262