Modules/businessdev.ALbuild.Apps/Public/Set-BcAppVersion.ps1
|
function Set-BcAppVersion { <# .SYNOPSIS Sets the version of one or more Business Central apps by updating their app.json. .DESCRIPTION Updates the top-level "version" field of every app.json found under -Path (recursively, excluding symbol/package folders) - or a single app.json if -Path points directly at one. The new version is either an explicit -Version or computed from a token -Schema applied to each app's current version (see the schema tokens below). The version value is rewritten in place with a targeted text replacement so the rest of the file (ordering, indentation, comments-as-properties) is preserved; only the top-level version value changes. Schema tokens (one per dotted position, case-insensitive): a number = literal; 'increment' = current component + 1; 'build-id' = the -BuildId value; 'major'/'minor'/'build'/'revision'/ 'latest'/'keep' = keep the current component. Example: 'major.minor.increment.0'. This cmdlet performs no git or pipeline I/O - it only reads/writes app.json (and, for -OnlyUpdateOnChangedSource, asks git whether an app folder changed in the last commit). Use Invoke-BcBuildVersionStamp for the pipeline-level "stamp + claim a build branch" flow. .PARAMETER Path A repository root to search recursively, an app folder, or a single app.json. Default: the current location. .PARAMETER Schema The token schema used to compute each new version from its current value. Default 'major.minor.increment.0'. Ignored when -Version is given. .PARAMETER Version An explicit version applied to every matched app (overrides -Schema). .PARAMETER BuildId Value substituted for the 'build-id' schema token. Defaults to the BUILD_BUILDID environment variable, or '0' when unset. .PARAMETER OnlyUpdateOnChangedSource Skip apps whose folder did not change in the most recent commit (git diff HEAD~1..HEAD). Requires a git working tree at -Path (or the app's repository). .EXAMPLE Set-BcAppVersion -Path . -Schema 'major.minor.build-id.0' -BuildId $env:BUILD_BUILDID .EXAMPLE Set-BcAppVersion -Path .\app -Version '2.3.0.0' .OUTPUTS PSCustomObject per app: AppJsonPath, AppFolder, PreviousVersion, NewVersion, Changed, Skipped. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Schema')] [OutputType([PSCustomObject])] param( [Parameter(Position = 0)] [string] $Path = (Get-Location).Path, [Parameter(ParameterSetName = 'Schema')] [ValidateNotNullOrEmpty()] [string] $Schema = 'major.minor.increment.0', [Parameter(Mandatory, ParameterSetName = 'Explicit')] [ValidateNotNullOrEmpty()] [string] $Version, [string] $BuildId, [switch] $OnlyUpdateOnChangedSource ) if (-not (Test-Path -LiteralPath $Path)) { throw "Path '$Path' does not exist." } if (-not $PSBoundParameters.ContainsKey('BuildId') -or [string]::IsNullOrEmpty($BuildId)) { $BuildId = if ($env:BUILD_BUILDID) { $env:BUILD_BUILDID } else { '0' } } # Collect the app.json files in scope. $item = Get-Item -LiteralPath $Path if ($item.PSIsContainer) { $repositoryRoot = $item.FullName $appJsonFiles = @(Get-ChildItem -LiteralPath $item.FullName -Filter 'app.json' -File -Recurse -ErrorAction SilentlyContinue | Where-Object { $_.FullName -notmatch '[\\/](\.alpackages|\.altemplates|\.snapshots|\.output)[\\/]' }) } elseif ($item.Name -eq 'app.json') { $repositoryRoot = $item.Directory.FullName $appJsonFiles = @($item) } else { throw "Path '$Path' is neither a folder nor an app.json file." } if ($appJsonFiles.Count -eq 0) { throw "No app.json found under '$Path'." } $versionPattern = [regex] '("version"\s*:\s*")[^"]*(")' $utf8NoBom = [System.Text.UTF8Encoding]::new($false) foreach ($file in $appJsonFiles) { $appFolder = $file.Directory.FullName $raw = [System.IO.File]::ReadAllText($file.FullName) $json = $raw | ConvertFrom-Json if (-not ($json.PSObject.Properties.Name -contains 'version')) { throw "'$($file.FullName)' has no top-level 'version' property." } $previous = ConvertTo-BcVersion ([string]$json.version) if ($OnlyUpdateOnChangedSource -and (-not (Test-BcSourceChanged -RepositoryRoot $repositoryRoot -AppFolder $appFolder))) { Write-ALbuildLog -Level Information "No source changes for '$appFolder'; keeping version $previous." [PSCustomObject]@{ AppJsonPath = $file.FullName AppFolder = $appFolder PreviousVersion = $previous.ToString() NewVersion = $previous.ToString() Changed = $false Skipped = $true } continue } $new = if ($PSCmdlet.ParameterSetName -eq 'Explicit') { ConvertTo-BcVersion $Version } else { Get-BcSchemaVersion -Current $previous -Schema $Schema -BuildId $BuildId } $newString = $new.ToString() $changed = $false if ($newString -ne $previous.ToString()) { if ($PSCmdlet.ShouldProcess($file.FullName, "Set version $previous -> $newString")) { # Replace only the first (top-level) "version" value; dependency versions are untouched. $updated = $versionPattern.Replace($raw, { param($m) "$($m.Groups[1].Value)$newString$($m.Groups[2].Value)" }, 1) if ($updated -eq $raw) { throw "Could not locate the top-level version value in '$($file.FullName)'." } [System.IO.File]::WriteAllText($file.FullName, $updated, $utf8NoBom) $changed = $true Write-ALbuildLog -Level Success "Set '$appFolder' version $previous -> $newString." } } else { Write-ALbuildLog -Level Information "'$appFolder' already at version $newString." } [PSCustomObject]@{ AppJsonPath = $file.FullName AppFolder = $appFolder PreviousVersion = $previous.ToString() NewVersion = $newString Changed = $changed Skipped = $false } } } |