src/PowerShell/ProjectMetadata.ps1
|
# cdp PowerShell domain: ProjectMetadata.ps1 # Loaded by src/cdp.psm1; do not dot-source peer files. function Set-ProjectPin { <# .SYNOPSIS Pin or unpin a project in the cdp list. .DESCRIPTION Marks a project as pinned so it appears before normal projects in cdp pickers and lists. If no name is provided, cdp tries to pin the project matching the current directory. .PARAMETER Name Project name or query to pin or unpin. Omit it inside a project root. .PARAMETER ConfigPath Optional custom path to projects.json file. .PARAMETER Pinned Set to true to pin the project or false to unpin it. .PARAMETER PassThru Returns the updated project object for tests and scripting. .EXAMPLE cdp pin api # Pins the matching project .EXAMPLE cdp unpin api # Removes the pin from the matching project #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory = $false, Position = 0)] [string]$Name, [Parameter(Mandatory = $false, Position = 1)] [string]$ConfigPath, [Parameter(Mandatory = $false)] [bool]$Pinned = $true, [Parameter(Mandatory = $false)] [switch]$PassThru ) if ([string]::IsNullOrWhiteSpace($ConfigPath)) { $ConfigPath = Get-DefaultConfigPath } try { $document = Read-CdpJsonArrayMutationDocument -LiteralPath $ConfigPath $projects = @($document.Value) $targetProjects = if ([string]::IsNullOrWhiteSpace($Name)) { $currentPath = Get-CdpComparablePath -Path (Get-Location).Path @($projects | Where-Object { $resolution = Resolve-CdpProjectPath -Project $_ -not $resolution.ErrorCode -and (Get-CdpComparablePath -Path $resolution.ResolvedPath) -eq $currentPath }) } else { @(Get-CdpProjectMatches -Projects $projects -Query $Name) } if ($targetProjects.Count -eq 0) { Write-Host "No project matched for pin update." -ForegroundColor Yellow return } if ($targetProjects.Count -gt 1) { Write-Host "Multiple projects matched. Please use a more specific name." -ForegroundColor Yellow $targetProjects | ForEach-Object { Write-Host " $($_.name)" -ForegroundColor Gray } return } $target = $targetProjects[0] $stateText = if ($Pinned) { "Pin" } else { "Unpin" } $currentPinned = $target.PSObject.Properties['pinned'] -and [bool]$target.pinned if ($currentPinned -eq $Pinned) { Write-Host "Project already has the requested pin state: $($target.name)" -ForegroundColor Yellow if ($PassThru) { return New-CdpActionResult -Action ($stateText.ToLowerInvariant()) -Target ([string]$target.name) -Status 'skipped' -Changed $false -Details $target } return } if (-not $PSCmdlet.ShouldProcess([string]$target.name, "$stateText project in $ConfigPath")) { if ($PassThru) { $status = if ($WhatIfPreference) { 'preview' } else { 'canceled' } return New-CdpActionResult -Action ($stateText.ToLowerInvariant()) -Target ([string]$target.name) -Status $status -Changed $false -Details $target } return } foreach ($project in $projects) { $sameName = [string]::Equals([string]$project.name, [string]$target.name, [StringComparison]::OrdinalIgnoreCase) $samePath = [string]::Equals([string]$project.rootPath, [string]$target.rootPath, [StringComparison]::OrdinalIgnoreCase) if ($sameName -and $samePath) { if ($project.PSObject.Properties['pinned']) { $project.pinned = $Pinned } else { $project | Add-Member -NotePropertyName pinned -NotePropertyValue $Pinned } } } [void](Write-CdpJsonFile -LiteralPath $ConfigPath -Value @($projects) -ExpectedFingerprint $document.Fingerprint) $completedText = if ($Pinned) { "Pinned" } else { "Unpinned" } Write-Host "$completedText project: $($target.name)" -ForegroundColor Green if ($PassThru) { $updatedProject = ($projects | Where-Object { [string]::Equals([string]$_.name, [string]$target.name, [StringComparison]::OrdinalIgnoreCase) -and [string]::Equals([string]$_.rootPath, [string]$target.rootPath, [StringComparison]::OrdinalIgnoreCase) } | Select-Object -First 1) return New-CdpActionResult -Action ($stateText.ToLowerInvariant()) -Target ([string]$target.name) -Status 'succeeded' -Changed $true -Details $updatedProject } } catch { Write-Host "Error: Failed to update project pin." -ForegroundColor Red Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Gray if ($PassThru) { $action = if ($Pinned) { 'pin' } else { 'unpin' } return New-CdpActionResult -Action $action -Target ([string]$Name) -Status 'failed' -Changed $false -Error $_.Exception.Message } } } function Clear-ProjectPin { <# .SYNOPSIS Remove the pinned state from a cdp project. .DESCRIPTION Convenience wrapper for Set-ProjectPin -Pinned false. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory = $false, Position = 0)] [string]$Name, [Parameter(Mandatory = $false, Position = 1)] [string]$ConfigPath, [Parameter(Mandatory = $false)] [switch]$PassThru ) $parameters = @{ Name = $Name ConfigPath = $ConfigPath Pinned = $false PassThru = $PassThru } if ($WhatIfPreference) { $parameters.WhatIf = $true } if ($PSBoundParameters.ContainsKey('Confirm')) { $parameters.Confirm = $PSBoundParameters['Confirm'] } Set-ProjectPin @parameters } function Update-CdpProjectStringList { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param( [Parameter(Mandatory = $false)] [string]$Name, [Parameter(Mandatory = $false)] [string]$Value, [Parameter(Mandatory = $true)] [ValidateSet('aliases', 'tags')] [string]$PropertyName, [Parameter(Mandatory = $false)] [string]$ConfigPath, [Parameter(Mandatory = $false)] [switch]$Remove, [Parameter(Mandatory = $false)] [switch]$PassThru ) if ([string]::IsNullOrWhiteSpace($ConfigPath)) { $ConfigPath = Get-DefaultConfigPath } if ([string]::IsNullOrWhiteSpace($Name) -or [string]::IsNullOrWhiteSpace($Value)) { Write-Host "Project name and metadata value are required." -ForegroundColor Yellow return } try { $document = Read-CdpJsonArrayMutationDocument -LiteralPath $ConfigPath $projects = @($document.Value) $targets = @(Get-CdpProjectMatches -Projects $projects -Query $Name) if ($targets.Count -ne 1) { Write-Host "Expected one project match, found $($targets.Count)." -ForegroundColor Yellow return } $target = $targets[0] $currentValues = @(Get-CdpProjectStringList -Project $target -PropertyName $PropertyName) $containsValue = @($currentValues | Where-Object { [string]::Equals($_, $Value, [StringComparison]::OrdinalIgnoreCase) }).Count -gt 0 $willChange = if ($Remove) { $containsValue } else { -not $containsValue } $metadataKind = if ($PropertyName -eq 'aliases') { 'alias' } else { 'tag' } $actionName = if ($Remove) { "remove-$metadataKind" } else { "add-$metadataKind" } if (-not $willChange) { Write-Host "Project metadata already has the requested state." -ForegroundColor Yellow if ($PassThru) { return New-CdpActionResult -Action $actionName -Target ([string]$target.name) -Status 'skipped' -Changed $false -Details $target } return } $operation = if ($Remove) { "Remove $metadataKind '$Value'" } else { "Add $metadataKind '$Value'" } if (-not $PSCmdlet.ShouldProcess([string]$target.name, "$operation in $ConfigPath")) { if ($PassThru) { $status = if ($WhatIfPreference) { 'preview' } else { 'canceled' } return New-CdpActionResult -Action $actionName -Target ([string]$target.name) -Status $status -Changed $false -Details $target } return } foreach ($project in $projects) { $sameName = [string]::Equals([string]$project.name, [string]$target.name, [StringComparison]::OrdinalIgnoreCase) $samePath = [string]::Equals([string]$project.rootPath, [string]$target.rootPath, [StringComparison]::OrdinalIgnoreCase) if ($sameName -and $samePath) { $values = @(Get-CdpProjectStringList -Project $project -PropertyName $PropertyName) if ($Remove) { $values = @($values | Where-Object { -not [string]::Equals($_, $Value, [StringComparison]::OrdinalIgnoreCase) }) } elseif (@($values | Where-Object { [string]::Equals($_, $Value, [StringComparison]::OrdinalIgnoreCase) }).Count -eq 0) { $values += $Value } if ($project.PSObject.Properties[$PropertyName]) { $project.$PropertyName = @($values) } else { $project | Add-Member -NotePropertyName $PropertyName -NotePropertyValue @($values) } } } [void](Write-CdpJsonFile -LiteralPath $ConfigPath -Value @($projects) -ExpectedFingerprint $document.Fingerprint) $action = if ($Remove) { "Removed" } else { "Added" } Write-Host "$action $PropertyName '$Value' for project: $($target.name)" -ForegroundColor Green if ($PassThru) { $updatedProject = ($projects | Where-Object { [string]::Equals([string]$_.name, [string]$target.name, [StringComparison]::OrdinalIgnoreCase) -and [string]::Equals([string]$_.rootPath, [string]$target.rootPath, [StringComparison]::OrdinalIgnoreCase) } | Select-Object -First 1) return New-CdpActionResult -Action $actionName -Target ([string]$target.name) -Status 'succeeded' -Changed $true -Details $updatedProject } } catch { Write-Host "Error: Failed to update project metadata." -ForegroundColor Red Write-Host "Details: $($_.Exception.Message)" -ForegroundColor Gray if ($PassThru) { $metadataKind = if ($PropertyName -eq 'aliases') { 'alias' } else { 'tag' } $actionName = if ($Remove) { "remove-$metadataKind" } else { "add-$metadataKind" } return New-CdpActionResult -Action $actionName -Target ([string]$Name) -Status 'failed' -Changed $false -Error $_.Exception.Message } } } function Add-ProjectAlias { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param([string]$Name, [string]$Alias, [string]$ConfigPath, [switch]$PassThru) $parameters = @{ Name = $Name; Value = $Alias; PropertyName = 'aliases'; ConfigPath = $ConfigPath; PassThru = $PassThru } if ($WhatIfPreference) { $parameters.WhatIf = $true } if ($PSBoundParameters.ContainsKey('Confirm')) { $parameters.Confirm = $PSBoundParameters['Confirm'] } Update-CdpProjectStringList @parameters } function Remove-ProjectAlias { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param([string]$Name, [string]$Alias, [string]$ConfigPath, [switch]$PassThru) $parameters = @{ Name = $Name; Value = $Alias; PropertyName = 'aliases'; ConfigPath = $ConfigPath; Remove = $true; PassThru = $PassThru } if ($WhatIfPreference) { $parameters.WhatIf = $true } if ($PSBoundParameters.ContainsKey('Confirm')) { $parameters.Confirm = $PSBoundParameters['Confirm'] } Update-CdpProjectStringList @parameters } function Add-ProjectTag { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param([string]$Name, [string]$Tag, [string]$ConfigPath, [switch]$PassThru) $parameters = @{ Name = $Name; Value = $Tag; PropertyName = 'tags'; ConfigPath = $ConfigPath; PassThru = $PassThru } if ($WhatIfPreference) { $parameters.WhatIf = $true } if ($PSBoundParameters.ContainsKey('Confirm')) { $parameters.Confirm = $PSBoundParameters['Confirm'] } Update-CdpProjectStringList @parameters } function Remove-ProjectTag { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param([string]$Name, [string]$Tag, [string]$ConfigPath, [switch]$PassThru) $parameters = @{ Name = $Name; Value = $Tag; PropertyName = 'tags'; ConfigPath = $ConfigPath; Remove = $true; PassThru = $PassThru } if ($WhatIfPreference) { $parameters.WhatIf = $true } if ($PSBoundParameters.ContainsKey('Confirm')) { $parameters.Confirm = $PSBoundParameters['Confirm'] } Update-CdpProjectStringList @parameters } |