Private/Repository/Set-CgrRepositoryProtectionConfiguration.ps1
|
function Set-CgrRepositoryProtectionConfiguration { [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The public Copy-GitHubRepository command performs ShouldProcess before this late-stage restoration boundary.' )] [CmdletBinding()] param( [Parameter(Mandatory)] [psobject] $SourceRepository, [Parameter(Mandatory)] [psobject] $DestinationRepository, [psobject] $SourceConfiguration, [ValidateNotNullOrEmpty()] [string] $HostName = 'github.com' ) $sourceConfiguration = if ($PSBoundParameters.ContainsKey('SourceConfiguration') -and $null -ne $SourceConfiguration) { $SourceConfiguration } else { Get-CgrRepositoryProtectionConfiguration ` -Repository $SourceRepository ` -HostName $HostName } $restored = [System.Collections.Generic.List[object]]::new() $skipped = [System.Collections.Generic.List[object]]::new() foreach ($item in @($sourceConfiguration.Unsupported)) { $skipped.Add([pscustomobject] @{ Kind = $item.Kind Name = $item.Name Reason = (@($item.Reasons) -join ',') }) } foreach ($ruleset in @($sourceConfiguration.Rulesets)) { $body = [ordered] @{ name = $ruleset.Name target = $ruleset.Target enforcement = $ruleset.Enforcement conditions = $ruleset.Conditions rules = @($ruleset.Rules) } $created = Invoke-CgrGitHubApiMutation ` -Method POST ` -Path "repos/$($DestinationRepository.FullName)/rulesets" ` -Body $body ` -HostName $HostName $restored.Add([pscustomobject] @{ Kind = 'Ruleset' Name = $ruleset.Name DestinationId = Get-CgrObjectProperty -InputObject $created -Name 'id' }) } if ($sourceConfiguration.BranchProtection) { $branch = $sourceConfiguration.BranchProtection $encodedBranch = [Uri]::EscapeDataString([string] $branch.BranchName) $body = [ordered] @{ required_status_checks = $branch.RequiredStatusChecks enforce_admins = [bool] $branch.EnforceAdmins required_pull_request_reviews = $branch.RequiredPullRequestReviews restrictions = $branch.Restrictions required_linear_history = [bool] $branch.RequiredLinearHistory allow_force_pushes = [bool] $branch.AllowForcePushes allow_deletions = [bool] $branch.AllowDeletions block_creations = [bool] $branch.BlockCreations required_conversation_resolution = [bool] $branch.RequiredConversationResolution lock_branch = [bool] $branch.LockBranch allow_fork_syncing = [bool] $branch.AllowForkSyncing } Invoke-CgrGitHubApiMutation ` -Method PUT ` -Path "repos/$($DestinationRepository.FullName)/branches/$encodedBranch/protection" ` -Body $body ` -HostName $HostName | Out-Null if ($branch.RequireSignatures) { Invoke-CgrGitHubApiMutation ` -Method POST ` -Path "repos/$($DestinationRepository.FullName)/branches/$encodedBranch/protection/required_signatures" ` -HostName $HostName | Out-Null } $restored.Add([pscustomobject] @{ Kind = 'BranchProtection' Name = $branch.BranchName DestinationId = $null }) } $verifiedConfiguration = Get-CgrRepositoryProtectionConfiguration ` -Repository $DestinationRepository ` -HostName $HostName $mismatches = [System.Collections.Generic.List[string]]::new() foreach ($sourceRuleset in @($sourceConfiguration.Rulesets)) { $destinationRuleset = @($verifiedConfiguration.Rulesets | Where-Object { $_.Name -eq $sourceRuleset.Name })[0] if (-not $destinationRuleset) { $mismatches.Add("Ruleset '$($sourceRuleset.Name)' is missing from the destination.") continue } foreach ($propertyName in @('Target', 'Enforcement')) { if ([string] $destinationRuleset.$propertyName -ne [string] $sourceRuleset.$propertyName) { $mismatches.Add("Ruleset '$($sourceRuleset.Name)' property '$propertyName' does not match.") } } $sourceConditions = $sourceRuleset.Conditions | ConvertTo-Json -Depth 100 -Compress $destinationConditions = $destinationRuleset.Conditions | ConvertTo-Json -Depth 100 -Compress if ($sourceConditions -ne $destinationConditions) { $mismatches.Add("Ruleset '$($sourceRuleset.Name)' conditions do not match.") } $sourceRules = @($sourceRuleset.Rules) | ConvertTo-Json -Depth 100 -Compress $destinationRules = @($destinationRuleset.Rules) | ConvertTo-Json -Depth 100 -Compress if ($sourceRules -ne $destinationRules) { $mismatches.Add("Ruleset '$($sourceRuleset.Name)' rules do not match.") } } if ($sourceConfiguration.BranchProtection) { if (-not $verifiedConfiguration.BranchProtection) { $mismatches.Add("Branch protection for '$($sourceConfiguration.BranchProtection.BranchName)' is missing from the destination.") } else { $sourceProtectionJson = $sourceConfiguration.BranchProtection | ConvertTo-Json -Depth 100 -Compress $destinationProtectionJson = $verifiedConfiguration.BranchProtection | ConvertTo-Json -Depth 100 -Compress if ($sourceProtectionJson -ne $destinationProtectionJson) { $mismatches.Add("Branch protection for '$($sourceConfiguration.BranchProtection.BranchName)' does not match the source.") } } } if ($mismatches.Count -gt 0) { $message = "Repository protection verification failed for '$($DestinationRepository.FullName)': $($mismatches -join '; ')" $exception = [System.InvalidOperationException]::new($message) $errorRecord = [System.Management.Automation.ErrorRecord]::new( $exception, 'RepositoryProtectionVerificationFailed', [System.Management.Automation.ErrorCategory]::InvalidResult, $DestinationRepository.FullName ) $PSCmdlet.ThrowTerminatingError($errorRecord) } $status = if ($restored.Count -eq 0 -and $skipped.Count -eq 0) { 'NotApplicable' } elseif ($restored.Count -gt 0 -and $skipped.Count -eq 0) { 'Restored' } elseif ($restored.Count -gt 0) { 'Partial' } else { 'Unsupported' } return [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.RepositoryProtectionRestoreResult' Repository = $DestinationRepository.FullName Status = $status Restored = @($restored) Skipped = @($skipped) IsSuccessful = $true IsComplete = @($skipped).Count -eq 0 } } |