Skip to content

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

Open
Marius Storhaug (MariusStorhaug) wants to merge 5 commits intomainfrom
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 intomainfrom
fix/custom-property-multiselect-value

Conversation

@MariusStorhaug
Copy link
Copy Markdown
Member

@MariusStorhaug Marius Storhaug (MariusStorhaug) commented Apr 5, 2026

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 thread src/classes/public/Repositories/GitHubCustomProperty.ps1
@MariusStorhaug Marius Storhaug (MariusStorhaug) changed the title 🐛 [Bug]: Preserve array values for multi-select custom properties 🪲 [Fix]: Multi-select custom properties no longer lose individual values Apr 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

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

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
Copilot AI review requested due to automatic review settings April 16, 2026 19:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 thread tests/GitHub.Tests.ps1 Outdated
…ctly instead of relying on pipeline enumeration
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@PSModule PSModule (PSModule) deleted a comment from github-actions Bot May 1, 2026
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.
Copilot AI review requested due to automatic review settings May 1, 2026 10:19
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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
}
Copy link

Copilot AI May 1, 2026

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]
Copy link

Copilot AI May 1, 2026

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]
}
Copy link

Copilot AI May 1, 2026

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 ($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]
}
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 ($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]

Copilot uses AI. Check for mistakes.
Comment thread tests/GitHub.Tests.ps1
Comment on lines 295 to +296
It 'Get-GitHubStamp - Each stamp has Name and BaseUrl properties' {
LogGroup "Stamps - Details" {
LogGroup 'Stamps - Details' {
Copy link

Copilot AI May 1, 2026

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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants