microsoft / PowerShellForGitHub

Microsoft PowerShell wrapper for GitHub API
Other
588 stars 185 forks source link

Get-GitHubRepositoryBranchProtectionRule no longer works #359

Closed HanwhaARudolph closed 2 years ago

HanwhaARudolph commented 2 years ago

Issue Details

As of 7/21/2022 the powershell call to get branch protection throws an error when the branch protection is not found. I have tried this in PS core and Windows PS but there are two different behaviors. Both of them error out, however PS core is a little more graceful.

Steps to reproduce the issue

Get-GitHubRepositoryBranchProtectionRule -RepositoryName MYREPO -OwnerName MYORG -BranchName "main" -AccessToken $token

Verbose logs showing the problem

On Windows PS it throws a 404 and a warning saying

The remote server returned an error: (404) Not Found.
404 | Not Found
Branch not protected | https://docs.github.com/rest/reference/repos#get-branch-protection
This typically happens when the current user isn't properly authenticated. You may need an Access Token with additional scopes checked.
RequestId: B3DF:5D98:7DF1C:E10C6:62DAC32B
At C:\Program Files\WindowsPowerShell\Modules\PowerShellForGitHub\0.16.0\GitHubCore.ps1:557 char:9
+         throw $newLineOutput
+         ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (The remote serv...:E10C6:62DAC32B:String) [], RuntimeException
    + FullyQualifiedErrorId : The remote server returned an error: (404) Not Found.
404 | Not Found
Branch not protected | https://docs.github.com/rest/reference/repos#get-branch-protection
This typically happens when the current user isn't properly authenticated. You may need an Access Token with additional scopes checked.

These logs come from PS core.

VERBOSE: [0.16.0] Executing: Get-GitHubRepositoryBranchProtectionRule -RepositoryName "MYREPO" -OwnerName "MYORG" -BranchName "main" -AccessToken <redacted>
VERBOSE: [PowerShellForGitHub] update check complete.  Running latest version: 0.16.0
VERBOSE: Getting branch protection status for MYREPO
VERBOSE: Accessing [Get] https://api.github.com/repos/MYORG/MYREPO/branches/main/protection [Timeout = 0)]
VERBOSE: GET with 0-byte payload
VERBOSE: received 123-byte response of content type application/json
VERBOSE: [0.16.0] Executing: Set-TelemetryException -ErrorBucket "Get-GitHubRepositoryBranchProtectionRule"
VERBOSE: Sending telemetry event data to https://dc.services.visualstudio.com/v2/track [Timeout = 0)]
VERBOSE: POST with 1459-byte payload
VERBOSE: received 49-byte response of content type application/json
Invoke-WebRequest: /home/axrh/.local/share/powershell/Modules/PowerShellForGitHub/0.16.0/GitHubCore.ps1:301
Line |
 301 |              $result = Invoke-WebRequest @params
     |                        ~~~~~~~~~~~~~~~~~~~~~~~~~
     | {"message":"Branch not protected","documentation_url":"https://docs.github.com/rest/reference/repos#get-branch-protection"}

Operating System

Name Value


OSVersion Microsoft Windows NT 10.0.19042.0 Is 64-bit True Current culture en-US Current UI culture en-US

PowerShell Version

PSVersion 5.1.19041.1682 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.19041.1682 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1

and

Name Value


PSVersion 7.2.5 PSEdition Core GitCommitId 7.2.5 OS Linux 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 Platform Unix PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0

Module Version

Running: 0.16.0 Installed: 0.16.0

HanwhaARudolph commented 2 years ago

My temporary work around is to restructure the call in a different module... this appears to work:

function Get-GitHubRepositoryBranchProtectionRuleSimple {
    param(
        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $RepositoryName,

        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $OwnerName,

        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $BranchName,

        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [Object[]]
        $AccessToken
    )

    $uri = "https://api.github.com/repos/$OwnerName/$RepositoryName/branches/$BranchName/protection"
    $params = @{
        Headers    = @{
            'User-Agent'                   = 'PowerShellForGitHub'
            Accept                         = 'application/vnd.github.luke-cage-preview+json'
            Authorization                  = "token $AccessToken"
        }
        Uri                                = $uri
        Method                             = "GET"
    }

    Try
    {
        Invoke-WebRequest @params -ErrorAction SilentlyContinue
    }
    Catch 
    {
        If (($_.ErrorDetails.Message | convertfrom-json).Message -eq "Branch not Found"){return $null}
    }
}
HowardWolosky commented 2 years ago

Thanks for the report. Will take a look at this further this week to see what might be going on.

HowardWolosky commented 2 years ago

I appreciate the report, but as far as I can tell, this is working by design. If there is no rule, the API returns an error. What behavior had you been expecting?

HanwhaARudolph commented 2 years ago

When it calls the invoke, if its not found, it shouldn't bomb out. For example, when you call Get-GitHubRepository if the repository doesn't exist, it doesn't error out but instead returns a null. I often use this type of format to avoid rerunning commands:

    $GetRepo = Get-GitHubRepository @splatParams -ErrorAction SilentlyContinue | Where-object {$_.full_name -eq "$($SplatParams.OrganizationName)/$repoName"}
    If(-not $GetRepo){
        $GetRepo = New-GitHubRepository @params
    }

the -ErrorAction silentlycontinue allows the script to continue gracefully and create it if it doesn't exist.

This only started happening recently (in the last week or so) on the Get-GitHubRepositoryBranchProtectionRule (the error action not being honored).

HowardWolosky commented 2 years ago

I guess I'm still missing something. We do throw an error in Get-GitHubRepository when the branch isn't found unless you specify -ErrorAction SilentlyContinue. The same is already true with Get-GitHubRepositoryBranchProtectionRule. The behavior is consistent.

# This throws an error
Get-GitHubRepository -OwnerName microsoft -RepositoryName foo

# This doesn't.
Get-GitHubRepository -OwnerName microsoft -RepositoryName foo -ErrorAction SilentlyContinue

# This throws an error
Get-GitHubRepositoryBranchProtectionRule -OwnerName <me> -RepositoryName <branch> -BranchName master

# This doesn't
Get-GitHubRepositoryBranchProtectionRule -OwnerName <me> -RepositoryName <branch> -BranchName master -ErrorAction SilentlyContinue

Let me know what I'm missing here, because I truly am trying to understand if there's something that needs to be fixed for consistency purposes.

HanwhaARudolph commented 2 years ago

This is what I use to create a new github repo (to force company standards) and when I use this inside a try/catch (so if it fails I can do something different than if it succeeds) it catches the error from the silently continue but only on the branch protection rule:

function New-CustomRepo
{
    <#
    .Synopsis
    .DESCRIPTION
    .EXAMPLE
    .NOTES
    #>
    [CmdletBinding(ConfirmImpact='Low')]
    Param
    (
        [Parameter(Mandatory=$true)]
        [string]$Token,

        [Parameter(Mandatory=$true)]
        $repoName,

        [Parameter(Mandatory=$true)]
        $repoOwner,

        $repoDescription
    )

    $cred = New-Object System.Management.Automation.PSCredential "username is ignored", ( $Token | ConvertTo-SecureString -AsPlainText -Force)
    Set-GitHubAuthentication -Credential $cred | Out-Null

    $splatParams = @{
        OrganizationName = "MYORG"
        AccessToken = $Token
    }

    $GetTeam = Get-GitHubTeam @splatParams -ErrorAction SilentlyContinue |  Where-object {$_.TeamName -eq $repoName}
    If(-not $GetTeam){
        $splatParams.MaintainerName = $repoOwner
        $GetTeam = New-GitHubTeam @splatParams `
            -TeamName $repoName `
            -Description "Maintainers team for $repoName" `
            -ParentTeamName "Developers" 
        $splatParams.Remove('MaintainerName')
    }

    $GetRepo = Get-GitHubRepository @splatParams -ErrorAction SilentlyContinue | Where-object {$_.full_name -eq "$($SplatParams.OrganizationName)/$repoName"}
    If(-not $GetRepo){
        $GetRepo = New-GitHubRepository @splatParams `
            -RepositoryName $repoName `
            -Description $repoDescription `
            -TeamId $GetTeam.id `
            -DeleteBranchOnMerge `
            -NoIssues `
            -NoProjects `
            -AutoInit `
            -DisallowMergeCommit `
            -Private
    }

    # set the repo permissions    
    Set-GitHubRepositoryTeamPermission -TeamName $repoName `
        -RepositoryName $repoName `
        -Permission "Maintain" `
        -OwnerName $splatParams.OrganizationName | Out-Null

    Set-GitHubRepositoryTeamPermission -TeamName "DevOps" `
        -RepositoryName $repoName `
        -Permission "Admin" `
        -OwnerName $splatParams.OrganizationName | Out-Null

    Set-GitHubRepositoryTeamPermission -TeamName "Developers" `
        -RepositoryName $repoName `
        -Permission "Pull" `
        -OwnerName $splatParams.OrganizationName | Out-Null

    $BranchProtection = Get-GitHubRepositoryBranchProtectionRule -RepositoryName $repoName -OwnerName $splatParams.OrganizationName -BranchName "main" -AccessToken $splatParams.AccessToken -ErrorAction SilentlyContinue
    If (-not $BranchProtection)
    {
        $BranchProtection = New-GitHubRepositoryBranchProtectionRule -RepositoryName $repoName `
            -OwnerName $splatParams.OrganizationName `
            -BranchName "main" `
            -RequireCodeOwnerReviews `
            -RequiredApprovingReviewCount 1
    }
}