<?php
namespace Tlf\Scrawl;
/**
* Bumps versions. This implementation may be moved to its own, separate library. Idk.
*
*/
class VersionBumper {
/**
* Use system git commands to determine current version from the branch name and available tags.
*
* You should run `git fetch` before using this for accurate results.
*
* @param $bump_rules array<int index_in_version_string, array string_prefixes> string prefixes are literal, except a single asterisk (`*`) will always bump that version number, and should only be present if you want a default case of bumping the last index
*
* @return string version like 0.8.0.1 from 0.8.0.0
*/
public function get_new_version_from_git(array $bump_rules): string {
ob_start();
system("git log -1 --pretty=%B");
$last_commit = ob_get_clean();
$branch = $this->get_current_branch();
$branch_version = $this->get_branch_version($branch);
$latest_tag = $this->get_latest_tag($branch_version);
echo "\nLatest Tag: $latest_tag";
$new_version = $this->get_new_version($latest_tag, $last_commit, $bump_rules);
if ($new_version == $latest_tag){
echo "\n\nFailed to bump version. \nLast Commit: $last_commit\n\nbump rules: \n";
print_r($bump_rules);
throw new \Exception("Failed to bump version");
}
return $new_version;
}
/**
* Get a bumped version number. Returns `$latest_version` if none of the `$bump_rules` apply.
*
* Ex: 0.8.0 gets commit 'bugfix: something', and bumps to 0.8.0.1 based on the bump rules provided.
* Ex: 0.8.0.2 gets commit 'feature: cool stuff', and bumps to 0.8.1.0 based on the bump rules provided.
* Ex: 0.8.0 gets commit 'notes', and does not bump, so '0.8.0' is returned.
*
* @param $latest_version string of numbers separated by periods, like '3.8.1'
* @param $latest_commit_msg string with a prefix that indicates which portion of the version string to bump (increase)
* @param $bump_rules array<int index_in_version_string, array string_prefixes> string prefixes are literal, except a single asterisk (`*`) will always bump that version number, and should only be present if you want a default case of bumping the last index
*
* @return string representing a new version number. This may be the SAME as $latest_version if no $bump_rules passed.
*/
public function get_new_version(string $latest_version, string $latest_commit_msg, array $bump_rules): string {
$index_to_bump = $this->get_index_to_bump($latest_commit_msg, $bump_rules);
$i = $index_to_bump;
$parts = explode(".", $latest_version);
$length = count($parts);
while (($length = count($parts)) <= $index_to_bump){
$parts[] = '0';
}
if ($i>=0){
$parts[$i] = ((int)$parts[$i])+1;
while (++$i < $length){
$parts[$i] = 0;
}
}
$new_version = implode(".", $parts);
return $new_version;
}
/**
* @return int index to increase by 1, or -1 if there should not be a version increase.
*/
public function get_index_to_bump(string $commit_message, array $index_rules): int{
foreach ($index_rules as $index_to_bump => $prefixes_to_cause_bump){
foreach ($prefixes_to_cause_bump as $prefix){
if ($prefix=='*')return $index_to_bump;
$len = strlen($prefix);
if (substr($commit_message,0,$len+1) == "$prefix:"){
return $index_to_bump;
}
}
}
return -1;
}
/**
*
* @param $version_prefix string like "1.0" or "0.8"
*/
public function get_latest_tag(string $version_prefix=''): string{
// TODO: If 0.8.0 exists & we have 0.8.0.1, ... getting the latest tag doesn't work right
// I tried to fix it, but I think I made it worse
ob_start();
system("git tag --list \"$version_prefix.*\"");
$available_versions = ob_get_clean();
$tags = explode("\n", trim($available_versions));
$max_index_version = -1;
$len = strlen($version_prefix);
$latest_tag = null;
foreach ($tags as $full_version){
if ($len > 0)$target_version = substr($full_version,$len);
else $target_version = $target_version = $full_version;
echo "\n\nTest: $target_version";
$parts = explode('.', $target_version);
if (count($parts)==0)continue;
else if (((int)$parts[0]) > $max_index_version){
$max_index_version = (int)array_shift($parts);
$postfix_version = implode('.', $parts);
$latest_tag = trim("$version_prefix.$max_index_version");
}
}
if ($latest_tag == null){
throw new \Exception("Could not find a matching tag for version prefix '$version_prefix'");
}
var_dump($latest_tag);
exit;
return $latest_tag;
}
/**
* Return the name of the current git branch
*
*/
public function get_current_branch(): string {
return exec("git rev-parse --abbrev-ref HEAD");
}
/**
* Assumes branch name starts with 'v', removes it, and returns the remaining string, assumed to be numbers separated by periods.
*
* @return string like 1.0
*/
public function get_branch_version(string $branch_name): string {
return substr($branch_name,1);
}
}