Skip to content

Instantly share code, notes, and snippets.

@worldspawn
Last active February 18, 2018 08:15
Show Gist options
  • Save worldspawn/f34c51fe4450d2c5e862fbd954ecf628 to your computer and use it in GitHub Desktop.
Save worldspawn/f34c51fe4450d2c5e862fbd954ecf628 to your computer and use it in GitHub Desktop.
param([Parameter(Mandatory=$true)]$mainRepo, [Parameter(Mandatory=$true)]$buildCount)
$types = @{
"patch" = @{Title = "PATCH"; Order = 2 };
"change" = @{Title = "CHANGE"; Order = 0 };
"bug" = @{Title = "BUG"; Order = 3 };
"feat" = @{Title = "FEATURE"; Order = 1 };
"feature" = @{Title = "FEATURE"; Order = 1 };
"hotfix" = @{Title = "HOTFIX"; ORDER = 0};
};
$separator = "<<>>";
function New-Version() {
param ($currentVersion)
$matches = $null
$currentVersion -match "^v?(?:(?<major>\d+)\.)?(?:(?<minor>\d+)\.)?(?<patch>\*|\d+)?(?:\-(?<release>[A-Za-z0-9\.]+))?(?:\+(?<meta>[A-Za-z0-9\.]+))?$" | Out-Null
#$groups = [regex]::matches($currentVersion, "^(?:(?<major>\d+)\.)?(?:(?<minor>\d+)\.)?(?<patch>\*|\d+)?(?:\.(?<build>\d+))?(?:\-(?<release>[A-Za-z0-9\.]+))?(?:\+(?<meta>[A-Za-z0-9\.]+))?$")
$major = [convert]::ToInt32($matches.major)
$minor = [convert]::ToInt32($matches.minor)
$patch = [convert]::ToInt32($matches.patch)
if ($matches.release -eq $null) {
$release = @()
}
else {
$release = $matches.release.split('.')
}
if ($matches.meta -eq $null) {
$meta = @()
}
else {
$meta = $matches.meta.split('.')
}
$version = new-object PSObject | select-object Major, Minor, Patch, Build, Release, Meta
$version.Major = $major
$version.Minor = $minor
$version.Patch = $patch
if ($release -eq $null) {
$version.Release = @()
}
else {
$version.Release = $release
}
if ($meta -eq $null) {
$version.Meta = @()
}
else {
$version.Meta = $meta
}
return $version
}
function VersionAsString () {
param ($version)
$v = "$($version.Major).$($version.Minor).$($version.Patch)"
if ($version.Release.Count -gt 0) {
$v += "-"
$v += $version.Release -join '.'
}
if ($version.Meta.Count -gt 0) {
$v += "+"
$v += $version.Meta -join '.'
}
return $v
}
function Get-Messages() {
param($folder,
$currentbranch,
$mastertag)
$log = git -C $folder @("log", "--format=%%ct$separator%%Bxxx-xxx", "$mastertag..$currentbranch")
$results = New-Object -TypeName System.Collections.Generic.List[string]
$releaseNote = ""
foreach ($logItem in $log) {
if ($logItem -eq "xxx-xxx") {
$results.Add("$releaseNote`r`n")
$releaseNote = ""
continue
}
$items = $logItem -split $separator
if ($items.Length -eq 2) {
$timestamp = $items[0];
$message = $items[1];
}
else {
$message = $items[0];
}
$transformed = $false
foreach ($item in $types.GetEnumerator()) {
if ($message -notlike "$($item.key)*(*)*") {
continue
}
$start = $message.IndexOf("(") + 1
$end = $message.IndexOf(")")
$type = $item.value.Title
$header = $message.substring($start, $end - $start)
$releaseNote = "$timestamp$separator$type - $header"
$transformed = $true
break;
}
if ($transformed -eq $false) {
if ($releaseNote.Length -ne 0) {
$releaseNote = "$releaseNote`r`n$logItem"
}
else {
$releaseNote = "$timestamp$($separator)OTHER - $message"
}
}
}
return $results
}
function GenerateReleaseNotesAndVersionNumber() {
param($mainRepo)
Write-Host "Fetching in $($mainRepo.Path)"
git -C $mainRepo.Path fetch --all --tags -v | Out-Null
$lastmastertag = git -C $mainRepo.Path @("describe", "origin/master", "--tags", "--abbrev=0")
Write-Host "$($mainRepo.Name) - Master Tag: $lastmastertag"
$versionObj = New-Version $lastmastertag
#get the main repo information
$mainRepo.Branch = git -C $mainRepo.Path rev-parse --abbrev-ref HEAD
$mainRepo.Messages = Get-Messages $mainRepo.Path $mainRepo.Branch $lastmastertag
$mainRepo.Hash = git -C $mainRepo.Path rev-parse --short $mainRepo.Branch
foreach ($subRepo in $mainRepo.SubRepos) {
$subRepo.Branch = git -C $subRepo.Path rev-parse --abbrev-ref HEAD
$subRepo.Messages = Get-Messages $subRepo.Path $subRepo.Branch $lastmastertag
$subRepo.Hash = git -C $subRepo.Path rev-parse --short $subRepo.Branch
}
$branchNames = @(, $mainRepo.Branch) + @(, $mainRepo.SubRepos.Branch)
if (($branchNames | Get-Unique | Measure-Object).Count -eq 1) {
$currentbranch = $mainRepo.Branch
$currenthash = $mainRepo.Hash
}
#if the main branch is a feature branch that has commits
elseif ($mainRepo.Branch -ne "develop" -and $mainRepo.Messages.Count -ne 0) {
$currentbranch = $mainRepo.Branch
$currenthash = $mainRepo.Hash
}
else {
#for the extra repos, find the first one that is not develop that has commits
foreach ($subRepo in $mainRepo.SubRepos) {
if ($subRepo.Branch -ne "develop" -and $subRepo.Messages.Count -ne 0) {
$currentbranch = $subRepo.Branch
$currenthash = $subRepo.Hash
break;
}
}
}
Write-Host "$($mainRepo.Name) - Branch: $currentbranch"
Write-Host "$($mainRepo.Name) - Hash $currenthash"
$tmpAll = New-Object -TypeName System.Collections.Generic.List[Object]
$tmpAll.AddRange(@($mainRepo.Messages))
foreach ($extraRepo in $mainRepo.SubRepos) {
if ($extraRepo.Messages -ne $null) {
$tmpAll.AddRange(@($extraRepo.Messages))
}
}
$allMsgs = $tmpAll | ForEach-Object {@(, ($_ -split $separator))} | Select-Object @{Name = "timestamp"; Expression = {[int]$_[0]}}, @{Name = "message"; Expression = {$_[1]}} | Group-Object message | ForEach-Object { $_.Group[0] }
# These are ordered by Timestamp
$versionMsgs = $allMsgs | Sort-Object -Property timestamp | Select-Object -ExpandProperty message
# These are ordered by Message priority
$releaseNoteMsgs = $allMsgs | Sort-Object -Property @{Expression = {
foreach ($item in $types.GetEnumerator()) {
if ($_.message -like "$($item.Value.Title)*") {
return $item.Value.Order
}
}
return 99
}; Ascending = $true
} | Select-Object -ExpandProperty message
foreach ($item in $versionMsgs) {
if ($item -like "CHANGE*") {
$versionObj.Major += 1;
$versionObj.Minor = 0;
$versionObj.Patch = 0;
}
elseif ($item -like "FEATURE*") {
$versionObj.Minor += 1;
$versionObj.Patch = 0;
}
elseif ($item -LIKE "PATCH*" -or $item -LIKE "BUG*" -or $item -LIKE "HOTFIX*") {
$versionObj.Patch += 1;
}
}
if ($currentbranch -ne "develop" -and $currentbranch -ne "master") {
$maxBranchLength = 2 + 7 + $build_count.toString().length #two dots + hash + build no
$maxBranchLength = 20 - $maxBranchLength
if ($currentbranch.Length -gt $maxBranchLength) {
$currentbranch = $currentbranch.substring(0, $maxBranchLength);
}
$versionObj.Release = @()
$versionObj.Release += $currentbranch
$versionObj.Release += $build_count
$versionObj.Release += $currenthash
}
else {
$versionObj.Release = @($build_count)
$versionObj.Release += $currenthash
}
$versionObj.Meta = @()
$version = VersionAsString($versionObj)
return @{
Version = $version;
ReleaseNotes = $releaseNoteMsgs
}
}
$error.Clear();
try {
$isLocal = $false;
$build_count = $buildCount
}
catch {
#we must be on a local machine
$build_count = 5000
$isLocal = $true;
}
$error.Clear();
$mainRepos = @(
@{
Name = "$mainRepo";
DisplayName = "$mainRepo";
Path = ".\";
Branch = "";
Hash = "";
Messages = $null;
IsTCVersion = $true;
SubRepos = @();
}
)
try {
#fix git host name checking
$mainRepos | ForEach-Object {
if (!$isLocal) {
git -C $_.Path config core.sshCommand 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
$_.SubRepos | ForEach-Object {
git -C $_.Path config core.sshCommand 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
}
}
}
$releaseNotes = $mainRepos | ForEach-Object {
$results = GenerateReleaseNotesAndVersionNumber $_
if ($_.IsTCVersion -eq $true) {
Write-Host "Build Number: $($results.Version)"
Write-Host "##teamcity[buildNumber '$($results.Version)']"
}
if ($results.ReleaseNotes.Length -eq 0) {
return @()
}
return @(
"#NAME",
$_.DisplayName,
"##VERSION",
$results.Version,
"##RELEASE NOTES",
$results.ReleaseNotes,
"---"
)
}
$releaseNotes | Out-File .\releasenotes.txt
}
finally {
#unset git hostname checking
$mainRepos | ForEach-Object {
if (!$isLocal) {
git -C $_.Path config --unset core.sshCommand
$_.SubRepos | ForEach-Object {
git -C $_.Path config --unset core.sshCommand
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment