diff --git a/GitHubBranches.ps1 b/GitHubBranches.ps1 index f595ace7..d36f8205 100644 --- a/GitHubBranches.ps1 +++ b/GitHubBranches.ps1 @@ -3,6 +3,7 @@ @{ GitHubBranchTypeName = 'GitHub.Branch' + GitHubBranchProtectionRuleName = 'GitHub.BranchProtectionRule' }.GetEnumerator() | ForEach-Object { Set-Variable -Scope Script -Option ReadOnly -Name $_.Key -Value $_.Value } @@ -154,6 +155,550 @@ filter Get-GitHubRepositoryBranch return (Invoke-GHRestMethodMultipleResult @params | Add-GitHubBranchAdditionalProperties) } +filter Get-GitHubRepositoryBranchProtectionRule +{ +<# + .SYNOPSIS + Retrieve branch protection rules for a given GitHub repository. + + .DESCRIPTION + Retrieve branch protection rules for a given GitHub repository. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER Name + Name of the specific branch to be retrieved. If not supplied, all branches will be retrieved. + + .PARAMETER Uri + Uri for the repository. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER OwnerName + Owner of the repository. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .PARAMETER NoStatus + If this switch is specified, long-running commands will run on the main thread + with no commandline status update. When not specified, those commands run in + the background, enabling the command prompt to provide status information. + If not supplied here, the DefaultNoStatus configuration property value will be used. + + .INPUTS + GitHub.Repository + GitHub.Branch + + .OUTPUTS + GitHub.BranchProtectionRule + + .EXAMPLE + Get-GitHubRepositoryBranchProtectionRule -OwnerName microsoft -RepositoryName PowerShellForGitHub -BranchName master + + Retrieves branch protection rules for the master branch of the PowerShellForGithub repository. + + .EXAMPLE + Get-GitHubRepositoryBranchProtectionRule -Uri 'https://github.com/microsoft/PowerShellForGitHub' -BranchName master + + Retrieves branch protection rules for the master branch of the PowerShellForGithub repository. +#> + [CmdletBinding( + PositionalBinding = $false, + DefaultParameterSetName='Elements')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification='One or more parameters (like NoStatus) are only referenced by helper methods which get access to it from the stack via Get-Variable -Scope 1.')] + param( + [Parameter( + Mandatory, + Position = 1, + ValueFromPipelineByPropertyName, + ParameterSetName='Uri')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + Position = 2)] + [string] $BranchName, + + [Parameter(ParameterSetName='Elements')] + [string] $OwnerName, + + [Parameter(ParameterSetName='Elements')] + [string] $RepositoryName, + + [string] $AccessToken, + + [switch] $NoStatus + ) + + Write-InvocationLog + + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties = @{ + 'OwnerName' = (Get-PiiSafeString -PlainText $OwnerName) + 'RepositoryName' = (Get-PiiSafeString -PlainText $RepositoryName) + } + + $params = @{ + UriFragment = "repos/$OwnerName/$RepositoryName/branches/$BranchName/protection" + Description = "Getting branch protection status for $RepositoryName" + Method = 'Get' + AcceptHeader = $script:lukeCageAcceptHeader + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + TelemetryProperties = $telemetryProperties + NoStatus = (Resolve-ParameterWithDefaultConfigurationValue -Name NoStatus -ConfigValueName DefaultNoStatus) + } + + return (Invoke-GHRestMethod @params | Add-GitHubBranchProtectionRuleAdditionalProperties) +} + +filter Set-GitHubRepositoryBranchProtectionRule +{ +<# + .SYNOPSIS + Set branch protection rules for a given GitHub repository. + + .DESCRIPTION + Set branch protection rules for a given GitHub repository. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER Name + Name of the specific branch to be retrieved. If not supplied, all branches will be retrieved. + + .PARAMETER Uri + Uri for the repository. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER OwnerName + Owner of the repository. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER StatusChecks + The list of status checks to require in order to merge into the branch. + + .PARAMETER RequireUpToDateBranches + Require branches to be up to date before merging. This setting will not take effect unless + at least one status check is defined. + + .PARAMETER EnforceAdmins + Enforce all configured restrictions for administrators. + + .PARAMETER DismissalUsers + Specify the user names of users who can dismiss pull request reviews. This can only be + specified for organization-owned repositories. + + .PARAMETER DismissalTeams + Specify which teams can dismiss pull request reviews. + + .PARAMETER DismissStaleReviews + If specified, approving reviews when someone pushes a new commit are automatically + dismissed. + + .PARAMETER RequireCodeOwnerReviews + Blocks merging pull requests until code owners review them. + + .PARAMETER RequiredApprovingReviewCount + Specify the number of reviewers required to approve pull requests. Use a number between 1 + and 6. + + .PARAMETER RestrictPushUsers + Specify which users have push access. + + .PARAMETER RestrictPushTeams + Specify which teams have push access. + + .PARAMETER RestrictPushApps + Specify which apps have push access. + + .PARAMETER RequireLinearHistory + Enforces a linear commit Git history, which prevents anyone from pushing merge commits to a + branch. Your repository must allow squash merging or rebase merging before you can enable a + linear commit history. + + .PARAMETER AllowForcePushes + Permits force pushes to the protected branch by anyone with write access to the repository. + + .PARAMETER AllowDeletions + Allows deletion of the protected branch by anyone with write access to the repository. + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .PARAMETER NoStatus + If this switch is specified, long-running commands will run on the main thread + with no commandline status update. When not specified, those commands run in + the background, enabling the command prompt to provide status information. + If not supplied here, the DefaultNoStatus configuration property value will be used. + + .INPUTS + GitHub.Repository + GitHub.Branch + GitHub.BranchRepositoryRule + + .OUTPUTS + GitHub.BranchRepositoryRule + + .NOTES + Protecting a branch requires admin or owner permissions to the repository. + + .EXAMPLE + Set-GitHubRepositoryBranchProtectionRule -OwnerName microsoft -RepositoryName PowerShellForGitHub -BranchName master -EnforceAdmins + + Sets a branch protection rule for the master branch of the PowerShellForGithub repository + enforcing all configuration restrictions for administrators. + + .EXAMPLE + Set-GitHubRepositoryBranchProtectionRule -Uri 'https://github.com/microsoft/PowerShellForGitHub' -BranchName master -RequiredApprovingReviewCount 1 + + Sets a branch protection rule for the master branch of the PowerShellForGithub repository + requiring one approving review. +#> + [CmdletBinding( + PositionalBinding = $false, + SupportsShouldProcess, + DefaultParameterSetName='Elements')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification='One or more parameters (like NoStatus) are only referenced by helper methods which get access to it from the stack via Get-Variable -Scope 1.')] + param( + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + Position = 1, + ParameterSetName='Uri')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + Position = 2)] + [string] $BranchName, + + [Parameter(ParameterSetName='Elements')] + [string] $OwnerName, + + [Parameter(ParameterSetName='Elements')] + [string] $RepositoryName, + + [string[]] $StatusChecks, + + [switch] $RequireUpToDateBranches, + + [switch] $EnforceAdmins, + + [string[]] $DismissalUsers, + + [string[]] $DismissalTeams, + + [switch] $DismissStaleReviews, + + [switch] $RequireCodeOwnerReviews, + + [ValidateRange(1, 6)] + [int] $RequiredApprovingReviewCount, + + [string[]] $RestrictPushUsers, + + [string[]] $RestrictPushTeams, + + [string[]] $RestrictPushApps, + + [switch] $RequireLinearHistory, + + [switch] $AllowForcePushes, + + [switch] $AllowDeletions, + + [string] $AccessToken, + + [switch] $NoStatus + ) + + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties = @{ + OwnerName = (Get-PiiSafeString -PlainText $OwnerName) + RepositoryName = (Get-PiiSafeString -PlainText $RepositoryName) + } + + if ($PSBoundParameters.ContainsKey('StatusChecks')) + { + $requiredStatusChecks = @{ + strict = $RequireUpToDateBranches.ToBool() + contexts = $StatusChecks + } + } + else + { + $requiredStatusChecks = $null + } + + $dismissalRestrictions = @{} + + if ($PSBoundParameters.ContainsKey('DismissalUsers')) + { + $dismissalRestrictions['users'] = $DismissalUsers + } + if ($PSBoundParameters.ContainsKey('DismissalTeams')) + { + $teams = Get-GitHubTeam -OwnerName $OwnerName -RepositoryName $RepositoryName | + Where-Object -FilterScript { $DismissalTeams -contains $_.name } + $dismissalRestrictions['teams'] = @($teams.slug) + } + + $requiredPullRequestReviews = @{} + + if ($PSBoundParameters.ContainsKey('DismissStaleReviews')) + { + $requiredPullRequestReviews['dismiss_stale_reviews'] = $DismissStaleReviews.ToBool() + } + if ($PSBoundParameters.ContainsKey('RequireCodeOwnerReviews')) + { + $requiredPullRequestReviews['require_code_owner_reviews'] = $RequireCodeOwnerReviews.ToBool() + } + if ($PSBoundParameters.ContainsKey('RequiredApprovingReviewCount')) + { + $requiredPullRequestReviews['required_approving_review_count'] = $RequiredApprovingReviewCount + } + + if ($dismissalRestrictions.count -gt 0) + { + $requiredPullRequestReviews['dismissal_restrictions'] = $dismissalRestrictions + } + + if ($requiredPullRequestReviews.count -eq 0) + { + $requiredPullRequestReviews = $null + } + + if ($PSBoundParameters.ContainsKey('RestrictPushUsers') -or + $PSBoundParameters.ContainsKey('RestrictPushTeams') -or + $PSBoundParameters.ContainsKey('RestrictPushApps')) + { + if ($null -eq $RestrictPushUsers) + { + $RestrictPushUsers = @() + } + + if ($null -eq $RestrictPushTeams) + { + $restrictPushTeamSlugs = @() + } + else + { + $teams = Get-GitHubTeam -OwnerName $OwnerName -RepositoryName $RepositoryName | + Where-Object -FilterScript { $RestrictPushTeams -contains $_.name } + $restrictPushTeamSlugs['teams'] = @($teams.slug) + } + + $restrictions = @{ + users = $RestrictPushUsers + teams = $restrictPushTeamSlugs + } + + if ($PSBoundParameters.ContainsKey('RestrictPushApps')) { + $restrictions['apps'] = $RestrictPushApps } + } + else + { + $restrictions = $null + } + + $hashBody = @{ + required_status_checks = $requiredStatusChecks + enforce_admins = $EnforceAdmins.ToBool() + required_pull_request_reviews = $requiredPullRequestReviews + restrictions = $restrictions + } + + if ($PSBoundParameters.ContainsKey('RequireLinearHistory')) + { + $hashBody['required_linear_history'] = $RequireLinearHistory.ToBool() + } + if ($PSBoundParameters.ContainsKey('AllowForcePushes')) + { + $hashBody['allow_force_pushes'] = $AllowForcePushes.ToBool() + } + if ($PSBoundParameters.ContainsKey('AllowDeletions')) + { + $hashBody['allow_deletions'] = $AllowDeletions.ToBool() + } + + if ($PSCmdlet.ShouldProcess( + "'$BranchName' branch of repository '$RepositoryName'", + 'Set GitHub Repository Branch Protection')) + { + Write-InvocationLog + + $jsonConversionDepth = 3 + + $params = @{ + UriFragment = "repos/$OwnerName/$RepositoryName/branches/$BranchName/protection" + Body = (ConvertTo-Json -InputObject $hashBody -Depth $jsonConversionDepth) + Description = "Setting $BranchName branch protection status for $RepositoryName" + Method = 'Put' + AcceptHeader = $script:lukeCageAcceptHeader + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + TelemetryProperties = $telemetryProperties + NoStatus = (Resolve-ParameterWithDefaultConfigurationValue -Name NoStatus ` + -ConfigValueName DefaultNoStatus) + } + + return (Invoke-GHRestMethod @params | Add-GitHubBranchProtectionRuleAdditionalProperties) + } +} + +filter Remove-GitHubRepositoryBranchProtectionRule +{ +<# + .SYNOPSIS + Remove branch protection rules from a given GitHub repository. + + .DESCRIPTION + Remove branch protection rules from a given GitHub repository. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER Name + Name of the specific branch to be removed. + + .PARAMETER Uri + Uri for the repository. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER OwnerName + Owner of the repository. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .PARAMETER NoStatus + If this switch is specified, long-running commands will run on the main thread + with no commandline status update. When not specified, those commands run in + the background, enabling the command prompt to provide status information. + If not supplied here, the DefaultNoStatus configuration property value will be used. + + .INPUTS + GitHub.Repository + GitHub.Branch + + .OUTPUTS + None + + .EXAMPLE + Remove-GitHubRepositoryBranchProtectionRule -OwnerName microsoft -RepositoryName PowerShellForGitHub -BranchName master + + Removes branch protection rules from the master branch of the PowerShellForGithub repository. + + .EXAMPLE + Removes-GitHubRepositoryBranchProtection -Uri 'https://github.com/microsoft/PowerShellForGitHub' -BranchName master + + Removes branch protection rules from the master branch of the PowerShellForGithub repository. + + .EXAMPLE + Removes-GitHubRepositoryBranchProtection -Uri 'https://github.com/master/PowerShellForGitHub' -BranchName master -Force + + Removes branch protection rules from the master branch of the PowerShellForGithub repository + without prompting for confirmation. +#> + [CmdletBinding( + PositionalBinding = $false, + SupportsShouldProcess, + DefaultParameterSetName='Elements', + ConfirmImpact="High")] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', + Justification='One or more parameters (like NoStatus) are only referenced by helper methods which get access to it from the stack via Get-Variable -Scope 1.')] + [Alias('Delete-GitHubRepositoryBranchProtectionRule')] + param( + [Parameter( + Mandatory, + Position = 1, + ValueFromPipelineByPropertyName, + ParameterSetName='Uri')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + Position = 2)] + [string] $BranchName, + + [Parameter(ParameterSetName='Elements')] + [string] $OwnerName, + + [Parameter(ParameterSetName='Elements')] + [string] $RepositoryName, + + [switch] $Force, + + [string] $AccessToken, + + [switch] $NoStatus + ) + + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties = @{ + 'OwnerName' = (Get-PiiSafeString -PlainText $OwnerName) + 'RepositoryName' = (Get-PiiSafeString -PlainText $RepositoryName) + } + + if ($Force -and (-not $Confirm)) + { + $ConfirmPreference = 'None' + } + + if ($PSCmdlet.ShouldProcess("'$BranchName' branch of repository '$RepositoryName'", + 'Remove GitHub Repository Branch Protection Rule')) + { + Write-InvocationLog + + $params = @{ + UriFragment = "repos/$OwnerName/$RepositoryName/branches/$BranchName/protection" + Description = "Removing $BranchName branch protection rule for $RepositoryName" + Method = 'Delete' + AcceptHeader = $script:lukeCageAcceptHeader + AccessToken = $AccessToken + TelemetryEventName = $MyInvocation.MyCommand.Name + TelemetryProperties = $telemetryProperties + NoStatus = (Resolve-ParameterWithDefaultConfigurationValue -Name NoStatus ` + -ConfigValueName DefaultNoStatus) + } + + return Invoke-GHRestMethod @params | Out-Null + } +} + filter Add-GitHubBranchAdditionalProperties { <# @@ -167,7 +712,7 @@ filter Add-GitHubBranchAdditionalProperties The type that should be assigned to the object. .INPUTS - [PSCustomObject] + PSCustomObject .OUTPUTS GitHub.Branch @@ -202,3 +747,64 @@ filter Add-GitHubBranchAdditionalProperties Write-Output $item } } + +filter Add-GitHubBranchProtectionRuleAdditionalProperties +{ +<# + .SYNOPSIS + Adds type name and additional properties to ease pipelining to GitHub Branch Protection Rule objects. + + .PARAMETER InputObject + The GitHub object to add additional properties to. + + .PARAMETER TypeName + The type that should be assigned to the object. + + .INPUTS + PSCustomObject + + .OUTPUTS + GitHub.Branch +#> + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', + Justification='Internal helper that is definitely adding more than one property.')] + param( + [Parameter( + Mandatory, + ValueFromPipeline)] + [AllowNull()] + [AllowEmptyCollection()] + [PSCustomObject[]] $InputObject, + + [ValidateNotNullOrEmpty()] + [string] $TypeName = $script:GitHubBranchProtectionRuleName + ) + + foreach ($item in $InputObject) + { + $item.PSObject.TypeNames.Insert(0, $TypeName) + + if (-not (Get-GitHubConfiguration -Name DisablePipelineSupport)) + { + $elements = Split-GitHubUri -Uri $item.url + $repositoryUrl = Join-GitHubUri @elements + Add-Member -InputObject $item -Name 'RepositoryUrl' -Value $repositoryUrl -MemberType NoteProperty -Force + + $hostName = $(Get-GitHubConfiguration -Name 'ApiHostName') + + if ($item.html_url -match "^https?://(?:www\.|api\.|)$hostName/repos/(?:[^/]+)/(?:[^/]+)/branches/([^/]+)/.*$") + { + $branchName = $Matches[1] + } + else + { + $branchName = '' + } + + Add-Member -InputObject $item -Name 'BranchName' -Value $branchName -MemberType NoteProperty -Force + } + + Write-Output $item + } +} diff --git a/GitHubCore.ps1 b/GitHubCore.ps1 index 09068429..767fbb12 100644 --- a/GitHubCore.ps1 +++ b/GitHubCore.ps1 @@ -19,6 +19,8 @@ squirrelGirlAcceptHeader = 'application/vnd.github.squirrel-girl-preview' starfoxAcceptHeader = 'application/vnd.github.starfox-preview+json' symmetraAcceptHeader = 'application/vnd.github.symmetra-preview+json' + lukeCageAcceptHeader = 'application/vnd.github.luke-cage-preview+json' + }.GetEnumerator() | ForEach-Object { Set-Variable -Scope Script -Option ReadOnly -Name $_.Key -Value $_.Value } diff --git a/PowerShellForGitHub.psd1 b/PowerShellForGitHub.psd1 index b49ff1f0..061e849d 100644 --- a/PowerShellForGitHub.psd1 +++ b/PowerShellForGitHub.psd1 @@ -88,6 +88,7 @@ 'Get-GitHubRelease', 'Get-GitHubRepository', 'Get-GitHubRepositoryBranch', + 'Get-GitHubRepositoryBranchProtectionRule', 'Get-GitHubRepositoryCollaborator', 'Get-GitHubRepositoryContributor', 'Get-GitHubRepositoryFork', @@ -130,6 +131,7 @@ 'Remove-GitHubProjectCard', 'Remove-GitHubProjectColumn', 'Remove-GitHubRepository', + 'Remove-GitHubRepositoryBranchProtectionRule', 'Rename-GitHubRepository', 'Reset-GitHubConfiguration', 'Restore-GitHubConfiguration', @@ -143,6 +145,7 @@ 'Set-GitHubProject', 'Set-GitHubProjectCard', 'Set-GitHubProjectColumn', + 'Set-GitHubRepositoryBranchProtectionRule', 'Set-GitHubRepositoryTopic', 'Split-GitHubUri', 'Test-GitHubAssignee', @@ -165,6 +168,7 @@ 'Delete-GitHubProjectCard', 'Delete-GitHubProjectColumn' 'Delete-GitHubRepository', + 'Delete-GitHubRepositoryBranchProtectionRule', 'Get-GitHubBranch', 'Get-GitHubComment', 'New-GitHubComment', diff --git a/Tests/GitHubBranches.tests.ps1 b/Tests/GitHubBranches.tests.ps1 index f0df1ca9..9968ccf3 100644 --- a/Tests/GitHubBranches.tests.ps1 +++ b/Tests/GitHubBranches.tests.ps1 @@ -107,6 +107,181 @@ try } } } + + Describe 'GitHubBranches\Get-GitHubRepositoryBranchProtectionRule' { + Context 'When getting GitHub repository branch protection' { + BeforeAll { + $repoName = [Guid]::NewGuid().Guid + $branchName = 'master' + $repo = New-GitHubRepository -RepositoryName $repoName -AutoInit + Set-GitHubRepositoryBranchProtectionRule -Uri $repo.svn_url -BranchName $branchName | Out-Null + $protection = Get-GitHubRepositoryBranchProtectionRule -uri $repo.svn_url -BranchName $branchName + } + + It 'Should return an object of the correct type' { + $protection | Should -BeOfType PSCustomObject + } + + It 'Should return the correct properties' { + $protection.url | + Should -Be "https://api.github.com/repos/$script:ownerName/$repoName/branches/$branchName/protection" + } + + AfterAll -ScriptBlock { + if ($repo) + { + Remove-GitHubRepository -Uri $repo.svn_url -Confirm:$false + } + } + } + } + + Describe 'GitHubBranches\Set-GitHubRepositoryBranchProtectionRule' { + + Context 'When setting GitHub repository branch protection' { + BeforeAll { + $repoName = [Guid]::NewGuid().Guid + $branchName = 'master' + $protectionUrl = "https://api.github.com/repos/$script:organizationName/" + + "$repoName/branches/$branchName/protection" + $newGitHubRepositoryParms = @{ + OrganizationName = $script:organizationName + RepositoryName = $repoName + AutoInit = $true + } + + $repo = New-GitHubRepository @newGitHubRepositoryParms + } + + Context 'When setting base protection options' { + BeforeAll { + $setGitHubRepositoryBranchProtectionParms = @{ + Uri = $repo.svn_url + BranchName = $branchName + EnforceAdmins = $true + RequireLinearHistory = $true + AllowForcePushes = $true + AllowDeletions = $true + } + + $protection = Set-GitHubRepositoryBranchProtectionRule @setGitHubRepositoryBranchProtectionParms + } + + It 'Should return an object of the correct type' { + $protection | Should -BeOfType PSCustomObject + } + + It 'Should return the correct properties' { + $protection.url | Should -Be $protectionUrl + $protection.enforce_admins.enabled | Should -BeTrue + $protection.required_linear_history.enabled | Should -BeTrue + $protection.allow_force_pushes | Should -BeTrue + $protection.allow_deletions | Should -BeTrue + } + } + + Context 'When setting required status checks' { + BeforeAll { + $statusChecks = 'test' + $setGitHubRepositoryBranchProtectionParms = @{ + Uri = $repo.svn_url + BranchName = $branchName + RequireUpToDateBranches = $true + StatusChecks = $statusChecks + } + + $protection = Set-GitHubRepositoryBranchProtectionRule @setGitHubRepositoryBranchProtectionParms + } + + It 'Should return an object of the correct type' { + $protection | Should -BeOfType PSCustomObject + } + + It 'Should return the correct properties' { + $protection.url | Should -Be $protectionUrl + $protection.required_status_checks.strict | Should -BeTrue + $protection.required_status_checks.contexts | Should -Be $statusChecks + } + } + + Context 'When setting required pull request reviews' { + BeforeAll { + $setGitHubRepositoryBranchProtectionParms = @{ + Uri = $repo.svn_url + BranchName = $branchName + DismissalUsers = $script:OwnerName + DismissStaleReviews = $true + RequireCodeOwnerReviews = $true + RequiredApprovingReviewCount = 1 + } + + $protection = Set-GitHubRepositoryBranchProtectionRule @setGitHubRepositoryBranchProtectionParms + } + + It 'Should return an object of the correct type' { + $protection | Should -BeOfType PSCustomObject + } + + It 'Should return the correct properties' { + $protection.url | Should -Be $protectionUrl + $protection.required_pull_request_reviews.dismissal_restrictions.users.login | + Should -Contain $script:OwnerName + } + } + + Context 'When setting push restrictions' { + BeforeAll { + $setGitHubRepositoryBranchProtectionParms = @{ + Uri = $repo.svn_url + BranchName = $branchName + RestrictPushUsers = $script:OwnerName + } + + $protection = Set-GitHubRepositoryBranchProtectionRule @setGitHubRepositoryBranchProtectionParms + } + + It 'Should return an object of the correct type' { + $protection | Should -BeOfType PSCustomObject + } + + It 'Should return the correct properties' { + $protection.url | Should -Be $protectionUrl + $protection.restrictions.users.login | Should -Contain $script:OwnerName + } + } + + AfterAll -ScriptBlock { + if ($repo) + { + Remove-GitHubRepository -Uri $repo.svn_url -Confirm:$false + } + } + } + } + + Describe 'GitHubBranches\Remove-GitHubRepositoryBranchProtectionRule' { + Context 'When removing GitHub repository branch protection' { + BeforeAll { + $repoName = [Guid]::NewGuid().Guid + $branchName = 'master' + $repo = New-GitHubRepository -RepositoryName $repoName -AutoInit + Set-GitHubRepositoryBranchProtectionRule -Uri $repo.svn_url -BranchName $branchName | Out-Null + } + + It 'Should not throw' { + { Remove-GitHubRepositoryBranchProtectionRule -Uri $repo.svn_url -BranchName $branchName -Force } | + Should -Not -Throw + } + + AfterAll -ScriptBlock { + if ($repo) + { + Remove-GitHubRepository -Uri $repo.svn_url -Confirm:$false + } + } + } + } + } finally { diff --git a/USAGE.md b/USAGE.md index 2b275c61..2b27dbf7 100644 --- a/USAGE.md +++ b/USAGE.md @@ -39,6 +39,10 @@ * [Disable repository vulnerability alerts](#disable-repository-vulnerability-alerts) * [Enable repository automatic security fixes](#enable-repository-automatic-security-fixes) * [Disable repository automatic security fixes](#disable-repository-automatic-security-fixes) + * [Branches](#branches) + * [Getting a repository branch protection rule](#getting-a-repository-branch-protection-rule) + * [Setting a repository branch protection rule](#setting-a-repository-branch-protection-rule) + * [Removing a repository branch protection rule](#removing-a-repository-branch-protection-rule) * [Forks](#forks) * [Get all the forks for a repository](#get-all-the-forks-for-a-repository) * [Create a new fork](#create-a-new-fork) @@ -456,7 +460,8 @@ New-GitHubRepository -RepositoryName TestRepo -OrganizationName MyOrg -TeamId $m ```powershell New-GitHubRepositoryFromTemplate -OwnerName MyOrg -RepositoryName MyNewRepo-TemplateOwnerName MyOrg -TemplateRepositoryName MyTemplateRepo -======= +``` + #### Get repository vulnerability alert status ```powershell @@ -487,6 +492,26 @@ Enable-GitHubRepositorySecurityFix -OwnerName microsoft -RepositoryName PowerShe Disable-GitHubRepositorySecurityFix -OwnerName microsoft -RepositoryName PowerShellForGitHub ``` +### Branches + +#### Getting a repository branch protection rule + +```powershell +Get-GitHubRepositoryBranchProtectionRule -OwnerName microsoft -RepositoryName PowerShellForGitHub -BranchName master +``` + +#### Setting a repository branch protection rule + +```powershell +Set-GitHubRepositoryBranchProtectionRule -OwnerName microsoft -RepositoryName PowerShellForGitHub -BranchName master -RequiredApprovingReviewCount 1 +``` + +#### Removing a repository branch protection rule + +```powershell +Remove-GitHubRepositoryBranchProtectionRule -OwnerName microsoft -RepositoryName PowerShellForGitHub -BranchName master +``` + ---------- ### Forks