Passed
Push — master ( abb8c8...9e21c4 )
by Michiel
06:37
created

VersionTask::getVersion()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 3

Importance

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