Skip to content

🪲 [Fix]: Multi-select custom properties no longer lose individual values - #577

Open
Marius Storhaug (MariusStorhaug) wants to merge 5 commits into
mainfrom
fix/custom-property-multiselect-value
Open

🪲 [Fix]: Multi-select custom properties no longer lose individual values#577
Marius Storhaug (MariusStorhaug) wants to merge 5 commits into
mainfrom
fix/custom-property-multiselect-value

Conversation

@MariusStorhaug

@MariusStorhaugMarius Storhaug (MariusStorhaug) commented Apr 5, 2026

Copy link
Copy Markdown
Member

Multi-select custom property values are now correctly preserved as arrays. Previously, properties with multiple selections (e.g., ["Custom Instructions", "License", "Prompts"]) were silently collapsed into a single space-joined string ("Custom Instructions License Prompts"), making it impossible to iterate over individual selections downstream.

Fixed: Multi-select custom property values collapsed into a single string

Commands that return repository custom properties (such as Get-GitHubRepository and Get-GitHubRepositoryCustomProperty) now preserve array values for multi-select properties. Each selection is available as a separate string element, so downstream logic that iterates over values works correctly.

$repo=Get-GitHubRepository-Owner 'MyOrg'-Name 'MyRepo'$repo.CustomProperties|Where-Object Name -EQ'SubscribeTo'# Before (broken):# Name Value# ---- -----# SubscribeTo Custom Instructions License Prompts# After (correct):# Name Value# ---- -----# SubscribeTo {Custom Instructions, License, Prompts}# Individual values are now accessible:$prop.Value[0] # "Custom Instructions"$prop.Value[1] # "License"$prop.Value[2] # "Prompts"

Single-select and text custom properties continue to work as before — their values remain plain strings.

Technical Details

  • GitHubCustomProperty.Value type changed from [string] to [object] to allow both scalar strings and string arrays.
  • The constructor now checks whether the raw value implements IEnumerable (excluding string). If so, it casts to [string[]]; otherwise it assigns the value directly.
  • Unit tests added covering multi-select array preservation, scalar string pass-through, and GraphQL-style property name resolution.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a bug in the repository custom properties model where multi-select custom property values returned as arrays by the GitHub API were being coerced into a single space-joined string, preventing correct downstream iteration.

Changes:

  • Changed GitHubCustomProperty.Value from [string] to [object] to allow array values.
  • Updated the GitHubCustomProperty constructor to detect non-string enumerables and cast them to [string[]].

Comment threadsrc/classes/public/Repositories/GitHubCustomProperty.ps1
@MariusStorhaugMarius Storhaug (MariusStorhaug) changed the title 🐛 [Bug]: Preserve array values for multi-select custom properties🪲 [Fix]: Multi-select custom properties no longer lose individual valuesApr 5, 2026
@MariusStorhaug
Marius Storhaug (MariusStorhaug) marked this pull request as draft April 5, 2026 11:53
@github-actions

Copy link
Copy Markdown
Contributor

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@MariusStorhaug
Marius Storhaug (MariusStorhaug) marked this pull request as ready for review April 16, 2026 19:22
CopilotAI review requested due to automatic review settings April 16, 2026 19:22

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment threadtests/GitHub.Tests.ps1 Outdated
…ctly instead of relying on pipeline enumeration
@github-actions

Copy link
Copy Markdown
Contributor

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Removed fabricated [PSCustomObject] tests from GitHub.Tests.ps1 and added
a real integration test in Repositories.Tests.ps1 that calls
Get-GitHubRepositoryCustomProperty against org repositories and validates
that multi-select values are preserved as [string[]] arrays.
CopilotAI review requested due to automatic review settings May 1, 2026 10:19
@github-actions

Copy link
Copy Markdown
Contributor

Super-linter summary

LanguageValidation result
CHECKOVPass ✅
GITHUB_ACTIONSPass ✅
GITLEAKSPass ✅
GIT_MERGE_CONFLICT_MARKERSPass ✅
MARKDOWNPass ✅
NATURAL_LANGUAGEPass ✅
PRE_COMMITPass ✅
SPELL_CODESPELLPass ✅
TRIVYPass ✅
YAMLPass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment on lines 10 to +17
GitHubCustomProperty([PSCustomObject] $Object) {
$this.Name = $Object.property_name ?? $Object.propertyName ?? $Object.Name
$this.Value = $Object.value ?? $Object.Value
$rawValue = $Object.value ?? $Object.Value
if ($rawValue -is [System.Collections.IEnumerable] -and $rawValue -isnot [string]) {
$this.Value = [string[]]$rawValue
} else {
$this.Value = $rawValue
}

CopilotAIMay 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor now branches on whether value is an IEnumerable (excluding string) to preserve multi-select values as arrays, but this behavior isn't covered by a deterministic unit test in tests/. Add a small Pester test that constructs GitHubCustomProperty from a [pscustomobject]@{ property_name = 'X'; value = @('a','b') } and from a scalar string, and asserts the resulting .Value type (and propertyName alias resolution).

Copilot uses AI. Check for mistakes.
Comment on lines +528 to +532
$prop.Name | Should -Not -BeNullOrEmpty
if ($prop.Value -is [System.Collections.IEnumerable] -and $prop.Value -isnot [string]) {
$prop.Value -is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($prop.Value.GetType().FullName)"
} else {
$prop.Value | Should -BeOfType [string]

CopilotAIMay 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get-GitHubRepositoryCustomProperty returns raw REST objects (from ConvertFrom-Json) with fields like property_name and value. This test asserts $prop.Name, which likely doesn't exist (and will be $null), causing a false failure. Consider asserting $prop.property_name instead, or explicitly converting each item to [GitHubCustomProperty] before validating Name/Value.

Suggested change
$prop.Name| Should -Not-BeNullOrEmpty
if ($prop.Value-is [System.Collections.IEnumerable] -and$prop.Value-isnot [string]) {
$prop.Value-is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($prop.Value.GetType().FullName)"
} else {
$prop.Value| Should -BeOfType [string]
$prop.property_name| Should -Not-BeNullOrEmpty
if ($prop.value-is [System.Collections.IEnumerable] -and$prop.value-isnot [string]) {
$prop.value-is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($prop.value.GetType().FullName)"
} else {
$prop.value| Should -BeOfType [string]

Copilot uses AI. Check for mistakes.
Comment on lines +521 to +533
It 'Get-GitHubRepositoryCustomProperty - Gets custom properties and preserves value types' -Skip:($OwnerType -ne 'organization') {
LogGroup 'Custom Properties' {
$properties = Get-GitHubRepositoryCustomProperty -Owner $owner -Repository $repoName
Write-Host ($properties | Format-List | Out-String)
}
if ($properties) {
foreach ($prop in $properties) {
$prop.Name | Should -Not -BeNullOrEmpty
if ($prop.Value -is [System.Collections.IEnumerable] -and $prop.Value -isnot [string]) {
$prop.Value -is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($prop.Value.GetType().FullName)"
} else {
$prop.Value | Should -BeOfType [string]
}

CopilotAIMay 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test can pass without validating the new behavior: if $properties is empty, there are no assertions, and if all values are scalar strings the array-preservation path is never exercised. To reliably catch regressions, add a deterministic unit test around GitHubCustomProperty([PSCustomObject]) (mocking an array vs scalar value) or otherwise ensure the test repo contains at least one multi-select property and assert it is a [string[]].

Suggested change
It 'Get-GitHubRepositoryCustomProperty - Gets custom properties and preserves value types'-Skip:($OwnerType-ne'organization') {
LogGroup 'Custom Properties' {
$properties=Get-GitHubRepositoryCustomProperty-Owner $owner-Repository $repoName
Write-Host ($properties|Format-List|Out-String)
}
if ($properties) {
foreach ($propin$properties) {
$prop.Name| Should -Not-BeNullOrEmpty
if ($prop.Value-is [System.Collections.IEnumerable] -and$prop.Value-isnot [string]) {
$prop.Value-is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($prop.Value.GetType().FullName)"
} else {
$prop.Value| Should -BeOfType [string]
}
It 'GitHubCustomProperty - Preserves scalar and multi-select value types' {
$scalarProperty= [GitHubCustomProperty]::new([PSCustomObject]@{
name ='Environment'
value ='Production'
})
$arrayProperty= [GitHubCustomProperty]::new([PSCustomObject]@{
name ='Regions'
value =@('eu-west-1','us-east-1')
})
$scalarProperty.Name| Should -Be 'Environment'
$scalarProperty.Value| Should -BeOfType [string]
$scalarProperty.Value| Should -Be 'Production'
$arrayProperty.Name| Should -Be 'Regions'
$arrayProperty.Value-is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($arrayProperty.Value.GetType().FullName)"
$arrayProperty.Value| Should -Be @('eu-west-1','us-east-1')
}
It 'Get-GitHubRepositoryCustomProperty - Gets custom properties and preserves value types'-Skip:($OwnerType-ne'organization') {
LogGroup 'Custom Properties' {
$properties=Get-GitHubRepositoryCustomProperty-Owner $owner-Repository $repoName
Write-Host ($properties|Format-List|Out-String)
}
$properties| Should -Not-BeNullOrEmpty
foreach ($propin$properties) {
$prop.Name| Should -Not-BeNullOrEmpty
if ($prop.Value-is [System.Collections.IEnumerable] -and$prop.Value-isnot [string]) {
$prop.Value-is [string[]] | Should -BeTrue -Because "multi-select values must be preserved as string arrays, got: $($prop.Value.GetType().FullName)"
} else {
$prop.Value| Should -BeOfType [string]

Copilot uses AI. Check for mistakes.
Comment on lines 295 to +296
It 'Get-GitHubStamp - Each stamp has Name and BaseUrl properties' {
LogGroup "Stamps - Details" {
LogGroup 'Stamps - Details' {

CopilotAIMay 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description (and prior review thread) mention adding focused unit tests for GitHubCustomProperty array vs scalar handling (and GraphQL-style propertyName), but there are no such tests in the current tests/ tree. Please add the mentioned unit tests (or update the PR description to match what actually changed) so this behavior is covered deterministically.

Copilot uses AI. Check for mistakes.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

No open projects
Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants

@MariusStorhaug