Private/GitHub/Set-CgrGitHubRepositorySetting.ps1
|
function Set-CgrGitHubRepositorySetting { [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'The public Copy-GitHubRepository command performs the ShouldProcess check before calling this GitHub API boundary.' )] [CmdletBinding()] param( [Parameter(Mandatory)] [psobject] $SourceRepository, [Parameter(Mandatory)] [psobject] $DestinationRepository, [ValidateNotNullOrEmpty()] [string] $HostName = 'github.com' ) $settingDefinitions = @( [pscustomobject] @{ Name = 'Description'; ApiName = 'description'; Kind = 'String' } [pscustomobject] @{ Name = 'Homepage'; ApiName = 'homepage'; Kind = 'String' } [pscustomobject] @{ Name = 'HasIssues'; ApiName = 'has_issues'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'HasProjects'; ApiName = 'has_projects'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'HasWiki'; ApiName = 'has_wiki'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'HasDiscussions'; ApiName = 'has_discussions'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'AllowSquashMerge'; ApiName = 'allow_squash_merge'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'AllowMergeCommit'; ApiName = 'allow_merge_commit'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'AllowRebaseMerge'; ApiName = 'allow_rebase_merge'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'AllowAutoMerge'; ApiName = 'allow_auto_merge'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'DeleteBranchOnMerge'; ApiName = 'delete_branch_on_merge'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'AllowUpdateBranch'; ApiName = 'allow_update_branch'; Kind = 'Boolean' } [pscustomobject] @{ Name = 'WebCommitSignoffRequired'; ApiName = 'web_commit_signoff_required'; Kind = 'Boolean' } ) $restored = [System.Collections.Generic.List[object]]::new() $skipped = [System.Collections.Generic.List[string]]::new() $patchArguments = [System.Collections.Generic.List[string]]::new() $patchArguments.Add('api') $patchArguments.Add('--hostname') $patchArguments.Add($HostName) $patchArguments.Add('-X') $patchArguments.Add('PATCH') $patchArguments.Add("repos/$($DestinationRepository.FullName)") foreach ($setting in $settingDefinitions) { $sourceValue = Get-CgrObjectProperty -InputObject $SourceRepository -Name $setting.Name if ($null -eq $sourceValue) { $skipped.Add("$($setting.Name):UnavailableFromSource") continue } $destinationValue = Get-CgrObjectProperty -InputObject $DestinationRepository -Name $setting.Name $changed = $destinationValue -ne $sourceValue if ($changed) { if ($setting.Kind -eq 'Boolean') { $patchArguments.Add('-F') $patchArguments.Add("$($setting.ApiName)=$(([bool] $sourceValue).ToString().ToLowerInvariant())") } else { $patchArguments.Add('-f') $patchArguments.Add("$($setting.ApiName)=$([string] $sourceValue)") } } $restored.Add([pscustomobject] @{ Name = $setting.Name Status = 'Restored' Value = $sourceValue Changed = $changed }) } if ($patchArguments.Count -gt 7) { $result = Invoke-CgrNativeCommand -FilePath 'gh' -ArgumentList $patchArguments.ToArray() if ($result.ExitCode -ne 0) { $message = "GitHub CLI failed to restore repository settings for '$($DestinationRepository.FullName)'. $($result.ErrorText)" $exception = [System.InvalidOperationException]::new($message.Trim()) $errorRecord = [System.Management.Automation.ErrorRecord]::new( $exception, 'GitHubRepositorySettingsRestoreFailed', [System.Management.Automation.ErrorCategory]::InvalidOperation, $DestinationRepository.FullName ) $PSCmdlet.ThrowTerminatingError($errorRecord) } } $sourceTopics = Get-CgrObjectProperty -InputObject $SourceRepository -Name 'Topics' $destinationTopics = Get-CgrObjectProperty -InputObject $DestinationRepository -Name 'Topics' $topicsChanged = $false $topicsWereAvailable = $null -ne $sourceTopics if ($topicsWereAvailable) { $sourceTopicList = @($sourceTopics | ForEach-Object { [string] $_ } | Sort-Object) $destinationTopicList = if ($null -eq $destinationTopics) { @() } else { @($destinationTopics | ForEach-Object { [string] $_ } | Sort-Object) } $topicsChanged = ($sourceTopicList -join "`n") -ne ($destinationTopicList -join "`n") if ($topicsChanged) { $topicsInputPath = Join-Path ([System.IO.Path]::GetTempPath()) "copy-github-repo-topics-$([guid]::NewGuid().ToString('N')).json" try { @{ names = $sourceTopicList } | ConvertTo-Json -Compress | Set-Content -LiteralPath $topicsInputPath -Encoding utf8NoBOM | Out-Null $topicsResult = Invoke-CgrNativeCommand -FilePath 'gh' -ArgumentList @('api', '--hostname', $HostName, '-X', 'PUT', "repos/$($DestinationRepository.FullName)/topics", '--input', $topicsInputPath) if ($topicsResult.ExitCode -ne 0) { $message = "GitHub CLI failed to restore repository topics for '$($DestinationRepository.FullName)'. $($topicsResult.ErrorText)" $exception = [System.InvalidOperationException]::new($message.Trim()) $errorRecord = [System.Management.Automation.ErrorRecord]::new($exception, 'GitHubRepositoryTopicsRestoreFailed', [System.Management.Automation.ErrorCategory]::InvalidOperation, $DestinationRepository.FullName) $PSCmdlet.ThrowTerminatingError($errorRecord) } } finally { Remove-Item -LiteralPath $topicsInputPath -Force -ErrorAction SilentlyContinue } } $restored.Add([pscustomobject] @{ Name = 'Topics'; Status = 'Restored'; Value = $sourceTopicList; Changed = $topicsChanged }) } else { $skipped.Add('Topics:UnavailableFromSource') } $verifiedDestination = Get-CgrRepository -Repository $DestinationRepository.FullName -HostName $HostName $mismatches = [System.Collections.Generic.List[string]]::new() foreach ($setting in $settingDefinitions) { $sourceValue = Get-CgrObjectProperty -InputObject $SourceRepository -Name $setting.Name if ($null -eq $sourceValue) { continue } $actualValue = Get-CgrObjectProperty -InputObject $verifiedDestination -Name $setting.Name if ($actualValue -ne $sourceValue) { $mismatches.Add("$($setting.Name): expected '$sourceValue' but found '$actualValue'") } } if ($topicsWereAvailable) { $actualTopics = Get-CgrObjectProperty -InputObject $verifiedDestination -Name 'Topics' $actualTopicList = if ($null -eq $actualTopics) { @() } else { @($actualTopics | ForEach-Object { [string] $_ } | Sort-Object) } if (($sourceTopicList -join "`n") -ne ($actualTopicList -join "`n")) { $mismatches.Add("Topics: expected '$($sourceTopicList -join ',')' but found '$($actualTopicList -join ',')'") } } if ($mismatches.Count -gt 0) { $message = "GitHub repository settings verification failed for '$($DestinationRepository.FullName)': $($mismatches -join '; ')" $exception = [System.InvalidOperationException]::new($message) $errorRecord = [System.Management.Automation.ErrorRecord]::new($exception, 'GitHubRepositorySettingsVerificationFailed', [System.Management.Automation.ErrorCategory]::InvalidResult, $DestinationRepository.FullName) $PSCmdlet.ThrowTerminatingError($errorRecord) } return [pscustomobject] @{ PSTypeName = 'CopyGitHubRepo.SettingsRestoreResult' Repository = $DestinationRepository.FullName Restored = @($restored) Skipped = @($skipped) Unsupported = @( 'GitHubPages' 'GitHubActionsActivation' 'Secrets' 'Webhooks' 'DeployKeys' 'Environments' 'CollaboratorsAndTeams' ) IsSuccessful = $true } } |