utilities/pipelines/resourcePublish/Get-ModulesToPublish.ps1 (347 lines of code) (raw):

#region Helper functions <# .SYNOPSIS Get modified files between previous and current commit depending on if you are running on main/master or a custom branch. .EXAMPLE Get-ModifiedFileList Directory: C:\Repo\Azure\ResourceModules\utilities\pipelines\resourcePublish Mode LastWriteTime Length Name ---- ------------- ------ ---- la--- 08.12.2021 15:50 7133 Script.ps1 Get modified files between previous and current commit depending on if you are running on main/master or a custom branch. #> function Get-ModifiedFileList { $CurrentBranch = Get-GitBranchName if (($CurrentBranch -eq 'main') -or ($CurrentBranch -eq 'master')) { Write-Verbose 'Gathering modified files from the pull request' -Verbose $Diff = git diff --name-only --diff-filter=AM HEAD^ HEAD } else { Write-Verbose 'Gathering modified files between current branch and main' -Verbose $Diff = git diff --name-only --diff-filter=AM origin/main if ($Diff.count -eq 0) { Write-Verbose 'Gathering modified files between current branch and master' -Verbose $Diff = git diff --name-only --diff-filter=AM origin/master } } $ModifiedFiles = $Diff | Get-Item -Force return $ModifiedFiles } <# .SYNOPSIS Get the name of the current checked out branch. .DESCRIPTION Get the name of the current checked out branch. If git cannot find it, best effort based on environment variables is used. .EXAMPLE Get-CurrentBranch feature-branch-1 Get the name of the current checked out branch. #> function Get-GitBranchName { [CmdletBinding()] param () # Get branch name from Git $BranchName = git branch --show-current # If git could not get name, try GitHub variable if ([string]::IsNullOrEmpty($BranchName) -and (Test-Path env:GITHUB_REF_NAME)) { $BranchName = $env:GITHUB_REF_NAME } # If git could not get name, try Azure DevOps variable if ([string]::IsNullOrEmpty($BranchName) -and (Test-Path env:BUILD_SOURCEBRANCHNAME)) { $BranchName = $env:BUILD_SOURCEBRANCHNAME } return $BranchName } <# .SYNOPSIS Find the closest main.bicep/json file to the current directory/file. .DESCRIPTION This function will search the current directory and all parent directories for a main.bicep/json file. .PARAMETER Path Mandatory. Path to the folder/file that should be searched .EXAMPLE Find-TemplateFile -Path "C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service\table\.bicep\nested_roleAssignments.bicep" Directory: C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service\table Mode LastWriteTime Length Name ---- ------------- ------ ---- la--- 05.12.2021 22:45 1230 main.bicep Gets the closest main.bicep/json file to the current directory. #> function Find-TemplateFile { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $Path ) $FolderPath = Split-Path $Path -Parent $FolderName = Split-Path $Path -Leaf if ($FolderName -eq 'modules') { return $null } #Prioritizing the bicep file $TemplateFilePath = Join-Path $FolderPath 'main.bicep' if (-not (Test-Path $TemplateFilePath)) { $TemplateFilePath = Join-Path $FolderPath 'main.json' } if (-not (Test-Path $TemplateFilePath)) { return Find-TemplateFile -Path $FolderPath } return $TemplateFilePath | Get-Item } <# .SYNOPSIS Find the closest main.bicep/json file to the changed files in the module folder structure. .DESCRIPTION Find the closest main.bicep/json file to the changed files in the module folder structure. .PARAMETER ModuleFolderPath Mandatory. Path to the main/parent module folder. .EXAMPLE Get-TemplateFileToPublish -ModuleFolderPath "C:\Repos\Azure\ResourceModules\modules\storage\storage-account\" C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service\table\main.bicep Gets the closest main.bicep/json file to the changed files in the module folder structure. Assuming there is a changed file in 'storage\storage-account\table-service\table' the function would return the main.bicep file in the same folder. #> function Get-TemplateFileToPublish { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $ModuleFolderPath ) $ModuleFolderRelPath = $ModuleFolderPath.Split('/modules/')[-1] $ModifiedFiles = Get-ModifiedFileList -Verbose Write-Verbose "Looking for modified files under: [$ModuleFolderRelPath]" -Verbose $ModifiedModuleFiles = $ModifiedFiles | Where-Object { $_.FullName -like "*$ModuleFolderPath*" } $TemplateFilesToPublish = $ModifiedModuleFiles | ForEach-Object { Find-TemplateFile -Path $_.FullName -Verbose } | Sort-Object -Property FullName -Unique -Descending if ($TemplateFilesToPublish.Count -eq 0) { Write-Verbose 'No template file found in the modified module.' -Verbose } Write-Verbose ('Modified modules found: [{0}]' -f $TemplateFilesToPublish.count) -Verbose $TemplateFilesToPublish | ForEach-Object { $RelPath = ($_.FullName).Split('/modules/')[-1] $RelPath = $RelPath.Split('/main.')[0] Write-Verbose " - [$RelPath]" -Verbose } return $TemplateFilesToPublish } <# .SYNOPSIS Gets the parent main.bicep/json file(s) to the changed files in the module folder structure. .DESCRIPTION Gets the parent main.bicep/json file(s) to the changed files in the module folder structure. .PARAMETER TemplateFilePath Mandatory. Path to a main.bicep/json file. .PARAMETER Recurse Optional. If true, the function will recurse up the folder structure to find the closest main.bicep/json file. .EXAMPLE Get-ParentModuleTemplateFile -TemplateFilePath 'C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service\table\main.bicep' -Recurse Directory: C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service Mode LastWriteTime Length Name ---- ------------- ------ ---- la--- 05.12.2021 22:45 1427 main.bicep Directory: C:\Repos\Azure\ResourceModules\modules\storage\storage-account Mode LastWriteTime Length Name ---- ------------- ------ ---- la--- 02.12.2021 13:19 10768 main.bicep Gets the parent main.bicep/json file(s) to the changed files in the module folder structure. #> function Get-ParentModuleTemplateFile { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $TemplateFilePath, [Parameter(Mandatory = $false)] [switch] $Recurse ) $ModuleFolderPath = Split-Path $TemplateFilePath -Parent $ParentFolderPath = Split-Path $ModuleFolderPath -Parent #Prioritizing the bicep file $ParentTemplateFilePath = Join-Path $ParentFolderPath 'main.bicep' if (-not (Test-Path $TemplateFilePath)) { $ParentTemplateFilePath = Join-Path $ParentFolderPath 'main.json' } if (-not (Test-Path -Path $ParentTemplateFilePath)) { return } $ParentTemplateFilesToPublish = [System.Collections.ArrayList]@() $ParentTemplateFilesToPublish += $ParentTemplateFilePath | Get-Item if ($Recurse) { $ParentTemplateFilesToPublish += Get-ParentModuleTemplateFile $ParentTemplateFilePath -Recurse } return $ParentTemplateFilesToPublish } <# .SYNOPSIS Get the number of commits following the specified commit. .PARAMETER Commit Optional. A specified git reference to get commit counts on. .EXAMPLE Get-GitDistance -Commit origin/main. 620 There are currently 620 commits on origin/main. When run as a push on main, this will be the current commit number on the main branch. #> function Get-GitDistance { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $Commit = 'HEAD' ) return [int](git rev-list --count $Commit) + 1 } <# .SYNOPSIS Gets the version from the version file from the corresponding main.bicep/json file. .DESCRIPTION Gets the version file from the corresponding main.bicep/json file. The file needs to be in the same folder as the template file itself. .PARAMETER TemplateFilePath Mandatory. Path to a main.bicep/json file. .EXAMPLE Get-ModuleVersionFromFile -TemplateFilePath 'C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service\table\main.bicep' 0.3 Get the version file from the specified main.bicep file. #> function Get-ModuleVersionFromFile { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $TemplateFilePath ) $ModuleFolder = Split-Path -Path $TemplateFilePath -Parent $VersionFilePath = Join-Path $ModuleFolder 'version.json' if (-not (Test-Path -Path $VersionFilePath)) { throw "No version file found at: [$VersionFilePath]" } $VersionFileContent = Get-Content $VersionFilePath | ConvertFrom-Json return $VersionFileContent.version } <# .SYNOPSIS Generates a new version for the specified module. .DESCRIPTION Generates a new version for the specified module, based on version.json file and git commit count. Major and minor version numbers are gathered from the version.json file. Patch version number is calculated based on the git commit count on the branch. .PARAMETER TemplateFilePath Mandatory. Path to a main.bicep/json file. .EXAMPLE Get-NewModuleVersion -TemplateFilePath 'C:\Repos\Azure\ResourceModules\modules\storage\storage-account\table-service\table\main.bicep' 0.3.630 Generates a new version for the table module. #> function Get-NewModuleVersion { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $TemplateFilePath ) $Version = Get-ModuleVersionFromFile -TemplateFilePath $TemplateFilePath $Patch = Get-GitDistance $NewVersion = "$Version.$Patch" $BranchName = Get-GitBranchName -Verbose if ($BranchName -ne 'main' -and $BranchName -ne 'master') { $NewVersion = "$NewVersion-prerelease".ToLower() } return $NewVersion } #endregion <# .SYNOPSIS Generates a hashtable with template file paths to publish with a new version. .DESCRIPTION Generates a hashtable with template file paths to publish with a new version. .PARAMETER TemplateFilePath Mandatory. Path to a main.bicep/json file. .PARAMETER PublishLatest Optional. Publish an absolute latest version. Note: This version may include breaking changes and is not recommended for production environments .EXAMPLE Get-ModulesToPublish -TemplateFilePath 'C:\Repos\Azure\ResourceModules\modules\storage\storage-account\main.bicep' Name Value ---- ----- TemplateFilePath C:\Repos\Azure\ResourceModules\modules\storage\storage-account\file-service\share\main.bicep Version 0.3.848-prerelease TemplateFilePath C:\Repos\Azure\ResourceModules\modules\storage\storage-account\file-service\main.bicep Version 0.3.848-prerelease TemplateFilePath C:\Repos\Azure\ResourceModules\modules\storage\storage-account\main.bicep Version 0.3.848-prerelease Generates a hashtable with template file paths to publish and their new versions. #># function Get-ModulesToPublish { [CmdletBinding()] param ( [Parameter(Mandatory)] [string] $TemplateFilePath, [Parameter(Mandatory = $false)] [bool] $PublishLatest = $true ) $ModuleFolderPath = Split-Path $TemplateFilePath -Parent $TemplateFilesToPublish = Get-TemplateFileToPublish -ModuleFolderPath $ModuleFolderPath | Sort-Object FullName -Descending $modulesToPublish = [System.Collections.ArrayList]@() foreach ($TemplateFileToPublish in $TemplateFilesToPublish) { $ModuleVersion = Get-NewModuleVersion -TemplateFilePath $TemplateFileToPublish.FullName -Verbose $modulesToPublish += @{ Version = $ModuleVersion TemplateFilePath = $TemplateFileToPublish.FullName } if ($ModuleVersion -notmatch 'prerelease') { # Latest Major,Minor $modulesToPublish += @{ Version = ($ModuleVersion.Split('.')[0..1] -join '.') TemplateFilePath = $TemplateFileToPublish.FullName } # Latest Major $modulesToPublish += @{ Version = ($ModuleVersion.Split('.')[0]) TemplateFilePath = $TemplateFileToPublish.FullName } if ($PublishLatest) { # Absolute latest $modulesToPublish += @{ Version = 'latest' TemplateFilePath = $TemplateFileToPublish.FullName } } } $ParentTemplateFilesToPublish = Get-ParentModuleTemplateFile -TemplateFilePath $TemplateFileToPublish.FullName -Recurse foreach ($ParentTemplateFileToPublish in $ParentTemplateFilesToPublish) { $ParentModuleVersion = Get-NewModuleVersion -TemplateFilePath $ParentTemplateFileToPublish.FullName $modulesToPublish += @{ Version = $ParentModuleVersion TemplateFilePath = $ParentTemplateFileToPublish.FullName } if ($ModuleVersion -notmatch 'prerelease') { # Latest Major,Minor $modulesToPublish += @{ Version = ($ParentModuleVersion.Split('.')[0..1] -join '.') TemplateFilePath = $ParentTemplateFileToPublish.FullName } # Latest Major $modulesToPublish += @{ Version = ($ParentModuleVersion.Split('.')[0]) TemplateFilePath = $ParentTemplateFileToPublish.FullName } if ($PublishLatest) { # Absolute latest $modulesToPublish += @{ Version = 'latest' TemplateFilePath = $ParentTemplateFileToPublish.FullName } } } } } $modulesToPublish = $modulesToPublish | Sort-Object TemplateFilePath, Version -Descending -Unique if ($modulesToPublish.count -gt 0) { Write-Verbose 'Publish the following modules:'-Verbose $modulesToPublish | ForEach-Object { $RelPath = ($_.TemplateFilePath).Split('/modules/')[-1] $RelPath = $RelPath.Split('/main.')[0] Write-Verbose (' - [{0}] [{1}] ' -f $RelPath, $_.Version) -Verbose } } else { Write-Verbose 'No modules with changes found to publish.'-Verbose } return $modulesToPublish }