Passed
Push — main ( 28d658...6dddcc )
by Michiel
06:12
created

VersionTask   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Test Coverage

Coverage 77.32%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 27
eloc 100
c 1
b 1
f 0
dl 0
loc 262
ccs 75
cts 97
cp 0.7732
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A setStartingVersion() 0 3 1
A setReleasetype() 0 3 1
A setFile() 0 3 1
A setPropFile() 0 3 1
A setProperty() 0 3 1
A loadProperties() 0 10 2
A checkProperty() 0 4 2
A checkReleasetype() 0 20 3
A checkFile() 0 30 6
B main() 0 47 6
A getVersion() 0 29 3
1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\System;
22
23
use Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Io\File;
26
use Phing\Io\FileUtils;
27
use Phing\Io\IOException;
28
use Phing\Project;
29
use Phing\Task;
30
use Phing\Util\Properties;
31
32
/**
33
 * VersionTask.
34
 *
35
 * Increments a three-part version number from a given file
36
 * and writes it back to the file.
37
 * Incrementing is based on given releasetype, which can be one
38
 * of Major, Minor and Bugfix.
39
 * Resulting version number is also published under supplied property.
40
 *
41
 * @author  Mike Wittje <[email protected]>
42
 */
43
class VersionTask extends Task
44
{
45
    /**
46
     * The name of the property in which the build number is stored.
47
     */
48
    public const DEFAULT_PROPERTY_NAME = 'build.version';
49
50
    /**
51
     * The default filename to use if no file specified.
52
     */
53
    public const DEFAULT_FILENAME = self::DEFAULT_PROPERTY_NAME;
54
55
    // Allowed Releastypes
56
    public const RELEASETYPE_MAJOR = 'MAJOR';
57
    public const RELEASETYPE_MINOR = 'MINOR';
58
    public const RELEASETYPE_BUGFIX = 'BUGFIX';
59
60
    private $startingVersion = '0.0.0';
61
62
    /**
63
     * Property for Releasetype.
64
     *
65
     * @var string
66
     */
67
    private $releasetype;
68
69
    /**
70
     * Property for File.
71
     *
72
     * @var File file
73
     */
74
    private $file;
75
76
    /**
77
     * Property to be set.
78
     *
79
     * @var string
80
     */
81
    private $property;
82
83
    private $propFile = false;
84
85
    /**
86
     * @param string $startingVersion
87
     */
88 1
    public function setStartingVersion($startingVersion): void
89
    {
90 1
        $this->startingVersion = $startingVersion;
91
    }
92
93
    /**
94
     * Set Property for Releasetype (Minor, Major, Bugfix).
95
     *
96
     * @param string $releasetype
97
     */
98 121
    public function setReleasetype($releasetype): void
99
    {
100 121
        $this->releasetype = strtoupper($releasetype);
101
    }
102
103
    /**
104
     * Set Property for File containing versioninformation.
105
     */
106 4
    public function setFile(File $file): void
107
    {
108 4
        $this->file = $file;
109
    }
110
111
    /**
112
     * Set name of property to be set.
113
     */
114 4
    public function setProperty(string $property): void
115
    {
116 4
        $this->property = $property;
117
    }
118
119 3
    public function setPropFile(bool $isPropFile): void
120
    {
121 3
        $this->propFile = $isPropFile;
122
    }
123
124
    /**
125
     * Main-Method for the Task.
126
     *
127
     * @throws BuildException
128
     */
129 7
    public function main()
130
    {
131 7
        $properties = null;
132
133
        // check supplied attributes
134 7
        $this->checkReleasetype();
135 7
        $this->checkFile();
136 7
        $this->checkProperty();
137
138
        // read file (or use fallback value if file is empty)
139
        try {
140 7
            if ($this->propFile) {
141 3
                $properties = $this->loadProperties();
142 3
                $content = $properties->getProperty($this->property);
143
            } else {
144 4
                $content = trim($this->file->contents());
145
            }
146 7
            if (empty($content)) {
147 7
                $content = $this->startingVersion;
148
            }
149
        } catch (Exception $e) {
150
            throw new BuildException($e);
151
        }
152
153
        // get new version
154 7
        $this->log("Old version: {$content}", Project::MSG_INFO);
155 7
        $newVersion = $this->getVersion($content);
156 7
        $this->log("New version: {$newVersion}", Project::MSG_INFO);
157
158 7
        if ($this->propFile) {
159 3
            $properties->put($this->property, $newVersion);
160
161
            try {
162 3
                $header = 'Build Number for PHING. Do not edit!';
163 3
                $properties->store($this->file, $header);
164
            } catch (IOException $ioe) {
165
                $message = 'Error while writing ' . $this->file;
166
167
                throw new BuildException($message, $ioe);
168
            }
169
        } else {
170
            // write new Version to file
171 4
            file_put_contents($this->file, $newVersion . $this->getProject()->getProperty('line.separator'));
172
        }
173
174
        //Finally set the property
175 7
        $this->getProject()->setNewProperty($this->property, $newVersion);
176
    }
177
178
    /**
179
     * Utility method to load properties from file.
180
     *
181
     * @throws BuildException
182
     *
183
     * @return Properties the loaded properties
184
     */
185 3
    private function loadProperties(): Properties
186
    {
187
        try {
188 3
            $properties = new Properties();
189 3
            $properties->load($this->file);
190
        } catch (IOException $ioe) {
191
            throw new BuildException($ioe);
192
        }
193
194 3
        return $properties;
195
    }
196
197
    /**
198
     * Returns new version number corresponding to Release type.
199
     *
200
     * @param string $oldVersion
201
     */
202 121
    private function getVersion($oldVersion): string
203
    {
204 121
        preg_match('#^(?<PREFIX>v)?(?<MAJOR>\d+)?(?:\.(?<MINOR>\d+))?(?:\.(?<BUGFIX>\d+))?#', $oldVersion, $version);
205
206
        // Setting values if not captured
207 121
        $version['PREFIX'] = $version['PREFIX'] ?? '';
208 121
        $version[self::RELEASETYPE_MAJOR] = $version[self::RELEASETYPE_MAJOR] ?? '0';
209 121
        $version[self::RELEASETYPE_MINOR] = $version[self::RELEASETYPE_MINOR] ?? '0';
210 121
        $version[self::RELEASETYPE_BUGFIX] = $version[self::RELEASETYPE_BUGFIX] ?? '0';
211
212
        // Resetting Minor and/or Bugfix number according to release type
213 121
        switch ($this->releasetype) {
214
            case self::RELEASETYPE_MAJOR:
215 40
                $version[self::RELEASETYPE_MINOR] = '0';
216
            // no break
217
            case self::RELEASETYPE_MINOR:
218 79
                $version[self::RELEASETYPE_BUGFIX] = '0';
219
220 79
                break;
221
        }
222
223 121
        ++$version[$this->releasetype];
224
225 121
        return sprintf(
226 121
            '%s%u.%u.%u',
227 121
            $version['PREFIX'],
228 121
            $version[self::RELEASETYPE_MAJOR],
229 121
            $version[self::RELEASETYPE_MINOR],
230 121
            $version[self::RELEASETYPE_BUGFIX]
231 121
        );
232
    }
233
234
    /**
235
     * checks releasetype attribute.
236
     *
237
     * @throws BuildException
238
     */
239 7
    private function checkReleasetype(): void
240
    {
241
        // check Releasetype
242 7
        if (null === $this->releasetype) {
243
            throw new BuildException('releasetype attribute is required', $this->getLocation());
244
        }
245
        // known releasetypes
246 7
        $releaseTypes = [
247 7
            self::RELEASETYPE_MAJOR,
248 7
            self::RELEASETYPE_MINOR,
249 7
            self::RELEASETYPE_BUGFIX,
250 7
        ];
251
252 7
        if (!in_array($this->releasetype, $releaseTypes)) {
253
            throw new BuildException(
254
                sprintf(
255
                    'Unknown Releasetype %s..Must be one of Major, Minor or Bugfix',
256
                    $this->releasetype
257
                ),
258
                $this->getLocation()
259
            );
260
        }
261
    }
262
263
    /**
264
     * checks file attribute.
265
     *
266
     * @throws BuildException
267
     */
268 7
    private function checkFile(): void
269
    {
270 7
        $fileUtils = new FileUtils();
271
        // check File
272
        try {
273 7
            if (null === $this->file) {
274 3
                $this->file = $fileUtils->resolveFile($this->getProject()->getBasedir(), self::DEFAULT_FILENAME);
275
            }
276 7
            if (!$this->file->exists()) {
277 2
                $this->file->createNewFile();
278 7
                $this->log(
279 7
                    'Creating file "' . $this->file->getName() . '" since it was not present',
280 7
                    Project::MSG_INFO
281 7
                );
282
            }
283
        } catch (IOException $ioe) {
284
            $message = $this->file . " doesn't exist and new file can't be created.";
285
286
            throw new BuildException($message, $ioe);
287
        }
288
289 7
        if (!$this->file->canRead()) {
290
            $message = 'Unable to read from ' . $this->file . '.';
291
292
            throw new BuildException($message);
293
        }
294 7
        if (!$this->file->canWrite()) {
295
            $message = 'Unable to write to ' . $this->file . '.';
296
297
            throw new BuildException($message);
298
        }
299
    }
300
301 7
    private function checkProperty(): void
302
    {
303 7
        if (null === $this->property) {
304 3
            $this->property = self::DEFAULT_PROPERTY_NAME;
305
        }
306
    }
307
}
308