2 namespace Robo\Task\Development;
5 use Robo\Contract\TaskInterface;
6 use Robo\Exception\TaskException;
9 * Helps to maintain `.semver` file.
13 * $this->taskSemVer('.semver')
20 class SemVer implements TaskInterface
22 const SEMVER = "---\n:major: %d\n:minor: %d\n:patch: %d\n:special: '%s'\n:metadata: '%s'";
24 const REGEX = "/^\-\-\-\n:major:\s(0|[1-9]\d*)\n:minor:\s(0|[1-9]\d*)\n:patch:\s(0|[1-9]\d*)\n:special:\s'([a-zA-z0-9]*\.?(?:0|[1-9]\d*)?)'\n:metadata:\s'((?:0|[1-9]\d*)?(?:\.[a-zA-z0-9\.]*)?)'/";
26 const REGEX_STRING = '/^(?<major>[0-9]+)\.(?<minor>[0-9]+)\.(?<patch>[0-9]+)(|-(?<special>[0-9a-zA-Z.]+))(|\+(?<metadata>[0-9a-zA-Z.]+))$/';
31 protected $format = 'v%M.%m.%p%s';
36 protected $specialSeparator = '-';
41 protected $metadataSeparator = '+';
51 protected $version = [
60 * @param string $filename
62 public function __construct($filename = '')
64 $this->path = $filename;
66 if (file_exists($this->path)) {
67 $semverFileContents = file_get_contents($this->path);
68 $this->parseFile($semverFileContents);
75 public function __toString()
77 $search = ['%M', '%m', '%p', '%s'];
78 $replace = $this->version + ['extra' => ''];
80 foreach (['special', 'metadata'] as $key) {
81 if (!empty($replace[$key])) {
82 $separator = $key . 'Separator';
83 $replace['extra'] .= $this->{$separator} . $replace[$key];
85 unset($replace[$key]);
88 return str_replace($search, $replace, $this->format);
91 public function version($version)
93 $this->parseString($version);
98 * @param string $format
102 public function setFormat($format)
104 $this->format = $format;
109 * @param string $separator
113 public function setMetadataSeparator($separator)
115 $this->metadataSeparator = $separator;
120 * @param string $separator
124 public function setPrereleaseSeparator($separator)
126 $this->specialSeparator = $separator;
131 * @param string $what
135 * @throws \Robo\Exception\TaskException
137 public function increment($what = 'patch')
141 $this->version['major']++;
142 $this->version['minor'] = 0;
143 $this->version['patch'] = 0;
146 $this->version['minor']++;
147 $this->version['patch'] = 0;
150 $this->version['patch']++;
153 throw new TaskException(
155 'Bad argument, only one of the following is allowed: major, minor, patch'
166 * @throws \Robo\Exception\TaskException
168 public function prerelease($tag = 'RC')
170 if (!is_string($tag)) {
171 throw new TaskException($this, 'Bad argument, only strings allowed.');
176 if (!empty($this->version['special'])) {
177 list($current, $number) = explode('.', $this->version['special']);
178 if ($tag != $current) {
185 $this->version['special'] = implode('.', [$tag, $number]);
190 * @param array|string $data
194 public function metadata($data)
196 if (is_array($data)) {
197 $data = implode('.', $data);
200 $this->version['metadata'] = $data;
207 public function run()
209 $written = $this->dump();
210 return new Result($this, (int)($written === false), $this->__toString());
216 * @throws \Robo\Exception\TaskException
218 protected function dump()
220 if (empty($this->path)) {
223 extract($this->version);
224 $semver = sprintf(self::SEMVER, $major, $minor, $patch, $special, $metadata);
225 if (is_writeable($this->path) === false || file_put_contents($this->path, $semver) === false) {
226 throw new TaskException($this, 'Failed to write semver file.');
231 protected function parseString($semverString)
233 if (!preg_match_all(self::REGEX_STRING, $semverString, $matches)) {
234 throw new TaskException($this, 'Bad semver value: ' . $semverString);
237 $this->version = array_intersect_key($matches, $this->version);
238 $this->version = array_map(function ($item) {
244 * @throws \Robo\Exception\TaskException
246 protected function parseFile($semverFileContents)
248 if (!preg_match_all(self::REGEX, $semverFileContents, $matches)) {
249 throw new TaskException($this, 'Bad semver file.');
252 list(, $major, $minor, $patch, $special, $metadata) = array_map('current', $matches);
253 $this->version = compact('major', 'minor', 'patch', 'special', 'metadata');