Module/DevOps/Initialize-BCSDevOpsRepository.ps1
<#
.SYNOPSIS Initialize a BrightCom Repository .DESCRIPTION Updates a BrightCom App Repository with default values You will need .PARAMETER projectName Name of the project where the repository is. .PARAMETER repositoryName Name of the repository to create, if left empty repositoryName will be the same as projectName. .PARAMETER projectNameShort Short name for the project 3-4 characters. .PARAMETER appIdRangeFrom From ID ranges for the app .PARAMETER appIdRangeTo From ID ranges for the app .PARAMETER ciTenants Tenant names where the app will be deployed, upgraded and installed when the CI pipeline runs. Divide multiple tenants with comma (e.g. Tenant1, Tenant2). Omit to deploy to global scope .PARAMETER cdTenants Tenant names where the app will be deployed, upgraded and installed when the Release pipeline runs. Divide multiple tenants with comma (e.g. Tenant1, Tenant2). Omit to deploy to global scope .PARAMETER templateRepoUrl Url to template repository if different from the default repository .PARAMETER sourcePat Personal Access Token .EXAMPLE Initialize-BCSDevOpsRepository -projectName "MyProjectName" -repositoryName "MyRepositoryName" -projectNameShort "MPN" -appIdRangeFrom "50000" -appIdRangeTo "50099" -ciTenants "MyQATenant" -cdTenants "MyProdTenant" -sourcePat (Get-BCSSecureString -InputString "MyDevOpsPat") .NOTES Author: Mathias Stjernfelt Website: http://www.brightcom.se #> function Initialize-BCSDevOpsRepository { Param ( [Parameter(Mandatory = $true)] [string]$projectNameShort, [Parameter(Mandatory = $true)] [securestring]$sourcePat, [Parameter(Mandatory = $false)] [string]$organisation = 'BrightComSolutions', [Parameter(Mandatory = $false)] [string]$projectName = "BrightCom Solutions", [Parameter(Mandatory = $false)] [string]$repositoryName, [Parameter(Mandatory = $false)] [string]$appIdRangeFrom, [Parameter(Mandatory = $false)] [string]$appIdRangeTo, [Parameter(Mandatory = $false)] [string]$ciTenants, [Parameter(Mandatory = $false)] [string]$cdTenants, [Parameter(Mandatory = $false)] [string]$templateRepoUrl, [Parameter(Mandatory = $false)] [string]$templateBranch = 'main', [Parameter(Mandatory = $false)] [switch]$localOnly, [Parameter(Mandatory = $false)] [string]$localOnlyProjectPath, [Parameter(Mandatory = $false)] [switch]$openInVSCode ) if ([string]::IsNullOrEmpty($ciTenants)) { $ciTenants = "" } if ([string]::IsNullOrEmpty($cdTenants)) { $cdTenants = "" } if ([string]::IsNullOrEmpty($templateRepoUrl)) { $templateRepoUrl = "https://BrightComSolutions@dev.azure.com/BrightComSolutions/BrightCom%20Solutions/_git/BCS%20AL%20Project%20Template" } if ($localOnly) { if ([string]::IsNullOrEmpty($localOnlyProjectPath)) { throw "Plese provide a path in parameter -localOnlyProjectPath" } } $tempGuid = New-Guid $templatePath = "$env:TEMP\$tempGuid\template" $oldLocation = Get-Location if (-not $localOnly) { #Check if repository exists, if not create it. $fullOrgUrl = "https://dev.azure.com/$organisation" $sourcePatPlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($sourcePat)) $sourcePatPlainText | az devops login --org $fullOrgUrl $ErrorActionPreference = "silentlyContinue" $repoInfo = az repos show --repository $repositoryName --org $fullOrgUrl --project $projectName | ConvertFrom-Json if (-not [string]::IsNullOrEmpty($repoInfo)) { $question = "Repository $repositoryName already exists in DevOps project $projectName, would you like to delete it?" $choices = '&Yes', '&No' $decision = $Host.UI.PromptForChoice($title, $question, $choices, 1) if ($decision -eq 0) { Write-Host "Deleting repository $($repoInfo.Name)" -ForegroundColor Green az repos delete --id $repoInfo.Id --org $fullOrgUrl --project $projectName --yes } else { throw "Aborted by user" } } } #Check if powershell-yaml is installed if (-not (Get-Module -ListAvailable -Name "powershell-yaml")) { Write-Host "powershell-yaml is not installed. Installing it from Powershell Gallery." -ForegroundColor Green Install-Module -Name powershell-yaml -force } Write-Host "Downloading template files from $templateRepoUrl" -ForegroundColor Green if (-not(Test-Path -Path $templatePath)) { New-Item -Path $templatePath -ItemType Directory | Out-Null } Set-Location $templatePath & git clone $templateRepoUrl $templatePath & git checkout $templateBranch Set-Location $oldLocation Remove-Item -Path "$templatePath\.git" -Recurse -Force $tempProjectPath = "$env:TEMP\$tempGuid\project" if (-not (Test-Path -Path $tempProjectPath)) { New-Item -Path $tempProjectPath -ItemType Directory | Out-Null } if (-not $localOnly) { Write-Host "Creating repository $repositoryName in DevOps project $projectName" -ForegroundColor Green az repos create --name $repositoryName --org $fullOrgUrl --project $projectName | Out-Null $repoUrl = [uri]::EscapeUriString("https://$organisation@dev.azure.com/$organisation/$projectName/_git/$repositoryName") Write-Host "Downloading target repository" -ForegroundColor Green Set-Location $tempProjectPath & git clone $repoUrl $tempProjectPath } else { Write-Host "Creating project $projectName locally" -ForegroundColor Green New-Item -Path $tempProjectPath -ItemType Directory | Out-Null } Copy-Item -Path "$templatePath\*" -Destination $tempProjectPath -Recurse -ErrorAction Stop Set-Location "$tempProjectPath" -ErrorAction Stop if ($localOnly) { & git init -q } #region UpdateProjectFles # Update app\app.json $fileName = "App\app.json" $filecontent = Get-Content -Path $fileName | ConvertFrom-Json $filecontent.id = [guid]::NewGuid(); $filecontent.name = $repositoryName; $filecontent.idRanges[0].from = $appIdRangeFrom; $filecontent.idRanges[0].to = $appIdRangeTo; Write-Host "Updating App/app.json" -ForegroundColor Green Write-Host " id `t`t $($filecontent.id)" -ForegroundColor Yellow Write-Host " name `t`t $($filecontent.name)" -ForegroundColor Yellow Write-Host " id range `t $appIdRangeFrom..$appIdRangeTo" -ForegroundColor Yellow $filecontent | ConvertTo-Json | Format-Json -Indentation 2 | out-file $filename #Rename Workspace file Write-Host "Renaming VS Code workspace file to $repositoryName.code-workspace" -ForegroundColor Green Move-Item -Path "BCS AL Project Template.code-workspace" -Destination "$repositoryName.code-workspace" # Update test\app.json if (Test-Path -Path "Test\app.json") { $fileName = "Test\app.json" $filecontent = Get-Content -Path $fileName | ConvertFrom-Json $filecontent.id = [guid]::NewGuid(); $filecontent.name = "$repositoryName - Test"; Write-Host "Updating Test/app.json" -ForegroundColor Green Write-Host " id `t`t $($filecontent.id)" -ForegroundColor Yellow Write-Host " name `t`t $($filecontent.name)" -ForegroundColor Yellow $filecontent | ConvertTo-Json | Format-Json -Indentation 2 | out-file $filename } # pte-qa-settings.json Write-Host "Updating CI Settings file" -ForegroundColor Green $fileName = ".azureDevOps\pte-qa-settings.json" $filecontent = Get-Content -Path $fileName | ConvertFrom-Json $filecontent.name = "$projectNameShort"; if ((-not [string]::IsNullOrEmpty($ciTenants)) -and ((-not [string]::IsNullOrEmpty($filecontent.deployments)))) { $filecontent.deployments[0].DeployToTenants = $ciTenants.Split(','); } Write-Host " name `t`t $projectNameShort" -ForegroundColor Yellow Write-Host " Tenants `t `"$ciTenants`"" -ForegroundColor Yellow $filecontent | ConvertTo-Json -Depth 3 | Format-Json -Indentation 2 | out-file $filename # pte-prod-settings.json Write-Host "Updating Release Settings file" -ForegroundColor Green $fileName = ".azureDevOps\pte-prod-settings.json" $filecontent = Get-Content -Path $fileName | ConvertFrom-Json $filecontent.name = "$projectNameShort"; if ((-not [string]::IsNullOrEmpty($ciTenants)) -and ((-not [string]::IsNullOrEmpty($filecontent.deployments)))) { $filecontent.deployments[0].DeployToTenants = $cdTenants.Split(','); } Write-Host " name `t`t $projectNameShort" -ForegroundColor Yellow Write-Host " Tenants `t `"$cdTenants`"" -ForegroundColor Yellow $filecontent | ConvertTo-Json -Depth 3 | Format-Json -Indentation 2 | out-file $filename # pte-prod-release.yml Write-Host "Updating Release Pipeline" -ForegroundColor Green $fileName = ".azureDevOps\pte-prod-release.yml"; $filecontent = Get-Content -Path $fileName; $content = ''; foreach ($line in $fileContent) { $content = $content + "`n" + $line; } $yaml = ConvertFrom-YAML $content -Ordered; $yaml.resources.pipelines[0].source = "$repositoryName - CI"; Write-Host " CI Pipeline `t $repositoryName - CI" -ForegroundColor Yellow $yaml | ConvertTo-Yaml | out-file $filename; #endregion #Update DevOps Repo with new setup & git add -A & git checkout -b main -q & git commit -m 'Initial commit' -q if (-not $localOnly) { Write-Host "Uploading files to repository branch main" -ForegroundColor Green & git push -u origin main -q } Set-Location $oldLocation if (-not $localOnly) { $newRepoUrl = [uri]::EscapeUriString("https://$organisation@dev.azure.com/$organisation/$projectName/_git/$repositoryName") Write-Host "Repsitory created successfully, please clone from $newRepoUrl" } else { if (Test-Path -Path $localOnlyProjectPath) { Remove-Item -Path $localOnlyProjectPath -Recurse -Force } New-Item -Path $localOnlyProjectPath -ItemType Directory | Out-Null Copy-Item -Path "$tempProjectPath\*" -Destination $localOnlyProjectPath -Recurse -ErrorAction Stop Write-Host "Repository created locally at $localOnlyProjectPath" -ForegroundColor Green if ($openInVSCode) { & code $localOnlyProjectPath } } Set-Location $oldLocation if (Test-Path -Path $tempProjectPath) { Remove-Item -Path $tempProjectPath -Recurse -Force } if (Test-Path -Path $templatePath) { Remove-Item -Path $templatePath -Recurse -Force } } Export-ModuleMember -Function Initialize-BCSDevOpsRepository |