diff --git a/examples/Repositories.ps1 b/examples/Repositories.ps1 new file mode 100644 index 000000000..1b3d52da0 --- /dev/null +++ b/examples/Repositories.ps1 @@ -0,0 +1,21 @@ +$param = @{ + Name = 'test-repo2' + Description = 'This is a test repository' + Visibility = 'Public' + AddReadme = $true + GitIgnore = 'VisualStudio' + License = 'mit' + HasIssues = $true + HasWiki = $false + HasProjects = $false +} + +New-GitHubRepository @param -Debug + +$repo = Get-GitHubRepository -Name 'test-repo2' +$repo2 = Update-GitHubRepository -Name 'test-repo2' -Homepage 'https://example123.com' + +$diff = $repo | Compare-PSCustomObject $repo2 -OnlyChanged +$diff.Property | Should -BeIn ('Homepage', 'UpdatedAt') + + diff --git a/examples/Repositories/Set-GitHubRepository.ps1 b/examples/Repositories/Set-GitHubRepository.ps1 new file mode 100644 index 000000000..2d82d9529 --- /dev/null +++ b/examples/Repositories/Set-GitHubRepository.ps1 @@ -0,0 +1,10 @@ +$params = @{ + Owner = 'octocat' + Name = 'Hello-World' + AllowSquashMergingWith = 'Pull request title and description' + HasIssues = $true + SuggestUpdateBranch = $true + AllowAutoMerge = $true + DeleteBranchOnMerge = $true +} +Set-GitHubRepository @params diff --git a/src/classes/public/Environment/GitHubEnvironment.ps1 b/src/classes/public/Environment/GitHubEnvironment.ps1 index 2345abf17..4da7639c7 100644 --- a/src/classes/public/Environment/GitHubEnvironment.ps1 +++ b/src/classes/public/Environment/GitHubEnvironment.ps1 @@ -32,7 +32,6 @@ $this.ID = $Object.id $this.NodeID = $Object.node_id $this.Name = $Object.name - $this.Url = $Object.html_url $this.Owner = $Owner $this.Repository = $Repository $this.CreatedAt = $Object.created_at diff --git a/src/classes/public/Repositories/GitHubRepository.ps1 b/src/classes/public/Repositories/GitHubRepository.ps1 index e35491894..44cf7c3a1 100644 --- a/src/classes/public/Repositories/GitHubRepository.ps1 +++ b/src/classes/public/Repositories/GitHubRepository.ps1 @@ -67,6 +67,10 @@ # Example: true [System.Nullable[bool]] $HasDiscussions + # Whether sponsorships are enabled. + # Example: false + [System.Nullable[bool]] $HasSponsorships + # Indicates whether the repository is archived. # Example: false [System.Nullable[bool]] $IsArchived @@ -175,10 +179,7 @@ [GithubRepository] $TemplateRepository # The repository this repository was forked from. - [GithubRepository] $ForkParent - - # The ultimate source for the fork network. - [GithubRepository] $ForkSource + [GithubRepository] $ForkRepository # Custom properties for the repository. [PSCustomObject] $CustomProperties @@ -213,6 +214,7 @@ HasProjects = 'hasProjectsEnabled' HasWiki = 'hasWikiEnabled' HasDiscussions = 'hasDiscussionsEnabled' + HasSponsorships = 'hasSponsorshipsEnabled' IsArchived = 'isArchived' IsTemplate = 'isTemplate' IsFork = 'isFork' @@ -239,8 +241,7 @@ MergeCommitTitle = 'mergeCommitTitle' MergeCommitMessage = 'mergeCommitMessage' TemplateRepository = 'templateRepository { id databaseId name owner { login } }' - ForkParent = 'parent { name owner { login } }' - ForkSource = 'parent { name owner { login } }' + ForkRepository = 'parent { id databaseId name owner { login } }' CustomProperties = '' CloneUrl = 'url' SshUrl = 'sshUrl' @@ -297,8 +298,7 @@ $this.MergeCommitTitle = $Object.merge_commit_title $this.CustomProperties = $Object.custom_properties $this.TemplateRepository = $null -ne $Object.template_repository ? [GitHubRepository]::New($Object.template_repository) : $null - $this.ForkParent = $null -ne $Object.parent ? [GitHubRepository]::New($Object.parent) : $null - $this.ForkSource = $null -ne $Object.source ? [GitHubRepository]::New($Object.source) : $null + $this.ForkRepository = $null -ne $Object.parent ? [GitHubRepository]::New($Object.parent) : $null $this.CloneUrl = $Object.clone_url $this.SshUrl = $Object.ssh_url $this.GitUrl = $Object.git_url @@ -323,6 +323,7 @@ $this.HasProjects = $Object.hasProjectsEnabled $this.HasWiki = $Object.hasWikiEnabled $this.HasDiscussions = $Object.hasDiscussionsEnabled + $this.HasSponsorships = $Object.hasSponsorshipsEnabled $this.IsArchived = $Object.isArchived $this.IsTemplate = $Object.isTemplate $this.IsFork = $Object.isFork @@ -349,8 +350,7 @@ $this.MergeCommitTitle = $Object.mergeCommitTitle $this.MergeCommitMessage = $Object.mergeCommitMessage $this.TemplateRepository = $null -ne $Object.templateRepository ? [GitHubRepository]::New($Object.templateRepository) : $null - $this.ForkParent = $null -ne $Object.parent ? [GitHubRepository]::New($Object.parent) : $null - $this.ForkSource = $null -ne $Object.source ? [GitHubRepository]::New($Object.source) : $null + $this.ForkRepository = $null -ne $Object.parent ? [GitHubRepository]::New($Object.parent) : $null $this.CloneUrl = -not [string]::IsNullOrEmpty($Object.url) ? $Object.url + '.git' : $null $this.SshUrl = $Object.sshUrl $this.GitUrl = -not [string]::IsNullOrEmpty($Object.url) ? ($Object.Url + '.git').Replace('https://', 'git://') : $null diff --git a/src/formats/GitHubEnvironment.Format.ps1xml b/src/formats/GitHubEnvironment.Format.ps1xml new file mode 100644 index 000000000..4d7b1ffd1 --- /dev/null +++ b/src/formats/GitHubEnvironment.Format.ps1xml @@ -0,0 +1,45 @@ + + + + + GitHubEnvironmentTable + + GitHubEnvironment + + + + + + + + + + + + + + + + + + + + + Name + + + Repository + + + Owner + + + Url + + + + + + + + diff --git a/src/functions/private/Environments/Get-GitHubEnvironmentByName.ps1 b/src/functions/private/Environments/Get-GitHubEnvironmentByName.ps1 index 7a0bd9fe3..d4286cb88 100644 --- a/src/functions/private/Environments/Get-GitHubEnvironmentByName.ps1 +++ b/src/functions/private/Environments/Get-GitHubEnvironmentByName.ps1 @@ -88,7 +88,9 @@ filter Get-GitHubEnvironmentByName { } try { Invoke-GitHubAPI @inputObject | ForEach-Object { - [GitHubEnvironment]::new($_.Response, $Owner, $Repository) + $environment = [GitHubEnvironment]::new($_.Response, $Owner, $Repository) + $environment.Url = "https://$($Context.HostName)/$Owner/$Repository/settings/environments/$($environment.ID)/edit" + $environment } } catch { return diff --git a/src/functions/private/Environments/Get-GitHubEnvironmentList.ps1 b/src/functions/private/Environments/Get-GitHubEnvironmentList.ps1 index 0fdbfe6fc..85d191c16 100644 --- a/src/functions/private/Environments/Get-GitHubEnvironmentList.ps1 +++ b/src/functions/private/Environments/Get-GitHubEnvironmentList.ps1 @@ -83,7 +83,9 @@ filter Get-GitHubEnvironmentList { Invoke-GitHubAPI @inputObject | ForEach-Object { $_.Response.environments | ForEach-Object { - [GitHubEnvironment]::new($_, $Owner, $Repository) + $environment = [GitHubEnvironment]::new($_, $Owner, $Repository) + $environment.Url = "https://$($Context.HostName)/$Owner/$Repository/settings/environments/$($environment.ID)/edit" + $environment } } } diff --git a/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 b/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 index cba5d3bf6..5cce87726 100644 --- a/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 +++ b/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 @@ -48,6 +48,7 @@ 'HasProjects', 'HasWiki', 'HasDiscussions', + 'HasSponsorships', 'IsArchived', 'IsTemplate', 'IsFork', @@ -74,8 +75,7 @@ 'MergeCommitTitle', 'MergeCommitMessage', 'TemplateRepository', - 'ForkParent', - 'ForkSource', + 'ForkRepository', 'CustomProperties', 'CloneUrl', 'SshUrl', diff --git a/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 b/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 index e6a901650..9ca9b6db8 100644 --- a/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 +++ b/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 @@ -51,6 +51,7 @@ 'HasProjects', 'HasWiki', 'HasDiscussions', + 'HasSponsorships', 'IsArchived', 'IsTemplate', 'IsFork', @@ -77,8 +78,7 @@ 'MergeCommitTitle', 'MergeCommitMessage', 'TemplateRepository', - 'ForkParent', - 'ForkSource', + 'ForkRepository', 'CustomProperties', 'CloneUrl', 'SshUrl', diff --git a/src/functions/public/API/Invoke-GitHubAPI.ps1 b/src/functions/public/API/Invoke-GitHubAPI.ps1 index b18d5af1b..a4c4f7a53 100644 --- a/src/functions/public/API/Invoke-GitHubAPI.ps1 +++ b/src/functions/public/API/Invoke-GitHubAPI.ps1 @@ -107,12 +107,15 @@ filter Invoke-GitHubAPI { begin { $stackPath = Get-PSCallStackPath Write-Debug "[$stackPath] - Start" + $debug = $DebugPreference -eq 'Continue' $Context = Resolve-GitHubContext -Context $Context - Write-Debug 'Invoking GitHub API...' - Write-Debug 'Parent function parameters:' - Get-FunctionParameter -Scope 1 | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } - Write-Debug 'Parameters:' - Get-FunctionParameter | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + if ($debug) { + Write-Debug 'Invoking GitHub API...' + Write-Debug 'Parent function parameters:' + Get-FunctionParameter -Scope 1 | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug 'Parameters:' + Get-FunctionParameter | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } } process { @@ -124,13 +127,15 @@ filter Invoke-GitHubAPI { $RetryCount = Resolve-GitHubContextSetting -Name 'RetryCount' -Value $RetryCount -Context $Context $RetryInterval = Resolve-GitHubContextSetting -Name 'RetryInterval' -Value $RetryInterval -Context $Context $TokenType = Resolve-GitHubContextSetting -Name 'TokenType' -Value $TokenType -Context $Context - [pscustomobject]@{ - Token = $Token - HttpVersion = $HttpVersion - ApiBaseUri = $ApiBaseUri - ApiVersion = $ApiVersion - TokenType = $TokenType - } | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + if ($debug) { + [pscustomobject]@{ + Token = $Token + HttpVersion = $HttpVersion + ApiBaseUri = $ApiBaseUri + ApiVersion = $ApiVersion + TokenType = $TokenType + } | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } $jwt = $null switch ($TokenType) { 'ghu' { @@ -190,10 +195,19 @@ filter Invoke-GitHubAPI { } try { - Write-Debug '----------------------------------' - Write-Debug 'Request:' - [pscustomobject]$APICall | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } - Write-Debug '----------------------------------' + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Request:' + [pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | + Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + Write-Debug 'Request headers:' + [pscustomobject]$APICall.Headers | Select-Object * | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + Write-Debug 'Request body:' + ($APICall.Body | Out-String).Split('\n') -split '\n' | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + } do { switch ($TokenType) { 'ghu' { @@ -209,7 +223,7 @@ filter Invoke-GitHubAPI { } } } - $response = Invoke-WebRequest @APICall -ProgressAction 'SilentlyContinue' + $response = Invoke-WebRequest @APICall -ProgressAction 'SilentlyContinue' -Debug:$false -Verbose:$false $headers = @{} foreach ($item in $response.Headers.GetEnumerator()) { @@ -233,13 +247,15 @@ filter Invoke-GitHubAPI { ($headers.'github-authentication-token-expiration').Replace('UTC', '').Trim() ).ToLocalTime().ToString('s') } - Write-Debug '----------------------------------' - Write-Debug 'Response headers:' - $headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } - Write-Debug '---------------------------' - Write-Debug 'Response:' - $response | Out-String -Stream | ForEach-Object { Write-Debug $_ } - Write-Debug '---------------------------' + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Response:' + $response | Select-Object -ExcludeProperty Content, Headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + Write-Debug 'Response headers:' + $headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + } switch -Regex ($headers.'Content-Type') { 'application/.*json' { $results = $response.Content | ConvertFrom-Json @@ -253,6 +269,19 @@ filter Invoke-GitHubAPI { } } + if ($debug) { + Write-Debug 'Response content:' + $results | ConvertTo-Json -Depth 5 -WarningAction SilentlyContinue | Out-String -Stream | ForEach-Object { + $content = $_ + $content = $content -split '\n' + $content = $content.Split('\n') + foreach ($item in $content) { + Write-Debug $item + } + } + Write-Debug '---------------------------' + } + [pscustomobject]@{ Request = $APICall Response = $results @@ -264,7 +293,12 @@ filter Invoke-GitHubAPI { } while ($APICall['Uri']) } catch { $failure = $_ - $failure | Out-String -Stream | ForEach-Object { Write-Debug $_ } + if ($debug) { + Write-Debug '----------------------------------' + Write-Debug 'Failure:' + $failure | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '----------------------------------' + } $headers = @{} foreach ($item in $failure.Exception.Response.Headers.GetEnumerator()) { $headers[$item.Key] = ($item.Value).Trim() -join ', ' @@ -287,9 +321,11 @@ filter Invoke-GitHubAPI { } $sortedProperties = $headers.PSObject.Properties.Name | Sort-Object $headers = $headers | Select-Object $sortedProperties - Write-Debug 'Response headers:' - $headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } - Write-Debug '---------------------------' + if ($debug) { + Write-Debug 'Response headers:' + $headers | Out-String -Stream | ForEach-Object { Write-Debug $_ } + Write-Debug '---------------------------' + } $errordetails = $failure.ErrorDetails | ConvertFrom-Json -AsHashtable $errors = $errordetails.errors @@ -307,9 +343,15 @@ filter Invoke-GitHubAPI { $exception = @" ---------------------------------- Request: -$([pscustomobject]$APICall | Format-List -Property Headers, HttpVersion, Method, Uri, ContentType, Authentication, Token | Out-String) +$([pscustomobject]$APICall | Select-Object -ExcludeProperty Body, Headers | Format-List | Out-String) +---------------------------------- +Request headers: +$([pscustomobject]$APICall.Headers | Format-List | Out-String) +---------------------------------- +Request body: +$(($APICall.Body | Out-String -Stream).Split('\n') -split '\n') ---------------------------------- -Response Headers: +Response headers: $($headers | Format-List | Out-String) ---------------------------------- Error: diff --git a/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 b/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 index a02a23f92..ab45e14e0 100644 --- a/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 +++ b/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 @@ -50,23 +50,23 @@ try { $apiResponse = Invoke-GitHubAPI @inputObject $graphQLResponse = $apiResponse.Response - # Handle GraphQL-specific errors (200 OK with errors in response) if ($graphQLResponse.errors) { - $queryLines = $Query -split "`n" $errorMessages = @() - $graphQLResponse.errors | ForEach-Object { - $lineNum = $_.locations.line - $lineText = if ($lineNum -and ($lineNum -le $queryLines.Count)) { $queryLines[$lineNum - 1].Trim() } else { '' } + $queryLines = $Query -split "`n" | ForEach-Object { $_.Trim() } + foreach ($errorItem in $graphQLResponse.errors) { $errorMessages += @" -GraphQL Error [$($_.type)]: -Message: $($_.message) -Path: $($_.path -join '/') -Location: $($_.locations.line):$($_.locations.column) -Query Line: $lineText -Extensions: $($_.extensions | Out-String) +GraphQL Error [$($errorItem.type)]: +Message: $($errorItem.message) +Path: $($errorItem.path -join '/') +Locations: +$($errorItem.locations | ForEach-Object { " - [$($_.line):$($_.column)] - $($queryLines[$_.line - 1])" }) + +Full Error: +$($errorItem | ConvertTo-Json -Depth 10 | Out-String -Stream) "@ + } $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( diff --git a/src/functions/public/Environments/Set-GitHubEnvironment.ps1 b/src/functions/public/Environments/Set-GitHubEnvironment.ps1 index 1a32722ac..7298d3847 100644 --- a/src/functions/public/Environments/Set-GitHubEnvironment.ps1 +++ b/src/functions/public/Environments/Set-GitHubEnvironment.ps1 @@ -182,7 +182,9 @@ filter Set-GitHubEnvironment { if ($PSCmdlet.ShouldProcess("Environment [$Owner/$Repository/$Name]", 'Set')) { Invoke-GitHubAPI @inputObject | ForEach-Object { - [GitHubEnvironment]::new($_.Response, $Owner, $Repository) + $environment = [GitHubEnvironment]::new($_.Response, $Owner, $Repository) + $environment.Url = "https://$($Context.HostName)/$Owner/$Repository/settings/environments/$($environment.ID)/edit" + $environment } } } diff --git a/src/functions/public/Organization/completers.ps1 b/src/functions/public/Organization/completers.ps1 index 45a42178d..337740981 100644 --- a/src/functions/public/Organization/completers.ps1 +++ b/src/functions/public/Organization/completers.ps1 @@ -14,3 +14,11 @@ Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } + +Register-ArgumentCompleter -CommandName ($script:PSModuleInfo.FunctionsToExport) -ParameterName Organization -ScriptBlock { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $null = $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters + (Get-GitHubOrganization).Name | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } +} diff --git a/src/functions/public/Repositories/Get-GitHubRepository.ps1 b/src/functions/public/Repositories/Get-GitHubRepository.ps1 index 7bea5c481..54d248b2d 100644 --- a/src/functions/public/Repositories/Get-GitHubRepository.ps1 +++ b/src/functions/public/Repositories/Get-GitHubRepository.ps1 @@ -99,8 +99,10 @@ AdditionalProperty = $AdditionalProperty } $params | Remove-HashtableEntry -NullOrEmptyValues - Write-Debug "ParamSet: [$($PSCmdlet.ParameterSetName)]" - Write-Debug ($params | Format-Table -AutoSize | Out-String) + if ($DebugPreference -eq 'Continue') { + Write-Debug "ParamSet: [$($PSCmdlet.ParameterSetName)]" + [pscustomobject]$params | Format-List | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } switch ($PSCmdlet.ParameterSetName) { 'Get a repository for the authenticated user by name' { try { diff --git a/src/functions/public/Repositories/New-GitHubRepository.ps1 b/src/functions/public/Repositories/New-GitHubRepository.ps1 index 736402d3f..65f150540 100644 --- a/src/functions/public/Repositories/New-GitHubRepository.ps1 +++ b/src/functions/public/Repositories/New-GitHubRepository.ps1 @@ -97,62 +97,71 @@ 'PSShouldProcess', '', Scope = 'Function', Justification = 'This check is performed in the private functions.' )] - [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'user')] + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Create a repository for the authenticated user')] param( # The account owner of the repository. The name is not case sensitive. - [Parameter(ParameterSetName = 'org')] - [Parameter(ParameterSetName = 'fork')] - [Parameter(ParameterSetName = 'template')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository in an organization')] + [Parameter(Mandatory, ParameterSetName = 'Fork a repository to an organization')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to an organization')] [Alias('Owner')] [string] $Organization, # The name of the repository. - [Parameter(ParameterSetName = 'fork')] - [Parameter(ParameterSetName = 'template')] - [Parameter(Mandatory, ParameterSetName = 'user')] - [Parameter(Mandatory, ParameterSetName = 'org')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to a user')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to an organization')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository for the authenticated user')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository in an organization')] + [Parameter(ParameterSetName = 'Fork a repository to a user')] + [Parameter(ParameterSetName = 'Fork a repository to an organization')] [string] $Name, # The account owner of the template repository. The name is not case sensitive. - [Parameter(Mandatory, ParameterSetName = 'template')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to a user')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to an organization')] [string] $TemplateOwner, # The name of the template repository without the .git extension. The name is not case sensitive. - [Parameter(Mandatory, ParameterSetName = 'template')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to a user')] + [Parameter(Mandatory, ParameterSetName = 'Create a repository from a template to an organization')] [string] $TemplateRepository, # The account owner of the repository. The name is not case sensitive. - [Parameter(Mandatory, ParameterSetName = 'fork')] + [Parameter(Mandatory, ParameterSetName = 'Fork a repository to a user')] + [Parameter(Mandatory, ParameterSetName = 'Fork a repository to an organization')] [string] $ForkOwner, # The name of the repository without the .git extension. The name is not case sensitive. - [Parameter(Mandatory, ParameterSetName = 'fork')] + [Parameter(Mandatory, ParameterSetName = 'Fork a repository to a user')] + [Parameter(Mandatory, ParameterSetName = 'Fork a repository to an organization')] [string] $ForkRepository, # Include all branches from the source repository. - [Parameter(ParameterSetName = 'template')] - [Parameter(ParameterSetName = 'fork')] + [Parameter(ParameterSetName = 'Create a repository from a template to a user')] + [Parameter(ParameterSetName = 'Create a repository from a template to an organization')] + [Parameter(ParameterSetName = 'Fork a repository to a user')] + [Parameter(ParameterSetName = 'Fork a repository to an organization')] [switch] $IncludeAllBranches, # Pass true to create an initial commit with empty README. - [Parameter(ParameterSetName = 'user')] - [Parameter(ParameterSetName = 'org')] + [Parameter(ParameterSetName = 'Create a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Create a repository in an organization')] [switch] $AddReadme, # The desired language or platform to apply to the .gitignore. - [Parameter(ParameterSetName = 'user')] - [Parameter(ParameterSetName = 'org')] + [Parameter(ParameterSetName = 'Create a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Create a repository in an organization')] [string] $Gitignore, # The license keyword of the open source license for this repository. - [Parameter(ParameterSetName = 'user')] - [Parameter(ParameterSetName = 'org')] + [Parameter(ParameterSetName = 'Create a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Create a repository in an organization')] [string] $License, # The visibility of the repository. - [Parameter(ParameterSetName = 'user')] - [Parameter(ParameterSetName = 'org')] - [Parameter(ParameterSetName = 'template')] + [Parameter(ParameterSetName = 'Create a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Create a repository in an organization')] + [Parameter(ParameterSetName = 'Create a repository from a template to an organization')] + [Parameter(ParameterSetName = 'Create a repository from a template to a user')] [ValidateSet('Public', 'Private', 'Internal')] [string] $Visibility = 'Public', @@ -189,7 +198,9 @@ [System.Nullable[bool]] $HasIssues, # Either true to allow private forks, or false to prevent private forks. - [Parameter()] + [Parameter(ParameterSetName = 'Fork a repository to an organization')] + [Parameter(ParameterSetName = 'Create a repository from a template to an organization')] + [Parameter(ParameterSetName = 'Create a repository in an organization')] [System.Nullable[bool]] $AllowForking, # Whether sponsorships are enabled. @@ -216,19 +227,19 @@ # Whether to allow rebase merges for pull requests. [Parameter()] - [switch] $AllowRebaseMerging, + [System.Nullable[bool]] $AllowRebaseMerging, # Whether to always suggest to update a head branch that is behind its base branch during a pull request. [Parameter()] - [System.Nullable[switch]] $SuggestUpdateBranch, + [System.Nullable[bool]] $SuggestUpdateBranch, # Whether to allow Auto-merge to be used on pull requests. [Parameter()] - [System.Nullable[switch]] $AllowAutoMerge, + [System.Nullable[bool]] $AllowAutoMerge, # Whether to delete head branches when pull requests are merged [Parameter()] - [System.Nullable[switch]] $DeleteBranchOnMerge, + [System.Nullable[bool]] $DeleteBranchOnMerge, # Whether to enable GitHub Advanced Security for this repository. [Parameter()] @@ -270,7 +281,7 @@ process { Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)" $repo = switch ($PSCmdlet.ParameterSetName) { - 'user' { + 'Create a repository for the authenticated user' { $params = @{ Context = $Context Name = $Name @@ -282,7 +293,7 @@ $params | Remove-HashtableEntry -NullOrEmptyValues New-GitHubRepositoryUser @params } - 'org' { + 'Create a repository in an organization' { $params = @{ Context = $Context Organization = $Organization @@ -295,7 +306,19 @@ $params | Remove-HashtableEntry -NullOrEmptyValues New-GitHubRepositoryOrg @params } - 'template' { + 'Create a repository from a template to a user' { + $params = @{ + Context = $Context + TemplateOwner = $TemplateOwner + TemplateRepository = $TemplateRepository + Name = $Name + IncludeAllBranches = $IncludeAllBranches + Visibility = $Visibility + } + $params | Remove-HashtableEntry -NullOrEmptyValues + New-GitHubRepositoryFromTemplate @params + } + 'Create a repository from a template to an organization' { $params = @{ Context = $Context TemplateOwner = $TemplateOwner @@ -308,7 +331,18 @@ $params | Remove-HashtableEntry -NullOrEmptyValues New-GitHubRepositoryFromTemplate @params } - 'fork' { + 'Fork a repository to a user' { + $params = @{ + Context = $Context + ForkOwner = $ForkOwner + ForkRepository = $ForkRepository + Name = $Name + IncludeAllBranches = $IncludeAllBranches + } + $params | Remove-HashtableEntry -NullOrEmptyValues + New-GitHubRepositoryAsFork @params + } + 'Fork a repository to an organization' { $params = @{ Context = $Context ForkOwner = $ForkOwner @@ -322,13 +356,15 @@ } } - - Write-Debug 'New repo created' - Write-Debug "$($repo | Format-Table | Out-String)" + if ($VerbosePreference -eq 'Continue') { + Write-Verbose 'New repo created' + $repo | Select-Object * | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + } $updateParams = @{ Owner = $repo.Owner Name = $repo.Name + Context = $Context Visibility = $Visibility Description = $Description Homepage = $Homepage @@ -338,13 +374,13 @@ DefaultBranch = $DefaultBranch HasWiki = $HasWiki HasIssues = $HasIssues - AllowForking = $AllowForking HasSponsorships = $HasSponsorships HasDiscussions = $HasDiscussions HasProjects = $HasProjects AllowMergeCommitWith = $AllowMergeCommitWith AllowSquashMergingWith = $AllowSquashMergingWith AllowRebaseMerging = $AllowRebaseMerging + AllowForking = $AllowForking SuggestUpdateBranch = $SuggestUpdateBranch AllowAutoMerge = $AllowAutoMerge DeleteBranchOnMerge = $DeleteBranchOnMerge @@ -355,7 +391,15 @@ EnableSecretScanningAIDetection = $EnableSecretScanningAIDetection EnableSecretScanningNonProviderPatterns = $EnableSecretScanningNonProviderPatterns } - Update-GitHubRepository @updateParams + $updatedRepo = Update-GitHubRepository @updateParams + + if ($VerbosePreference -eq 'Continue') { + Write-Verbose 'Updated repo' + $updatedRepo | Select-Object * | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + } + + $updatedRepo.DefaultBranch = $repo.DefaultBranch + $updatedRepo } end { diff --git a/src/functions/public/Repositories/Set-GitHubRepository.ps1 b/src/functions/public/Repositories/Set-GitHubRepository.ps1 new file mode 100644 index 000000000..80df95352 --- /dev/null +++ b/src/functions/public/Repositories/Set-GitHubRepository.ps1 @@ -0,0 +1,293 @@ +function Set-GitHubRepository { + <# + .SYNOPSIS + Creates or updates a repository. + + .DESCRIPTION + Checks if the specified repository exists. If it does, the repository is updated using + the provided parameters. If it does not exist, a new repository is created with the + provided parameters. The updated or newly created repository is returned. + + .EXAMPLE + Set-GitHubRepository -Name 'Hello-World' -Description 'My repo' + + Sets the repository `Hello-World` for the authenticated user if it does not exist, + or updates it if it already exists. The repository uses GitHub's default settings. + + .EXAMPLE + $params = @{ + Owner = 'octocat' + Name = 'Hello-World' + AllowSquashMergingWith = 'Pull request title and description' + HasIssues = $true + SuggestUpdateBranch = $true + AllowAutoMerge = $true + DeleteBranchOnMerge = $true + } + Set-GitHubRepository @params + + Sets a repository using splatting for the configuration. + + .OUTPUTS + GitHubRepository + + .LINK + https://psmodule.io/GitHub/Functions/Repositories/Set-GitHubRepository/ + #> + [OutputType([GitHubRepository])] + [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Set a repository for the authenticated user')] + param( + # The account owner of the repository. The name is not case sensitive. + [Parameter(Mandatory, ParameterSetName = 'Set a repository in an organization')] + [Parameter(Mandatory, ParameterSetName = 'Set a forked repository in an organization')] + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to an organization')] + [Alias('Owner')] + [string] $Organization, + + # The name of the repository. + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to a user')] + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to an organization')] + [Parameter(Mandatory, ParameterSetName = 'Set a repository for the authenticated user')] + [Parameter(Mandatory, ParameterSetName = 'Set a repository in an organization')] + [Parameter(ParameterSetName = 'Set a forked repository for a user')] + [Parameter(ParameterSetName = 'Set a forked repository in an organization')] + [string] $Name, + + # The account owner of the template repository. The name is not case sensitive. + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to a user')] + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to an organization')] + [string] $TemplateOwner, + + # The name of the template repository without the .git extension. The name is not case sensitive. + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to a user')] + [Parameter(Mandatory, ParameterSetName = 'Set a repository from a template to an organization')] + [string] $TemplateRepository, + + # The account owner of the repository. The name is not case sensitive. + [Parameter(Mandatory, ParameterSetName = 'Set a forked repository for a user')] + [Parameter(Mandatory, ParameterSetName = 'Set a forked repository in an organization')] + [string] $ForkOwner, + + # The name of the repository without the .git extension. The name is not case sensitive. + [Parameter(Mandatory, ParameterSetName = 'Set a forked repository for a user')] + [Parameter(Mandatory, ParameterSetName = 'Set a forked repository in an organization')] + [string] $ForkRepository, + + # Include all branches from the source repository. + [Parameter(ParameterSetName = 'Set a repository from a template to a user')] + [Parameter(ParameterSetName = 'Set a repository from a template to an organization')] + [Parameter(ParameterSetName = 'Set a forked repository for a user')] + [Parameter(ParameterSetName = 'Set a forked repository in an organization')] + [switch] $IncludeAllBranches, + + # Pass true to Set an initial commit with empty README. + [Parameter(ParameterSetName = 'Set a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Set a repository in an organization')] + [switch] $AddReadme, + + # The desired language or platform to apply to the .gitignore. + [Parameter(ParameterSetName = 'Set a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Set a repository in an organization')] + [string] $Gitignore, + + # The license keyword of the open source license for this repository. + [Parameter(ParameterSetName = 'Set a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Set a repository in an organization')] + [string] $License, + + # The visibility of the repository. + [Parameter(ParameterSetName = 'Set a repository from a template to a user')] + [Parameter(ParameterSetName = 'Set a repository from a template to an organization')] + [Parameter(ParameterSetName = 'Set a repository for the authenticated user')] + [Parameter(ParameterSetName = 'Set a repository in an organization')] + [ValidateSet('Public', 'Private', 'Internal')] + [string] $Visibility = 'Public', + + # A short description of the new repository. + [Parameter()] + [string] $Description, + + # A URL with more information about the repository. + [Parameter()] + [uri] $Homepage, + + # Whether the repository is archived. + [Parameter()] + [System.Nullable[bool]] $IsArchived, + + # Whether this repository acts as a template that can be used to generate new repositories. + [Parameter()] + [System.Nullable[bool]] $IsTemplate, + + # Whether to require contributors to sign off on web-based commits. + [Parameter()] + [System.Nullable[bool]] $WebCommitSignoffRequired, + + # Updates the default branch for this repository. + [Parameter()] + [string] $DefaultBranch, + + # Whether the wiki is enabled. + [Parameter()] + [System.Nullable[bool]] $HasWiki, + + # Whether issues are enabled. + [Parameter()] + [System.Nullable[bool]] $HasIssues, + + # Either true to allow private forks, or false to prevent private forks. + [Parameter(ParameterSetName = 'Set a forked repository in an organization')] + [Parameter(ParameterSetName = 'Set a repository from a template to an organization')] + [Parameter(ParameterSetName = 'Set a repository in an organization')] + [System.Nullable[bool]] $AllowForking, + + # Whether sponsorships are enabled. + [Parameter()] + [System.Nullable[bool]] $HasSponsorships, + + # Whether discussions are enabled. + [Parameter()] + [System.Nullable[bool]] $HasDiscussions, + + # Whether projects are enabled. + [Parameter()] + [System.Nullable[bool]] $HasProjects, + + # Allow merge commits for pull requests with the specified setting. + [Parameter()] + [ValidateSet('', 'Default message', 'Pull request title', 'Pull request title and description')] + [string] $AllowMergeCommitWith = 'Default message', + + # Allow squash merges for pull requests with the specified setting. + [Parameter()] + [ValidateSet('', 'Default message', 'Pull request title', 'Pull request title and description', 'Pull request title and commit details')] + [string] $AllowSquashMergingWith = 'Default message', + + # Whether to allow rebase merges for pull requests. + [Parameter()] + [System.Nullable[bool]] $AllowRebaseMerging, + + # Whether to always suggest to update a head branch that is behind its base branch during a pull request. + [Parameter()] + [System.Nullable[bool]] $SuggestUpdateBranch, + + # Whether to allow Auto-merge to be used on pull requests. + [Parameter()] + [System.Nullable[bool]] $AllowAutoMerge, + + # Whether to delete head branches when pull requests are merged + [Parameter()] + [System.Nullable[bool]] $DeleteBranchOnMerge, + + # Whether to enable GitHub Advanced Security for this repository. + [Parameter()] + [System.Nullable[bool]] $EnableAdvancedSecurity, + + # Whether to enable code security for this repository. + [Parameter()] + [System.Nullable[bool]] $EnableCodeSecurity, + + # Whether to enable secret scanning for this repository. + [Parameter()] + [System.Nullable[bool]] $EnableSecretScanning, + + # Whether to enable secret scanning push protection for this repository. + [Parameter()] + [System.Nullable[bool]] $EnableSecretScanningPushProtection, + + # Whether to enable secret scanning AI detection for this repository. + [Parameter()] + [System.Nullable[bool]] $EnableSecretScanningAIDetection, + + # Whether to enable secret scanning non-provider patterns for this repository. + [Parameter()] + [System.Nullable[bool]] $EnableSecretScanningNonProviderPatterns, + + # The context to run the command in. Used to get the details for the API call. + # Can be either a string or a GitHubContext object. + [Parameter()] + [object] $Context = (Get-GitHubContext) + ) + begin { + $stackPath = Get-PSCallStackPath + Write-Debug "[$stackPath] - Start" + $Context = Resolve-GitHubContext -Context $Context + Assert-GitHubContext -Context $Context -AuthType IAT, PAT, UAT + } + + process { + $getParams = @{ + Owner = $Organization + Name = $Name + Context = $Context + } + $getParams | Remove-HashtableEntry -NullOrEmptyValues + $repo = Get-GitHubRepository @getParams -ErrorAction Stop + + $configParams = @{ + Description = $Description + Homepage = $Homepage + IsArchived = $IsArchived + IsTemplate = $IsTemplate + WebCommitSignoffRequired = $WebCommitSignoffRequired + DefaultBranch = $DefaultBranch + HasWiki = $HasWiki + HasIssues = $HasIssues + AllowForking = $AllowForking + HasSponsorships = $HasSponsorships + HasDiscussions = $HasDiscussions + HasProjects = $HasProjects + AllowMergeCommitWith = $AllowMergeCommitWith + AllowSquashMergingWith = $AllowSquashMergingWith + AllowRebaseMerging = $AllowRebaseMerging + SuggestUpdateBranch = $SuggestUpdateBranch + AllowAutoMerge = $AllowAutoMerge + DeleteBranchOnMerge = $DeleteBranchOnMerge + EnableAdvancedSecurity = $EnableAdvancedSecurity + EnableCodeSecurity = $EnableCodeSecurity + EnableSecretScanning = $EnableSecretScanning + EnableSecretScanningPushProtection = $EnableSecretScanningPushProtection + EnableSecretScanningAIDetection = $EnableSecretScanningAIDetection + EnableSecretScanningNonProviderPatterns = $EnableSecretScanningNonProviderPatterns + } + if ($PSCmdlet.ParameterSetName -notlike '*fork*') { + $configParams['Visibility'] = $Visibility + } + $configParams | Remove-HashtableEntry -NullOrEmptyValues + + if ($repo) { + $updateParams = @{ + Owner = $repo.Owner + Name = $repo.Name + Context = $Context + } + Update-GitHubRepository @updateParams @configParams -ErrorAction Stop + } else { + $newParams = @{ + Organization = $Organization + Name = $Name + TemplateOwner = $TemplateOwner + TemplateRepository = $TemplateRepository + ForkOwner = $ForkOwner + ForkRepository = $ForkRepository + IncludeAllBranches = $PSBoundParameters.ContainsKey('IncludeAllBranches') ? $IncludeAllBranches : $null + AddReadme = $PSBoundParameters.ContainsKey('AddReadme') ? $AddReadme : $null + Gitignore = $Gitignore + License = $License + Context = $Context + } + $newParams | Remove-HashtableEntry -NullOrEmptyValues + if ($VerbosePreference -eq 'Continue') { + Write-Verbose 'New repo params:' + $newParams | Select-Object * | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + Write-Verbose 'Config params:' + $configParams | Select-Object * | Format-List | Out-String -Stream | ForEach-Object { Write-Verbose $_ } + } + New-GitHubRepository @newParams @configParams -ErrorAction Stop + } + } + + end { + Write-Debug "[$stackPath] - End" + } +} diff --git a/src/functions/public/Repositories/Update-GitHubRepository.ps1 b/src/functions/public/Repositories/Update-GitHubRepository.ps1 index 02da569f4..7eec5d611 100644 --- a/src/functions/public/Repositories/Update-GitHubRepository.ps1 +++ b/src/functions/public/Repositories/Update-GitHubRepository.ps1 @@ -150,11 +150,6 @@ [Parameter()] [System.Nullable[bool]] $EnableSecretScanningNonProviderPatterns, - # Takes all parameters and updates the repository with the provided _AND_ the default values of the non-provided parameters. - # Used for Set-GitHubRepository. - [Parameter()] - [switch] $Declare, - # The context to run the command in. Used to get the details for the API call. # Can be either a string or a GitHubContext object. [Parameter()] @@ -279,12 +274,12 @@ } : $null } - if (-not $Declare) { - $body | Remove-HashtableEntry -NullOrEmptyValues - } + $body | Remove-HashtableEntry -NullOrEmptyValues - Write-Debug 'Changed settings for REST call is:' - Write-Debug "$($body | Out-String)" + if ($DebugPreference -eq 'Continue') { + Write-Debug 'Changed settings for REST call is:' + [pscustomobject]$body | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } if ($body.Keys.Count -gt 0) { $inputObject = @{ Method = 'PATCH' @@ -296,8 +291,10 @@ if ($PSCmdlet.ShouldProcess("Repository [$Owner/$Name]", 'Update')) { $updatedRepo = Invoke-GitHubAPI @inputObject | Select-Object -ExpandProperty Response } - Write-Debug 'Repo has been updated' - Write-Debug "$($updatedRepo | Select-Object * | Out-String)" + if ($DebugPreference -eq 'Continue') { + Write-Debug 'Repo has been updated' + $updatedRepo | Select-Object * | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } } else { Write-Debug 'No changes made to repo via REST' } @@ -308,8 +305,10 @@ } $inputParams | Remove-HashtableEntry -NullOrEmptyValues - Write-Debug 'Changed settings for GraphQL call is:' - Write-Debug "$($inputParams | Out-String)" + if ($DebugPreference -eq 'Continue') { + Write-Debug 'Changed settings for GraphQL call is:' + $inputParams | Out-String -Stream | ForEach-Object { Write-Debug $_ } + } if ($inputParams.Keys.Count -gt 0) { $inputParams += @{ repositoryId = $repo.NodeID @@ -322,9 +321,6 @@ repository { id name - owner { - login - } hasSponsorshipsEnabled hasDiscussionsEnabled } @@ -335,8 +331,7 @@ input = $inputParams } } - - Invoke-GitHubGraphQLQuery @updateGraphQLInputs + $null = Invoke-GitHubGraphQLQuery @updateGraphQLInputs } else { Write-Debug 'No changes made to repo via GraphQL' } diff --git a/tests/README.md b/tests/README.md index af8b2013c..dba4a4440 100644 --- a/tests/README.md +++ b/tests/README.md @@ -12,6 +12,7 @@ Secrets: - TEST_USER_USER_FG_PAT -> 'psmodule-user' (user) - TEST_USER_ORG_FG_PAT -> 'psmodule-test-org2' (org) + ## APP_ENT - PSModule Enterprise App Homed in 'MSX' diff --git a/tests/Repositories.Tests.ps1 b/tests/Repositories.Tests.ps1 index b22975cb7..fc2212ae5 100644 --- a/tests/Repositories.Tests.ps1 +++ b/tests/Repositories.Tests.ps1 @@ -67,54 +67,213 @@ Describe 'Repositories' { It 'New-GitHubRepository - Creates a new repository' -Skip:($OwnerType -eq 'repository') { LogGroup 'Repository - Creation' { - switch ($OwnerType) { + $params = @{ + Name = $repoName + HasWiki = $false + HasIssues = $false + } + $repo = switch ($OwnerType) { + 'user' { + New-GitHubRepository @params + } + 'organization' { + New-GitHubRepository -Organization $owner @params + } + } + Write-Host ($repo | Format-List | Out-String) + $repo | Should -BeOfType 'GitHubRepository' + $repo | Should -Not -BeNullOrEmpty + $repo.Name | Should -Be $repoName + $repo.FullName | Should -Be "$owner/$repoName" + $repo.Owner | Should -Be $owner + $repo.DatabaseID | Should -Not -BeNullOrEmpty + $repo.ID | Should -Not -BeNullOrEmpty + $repo.Url | Should -Not -BeNullOrEmpty + $repo.CloneUrl | Should -Not -BeNullOrEmpty + $repo.SshUrl | Should -Not -BeNullOrEmpty + $repo.GitUrl | Should -Not -BeNullOrEmpty + $repo.CreatedAt | Should -Not -BeNullOrEmpty + $repo.UpdatedAt | Should -Not -BeNullOrEmpty + $repo.IsTemplate | Should -Be $false + $repo.IsFork | Should -Be $false + $repo.TemplateRepository | Should -BeNullOrEmpty + $repo.Forks | Should -Be 0 + $repo.Stargazers | Should -Be 0 + $repo.Watchers | Should -Be 0 + $repo.Language | Should -BeNullOrEmpty + $repo.ForkRepository | Should -BeNullOrEmpty + $repo.Visibility | Should -Be 'Public' + $repo.DefaultBranch | Should -Be 'main' + $repo.HasIssues | Should -Be $false + $repo.HasProjects | Should -Be $true + $repo.HasWiki | Should -Be $false + $repo.IsArchived | Should -Be $false + } + } + It 'New-GitHubRepository - Creates a new repository with settings' -Skip:($OwnerType -eq 'repository') { + $name = "$repoName-settings" + LogGroup 'Repository - Creation + Settings' { + $params = @{ + Name = $name + Description = 'Test repository with settings' + HasIssues = $false + HasProjects = $false + HasWiki = $false + HasDiscussions = $true + HasSponsorships = $true + AllowSquashMergingWith = 'Pull request title and description' + SuggestUpdateBranch = $true + DeleteBranchOnMerge = $true + AllowAutoMerge = $true + IsTemplate = $true + AddReadme = $true + License = 'mit' + Visibility = 'Private' + Gitignore = 'VisualStudio' + Homepage = 'https://example.com' + } + $repo = switch ($OwnerType) { 'user' { - $repo = New-GitHubRepository -Name $repoName -Debug + New-GitHubRepository @params } 'organization' { - $repo = New-GitHubRepository -Organization $owner -Name $repoName -Debug + New-GitHubRepository -Organization $owner @params } } Write-Host ($repo | Format-List | Out-String) + $repo | Should -BeOfType 'GitHubRepository' + $repo | Should -Not -BeNullOrEmpty + $repo.Name | Should -Be $name + $repo.FullName | Should -Be "$owner/$name" + $repo.Owner | Should -Be $owner + $repo.DatabaseID | Should -Not -BeNullOrEmpty + $repo.ID | Should -Not -BeNullOrEmpty + $repo.Description | Should -Be 'Test repository with settings' + $repo.Url | Should -Not -BeNullOrEmpty + $repo.CloneUrl | Should -Not -BeNullOrEmpty + $repo.SshUrl | Should -Not -BeNullOrEmpty + $repo.GitUrl | Should -Not -BeNullOrEmpty + $repo.CreatedAt | Should -Not -BeNullOrEmpty + $repo.UpdatedAt | Should -Not -BeNullOrEmpty + $repo.IsTemplate | Should -Be $true + $repo.IsFork | Should -Be $false + $repo.TemplateRepository | Should -BeNullOrEmpty + $repo.Forks | Should -Be 0 + $repo.Stargazers | Should -Be 0 + $repo.Watchers | Should -Be 0 + $repo.Language | Should -BeNullOrEmpty + $repo.ForkRepository | Should -BeNullOrEmpty + $repo.Visibility | Should -Be 'Private' + $repo.DefaultBranch | Should -Be 'main' + $repo.HasIssues | Should -Be $false + $repo.HasWiki | Should -Be $false + $repo.HasProjects | Should -Be $false + $repo.HasDiscussions | Should -Be $true + $repo.HasSponsorships | Should -Be $true + $repo.IsArchived | Should -Be $false + $repo.SuggestUpdateBranch | Should -Be $true + $repo.DeleteBranchOnMerge | Should -Be $true + # $repo.AllowAutoMerge | Should -Be $true + $repo.License | Should -Be 'MIT License' + $repo.Homepage | Should -Be 'https://example.com' } } It 'New-GitHubRepository - Creates a new repository from a template' -Skip:($OwnerType -eq 'repository') { + $name = "$repoName-template" LogGroup 'Repository - Template' { $params = @{ - Name = "$repoName-tmp" + Name = $name TemplateOwner = 'PSModule' TemplateRepository = 'Template-Action' + HasWiki = $false + HasIssues = $false } - switch ($OwnerType) { + $repo = switch ($OwnerType) { 'user' { - $repo = New-GitHubRepository @params + New-GitHubRepository @params } 'organization' { - $repo = New-GitHubRepository @params -Organization $owner + New-GitHubRepository @params -Organization $owner } } Write-Host ($repo | Format-List | Out-String) + $repo | Should -BeOfType 'GitHubRepository' + $repo | Should -Not -BeNullOrEmpty + $repo.Name | Should -Be $name + $repo.FullName | Should -Be "$owner/$name" + $repo.Owner | Should -Be $owner + $repo.DatabaseID | Should -Not -BeNullOrEmpty + $repo.ID | Should -Not -BeNullOrEmpty + $repo.Url | Should -Not -BeNullOrEmpty + $repo.CloneUrl | Should -Not -BeNullOrEmpty + $repo.SshUrl | Should -Not -BeNullOrEmpty + $repo.GitUrl | Should -Not -BeNullOrEmpty + $repo.CreatedAt | Should -Not -BeNullOrEmpty + $repo.UpdatedAt | Should -Not -BeNullOrEmpty + $repo.IsTemplate | Should -Be $false + $repo.IsFork | Should -Be $false + $repo.Forks | Should -Be 0 + $repo.Stargazers | Should -Be 0 + $repo.Watchers | Should -Be 0 + # $repo.Language | Should -Be 'PowerShell' - Not always available on the object. + $repo.TemplateRepository | Should -Be 'Template-Action' + $repo.TemplateRepository.Owner | Should -Be 'PSModule' + $repo.ForkRepository | Should -BeNullOrEmpty + $repo.Visibility | Should -Be 'Public' + $repo.DefaultBranch | Should -Be 'main' + $repo.HasIssues | Should -Be $false + $repo.HasProjects | Should -Be $true + $repo.HasWiki | Should -Be $false + $repo.IsArchived | Should -Be $false } - $repo | Should -Not -BeNullOrEmpty } It 'New-GitHubRepository - Creates a new repository as a fork' -Skip:($OwnerType -eq 'repository') { + $name = "$repoName-fork" LogGroup 'Repository - Fork' { $params = @{ - Name = "$repoName-fork" - ForkOwner = 'PSModule' - ForkRepository = 'Template-Action' + Name = $name + ForkOwner = 'psmodule-test' + ForkRepository = "fork-$os" + HasWiki = $false + HasIssues = $false } - switch ($OwnerType) { + $repo = switch ($OwnerType) { 'user' { - $repo = New-GitHubRepository @params + New-GitHubRepository @params } 'organization' { - $repo = New-GitHubRepository @params -Organization $owner + New-GitHubRepository @params -Organization $owner } } Write-Host ($repo | Format-List | Out-String) + $repo | Should -BeOfType 'GitHubRepository' + $repo | Should -Not -BeNullOrEmpty + $repo.Name | Should -Be $name + $repo.FullName | Should -Be "$owner/$name" + $repo.Owner | Should -Be $owner + $repo.DatabaseID | Should -Not -BeNullOrEmpty + $repo.ID | Should -Not -BeNullOrEmpty + $repo.Url | Should -Not -BeNullOrEmpty + $repo.CloneUrl | Should -Not -BeNullOrEmpty + $repo.SshUrl | Should -Not -BeNullOrEmpty + $repo.GitUrl | Should -Not -BeNullOrEmpty + $repo.CreatedAt | Should -Not -BeNullOrEmpty + $repo.UpdatedAt | Should -Not -BeNullOrEmpty + $repo.IsTemplate | Should -Be $false + $repo.IsFork | Should -Be $true + $repo.Forks | Should -Be 0 + $repo.Stargazers | Should -Be 0 + $repo.Watchers | Should -Be 0 + $repo.Language | Should -BeNullOrEmpty + $repo.TemplateRepository | Should -BeNullOrEmpty + $repo.ForkRepository | Should -Be "fork-$os" + $repo.Visibility | Should -Be 'Public' + $repo.DefaultBranch | Should -Be 'main' + $repo.HasIssues | Should -Be $false + $repo.HasProjects | Should -Be $true + $repo.HasWiki | Should -Be $false + $repo.IsArchived | Should -Be $false } - $repo | Should -Not -BeNullOrEmpty } It "Get-GitHubRepository - Gets the authenticated user's repositories" -Skip:($OwnerType -ne 'user') { LogGroup 'Repositories' { @@ -226,21 +385,84 @@ Describe 'Repositories' { } $repos.Count | Should -BeGreaterThan 0 } + It 'Set-GitHubRepository - Updates an existing repository' -Skip:($OwnerType -eq 'repository') { + $description = 'Updated description' + LogGroup 'Repository - Set update' { + switch ($OwnerType) { + 'user' { + $repoBefore = Get-GitHubRepository -Name $repoName + $repo = Set-GitHubRepository -Name $repoName -Description $description + } + 'organization' { + $repoBefore = Get-GitHubRepository -Owner $owner -Name $repoName + $repo = Set-GitHubRepository -Owner $owner -Name $repoName -Description $description + } + } + Write-Host ($repo | Format-List | Out-String) + } + LogGroup 'Changed properties:' { + $changes = Compare-PSCustomObject -Left $repoBefore -Right $repo -OnlyChanged + Write-Host "$($changes | Format-Table | Out-String)" + $repo | Should -Not -BeNullOrEmpty + $repo.Description | Should -Be $description + $changedProps = $changes.Property + $changedProps | Should -Contain 'UpdatedAt' + $changedProps | Should -Contain 'Description' + $changedProps.Count | Should -Be 2 + } + } + It 'Set-GitHubRepository - Creates a new repository when missing' -Skip:($OwnerType -eq 'repository') { + $newRepoName = "$repoName-new" + LogGroup 'Repository - Set create' { + switch ($OwnerType) { + 'user' { + $repoBefore = Get-GitHubRepository -Name $newRepoName + $repo = Set-GitHubRepository -Name $newRepoName + } + 'organization' { + $repoBefore = Get-GitHubRepository -Owner $owner -Name $newRepoName + $repo = Set-GitHubRepository -Organization $owner -Name $newRepoName + } + } + Write-Host 'Repo before creation' + Write-Host ($repoBefore | Format-List | Out-String) + Write-Host 'Repo after creation' + Write-Host ($repo | Format-List | Out-String) + $repoBefore | Should -BeNullOrEmpty + $repo | Should -Not -BeNullOrEmpty + } + } It 'Update-GitHubRepository - Renames a repository' -Skip:($OwnerType -eq 'repository') { + $newName = "$repoName-newname" LogGroup 'Repository - Renamed' { - $newName = "$repoName-newname" switch ($OwnerType) { 'user' { - $updatedRepo = Update-GitHubRepository -Name $repoName -NewName $newName + $repoBefore = Get-GitHubRepository -Name $repoName + $repo = Update-GitHubRepository -Name $repoName -NewName $newName } 'organization' { - $updatedRepo = Update-GitHubRepository -Owner $owner -Name $repoName -NewName $newName + $repoBefore = Get-GitHubRepository -Owner $owner -Name $repoName + $repo = Update-GitHubRepository -Owner $owner -Name $repoName -NewName $newName } } - Write-Host ($updatedRepo | Format-List | Out-String) + Write-Host ($repo | Format-List | Out-String) + } + LogGroup 'Changed properties: ' { + $changes = Compare-PSCustomObject -Left $repoBefore -Right $repo -OnlyChanged + Write-Host "$($changes | Format-Table | Out-String)" + $repo | Should -Not -BeNullOrEmpty + $repo.Name | Should -Be $newName + $changedProps = $changes.Property + $changedProps | Should -Contain 'UpdatedAt' + $changedProps | Should -Contain 'Name' + $changedProps | Should -Contain 'FullName' + $changedProps | Should -Contain 'Url' + $changedProps | Should -Contain 'UpdatedAt' + $changedProps | Should -Contain 'CloneUrl' + $changedProps | Should -Contain 'SshUrl' + $changedProps | Should -Contain 'GitUrl' + $changedProps.Count | Should -Be 8 } - $updatedRepo | Should -Not -BeNullOrEmpty - $updatedRepo.Name | Should -Be $newName } It 'Remove-GitHubRepository - Removes all repositories' -Skip:($OwnerType -eq 'repository') { switch ($OwnerType) { @@ -254,15 +476,119 @@ Describe 'Repositories' { } } It 'Get-GitHubRepository - Gets none repositories after removal' -Skip:($OwnerType -eq 'repository') { - if ($OwnerType -eq 'user') { - $repos = Get-GitHubRepository -Username $Owner | Where-Object { $_.name -like "$repoName*" } - } else { - $repos = Get-GitHubRepository -Organization $Owner | Where-Object { $_.name -like "$repoName*" } + switch ($OwnerType) { + 'user' { + $repos = Get-GitHubRepository -Username $Owner | Where-Object { $_.name -like "$repoName*" } + } + default { + $repos = Get-GitHubRepository -Organization $Owner | Where-Object { $_.name -like "$repoName*" } + } } LogGroup 'Repositories' { Write-Host ($repos | Format-List | Out-String) + $repos | Should -BeNullOrEmpty + } + } + It 'Set-GitHubRepository - Creates and updates a repository from a template' -Skip:($OwnerType -eq 'repository') { + $templateRepoName = "$repoName-template" + $templateParams = @{ + Name = $templateRepoName + TemplateOwner = 'PSModule' + TemplateRepository = 'Template-Docs' + } + LogGroup 'Repository - Set create from template' { + switch ($OwnerType) { + 'user' { + $repo = Set-GitHubRepository @templateParams + } + 'organization' { + $repo = Set-GitHubRepository @templateParams -Organization $owner + } + } + Write-Host ($repo | Format-List | Out-String) + } + + $newDescription = 'Updated description for template repo' + LogGroup 'Repository - Set update from template' { + $changeParams = @{ + Description = $newDescription + HasIssues = $false + HasWiki = $false + } + switch ($OwnerType) { + 'user' { + $repoBefore = Get-GitHubRepository -Name $templateRepoName + $updatedRepo = Set-GitHubRepository -Name $templateRepoName @changeParams + } + 'organization' { + $repoBefore = Get-GitHubRepository -Owner $owner -Name $templateRepoName + $updatedRepo = Set-GitHubRepository -Organization $owner -Name $templateRepoName @changeParams + } + } + Write-Host ($updatedRepo | Format-List | Out-String) + } + LogGroup 'Changed properties:' { + $changes = Compare-PSCustomObject -Left $repoBefore -Right $updatedRepo -OnlyChanged + Write-Host "$($changes | Format-Table | Out-String)" + $updatedRepo | Should -Not -BeNullOrEmpty + $updatedRepo.Description | Should -Be $newDescription + $changedProps = $changes.Property + $changedProps | Should -Contain 'UpdatedAt' + $changedProps | Should -Contain 'Description' + $changedProps | Should -Contain 'HasIssues' + $changedProps | Should -Contain 'HasWiki' + $changedProps.Count | Should -Be 4 + } + } + It 'Set-GitHubRepository - Creates and updates a repository as a fork' -Skip:($OwnerType -eq 'repository') { + $name = "$repoName-fork3" + $forkParams = @{ + Name = $name + ForkOwner = 'psmodule-test' + ForkRepository = "fork-$os" + } + LogGroup 'Repository - Set create as fork' { + switch ($OwnerType) { + 'user' { + $repo = Set-GitHubRepository @forkParams + } + 'organization' { + $repo = Set-GitHubRepository @forkParams -Organization $owner + } + } + Write-Host ($repo | Format-List | Out-String) + } + + $newDescription = 'Updated description for forked repo' + LogGroup 'Repository - Set update as fork' { + $setParams = @{ + Name = $name + Description = $newDescription + HasSponsorships = $true + HasIssues = $true + } + switch ($OwnerType) { + 'user' { + $updatedRepo = Set-GitHubRepository @setParams + } + 'organization' { + $updatedRepo = Set-GitHubRepository -Organization $owner @setParams + } + } + Write-Host ($updatedRepo | Format-List | Out-String) + } + LogGroup 'Changed properties:' { + $changes = Compare-PSCustomObject -Left $repo -Right $updatedRepo -OnlyChanged + Write-Host "$($changes | Format-Table | Out-String)" + $updatedRepo | Should -Not -BeNullOrEmpty + $updatedRepo.Description | Should -Be $newDescription + $changedProps = $changes.Property + $changedProps | Should -Contain 'UpdatedAt' + $changedProps | Should -Contain 'Description' + $changedProps | Should -Contain 'HasSponsorships' + $changedProps | Should -Contain 'HasIssues' + $changedProps.Count | Should -Be 4 } - $repos | Should -BeNullOrEmpty } } } diff --git a/tests/Secrets.Tests.ps1 b/tests/Secrets.Tests.ps1 index e043e02ba..b5f05e682 100644 --- a/tests/Secrets.Tests.ps1 +++ b/tests/Secrets.Tests.ps1 @@ -69,7 +69,7 @@ Describe 'Secrets' { SelectedRepositories = $repo.id } - $orgSecret += Set-GitHubSecret @params + $orgSecret += Set-GitHubSecret @params -Debug Write-Host ($orgSecret | Select-Object * | Out-String) } } diff --git a/tests/Variables.Tests.ps1 b/tests/Variables.Tests.ps1 index ee64b6608..293d3bd61 100644 --- a/tests/Variables.Tests.ps1 +++ b/tests/Variables.Tests.ps1 @@ -68,7 +68,7 @@ Describe 'Variables' { Visibility = 'selected' SelectedRepositories = $repo.id } - $orgVariable = Set-GitHubVariable @params + $orgVariable = Set-GitHubVariable @params -Debug Write-Host ($orgVariable | Select-Object * | Out-String) } }