zscripts-token-savers/zversion.ps1
Kelly Michels e4c563d1d5
fix(zversion): help text says one bump per release, not one per PR (#61)
zversion's usage block and its bump help line both said 'one per PR, one per
defect fix'. The build counter advances once per release: the number names
something that shipped, so a release carrying five PRs moves it by one, and
PRs that never shipped on their own were never separate builds.

This is help text rather than behaviour, but it is the wording that gets
followed - it is what stamped a single evo.www release as two builds. The
matching comments in ZHelpers.ps1 and zdeploy.ps1 are corrected with it.

CHECKSUMS.txt regenerated for the three edited scripts, since the manifest
tests fail the moment it drifts. CHANGELOG entry added under Unreleased, with
its .txt twin. The older CHANGELOG entry recording what the rule was when
zversion shipped is deliberately left as written - a changelog describes what
happened, not what is currently true.

Pester: 231 passed, 0 failed.
2026-08-30 20:08:26 -05:00

167 lines
6.8 KiB
PowerShell

# Evomedia.net — https://github.com/evomedia-net/evo.zscripts
# Created by Kelly Michels · dev@evomedia.net
# Licensed under the MIT License. See LICENSE.
# Version: v1.0.0.0.19
# zversion.ps1 - manage the toolkit version.
#
# Usage:
# zversion print the current version
# zversion bump build + 1 (one bump per release)
# zversion bump-stage alpha alpha + 1, build -> 0
# zversion bump-stage beta beta + 1, alpha/build -> 0
# zversion bump-stage rc rc + 1, beta/alpha/build -> 0
# zversion bump-stage release major + 1, everything below -> 0
# zversion set v1.2.0.0.5 set an exact version
#
# THE SCHEME
# ----------
# v{major}.{rc}.{beta}.{alpha}.{build}
#
# Priority runs left to right, and bumping any stage zeroes every lower segment
# including build. One version for the whole toolkit, not per script.
#
# WHAT A BUMP TOUCHES
# -------------------
# Anything other than a plain read rewrites three things together, because they
# are only useful if they agree:
# 1. build-version.json - the source of truth
# 2. the "# Version:" line in every .ps1 / .cmd header, so a script that has
# been copied out of the repo still says which release it came from
# 3. CHECKSUMS.txt - stamping changes every file, so the manifest must
# be regenerated or verification fails immediately
#
# Run this on a clean checkout: it hashes files as they sit on disk, and an
# editor that writes LF leaves a file git still considers unchanged. See the
# note in zchecksums.ps1.
[CmdletBinding()]
param(
[Parameter(Position = 0)][string]$Command = "get",
[Parameter(Position = 1)][string]$Value
)
$ErrorActionPreference = "Stop"
# Two blank lines after this script's output, matching every other z-script, so
# a run is visually separated from the next prompt. Local rather than from
# ZHelpers: this script is deliberately standalone, so it can run from an
# extracted release zip with nothing beside it.
function Write-ZTrailer { Write-Host ""; Write-Host "" }
$VersionFile = Join-Path $PSScriptRoot "build-version.json"
$VersionRe = '^v(\d+)\.(\d+)\.(\d+)\.(\d+)\.(\d+)$'
$DefaultVersion = "v1.0.0.0.0"
function Read-Version {
if (-not (Test-Path -LiteralPath $VersionFile)) { return $DefaultVersion }
try {
$v = (Get-Content -LiteralPath $VersionFile -Raw -Encoding UTF8 | ConvertFrom-Json).version
if ($v -match $VersionRe) { return $v }
} catch { }
return $DefaultVersion
}
function Write-Version([string]$Version) {
$json = [ordered]@{ version = $Version } | ConvertTo-Json
[IO.File]::WriteAllText($VersionFile, $json + "`n", (New-Object Text.UTF8Encoding($false)))
}
function Split-Version([string]$Version) {
if ($Version -notmatch $VersionRe) {
throw "Invalid version '$Version'. Expected v{major}.{rc}.{beta}.{alpha}.{build}, e.g. v1.0.0.0.3."
}
return [int[]]@($Matches[1], $Matches[2], $Matches[3], $Matches[4], $Matches[5])
}
# Rewrite (or insert) the "# Version:" header line in every covered script.
function Set-ScriptVersionHeaders([string]$Version) {
$stamped = 0
foreach ($f in Get-ChildItem -LiteralPath $PSScriptRoot -File | Where-Object { $_.Extension -in @('.ps1', '.cmd') }) {
$text = [IO.File]::ReadAllText($f.FullName)
# Keep each file's own comment marker: # for PowerShell, REM for cmd.
$marker = if ($f.Extension -eq '.cmd') { 'REM' } else { '#' }
$line = "$marker Version: $Version"
if ($text -match "(?m)^(#|REM) Version: v[\d\.]+\r?$") {
$new = [regex]::Replace($text, "(?m)^(#|REM) Version: v[\d\.]+\r?$", [System.Text.RegularExpressions.MatchEvaluator] { param($m) $line })
} else {
# Insert directly after the licence line, which every header carries.
$pattern = "(?m)^((#|REM) Licensed under the MIT License\. See LICENSE\.)\r?$"
if ($text -notmatch $pattern) { continue }
$new = [regex]::Replace($text, $pattern, [System.Text.RegularExpressions.MatchEvaluator] { param($m) $m.Groups[1].Value + "`r`n" + $line }, 1)
}
if ($new -ne $text) {
# .ps1/.cmd are pinned to CRLF by .gitattributes - write them that
# way or every stamped file shows up as changed on the next clone.
$new = ($new -replace "`r`n", "`n") -replace "`n", "`r`n"
[IO.File]::WriteAllText($f.FullName, $new, (New-Object Text.UTF8Encoding($false)))
$stamped++
}
}
return $stamped
}
function Update-Everything([string]$Version) {
Write-Version $Version
$n = Set-ScriptVersionHeaders $Version
& (Join-Path $PSScriptRoot "zchecksums.ps1") -Update | Out-Null
Write-Host ""
Write-Host "=== zversion ===" -ForegroundColor Cyan
Write-Host " Version: $Version" -ForegroundColor Green
Write-Host " Stamped: $n script header(s)" -ForegroundColor Gray
Write-Host " Refreshed: CHECKSUMS.txt" -ForegroundColor Gray
Write-Host " Commit build-version.json, the stamped scripts and CHECKSUMS.txt together." -ForegroundColor DarkGray
Write-Host ""
}
$current = Read-Version
switch ($Command.ToLowerInvariant().TrimStart('-')) {
"get" {
Write-Host $current
Write-ZTrailer
exit 0
}
"bump" {
$p = Split-Version $current
Update-Everything ("v{0}.{1}.{2}.{3}.{4}" -f $p[0], $p[1], $p[2], $p[3], ($p[4] + 1))
Write-ZTrailer
exit 0
}
"bump-stage" {
$p = Split-Version $current
$major, $rc, $beta, $alpha = $p[0], $p[1], $p[2], $p[3]
switch (("$Value").ToLowerInvariant()) {
"release" { $major++; $rc = 0; $beta = 0; $alpha = 0 }
"rc" { $rc++; $beta = 0; $alpha = 0 }
"beta" { $beta++; $alpha = 0 }
"alpha" { $alpha++ }
default {
Write-Host "ERROR: stage must be one of: release, rc, beta, alpha" -ForegroundColor Red
Write-ZTrailer
exit 1
}
}
# Bumping a stage zeroes every lower segment, build included.
Update-Everything ("v{0}.{1}.{2}.{3}.0" -f $major, $rc, $beta, $alpha)
Write-ZTrailer
exit 0
}
"set" {
[void](Split-Version $Value)
Update-Everything $Value
Write-ZTrailer
exit 0
}
default {
Write-Host ""
Write-Host "Usage: zversion [get | bump | bump-stage <release|rc|beta|alpha> | set <version>]" -ForegroundColor Yellow
Write-Host " Scheme: v{major}.{rc}.{beta}.{alpha}.{build} (current: $current)" -ForegroundColor Gray
Write-Host " bump build + 1 - one per release, not one per PR" -ForegroundColor Gray
Write-Host " bump-stage raise a stage; every lower segment resets to 0" -ForegroundColor Gray
Write-ZTrailer
exit 1
}
}