diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 33ad04d5c..3596714dd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,7 +1,7 @@ # Coding Standards for `GitHub` Start by reading the general coding standards for [`PSModule`](https://psmodule.io/docs) which is the basis for all modules in the framework. -Additions or adjustments to those defaults are covered in this document to ensure that the modules drive consistancy for all developers. +Additions or adjustments to those defaults are covered in this document to ensure that the modules drive consistency for all developers. ## General Coding Standards @@ -173,7 +173,7 @@ All function documentation follows standard PowerShell help conventions, with so - Remove any properties that are purely “API wrapper” fields (e.g., raw HTTP artifacts that aren’t relevant to the user). - Cl -- Classes should have ID as the main resource ID, this is the databaseID. The node_id is spesifically in the NodeID property. +- Classes should have ID as the main resource ID, this is the databaseID. The node_id is specifically in the NodeID property. - Classes that use nodeid and databaseid should extend the class called GitHubNode. - Objects that belong inside another scope, has the parts of the scope in properties of the class, i.e. Enterprise, Owner/Organization/Account, Repository, Environment, etc. diff --git a/examples/Connecting.ps1 b/examples/Connecting.ps1 index 005786ce0..5fabc15ef 100644 --- a/examples/Connecting.ps1 +++ b/examples/Connecting.ps1 @@ -7,7 +7,7 @@ Connect-GitHub # Log on to a specific instance of GitHub (enterprise) Connect-GitHub -Host 'msx.ghe.com' -Get-GitHubRepository -Context 'msx.ghe.com/MariusStorhaug' # Contexts are selectable/overrideable on any call +Get-GitHubRepository -Context 'msx.ghe.com/MariusStorhaug' # Contexts are selectable/overridable on any call # Connect to GitHub interactively using OAuth App and Device Flow. Connect-GitHub -Mode 'OAuthApp' -Scope 'gist read:org repo workflow' @@ -15,10 +15,10 @@ Connect-GitHub -Mode 'OAuthApp' -Scope 'gist read:org repo workflow' # Connect to GitHub interactively using less desired PAT flow, supports both fine-grained and classic PATs Connect-GitHub -UseAccessToken -# Connect to GitHub programatically (GitHub App Installation Access Token or PAT) +# Connect to GitHub programmatically (GitHub App Installation Access Token or PAT) Connect-GitHub -Token *********** -# Connect to GitHub programatically (GitHub Actions) +# Connect to GitHub programmatically (GitHub Actions) Connect-GitHub # Looks for the GITHUB_TOKEN variable # Connect using a GitHub App and its private key (local signing of JWT) diff --git a/src/classes/public/Releases/GitHubRelease.ps1 b/src/classes/public/Releases/GitHubRelease.ps1 index 8ef7adbaf..1674c2004 100644 --- a/src/classes/public/Releases/GitHubRelease.ps1 +++ b/src/classes/public/Releases/GitHubRelease.ps1 @@ -17,7 +17,7 @@ # Example: "## What's Changed\n### Other Changes\n* Fix: Enhance repository deletion feedback and fix typo..." [string] $Notes - # Specifies the commitish value that determines where the Git tag is created from + # Specifies the committish value that determines where the Git tag is created from # Example: "main" [string] $Target diff --git a/src/classes/public/Repositories/GitHubCustomProperty.ps1 b/src/classes/public/Repositories/GitHubCustomProperty.ps1 new file mode 100644 index 000000000..e00f7e4d0 --- /dev/null +++ b/src/classes/public/Repositories/GitHubCustomProperty.ps1 @@ -0,0 +1,18 @@ +class GitHubCustomProperty { + # The name of the custom property. + [string] $Name + + # The value of the custom property. + [string] $Value + + GitHubCustomProperty() {} + + GitHubCustomProperty([PSCustomObject] $Object) { + $this.Name = $Object.property_name ?? $Object.propertyName ?? $Object.Name + $this.Value = $Object.value ?? $Object.Value + } + + [string] ToString() { + return $this.Name + } +} diff --git a/src/classes/public/Repositories/GitHubRepository.ps1 b/src/classes/public/Repositories/GitHubRepository.ps1 index ee1e1462c..5ab50a41f 100644 --- a/src/classes/public/Repositories/GitHubRepository.ps1 +++ b/src/classes/public/Repositories/GitHubRepository.ps1 @@ -182,7 +182,7 @@ [GithubRepository] $ForkRepository # Custom properties for the repository. - [PSCustomObject] $CustomProperties + [GitHubCustomProperty[]] $CustomProperties # The clone URL of the repository. # Example: git://github.com/octocat/Hello-World.git @@ -242,7 +242,7 @@ MergeCommitMessage = 'mergeCommitMessage' TemplateRepository = 'templateRepository { id databaseId name owner { login } }' ForkRepository = 'parent { id databaseId name owner { login } }' - CustomProperties = '' + CustomProperties = 'repositoryCustomPropertyValues(first: 100) { nodes { propertyName value } }' CloneUrl = 'url' SshUrl = 'sshUrl' GitUrl = 'url' @@ -298,7 +298,9 @@ $this.SquashMergeCommitTitle = $Object.squash_merge_commit_title $this.MergeCommitMessage = $Object.merge_commit_message $this.MergeCommitTitle = $Object.merge_commit_title - $this.CustomProperties = $Object.custom_properties + $this.CustomProperties = $Object.custom_properties | ForEach-Object { + [GitHubCustomProperty]::new($_) + } $this.TemplateRepository = $null -ne $Object.template_repository ? [GitHubRepository]::New($Object.template_repository) : $null $this.ForkRepository = $null -ne $Object.parent ? [GitHubRepository]::New($Object.parent) : $null $this.CloneUrl = $Object.clone_url @@ -353,6 +355,11 @@ $this.SquashMergeCommitMessage = $Object.squashMergeCommitMessage $this.MergeCommitTitle = $Object.mergeCommitTitle $this.MergeCommitMessage = $Object.mergeCommitMessage + if ($null -ne $Object.repositoryCustomPropertyValues -and $null -ne $Object.repositoryCustomPropertyValues.nodes) { + $this.CustomProperties = $Object.repositoryCustomPropertyValues.nodes | ForEach-Object { + [GitHubCustomProperty]::new($_) + } + } $this.TemplateRepository = $null -ne $Object.templateRepository ? [GitHubRepository]::New($Object.templateRepository) : $null $this.ForkRepository = $null -ne $Object.parent ? [GitHubRepository]::New($Object.parent) : $null $this.CloneUrl = -not [string]::IsNullOrEmpty($Object.url) ? $Object.url + '.git' : $null diff --git a/src/functions/private/Auth/DeviceFlow/Update-GitHubUserAccessToken.ps1 b/src/functions/private/Auth/DeviceFlow/Update-GitHubUserAccessToken.ps1 index d3760379f..16251d6fb 100644 --- a/src/functions/private/Auth/DeviceFlow/Update-GitHubUserAccessToken.ps1 +++ b/src/functions/private/Auth/DeviceFlow/Update-GitHubUserAccessToken.ps1 @@ -31,7 +31,7 @@ )] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingConvertToSecureStringWithPlainText', '', - Justification = 'The tokens are recieved as clear text. Mitigating exposure by removing variables and performing garbage collection.' + Justification = 'The tokens are received as clear text. Mitigating exposure by removing variables and performing garbage collection.' )] [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidLongLines', '', diff --git a/src/functions/private/Enterprise/Get-GitHubEnterpriseByName.ps1 b/src/functions/private/Enterprise/Get-GitHubEnterpriseByName.ps1 index a26ff00b8..3d19fdcd7 100644 --- a/src/functions/private/Enterprise/Get-GitHubEnterpriseByName.ps1 +++ b/src/functions/private/Enterprise/Get-GitHubEnterpriseByName.ps1 @@ -67,7 +67,9 @@ query(`$Slug: String!) { Context = $Context } $enterpriseResult = Invoke-GitHubGraphQLQuery @enterpriseQuery - [GitHubEnterprise]::new($enterpriseResult.enterprise) + if ($enterpriseResult.enterprise) { + [GitHubEnterprise]::new($enterpriseResult.enterprise) + } } end { diff --git a/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetByTag.ps1 b/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetByTag.ps1 index b51471287..9dd952dc5 100644 --- a/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetByTag.ps1 +++ b/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetByTag.ps1 @@ -115,12 +115,14 @@ query(`$owner: String!, `$repository: String!, `$tag: String!, `$perPage: Int, ` Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { $release = $_.repository.release - $assets = $release.releaseAssets - foreach ($asset in $assets.nodes) { - [GitHubReleaseAsset]::new($asset) + if ($release) { + $assets = $release.releaseAssets + foreach ($asset in $assets.nodes) { + [GitHubReleaseAsset]::new($asset) + } + $hasNextPage = $assets.pageInfo.hasNextPage + $after = $assets.pageInfo.endCursor } - $hasNextPage = $assets.pageInfo.hasNextPage - $after = $assets.pageInfo.endCursor } } while ($hasNextPage) } diff --git a/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetFromLatest.ps1 b/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetFromLatest.ps1 index 26628315f..0d0900c6c 100644 --- a/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetFromLatest.ps1 +++ b/src/functions/private/Releases/Assets/Get-GitHubReleaseAssetFromLatest.ps1 @@ -114,12 +114,14 @@ query(`$owner: String!, `$repository: String!, `$perPage: Int, `$after: String) Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { $release = $_.repository.latestRelease - $assets = $release.releaseAssets - foreach ($asset in $assets.nodes) { - [GitHubReleaseAsset]::new($asset) + if ($release) { + $assets = $release.releaseAssets + foreach ($asset in $assets.nodes) { + [GitHubReleaseAsset]::new($asset) + } + $hasNextPage = $assets.pageInfo.hasNextPage + $after = $assets.pageInfo.endCursor } - $hasNextPage = $assets.pageInfo.hasNextPage - $after = $assets.pageInfo.endCursor } } while ($hasNextPage) } diff --git a/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 b/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 index ff9d01bf8..2422460f4 100644 --- a/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 +++ b/src/functions/private/Repositories/Get-GitHubMyRepositoryByName.ps1 @@ -78,7 +78,6 @@ 'MergeCommitMessage', 'TemplateRepository', 'ForkRepository', - 'CustomProperties', 'CloneUrl', 'SshUrl', 'GitUrl' @@ -128,7 +127,10 @@ $graphQLFields } Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { - [GitHubRepository]::new($_.viewer.repository) + $repository = $_.viewer.repository + if ($repository) { + [GitHubRepository]::new($repository) + } } } diff --git a/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 b/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 index d3788be41..08de2435f 100644 --- a/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 +++ b/src/functions/private/Repositories/Get-GitHubRepositoryByName.ps1 @@ -81,7 +81,6 @@ 'MergeCommitMessage', 'TemplateRepository', 'ForkRepository', - 'CustomProperties', 'CloneUrl', 'SshUrl', 'GitUrl' @@ -135,7 +134,10 @@ $graphQLFields } Invoke-GitHubGraphQLQuery @apiParams | ForEach-Object { - [GitHubRepository]::new($_.repositoryOwner.repository) + $repository = $_.repositoryOwner.repository + if ($repository) { + [GitHubRepository]::new($repository) + } } } diff --git a/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 b/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 index 23f181675..3a7cd65c2 100644 --- a/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 +++ b/src/functions/public/API/Invoke-GitHubGraphQLQuery.ps1 @@ -58,32 +58,44 @@ $graphQLResponse = $apiResponse.Response # Handle GraphQL-specific errors (200 OK with errors in response) if ($graphQLResponse.errors) { - $errorMessages = @() - $queryLines = $Query -split "`n" | ForEach-Object { $_.Trim() } - foreach ($errorItem in $graphQLResponse.errors) { - $errorMessages += @" + # Partial success: data was returned alongside errors (per GraphQL spec). + # Emit warnings for the partial errors and return the data. + if ($null -ne $graphQLResponse.data) { + foreach ($errorItem in $graphQLResponse.errors) { + $warningMessage = @" +GraphQL partial errors occurred +Type: $($errorItem.type) +Message: $($errorItem.message) +Path: $($errorItem.path -join '/') + +"@ + Write-Warning $warningMessage + } + } else { + # Full failure: no data returned, only errors. + $errorMessages = @() + foreach ($errorItem in $graphQLResponse.errors) { + $errorMessages += @" +GraphQL terminating errors occurred +Type: $($errorItem.type) +Message: $($errorItem.message) +Path: $($errorItem.path -join '/') -GraphQL errors occurred: Full Error: $($errorItem | ConvertTo-Json -Depth 10 | Out-String) -GraphQL Error [$($errorItem.type)]: -Message: $($errorItem.message) -Path: $($errorItem.path -join '/') -Locations: -$($errorItem.locations | ForEach-Object { " - [$($_.line):$($_.column)] - $($queryLines[$_.line - 1])" }) - "@ - } - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - [System.Exception]::new($errorMessages), - 'GraphQLError', - [System.Management.Automation.ErrorCategory]::InvalidOperation, - $graphQLResponse + } + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Exception]::new($errorMessages), + 'GraphQLError', + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $graphQLResponse + ) ) - ) + } } $graphQLResponse.data diff --git a/src/functions/public/Actions/Data/Get-GitHubRunnerData.ps1 b/src/functions/public/Actions/Data/Get-GitHubRunnerData.ps1 index 9f83de10a..e5f55772e 100644 --- a/src/functions/public/Actions/Data/Get-GitHubRunnerData.ps1 +++ b/src/functions/public/Actions/Data/Get-GitHubRunnerData.ps1 @@ -1,10 +1,10 @@ function Get-GitHubRunnerData { <# .SYNOPSIS - Gets data about the runner thats running the workflow. + Gets data about the runner that's running the workflow. .DESCRIPTION - Gets data about the runner thats running the workflow. + Gets data about the runner that's running the workflow. .EXAMPLE ```powershell diff --git a/src/functions/public/Commands/Set-GitHubLogGroup.ps1 b/src/functions/public/Commands/Set-GitHubLogGroup.ps1 index 0af48b24d..3f4796600 100644 --- a/src/functions/public/Commands/Set-GitHubLogGroup.ps1 +++ b/src/functions/public/Commands/Set-GitHubLogGroup.ps1 @@ -5,7 +5,7 @@ .DESCRIPTION DSL approach for GitHub Action commands. - Allows for colapsing of code in IDE for code that belong together. + Allows for collapsing of code in IDE for code that belong together. .EXAMPLE ```powershell diff --git a/src/functions/public/Commands/Set-GitHubNoCommandGroup.ps1 b/src/functions/public/Commands/Set-GitHubNoCommandGroup.ps1 index 91b32e07f..3c1159e57 100644 --- a/src/functions/public/Commands/Set-GitHubNoCommandGroup.ps1 +++ b/src/functions/public/Commands/Set-GitHubNoCommandGroup.ps1 @@ -5,7 +5,7 @@ .DESCRIPTION DSL approach for GitHub Action commands. - Allows for colapsing of code in IDE for code that belong together. + Allows for collapsing of code in IDE for code that belong together. .EXAMPLE ```powershell diff --git a/src/functions/public/Releases/New-GitHubRelease.ps1 b/src/functions/public/Releases/New-GitHubRelease.ps1 index 275b3abe9..de3cf5ae1 100644 --- a/src/functions/public/Releases/New-GitHubRelease.ps1 +++ b/src/functions/public/Releases/New-GitHubRelease.ps1 @@ -104,7 +104,7 @@ [string] $DiscussionCategoryName, # Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, - # a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes. + # a name will be automatically generated. If body is specified, the body will be prepended to the automatically generated notes. [Parameter()] [switch] $GenerateReleaseNotes, diff --git a/src/functions/public/Releases/New-GitHubReleaseNote.ps1 b/src/functions/public/Releases/New-GitHubReleaseNote.ps1 index fe07d0233..7c29e271b 100644 --- a/src/functions/public/Releases/New-GitHubReleaseNote.ps1 +++ b/src/functions/public/Releases/New-GitHubReleaseNote.ps1 @@ -85,7 +85,7 @@ [Parameter(Mandatory, ValueFromPipelineByPropertyName)] [string] $Tag, - # Specifies the commitish value that will be the target for the release's tag. + # Specifies the committish value that will be the target for the release's tag. # Required if the supplied tag_name does not reference an existing tag. # Ignored if the tag_name already exists. [Parameter()] diff --git a/src/functions/public/Releases/Set-GitHubRelease.ps1 b/src/functions/public/Releases/Set-GitHubRelease.ps1 index f11f55055..a756a4a85 100644 --- a/src/functions/public/Releases/Set-GitHubRelease.ps1 +++ b/src/functions/public/Releases/Set-GitHubRelease.ps1 @@ -122,7 +122,7 @@ [string] $DiscussionCategoryName, # Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, - # a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes. + # a name will be automatically generated. If body is specified, the body will be prepended to the automatically generated notes. [Parameter()] [switch] $GenerateReleaseNotes, diff --git a/src/functions/public/Releases/Update-GitHubRelease.ps1 b/src/functions/public/Releases/Update-GitHubRelease.ps1 index 4671e3519..8b5d1f423 100644 --- a/src/functions/public/Releases/Update-GitHubRelease.ps1 +++ b/src/functions/public/Releases/Update-GitHubRelease.ps1 @@ -71,7 +71,7 @@ [Parameter()] [string] $Tag, - # Specifies the commitish value that determines where the Git tag is created from. + # Specifies the committish value that determines where the Git tag is created from. # Can be any branch or commit SHA. Unused if the Git tag already exists. # API Default: the repository's default branch. [Parameter()] @@ -100,7 +100,7 @@ [string] $DiscussionCategoryName, # Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, - # a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes. + # a name will be automatically generated. If body is specified, the body will be prepended to the automatically generated notes. [Parameter()] [switch] $GenerateReleaseNotes, diff --git a/src/functions/public/Repositories/Repositories/Get-GitHubRepositoryContributor.ps1 b/src/functions/public/Repositories/Repositories/Get-GitHubRepositoryContributor.ps1 index 46d7fb7c6..2be619ce5 100644 --- a/src/functions/public/Repositories/Repositories/Get-GitHubRepositoryContributor.ps1 +++ b/src/functions/public/Repositories/Repositories/Get-GitHubRepositoryContributor.ps1 @@ -36,7 +36,7 @@ [Parameter(Mandatory)] [string] $Name, - # Wether to include anonymous contributors in results. + # Whether to include anonymous contributors in results. [Parameter()] [switch] $Anon, diff --git a/src/functions/public/Status/Get-GitHubScheduledMaintenance.ps1 b/src/functions/public/Status/Get-GitHubScheduledMaintenance.ps1 index 230452fdb..28c2171d7 100644 --- a/src/functions/public/Status/Get-GitHubScheduledMaintenance.ps1 +++ b/src/functions/public/Status/Get-GitHubScheduledMaintenance.ps1 @@ -5,7 +5,7 @@ .DESCRIPTION Scheduled maintenances are planned outages, upgrades, or general notices that you're working - on infrastructure and disruptions may occurr. A close sibling of Incidents, each usually goes + on infrastructure and disruptions may occur. A close sibling of Incidents, each usually goes through a progression of statuses listed below, with an impact calculated from a blend of component statuses (or an optional override). diff --git a/tests/Enterprise.Tests.ps1 b/tests/Enterprise.Tests.ps1 index b6b7a73dd..98f3cd03c 100644 --- a/tests/Enterprise.Tests.ps1 +++ b/tests/Enterprise.Tests.ps1 @@ -20,7 +20,7 @@ param() BeforeAll { - # DEFAULTS ACCROSS ALL TESTS + # DEFAULTS ACROSS ALL TESTS } Describe 'Template' { diff --git a/tests/GitHub.Tests.ps1 b/tests/GitHub.Tests.ps1 index fb16e1c3c..dab477d5a 100644 --- a/tests/GitHub.Tests.ps1 +++ b/tests/GitHub.Tests.ps1 @@ -725,7 +725,7 @@ Describe 'API' { } $licenseList | Should -Not -BeNullOrEmpty } - It 'Get-GitHubLicense - Gets a spesific license' { + It 'Get-GitHubLicense - Gets a specific license' { $mitLicense = Get-GitHubLicense -Name 'mit' LogGroup 'MIT License' { Write-Host ($mitLicense | Format-Table | Out-String) @@ -766,7 +766,7 @@ Describe 'API' { } $markdown | Should -Not -BeNullOrEmpty } - It 'Get-GitHubMarkdown - Gets the rendered markdown for provided text using GitHub Formated Markdown' { + It 'Get-GitHubMarkdown - Gets the rendered markdown for provided text using GitHub Formatted Markdown' { $gfmMarkdown = Get-GitHubMarkdown -Text 'Hello, World!' -Mode gfm LogGroup 'GFM Markdown' { Write-Host ($gfmMarkdown | Format-Table | Out-String) diff --git a/tests/Secrets.Tests.ps1 b/tests/Secrets.Tests.ps1 index ead8632ca..e3937cfc1 100644 --- a/tests/Secrets.Tests.ps1 +++ b/tests/Secrets.Tests.ps1 @@ -158,7 +158,7 @@ Describe 'Secrets' { } } - It 'Set-GitHubSecret - should ensure existance of a organization secret' { + It 'Set-GitHubSecret - should ensure existence of a organization secret' { $name = "$secretName`_TestSecret" LogGroup "Secret - [$name]" { $param = @{ diff --git a/tests/TEMPLATE.ps1 b/tests/TEMPLATE.ps1 index d68fde218..b73dce38e 100644 --- a/tests/TEMPLATE.ps1 +++ b/tests/TEMPLATE.ps1 @@ -20,7 +20,7 @@ param() BeforeAll { - # DEFAULTS ACCROSS ALL TESTS + # DEFAULTS ACROSS ALL TESTS } Describe 'Template' { diff --git a/tests/Variables.Tests.ps1 b/tests/Variables.Tests.ps1 index 0599ac435..ba063b04e 100644 --- a/tests/Variables.Tests.ps1 +++ b/tests/Variables.Tests.ps1 @@ -114,7 +114,7 @@ Describe 'Variables' { Owner = $owner } } - It 'Set-GitHubVariable - should ensure existance of a organization variable' { + It 'Set-GitHubVariable - should ensure existence of a organization variable' { $name = "$variableName`TestVariable" LogGroup "Variable - [$name]" { $param = @{