From ddb4ff0b01c76b11ea9fbc53a24335e9993bfc18 Mon Sep 17 00:00:00 2001 From: Lucas-c-B Date: Fri, 17 May 2024 14:07:21 +0200 Subject: [PATCH 1/7] feat: Added function New-PipelineRun Added a new function called New-PipelineRun, which can start a pipelin run in an Azure DevOps Project programmatically docs: Added comment based help to New-PipelineRun Added comment based help to New-PipelineRun fix: Add missing ValidateScript Add missing ValidateScript fix: should process input fix: param validator collectionUri fix: fixed service connection parameter set feat: Add function Get-PipelineRun Add function Get-PipelineRun that gets all or specific runs from the specified pipeline fix: formatting formatting feat: added classification nodes endpoint feat: Add PipelineFolderPath parameter to be able to place a pipeline in a folder in azure devops to the module: New-AzDoPipeline Add PipelineFolderPath parameter to be able to place a pipeline in a folder in azure devops to the module: New-AzDoPipeline feat: add public cmdlet to create pull requests individually or in bulk (supports pipeline) fix: :poop: Fixing breaking change for Get-AzAccessToken Fixing Get-AzAccessToken, but this needs the Invoke-RestMethod function needs refactoring feat: add support for content type header fix: Add support for files beginning with a dot refactor: Enhance Get-PipelineRun function to use splatting and return structured output refactor: Improve New-PipelineRun function output structure --- .../Private/Invoke-AzDoRestMethod.ps1 | 9 +- .../Private/New-AzDoAuthHeader.ps1 | 3 +- .../Private/Validate-CollectionUri.ps1 | 2 +- .../Git/PullRequests/New-AzDoPullRequest.ps1 | 154 ++++++++++++ .../Public/Api/Git/Pushes/Add-FilesToRepo.ps1 | 2 +- .../Pipelines/Pipelines/New-AzDoPipeline.ps1 | 6 +- .../Api/Pipelines/Runs/Get-PipelineRun.ps1 | 106 +++++++++ .../Api/Pipelines/Runs/New-PipelineRun.ps1 | 125 ++++++++++ .../Endpoints/New-AzDoServiceConnection.ps1 | 3 + .../Get-AzDoClassificationNode.ps1 | 140 +++++++++++ .../New-AzDoClassificationNode.ps1 | 185 ++++++++++++++ .../Remove-AzDoClassificationNode.ps1 | 116 +++++++++ .../Public/Helpers/Set-AzDoPAT.ps1 | 2 +- docs/en-US/New-AzDoPullRequest.md | 225 ++++++++++++++++++ 14 files changed, 1071 insertions(+), 7 deletions(-) create mode 100644 AzureDevOpsPowerShell/Public/Api/Git/PullRequests/New-AzDoPullRequest.ps1 create mode 100644 AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 create mode 100644 AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 create mode 100644 AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Get-AzDoClassificationNode.ps1 create mode 100644 AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/New-AzDoClassificationNode.ps1 create mode 100644 AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Remove-AzDoClassificationNode.ps1 create mode 100644 docs/en-US/New-AzDoPullRequest.md diff --git a/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 b/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 index 7655c88b..a2dd1750 100644 --- a/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 +++ b/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 @@ -33,7 +33,12 @@ function Invoke-AzDoRestMethod { [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] [PSCustomObject[]] - $Body + $Body, + + [Parameter()] + [ValidateSet('application/json', 'application/json-patch+json')] + [string] + $ContentType = 'application/json' ) begin { @@ -73,7 +78,7 @@ function Invoke-AzDoRestMethod { $params = @{ Method = $Method Headers = $script:header - ContentType = 'application/json' + ContentType = $ContentType } if ($QueryParameters) { diff --git a/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 b/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 index 1318f209..537a363d 100644 --- a/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 +++ b/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 @@ -17,7 +17,8 @@ function New-AzDoAuthHeader { $PSCmdlet.ThrowTerminatingError($PSItem) } Write-Verbose "Getting Access Token" - $script:header = @{Authorization = 'Bearer ' + (Get-AzAccessToken -Resource 499b84ac-1321-427f-aa17-267ca6975798).token + $script:header = @{ + Authorization = 'Bearer ' + ((Get-AzAccessToken -Resource 499b84ac-1321-427f-aa17-267ca6975798 -AsSecureString).token | ConvertFrom-SecureString -AsPlainText) } } catch { throw 'Please login to Azure PowerShell first' diff --git a/AzureDevOpsPowerShell/Private/Validate-CollectionUri.ps1 b/AzureDevOpsPowerShell/Private/Validate-CollectionUri.ps1 index 5ea7b8c1..551c2ae6 100644 --- a/AzureDevOpsPowerShell/Private/Validate-CollectionUri.ps1 +++ b/AzureDevOpsPowerShell/Private/Validate-CollectionUri.ps1 @@ -7,7 +7,7 @@ function Validate-CollectionUri { $CollectionUri ) - if ($CollectionUri -notmatch '^https:\/\/dev\.azure\.com\/\w+$') { + if ($CollectionUri -notmatch '^https:\/\/dev\.azure\.com\/\w+') { Write-AzdoError "CollectionUri must be a valid Azure DevOps collection URI starting with 'https://dev.azure.com/'" } else { $true diff --git a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/New-AzDoPullRequest.ps1 b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/New-AzDoPullRequest.ps1 new file mode 100644 index 00000000..7c5eda06 --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/New-AzDoPullRequest.ps1 @@ -0,0 +1,154 @@ +function New-AzDoPullRequest { + <# +.SYNOPSIS + Creates a pull request in Azure DevOps. +.DESCRIPTION + Creates a pull request in Azure DevOps. +.EXAMPLE + $params = @{ + CollectionUri = "https://dev.azure.com/contoso" + RepoName = "Repo1" + ProjectName = "Project 1" + Title = "New Pull Request" + Description = "This is a new pull request" + SourceRefName = "refs/heads/feature1" + TargetRefName = "refs/heads/main" + } + New-AzDoPullRequest @params + + This example creates a new Azure DevOps Pull Request with splatting parameters +.EXAMPLE + $params = @{ + CollectionUri = "https://dev.azure.com/contoso" + RepoName = "Repo1" + ProjectName = "Project 1" + Title = "New Pull Request" + Description = "This is a new pull request" + SourceRefName = "refs/heads/feature1" + TargetRefName = "refs/heads/main" + } + $params | New-AzDoPullRequest + + This example creates a new Azure DevOps Pull Request with pipeline parameters +.OUTPUTS + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + RepoName = $RepoName + PullRequestId = $res.pullRequestId + PullRequestURL = $res.url + } +.NOTES +#> + + [CmdletBinding(SupportsShouldProcess)] + param ( + # Collection Uri of the organization + [Parameter(Mandatory)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + # Id of the repository + [Parameter(Mandatory)] + [string] + $RepoName, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory)] + [string] + $ProjectName, + + # Title of the pull request + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string[]] + $Title, + + # Description of the pull request + [Parameter(ValueFromPipelineByPropertyName)] + [string[]] + $Description = 'Describe the changes made in this pull request', + + # Source ref name + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string[]] + $SourceRefName, + + # Target ref name + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string[]] + $TargetRefName + ) + + begin { + Write-Verbose "Starting function: New-AzDoPullRequest" + $CollectionUri = $CollectionUri.TrimEnd('/') + $result = New-Object System.Collections.Generic.List[System.Object] + } + + process { + foreach ($pr in $Title) { + $prTitle = $pr + $prDescription = $Description[$Title.IndexOf($pr)] + $prSourceRefName = $SourceRefName[$Title.IndexOf($pr)] + $prTargetRefName = $TargetRefName[$Title.IndexOf($pr)] + + # If the ref name is not in the format refs/heads/branch, then add it + if ($prSourceRefName -notlike "refs/*") { + $prSourceRefName = "refs/heads/$prSourceRefName" + } + if ($prTargetRefName -notlike "refs/*") { + $prTargetRefName = "refs/heads/$prTargetRefName" + } + + $params = @{ + uri = "$CollectionUri/$ProjectName/_apis/git/repositories/$RepoName/pullrequests" + version = '7.2-preview.2' + method = 'POST' + body = @{ + sourceRefName = $prSourceRefName + targetRefName = $prTargetRefName + title = $prTitle + description = $prDescription + } + } + + if ($PSCmdlet.ShouldProcess($CollectionUri, "Create pull request named: $($PSStyle.Bold)$prTitle$($PSStyle.Reset)")) { + try { + $result += Invoke-AzDoRestMethod @params + } catch { + if ($_ -match 'TF401179') { + Write-Warning "Pull request between those branches already exists, trying to get it" + $getParams = @{ + uri = "$CollectionUri/$ProjectName/_apis/git/repositories/$RepoName/pullrequests" + version = '7.1-preview.1' + method = 'GET' + } + $result += (Invoke-AzDoRestMethod @getParams).value | Where-Object { $_.sourceRefName -eq $prSourceRefName -and $_.targetRefName -eq $prTargetRefName } + } else { + Write-AzDoError -message $_ + } + } + } else { + Write-Verbose "Calling Invoke-AzDoRestMethod with $($params| ConvertTo-Json -Depth 10)" + } + } + } + + end { + if ($result) { + $result | ForEach-Object { + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + RepoName = $RepoName + PullRequestTitle = $_.title + PullRequestId = $_.pullRequestId + PullRequestURL = $_.url + PullRequestWebUrl = "$CollectionUri/$ProjectName/_git/$RepoName/pullrequest/$($_.pullRequestId)" + PullRequestStatus = $_.status + } + } + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Api/Git/Pushes/Add-FilesToRepo.ps1 b/AzureDevOpsPowerShell/Public/Api/Git/Pushes/Add-FilesToRepo.ps1 index fc7d4b87..60b952fe 100644 --- a/AzureDevOpsPowerShell/Public/Api/Git/Pushes/Add-FilesToRepo.ps1 +++ b/AzureDevOpsPowerShell/Public/Api/Git/Pushes/Add-FilesToRepo.ps1 @@ -49,7 +49,7 @@ function Add-FilesToRepo { process { $changes = @() - $files = Get-ChildItem -Path $Path -Recurse -File + $files = Get-ChildItem -Path $Path -Recurse -File -Force | Where-Object {$_.FullName -notmatch ".git"} foreach ($file in $files) { if (($file.Extension -in '.png', '.svg, .jpg', '.jpeg')) { diff --git a/AzureDevOpsPowerShell/Public/Api/Pipelines/Pipelines/New-AzDoPipeline.ps1 b/AzureDevOpsPowerShell/Public/Api/Pipelines/Pipelines/New-AzDoPipeline.ps1 index 1fc9fd29..218ddd9b 100644 --- a/AzureDevOpsPowerShell/Public/Api/Pipelines/Pipelines/New-AzDoPipeline.ps1 +++ b/AzureDevOpsPowerShell/Public/Api/Pipelines/Pipelines/New-AzDoPipeline.ps1 @@ -48,6 +48,10 @@ function New-AzDoPipeline { [Parameter(Mandatory, ValueFromPipelineByPropertyName)] $RepoName, + # Folder to put Azure Devops Pipeline in + [Parameter(ValueFromPipelineByPropertyName)] + $PipelineFolderPath = $null, + # Path of the YAML-sourcecode in the Repository [Parameter(ValueFromPipelineByPropertyName)] [string] @@ -70,7 +74,7 @@ function New-AzDoPipeline { $body = @{ name = $PipelineName - folder = $null + folder = $PipelineFolderPath configuration = @{ type = "yaml" path = $Path diff --git a/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 new file mode 100644 index 00000000..40e3fc3d --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 @@ -0,0 +1,106 @@ +function Get-PipelineRun { + <# +.SYNOPSIS + Retrieves pipeline run information from Azure DevOps for a specified pipeline within a project. +.DESCRIPTION + The `Get-PipelineRun` function fetches details about one or more pipeline runs from an Azure DevOps project. + It requires the collection URI, project name, and pipeline ID. Optionally, specific run IDs can be provided + to filter the results. The function uses the `Invoke-AzDoRestMethod` cmdlet to make the REST API call to + Azure DevOps and returns the run details. +.EXAMPLE + $getPipelineRunSplat = @{ + CollectionUri = "https://dev.azure.com/YourOrg" + ProjectName = "YourProject" + PipelineId = 123 + } + + Get-PipelineRun @getPipelineRunSplat + Retrieves all runs for the specified pipeline in the given project. +.EXAMPLE + $getPipelineRunSplat = @{ + CollectionUri = "https://dev.azure.com/YourOrg" + ProjectName = "YourProject" + PipelineId = 123 + RunId = 456 + } + + Get-PipelineRun @getPipelineRunSplat + + Retrieves the details of the specified run (with ID 456) for the given pipeline. +.OUTPUTS + System.Management.Automation.PSCustomObject + + Returns an array of pipeline run objects. If specific run IDs are provided, only the matching runs are returned. +.NOTES +#> + [CmdletBinding(SupportsShouldProcess)] + param ( + # Collection Uri of the organization + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + # The name of the project containing the pipeline + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $ProjectName, + + # The ID of the pipeline to retrieve the run for + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [int]$PipelineId, + + # The ID of the run to retrieve. If not provided, all runs for the pipeline are returned. + [Parameter(ValueFromPipelineByPropertyName)] + [int[]]$RunId + ) + + process { + $params = @{ + Uri = "$CollectionUri/$ProjectName/_apis/pipelines/$PipelineId/runs" + Version = "6.0" + Method = 'GET' + } + + if ($PSCmdlet.ShouldProcess("Pipeline Id: $PipelineId", "Get run")) { + $response = Invoke-AzDoRestMethod @params + $runs = $response.value + + if (-not $RunId) { + $runs | ForEach-Object { + [PSCustomObject]@{ + PipelineId = $_.pipeline.id + RunId = $_.id + RunName = $_.name + Result = $_.result + State = $_.state + CreatedAt = $_.createdDate + FinishedAt = $_.finishedDate + ProjectName = $ProjectName + CollectionUri = $CollectionUri + URL = $_.url + } + } + return + } + + @($runs | Where-Object { $_.id -in $RunId } | ForEach-Object { + [PSCustomObject]@{ + PipelineId = $_.pipeline.id + RunId = $_.id + RunName = $_.name + Result = $_.result + State = $_.state + CreatedAt = $_.createdDate + FinishedAt = $_.finishedDate + ProjectName = $ProjectName + CollectionUri = $CollectionUri + URL = $_.url + } + } + ) + } else { + Write-Verbose "Calling Invoke-AzDoRestMethod with $($params| ConvertTo-Json -Depth 10)" + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 new file mode 100644 index 00000000..df2292a3 --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 @@ -0,0 +1,125 @@ +function New-PipelineRun { + <# +.SYNOPSIS + Initiates a new run of an Azure DevOps pipeline. +.DESCRIPTION + The New-PipelineRun function starts a new run for a specified Azure DevOps pipeline. + It supports various parameters to customize the pipeline run, including the collection URI, project name, pipeline ID, branch, parameters, preview run flag, and stages to skip. + This function leverages the Azure DevOps REST API to trigger the pipeline run. +.EXAMPLE + $newPipelineRunSplat = @{ + CollectionUri = "https://dev.azure.com/organization" + ProjectName = "SampleProject" + PipelineId = 123 + StagesToSkip = @("Stage1", "Stage2") + } + + New-PipelineRun @newPipelineRunSplat + This command initiates a new run of the pipeline with ID 123 in the "SampleProject" project. +.EXAMPLE + $newPipelineRunSplat = @{ + CollectionUri = "https://dev.azure.com/organization" + ProjectName = "SampleProject" + PipelineId = 123 + Branch = "dev" + Parameters = @{param1 = "value1"; param2 = "value2"} + } + + New-PipelineRun @newPipelineRunSplat + This command initiates a new run of the pipeline with ID 123 in the "SampleProject" project, targeting the "dev" branch. +.OUTPUTS + System.Management.Automation.PSCustomObject + + Returns the response from the Azure DevOps REST API, which includes details of the pipeline run. +.NOTES +#> + [CmdletBinding(SupportsShouldProcess)] + param ( + # Collection Uri of the organization + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + # Project where the pipeline is defined + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $ProjectName, + + # Pipeline ID of the pipeline to start the run for + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [int]$PipelineId, + + # Parameters to pass to the pipeline + [Parameter(ValueFromPipelineByPropertyName)] + [object]$Parameters, + + # If the run should be a preview run + [Parameter(ValueFromPipelineByPropertyName)] + [switch]$PreviewRun, + + # Stages to skip in the pipeline run + [Parameter(ValueFromPipelineByPropertyName)] + [array]$StagesToSkip, + + # Branch to run the pipeline for + [Parameter(ValueFromPipelineByPropertyName)] + [string]$Branch + ) + process { + $body = @{} + + if ($PreviewRun) { + $body.Add("previewRun", $PreviewRun) + } + + if ($Branch) { + $resources = @{ + repositories = @{ + self = @{ + refName = $Branch + } + } + } + + $body.Add("resources", $resources) + } + + if ($StagesToSkip) { + $body.Add("stagesToSkip", $StagesToSkip) + } + + if ($Parameters) { + $body.Add("templateParameters", $Parameters) + } else { + $body.Add("templateParameters", @{}) + } + + $params = @{ + Uri = "$CollectionUri/$ProjectName/_apis/pipelines/$PipelineId/runs" + Version = "7.0" + Method = 'POST' + Body = $body + } + + if ($PSCmdlet.ShouldProcess("PipelineId: $PipelineId", "Trigger a new run on pipeline")) { + try { + $output = Invoke-AzDoRestMethod @params + [PSCustomObject]@{ + PipelineId = $output.pipeline.id + RunId = $output.id + RunName = $output.name + CreatedAt = $output.createdDate + ProjectName = $ProjectName + CollectionUri = $CollectionUri + URL = $output.url + Resources = $output.resources + } + } catch { + Write-AzDoError -Message $_ + } + } else { + Write-Verbose "Calling Invoke-AzDoRestMethod with $($params| ConvertTo-Json -Depth 10)" + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Api/ServiceEndpoints/Endpoints/New-AzDoServiceConnection.ps1 b/AzureDevOpsPowerShell/Public/Api/ServiceEndpoints/Endpoints/New-AzDoServiceConnection.ps1 index 3219df9f..1e14f733 100644 --- a/AzureDevOpsPowerShell/Public/Api/ServiceEndpoints/Endpoints/New-AzDoServiceConnection.ps1 +++ b/AzureDevOpsPowerShell/Public/Api/ServiceEndpoints/Endpoints/New-AzDoServiceConnection.ps1 @@ -77,6 +77,7 @@ function New-AzDoServiceConnection { # Scope level (Subscription or ManagementGroup). [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ServiceprincipalSecret')] [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ServiceprincipalCertificate')] + [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'WorkloadIdentityFederation')] [ValidateSet('Subscription', 'ManagementGroup')] [string] $ScopeLevel, @@ -89,11 +90,13 @@ function New-AzDoServiceConnection { # ID of the subscriptionn. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Subscription')] + [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'WorkloadIdentityFederation')] [string] $SubscriptionId, # Name of the subscription. [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'Subscription')] + [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = 'WorkloadIdentityFederation')] [string] $SubscriptionName, diff --git a/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Get-AzDoClassificationNode.ps1 b/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Get-AzDoClassificationNode.ps1 new file mode 100644 index 00000000..b4cff201 --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Get-AzDoClassificationNode.ps1 @@ -0,0 +1,140 @@ +function Get-AzDoClassificationNode { + <# +.SYNOPSIS + Get a Classification Node in Azure DevOps. +.DESCRIPTION + Get a Classification Node in Azure DevOps. This could be an area or an iteration. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "areas" + Name = "Area1" + Depth = '2' + } + + Get-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'areas' within the Project. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "areas" + Name = "Area1" + Path = "Path1" + Depth = '2' + } + + Get-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'areas' within the specified path. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "iterations" + Name = "Iteration1" + Depth = '2' + } + + Get-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'iterations' within the specified path. + +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "iterations" + Name = "Iteration1" + Path = "Path1" + Depth = '2' + } + + Get-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'iterations' within the specified path. +#> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + # Collection Uri of the organization + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $ProjectName, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $StructureGroup, + + # Path of the classification node (optional) + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Path, + + # Name of the classification node + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $Name, + + # Optional parameter to specify the depth of child nodes to retrieve + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Depth = '2' + ) + + begin { + Write-Verbose "Starting function: Get-AzDoClassificationNode" + $result = @() + } + + process { + $ProjectId = (Get-AzDoProject -CollectionUri $CollectionUri -ProjectName $ProjectName).Projectid + + if ($Path) { + $uri = "$CollectionUri/$ProjectName/_apis/wit/classificationnodes/$StructureGroup/$Path/$Name" + } else { + $uri = "$CollectionUri/$ProjectName/_apis/wit/classificationnodes/$StructureGroup/$Name" + } + + $params = @{ + uri = $uri + version = "7.2-preview.2" + QueryParameters = "`$depth=$depth" + Method = 'GET' + } + + if ($PSCmdlet.ShouldProcess($ProjectName, "Get Classification Node named: $($PSStyle.Bold)$Name$($PSStyle.Reset)")) { + $result += Invoke-AzDoRestMethod @params | ForEach-Object { + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + Id = $_.id + Identifier = $_.identifier + Name = $_.name + StructureType = $_.structureType + HasChildren = $_.hasChildren + Children = $_.children + Path = $_.path + Links = $_._links + Url = $_.url + } + } + } else { + Write-Verbose "Calling Invoke-AzDoRestMethod with $($params| ConvertTo-Json -Depth 10)" + } + } + + end { + if ($result) { + $result + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/New-AzDoClassificationNode.ps1 b/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/New-AzDoClassificationNode.ps1 new file mode 100644 index 00000000..7fc1062d --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/New-AzDoClassificationNode.ps1 @@ -0,0 +1,185 @@ +function New-AzDoClassificationNode { + <# +.SYNOPSIS + Creates a Classification Node in Azure DevOps. +.DESCRIPTION + Creates a Classification Node in Azure DevOps. This could be an area or an iteration. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "areas" + Name = "Area1" + } + + New-AzDoClassificationNode @Params + + This example creates a Classification Node of the type 'areas' within the Project. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "areas" + Name = "Area1" + Path = "Path1" + } + + New-AzDoClassificationNode @Params + + This example creates a Classification Node of the type 'areas' within the specified path. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "iterations" + Name = "Iteration1" + } + + New-AzDoClassificationNode @Params + + This example creates a Classification Node of the type 'iterations' within the Project. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "iterations" + Name = "Iteration1" + Path = "Path1" + startDate = "10//701001 00:00:00 + finishDate = "10//701008 00:00:00 + } + + New-AzDoClassificationNode @Params + + This example creates a Classification Node of the type 'iterations' within the specified path, it is also possible to use a start and finish date of the iteration by adding the optional parameters 'startDate' and 'finishDate'. +.OUTPUTS + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + Id = $_.id + Identifier = $_.identifier + Name = $_.name + StructureType = $_.structureType + HasChildren = $_.hasChildren + Path = $_.path + Links = $_._links + Url = $_.url + } +.NOTES +#> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + # Collection Uri of the organization + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $ProjectName, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $StructureGroup, + + # Path of the classification node (optional) + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Path, + + # Name of the classification node + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $Name, + + # Start date of the iteration (optional) + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $startDate, + + # Finish date of the iteration (optional) + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $finishDate + ) + + begin { + Write-Verbose "Starting function: New-AzDoClassificationNode" + } + + process { + $ProjectId = (Get-AzDoProject -CollectionUri $CollectionUri -ProjectName $ProjectName).Projectid + + if ($Path) { + $uri = "$CollectionUri/$ProjectName/_apis/wit/classificationnodes/$StructureGroup/$Path" + } else { + $uri = "$CollectionUri/$ProjectName/_apis/wit/classificationnodes/$StructureGroup" + } + + $params = @{ + uri = $uri + version = "7.2-preview.2" + Method = 'POST' + } + + if ($StructureGroup -eq 'areas') { + $body = @{ + name = $Name + } + } + if ($StructureGroup -eq 'iterations') { + $body = @{ + name = $Name + attributes = @{ + startDate = $startDate + finishDate = $finishDate + } + } + } + + + if ($PSCmdlet.ShouldProcess($ProjectName, "Create Classification Node named: $($PSStyle.Bold)$Name$($PSStyle.Reset)")) { + try { + $body | Invoke-AzDoRestMethod @params | ForEach-Object { + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + Id = $_.id + Identifier = $_.identifier + Name = $_.name + StructureType = $_.structureType + HasChildren = $_.hasChildren + Path = $_.path + Links = $_._links + Url = $_.url + } + } + } catch { + if ($_ -match 'is already in use by a different') { + Write-Warning "Classification Node already exists within the path, trying to get it" + $params.Method = 'GET' + $params.uri = "$uri/$Name" + Invoke-AzDoRestMethod @params | ForEach-Object { + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + Id = $_.id + Identifier = $_.identifier + Name = $_.name + StructureType = $_.structureType + HasChildren = $_.hasChildren + Path = $_.path + Links = $_._links + Url = $_.url + } + } + } else { + Write-AzDoError -message $_ + } + } + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Remove-AzDoClassificationNode.ps1 b/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Remove-AzDoClassificationNode.ps1 new file mode 100644 index 00000000..815f7cc3 --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/WorkItemTracking/ClassificationNodes/Remove-AzDoClassificationNode.ps1 @@ -0,0 +1,116 @@ +function Remove-AzDoClassificationNode { + <# +.SYNOPSIS + Delete a Classification Node in Azure DevOps. +.DESCRIPTION + Delete a Classification Node in Azure DevOps. This could be an area or an iteration. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "areas" + Name = "Area1" + } + + Remove-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'areas' within the Project. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "areas" + Name = "Area1" + Path = "Path1" + } + + Remove-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'areas' within the specified path. +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "iterations" + Name = "Iteration1" + } + + Remove-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'iterations' within the specified path. + +.EXAMPLE + $Params = @{ + CollectionUri = "https://dev.azure.com/cantoso" + ProjectName = "Playground" + StructureGroup = "iterations" + Name = "Iteration1" + Path = "Path1" + } + + Remove-AzDoClassificationNode @Params + + This example removes a Classification Node of the type 'iterations' within the specified path. +#> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] + param ( + # Collection Uri of the organization + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $ProjectName, + + # Name of the project where the new repository has to be created + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $StructureGroup, + + # Path of the classification node (optional) + [Parameter(ValueFromPipelineByPropertyName)] + [string] + $Path, + + # Name of the classification node + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [string] + $Name + ) + + begin { + Write-Verbose "Starting function: Remove-AzDoClassificationNode" + $result = @() + } + + process { + $ProjectId = (Get-AzDoProject -CollectionUri $CollectionUri -ProjectName $ProjectName).Projectid + + if ($Path) { + $uri = "$CollectionUri/$ProjectName/_apis/wit/classificationnodes/$StructureGroup/$Path/$Name" + } else { + $uri = "$CollectionUri/$ProjectName/_apis/wit/classificationnodes/$StructureGroup/$Name" + } + + $params = @{ + uri = $uri + version = "7.2-preview.2" + Method = 'DELETE' + } + + if ($PSCmdlet.ShouldProcess($ProjectName, "Delete Classification Node named: $($PSStyle.Bold)$Name$($PSStyle.Reset)")) { + $result += Invoke-AzDoRestMethod @params + } else { + Write-Verbose "Calling Invoke-AzDoRestMethod with $($params| ConvertTo-Json -Depth 10)" + } + } + + end { + if ($result) { + $result + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Helpers/Set-AzDoPAT.ps1 b/AzureDevOpsPowerShell/Public/Helpers/Set-AzDoPAT.ps1 index ec01c08f..98a9d502 100644 --- a/AzureDevOpsPowerShell/Public/Helpers/Set-AzDoPAT.ps1 +++ b/AzureDevOpsPowerShell/Public/Helpers/Set-AzDoPAT.ps1 @@ -20,7 +20,7 @@ function Set-AzDoPAT { ) process { - if ($PSCmdlet.ShouldProcess()) { + if ($PSCmdlet.ShouldProcess("AzureDevopsPowerShell module", "Set PAT")) { New-AzDoAuthHeader -Pat $Pat } else { Write-Verbose "Calling New-AzDoAuthHeader with pat" diff --git a/docs/en-US/New-AzDoPullRequest.md b/docs/en-US/New-AzDoPullRequest.md new file mode 100644 index 00000000..e1f791d8 --- /dev/null +++ b/docs/en-US/New-AzDoPullRequest.md @@ -0,0 +1,225 @@ +--- +external help file: AzureDevOpsPowerShell-help.xml +Module Name: AzureDevOpsPowerShell +online version: +schema: 2.0.0 +--- + +# New-AzDoPullRequest + +## SYNOPSIS +Creates a pull request in Azure DevOps. + +## SYNTAX + +``` +New-AzDoPullRequest [-CollectionUri] [-RepoName] [-ProjectName] [-Title] [-Description] [-SourceRefName] [-TargetRefName] + [-ProgressAction ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION +Creates a pull request in Azure DevOps. + +## EXAMPLES + +### EXAMPLE 1 +``` +$params = @{ + CollectionUri = "https://dev.azure.com/contoso" + RepoName = "Repo 1" + ProjectName = "Project 1" + Title = "New Pull Request" + Description = "This is a new pull request" + SourceRefName = "refs/heads/feature1" + TargetRefName = "refs/heads/main" + } + New-AzDoPullRequest @params +``` + +This example creates a new Azure DevOps Pull Request with splatting parameters + +### Example 2 +``` +$params = @{ + CollectionUri = "https://dev.azure.com/contoso" + RepoName = "Repo1" + ProjectName = "Project 1" + Title = "New Pull Request" + Description = "This is a new pull request" + SourceRefName = "refs/heads/feature1" + TargetRefName = "refs/heads/main" + } + $params | New-AzDoPullRequest + + This example creates a new Azure DevOps Pull Request with pipeline parameters +``` +## PARAMETERS + +### -CollectionUri +Collection Uri of the organization + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -RepoName +Name of the repo + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: 2 +Default value: None +Accept pipeline input: True (ByPropertyName, ByValue) +Accept wildcard characters: False +``` + +### -ProjectName +Name of the project where the repository is hosted + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 3 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Title +The title of the new pull request + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 4 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Description +The description of the new pull request + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 5 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -SourceRefName +The source branch path of the pull request + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 6 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -TargetRefName +The target branch path of the pull request + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 7 +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -WhatIf +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ProgressAction +{{ Fill ProgressAction Description }} + +```yaml +Type: ActionPreference +Parameter Sets: (All) +Aliases: proga + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +## OUTPUTS + +### [PSCustomObject]@{ +### CollectionUri = $CollectionUri +### ProjectName = $ProjectName +### RepoId = $RepoId +### PullRequestId = $res.pullRequestId +### PullRequestURL = $res.url +### } +## NOTES + +## RELATED LINKS From b7f5b02462fd82bb91912e21a635df991a33a117 Mon Sep 17 00:00:00 2001 From: Bas Wijdenes Date: Fri, 7 Feb 2025 16:48:17 +0100 Subject: [PATCH 2/7] updated Get-AzDoPull~ and Set-AzDoPull~ --- .gitignore | 3 + .../AzureDevOpsPowerShell.psd1 | 2 +- .../AzureDevOpsPowerShell.psm1 | 10 +- .../Private/Invoke-AzDoRestMethod.ps1 | 3 + .../Private/New-AzDoAuthHeader.ps1 | 2 +- .../Git/PullRequests/Get-AzDoPullRequest.ps1 | 141 +++++++++++++ .../Git/PullRequests/Set-AzDoPullRequest.ps1 | 199 ++++++++++++++++++ 7 files changed, 353 insertions(+), 7 deletions(-) create mode 100644 AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 create mode 100644 AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 diff --git a/.gitignore b/.gitignore index 78a4fbb7..f0bb716c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ # Don't check in the Output dir Output/ site/ +baswijdenes/ +test.ps1 +.vs diff --git a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 index 1f0dd4de..6b27807d 100644 --- a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 +++ b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 @@ -126,7 +126,7 @@ } # End of PrivateData hashtable # HelpInfo URI of this module - # HelpInfoURI = '' + HelpInfoURI = 'https://github.com/WeAreInSpark/AzureDevOpsPowerShellAPI' # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # DefaultCommandPrefix = '' diff --git a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 index c5dec8e7..ee9dfcfd 100644 --- a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 +++ b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 @@ -2,11 +2,11 @@ $public = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Public/*.ps1') -Recurse -ErrorAction Stop) $private = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Private/*.ps1') -Recurse -ErrorAction Stop) foreach ($import in @($private + $public)) { - try { - . $import.FullName - } catch { - throw "Unable to dot source [$($import.FullName)]" - } + try { + . $import.FullName + } catch { + throw "Unable to dot source [$($import.FullName)]" + } } Export-ModuleMember -Function $public.Basename diff --git a/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 b/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 index a2dd1750..a36790aa 100644 --- a/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 +++ b/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 @@ -82,6 +82,9 @@ function Invoke-AzDoRestMethod { } if ($QueryParameters) { + if ($QueryParameters -notlike "?*") { + $QueryParameters = "?$QueryParameters" + } $params.Uri = "$($Uri)?$($QueryParameters)&api-version=$($Version)" } else { $params.Uri = "$($Uri)?api-version=$($Version)" diff --git a/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 b/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 index 537a363d..f2048e26 100644 --- a/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 +++ b/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 @@ -8,7 +8,7 @@ function New-AzDoAuthHeader { ) if ($PSCmdlet.ShouldProcess("Creating new authentication header")) { Write-Verbose "Function: New-AzDoAuthHeader" - if ($Pat -eq '') { + if ([string]::IsNullOrEmpty($Pat)) { # validate if user is logged in to Azure PowerShell Write-Verbose "Using Access Token" try { diff --git a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 new file mode 100644 index 00000000..26e79a8c --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 @@ -0,0 +1,141 @@ +function Get-AzDoPullRequest { + <# +.SYNOPSIS + Retrieves pull request information from Azure DevOps. + +.DESCRIPTION + This function fetches pull request details using Azure DevOps REST API. + +.PARAMETER CollectionUri + The base URL of the Azure DevOps organization (e.g., https://dev.azure.com/my-org). + +.PARAMETER ProjectName + The name of the Azure DevOps project. + +.PARAMETER RepositoryName + The name of the repository (optional for project-wide pull request queries). + +.PARAMETER PullRequestId + The ID of a specific pull request (optional for listing all pull requests). + +.PARAMETER Query + A query string to filter the results (optional). + Can only be used without PullRequestId: + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-7.2&tabs=HTTP#uri-parameters + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests-by-project?view=azure-devops-rest-7.2&tabs=HTTP#uri-parameters + +.EXAMPLE + Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" + +.EXAMPLE + Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "RepositoryName" + +.EXAMPLE + Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "RepositoryName" -Query "searchCriteria.status=completed" + +.EXAMPLE + Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "RepositoryName" -PullRequestId "6789" + +.EXAMPLE + Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -PullRequestId "6789" + +.OUTPUTS + PSCustomObject with pull request details. + +.NOTES + Requires authentication with Azure DevOps REST API. + +.LINK + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-request?view=azure-devops-rest-7.2 + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-request-by-id?view=azure-devops-rest-7.2 + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-7.2 + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests-by-project?view=azure-devops-rest-7.2 +#> + [CmdletBinding(DefaultParameterSetName = "AllProjectPullRequests", SupportsShouldProcess)] + param ( + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "RepoSpecificPullRequest")] + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "ProjectSpecificPullRequest")] + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllRepoPullRequests")] + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllProjectPullRequests")] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "RepoSpecificPullRequest")] + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "ProjectSpecificPullRequest")] + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllRepoPullRequests")] + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllProjectPullRequests")] + [string] + $ProjectName, + + [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "RepoSpecificPullRequest")] + [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "AllRepoPullRequests")] + [string] + $RepoName, + + [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "RepoSpecificPullRequest")] + [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "ProjectSpecificPullRequest")] + [string] + $PullRequestId, + + [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "AllRepoPullRequests")] + [string] + $Query + ) + + + begin { + $result = @() + Write-Verbose "Starting function: Get-AzDoPullRequest" + } + + process { + $uriBase = "$CollectionUri/$ProjectName/_apis/git" + $apiVersion = "7.2-preview.2" + $uri = "" + + switch ($PSCmdlet.ParameterSetName) { + "RepoSpecificPullRequest" { + $uri = "$uriBase/repositories/$RepoName/pullrequests/$PullRequestId" + } + "ProjectSpecificPullRequest" { + $uri = "$uriBase/pullrequests/$PullRequestId" + } + "AllRepoPullRequests" { + $uri = "$uriBase/repositories/$RepoName/pullrequests" + } + "AllProjectPullRequests" { + $uri = "$uriBase/pullrequests" + } + } + + if ($PSCmdlet.ShouldProcess($CollectionUri, "Get Pull Request from: $ProjectName")) { + Write-Verbose "Calling API: $uri" + + $InvokeAzDoRestMethodSplat = @{ + Uri = $uri + Method = "GET" + Version = $apiVersion + } + if (-not([string]::IsNullOrEmpty($Query))) { + $InvokeAzDoRestMethodSplat.QueryParameters = $Query + } + $response = Invoke-AzDoRestMethod @InvokeAzDoRestMethodSplat + if ($response.value) { + $result += $response.value + } else { + $result += $response + } + if ($result) { + $result | ForEach-Object { + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + RepoName = $RepoName + PullRequest = $PSItem + } + } + } + } + } +} diff --git a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 new file mode 100644 index 00000000..5f612b09 --- /dev/null +++ b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 @@ -0,0 +1,199 @@ +function Set-AzDoPullRequest { + <# +.SYNOPSIS + Retrieves pull request information from Azure DevOps. + +.DESCRIPTION + This function fetches pull request details using Azure DevOps REST API. + +.PARAMETER CollectionUri + The base URL of the Azure DevOps organization (e.g., https://dev.azure.com/my-org). + +.PARAMETER ProjectName + The name of the Azure DevOps project. + +.PARAMETER RepositoryName + The name of the repository (optional for project-wide pull request queries). + +.PARAMETER PullRequestId + The ID of a specific pull request (optional for listing all pull requests). + +.PARAMETER Status + The new status of the pull request. Allowed values: active, abandoned, completed. + +.PARAMETER Title + The new title for the pull request (max 256 characters). + +.PARAMETER Description + The new description for the pull request (max 4000 characters). + +.PARAMETER CompletionOptions + Specifies how the PR should be completed. Example: @{ deleteSourceBranch = $true; mergeCommitMessage = "Merged PR" } + +.PARAMETER MergeOptions + Specifies how the PR should be merged. Allowed values: noMerge, squash, rebase, rebaseMerge. + +.PARAMETER AutoCompleteSetBy + The Azure DevOps user ID who sets the PR to auto-complete. + +.PARAMETER TargetRefName + The new target branch for the pull request. Example: "main" (automatically prefixed with "refs/heads/"). + Retargeting a pull request means changing the destination branch where the pull request will be merged. + +.EXAMPLE + # Update only the title and description of a pull request + Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -Title "Updated PR Title" -Description "New description" + +.EXAMPLE + # Set auto-complete with completion options + $completionOptions = @{ + deleteSourceBranch = $true + mergeCommitMessage = "Auto-merging PR" + } + Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -AutoCompleteSetBy "user-id-123" -CompletionOptions $completionOptions + +.EXAMPLE + # Change the merge strategy to squash and complete the PR + Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -MergeOptions "squash" -Status "completed" + +.EXAMPLE + # Retarget a pull request to a different branch + Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -TargetRefName "develop" + +.EXAMPLE + # Set auto-complete for a pull request with a transition work item option + $completionOptions = @{ + transitionWorkItems = $true + deleteSourceBranch = $true + } + Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -AutoCompleteSetBy "user-id-123" -CompletionOptions $completionOptions + + +.OUTPUTS + PSCustomObject with pull request details. + +.NOTES + Requires authentication with Azure DevOps REST API. + + I have currently added the 'extra parameters' to the function, that are registered in the first paragraph on this page: + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update?view=azure-devops-rest-7.2&tabs=HTTP#request-body + + With the text currently being: + These are the properties that can be updated with the API: + Status + Title + Description (up to 4000 characters) + CompletionOptions + MergeOptions + AutoCompleteSetBy.Id + TargetRefName (when the PR retargeting feature is enabled) Attempting to update other properties outside of this list will either cause the server to throw an InvalidArgumentValueException, or to silently ignore the update. + +.LINK + https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update?view=azure-devops-rest-7.2&tabs=HTTP#request-body +#> + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] + [string] + $CollectionUri, + + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline)] + [string] + $ProjectName, + + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline)] + [string] + $RepositoryName, + + [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline)] + [string] + $PullRequestId, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [ValidateSet('active', 'abandoned', 'completed', 'all')] + [string] + $Status, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [ValidateLength(0, 256)] + [string] + $Title, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [ValidateLength(0, 4000)] + [string] + $Description, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [ValidateSet('setAutoComplete', 'setAutoCompleteSetBy')] + [string] + $CompletionOptions, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [ValidateSet('noMerge', 'squash', 'rebase', 'rebaseMerge')] + [string] + $MergeOptions, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [string] + $AutoCompleteSetBy, + + [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] + [string] + $TargetRefName + ) + + begin { + $result = @() + Write-Verbose "Starting function: Set-AzDoPullRequest" + } + + process { + $uriBase = "$CollectionUri/$ProjectName/_apis/git" + $apiVersion = "7.2-preview.2" + + $body = @{} + + if ($Status) { $body["status"] = $Status } + if ($Title) { $body["title"] = $Title } + if ($Description) { $body["description"] = $Description } + if ($MergeOptions) { $body["mergeOptions"] = $MergeOptions } + if ($TargetRefName) { $body["targetRefName"] = "refs/heads/$TargetRefName" } + + if ($CompletionOptions -and $CompletionOptions.Count -gt 0) { + $body["completionOptions"] = $CompletionOptions + } + + if ($AutoCompleteSetBy) { + $body["autoCompleteSetBy"] = @{ id = $AutoCompleteSetBy } + } + + if ($PSCmdlet.ShouldProcess($CollectionUri, "Get Pull Request from: $ProjectName")) { + Write-Verbose "Calling API: $uri" + + $InvokeAzDoRestMethodSplat = @{ + Uri = "$uriBase/repositories/$RepositoryName/pullrequests/$PullRequestId" + Method = "PATCH" + Version = $apiVersion + Body = $Body + } + $response = Invoke-AzDoRestMethod @InvokeAzDoRestMethodSplat + if ($response.value) { + $result += $response.value + } else { + $result += $response + } + if ($result) { + $result | ForEach-Object { + [PSCustomObject]@{ + CollectionUri = $CollectionUri + ProjectName = $ProjectName + RepoName = $RepoName + PullRequest = $PSItem + } + } + } + } + } +} From d1e0a742f7afdec4bd9ad4641e17a133a4a44b55 Mon Sep 17 00:00:00 2001 From: Bas Wijdenes Date: Tue, 11 Feb 2025 09:44:19 +0100 Subject: [PATCH 3/7] Revert "updated Get-AzDoPull~ and Set-AzDoPull~" This reverts commit b7f5b02462fd82bb91912e21a635df991a33a117. --- .gitignore | 3 - .../AzureDevOpsPowerShell.psd1 | 2 +- .../AzureDevOpsPowerShell.psm1 | 10 +- .../Private/Invoke-AzDoRestMethod.ps1 | 3 - .../Private/New-AzDoAuthHeader.ps1 | 2 +- .../Git/PullRequests/Get-AzDoPullRequest.ps1 | 141 ------------- .../Git/PullRequests/Set-AzDoPullRequest.ps1 | 199 ------------------ 7 files changed, 7 insertions(+), 353 deletions(-) delete mode 100644 AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 delete mode 100644 AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 diff --git a/.gitignore b/.gitignore index f0bb716c..78a4fbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,3 @@ # Don't check in the Output dir Output/ site/ -baswijdenes/ -test.ps1 -.vs diff --git a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 index 6b27807d..1f0dd4de 100644 --- a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 +++ b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psd1 @@ -126,7 +126,7 @@ } # End of PrivateData hashtable # HelpInfo URI of this module - HelpInfoURI = 'https://github.com/WeAreInSpark/AzureDevOpsPowerShellAPI' + # HelpInfoURI = '' # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # DefaultCommandPrefix = '' diff --git a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 index ee9dfcfd..c5dec8e7 100644 --- a/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 +++ b/AzureDevOpsPowerShell/AzureDevOpsPowerShell.psm1 @@ -2,11 +2,11 @@ $public = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Public/*.ps1') -Recurse -ErrorAction Stop) $private = @(Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Private/*.ps1') -Recurse -ErrorAction Stop) foreach ($import in @($private + $public)) { - try { - . $import.FullName - } catch { - throw "Unable to dot source [$($import.FullName)]" - } + try { + . $import.FullName + } catch { + throw "Unable to dot source [$($import.FullName)]" + } } Export-ModuleMember -Function $public.Basename diff --git a/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 b/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 index a36790aa..a2dd1750 100644 --- a/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 +++ b/AzureDevOpsPowerShell/Private/Invoke-AzDoRestMethod.ps1 @@ -82,9 +82,6 @@ function Invoke-AzDoRestMethod { } if ($QueryParameters) { - if ($QueryParameters -notlike "?*") { - $QueryParameters = "?$QueryParameters" - } $params.Uri = "$($Uri)?$($QueryParameters)&api-version=$($Version)" } else { $params.Uri = "$($Uri)?api-version=$($Version)" diff --git a/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 b/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 index f2048e26..537a363d 100644 --- a/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 +++ b/AzureDevOpsPowerShell/Private/New-AzDoAuthHeader.ps1 @@ -8,7 +8,7 @@ function New-AzDoAuthHeader { ) if ($PSCmdlet.ShouldProcess("Creating new authentication header")) { Write-Verbose "Function: New-AzDoAuthHeader" - if ([string]::IsNullOrEmpty($Pat)) { + if ($Pat -eq '') { # validate if user is logged in to Azure PowerShell Write-Verbose "Using Access Token" try { diff --git a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 deleted file mode 100644 index 26e79a8c..00000000 --- a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Get-AzDoPullRequest.ps1 +++ /dev/null @@ -1,141 +0,0 @@ -function Get-AzDoPullRequest { - <# -.SYNOPSIS - Retrieves pull request information from Azure DevOps. - -.DESCRIPTION - This function fetches pull request details using Azure DevOps REST API. - -.PARAMETER CollectionUri - The base URL of the Azure DevOps organization (e.g., https://dev.azure.com/my-org). - -.PARAMETER ProjectName - The name of the Azure DevOps project. - -.PARAMETER RepositoryName - The name of the repository (optional for project-wide pull request queries). - -.PARAMETER PullRequestId - The ID of a specific pull request (optional for listing all pull requests). - -.PARAMETER Query - A query string to filter the results (optional). - Can only be used without PullRequestId: - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-7.2&tabs=HTTP#uri-parameters - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests-by-project?view=azure-devops-rest-7.2&tabs=HTTP#uri-parameters - -.EXAMPLE - Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" - -.EXAMPLE - Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "RepositoryName" - -.EXAMPLE - Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "RepositoryName" -Query "searchCriteria.status=completed" - -.EXAMPLE - Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "RepositoryName" -PullRequestId "6789" - -.EXAMPLE - Get-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -PullRequestId "6789" - -.OUTPUTS - PSCustomObject with pull request details. - -.NOTES - Requires authentication with Azure DevOps REST API. - -.LINK - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-request?view=azure-devops-rest-7.2 - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-request-by-id?view=azure-devops-rest-7.2 - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests?view=azure-devops-rest-7.2 - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/get-pull-requests-by-project?view=azure-devops-rest-7.2 -#> - [CmdletBinding(DefaultParameterSetName = "AllProjectPullRequests", SupportsShouldProcess)] - param ( - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "RepoSpecificPullRequest")] - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "ProjectSpecificPullRequest")] - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllRepoPullRequests")] - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllProjectPullRequests")] - [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] - [string] - $CollectionUri, - - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "RepoSpecificPullRequest")] - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "ProjectSpecificPullRequest")] - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllRepoPullRequests")] - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = "AllProjectPullRequests")] - [string] - $ProjectName, - - [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "RepoSpecificPullRequest")] - [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "AllRepoPullRequests")] - [string] - $RepoName, - - [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "RepoSpecificPullRequest")] - [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = "ProjectSpecificPullRequest")] - [string] - $PullRequestId, - - [Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "AllRepoPullRequests")] - [string] - $Query - ) - - - begin { - $result = @() - Write-Verbose "Starting function: Get-AzDoPullRequest" - } - - process { - $uriBase = "$CollectionUri/$ProjectName/_apis/git" - $apiVersion = "7.2-preview.2" - $uri = "" - - switch ($PSCmdlet.ParameterSetName) { - "RepoSpecificPullRequest" { - $uri = "$uriBase/repositories/$RepoName/pullrequests/$PullRequestId" - } - "ProjectSpecificPullRequest" { - $uri = "$uriBase/pullrequests/$PullRequestId" - } - "AllRepoPullRequests" { - $uri = "$uriBase/repositories/$RepoName/pullrequests" - } - "AllProjectPullRequests" { - $uri = "$uriBase/pullrequests" - } - } - - if ($PSCmdlet.ShouldProcess($CollectionUri, "Get Pull Request from: $ProjectName")) { - Write-Verbose "Calling API: $uri" - - $InvokeAzDoRestMethodSplat = @{ - Uri = $uri - Method = "GET" - Version = $apiVersion - } - if (-not([string]::IsNullOrEmpty($Query))) { - $InvokeAzDoRestMethodSplat.QueryParameters = $Query - } - $response = Invoke-AzDoRestMethod @InvokeAzDoRestMethodSplat - if ($response.value) { - $result += $response.value - } else { - $result += $response - } - if ($result) { - $result | ForEach-Object { - [PSCustomObject]@{ - CollectionUri = $CollectionUri - ProjectName = $ProjectName - RepoName = $RepoName - PullRequest = $PSItem - } - } - } - } - } -} diff --git a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 b/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 deleted file mode 100644 index 5f612b09..00000000 --- a/AzureDevOpsPowerShell/Public/Api/Git/PullRequests/Set-AzDoPullRequest.ps1 +++ /dev/null @@ -1,199 +0,0 @@ -function Set-AzDoPullRequest { - <# -.SYNOPSIS - Retrieves pull request information from Azure DevOps. - -.DESCRIPTION - This function fetches pull request details using Azure DevOps REST API. - -.PARAMETER CollectionUri - The base URL of the Azure DevOps organization (e.g., https://dev.azure.com/my-org). - -.PARAMETER ProjectName - The name of the Azure DevOps project. - -.PARAMETER RepositoryName - The name of the repository (optional for project-wide pull request queries). - -.PARAMETER PullRequestId - The ID of a specific pull request (optional for listing all pull requests). - -.PARAMETER Status - The new status of the pull request. Allowed values: active, abandoned, completed. - -.PARAMETER Title - The new title for the pull request (max 256 characters). - -.PARAMETER Description - The new description for the pull request (max 4000 characters). - -.PARAMETER CompletionOptions - Specifies how the PR should be completed. Example: @{ deleteSourceBranch = $true; mergeCommitMessage = "Merged PR" } - -.PARAMETER MergeOptions - Specifies how the PR should be merged. Allowed values: noMerge, squash, rebase, rebaseMerge. - -.PARAMETER AutoCompleteSetBy - The Azure DevOps user ID who sets the PR to auto-complete. - -.PARAMETER TargetRefName - The new target branch for the pull request. Example: "main" (automatically prefixed with "refs/heads/"). - Retargeting a pull request means changing the destination branch where the pull request will be merged. - -.EXAMPLE - # Update only the title and description of a pull request - Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -Title "Updated PR Title" -Description "New description" - -.EXAMPLE - # Set auto-complete with completion options - $completionOptions = @{ - deleteSourceBranch = $true - mergeCommitMessage = "Auto-merging PR" - } - Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -AutoCompleteSetBy "user-id-123" -CompletionOptions $completionOptions - -.EXAMPLE - # Change the merge strategy to squash and complete the PR - Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -MergeOptions "squash" -Status "completed" - -.EXAMPLE - # Retarget a pull request to a different branch - Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -TargetRefName "develop" - -.EXAMPLE - # Set auto-complete for a pull request with a transition work item option - $completionOptions = @{ - transitionWorkItems = $true - deleteSourceBranch = $true - } - Set-AzDoPullRequest -CollectionUri "https://dev.azure.com/my-org" -ProjectName "MyProject" -RepositoryName "Repo" -PullRequestId "123" -AutoCompleteSetBy "user-id-123" -CompletionOptions $completionOptions - - -.OUTPUTS - PSCustomObject with pull request details. - -.NOTES - Requires authentication with Azure DevOps REST API. - - I have currently added the 'extra parameters' to the function, that are registered in the first paragraph on this page: - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update?view=azure-devops-rest-7.2&tabs=HTTP#request-body - - With the text currently being: - These are the properties that can be updated with the API: - Status - Title - Description (up to 4000 characters) - CompletionOptions - MergeOptions - AutoCompleteSetBy.Id - TargetRefName (when the PR retargeting feature is enabled) Attempting to update other properties outside of this list will either cause the server to throw an InvalidArgumentValueException, or to silently ignore the update. - -.LINK - https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update?view=azure-devops-rest-7.2&tabs=HTTP#request-body -#> - [CmdletBinding(SupportsShouldProcess)] - param ( - [Parameter(Mandatory, ValueFromPipelineByPropertyName)] - [ValidateScript({ Validate-CollectionUri -CollectionUri $_ })] - [string] - $CollectionUri, - - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline)] - [string] - $ProjectName, - - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline)] - [string] - $RepositoryName, - - [Parameter(Mandatory, ValueFromPipelineByPropertyName, ValueFromPipeline)] - [string] - $PullRequestId, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [ValidateSet('active', 'abandoned', 'completed', 'all')] - [string] - $Status, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [ValidateLength(0, 256)] - [string] - $Title, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [ValidateLength(0, 4000)] - [string] - $Description, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [ValidateSet('setAutoComplete', 'setAutoCompleteSetBy')] - [string] - $CompletionOptions, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [ValidateSet('noMerge', 'squash', 'rebase', 'rebaseMerge')] - [string] - $MergeOptions, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [string] - $AutoCompleteSetBy, - - [Parameter( ValueFromPipelineByPropertyName, ValueFromPipeline)] - [string] - $TargetRefName - ) - - begin { - $result = @() - Write-Verbose "Starting function: Set-AzDoPullRequest" - } - - process { - $uriBase = "$CollectionUri/$ProjectName/_apis/git" - $apiVersion = "7.2-preview.2" - - $body = @{} - - if ($Status) { $body["status"] = $Status } - if ($Title) { $body["title"] = $Title } - if ($Description) { $body["description"] = $Description } - if ($MergeOptions) { $body["mergeOptions"] = $MergeOptions } - if ($TargetRefName) { $body["targetRefName"] = "refs/heads/$TargetRefName" } - - if ($CompletionOptions -and $CompletionOptions.Count -gt 0) { - $body["completionOptions"] = $CompletionOptions - } - - if ($AutoCompleteSetBy) { - $body["autoCompleteSetBy"] = @{ id = $AutoCompleteSetBy } - } - - if ($PSCmdlet.ShouldProcess($CollectionUri, "Get Pull Request from: $ProjectName")) { - Write-Verbose "Calling API: $uri" - - $InvokeAzDoRestMethodSplat = @{ - Uri = "$uriBase/repositories/$RepositoryName/pullrequests/$PullRequestId" - Method = "PATCH" - Version = $apiVersion - Body = $Body - } - $response = Invoke-AzDoRestMethod @InvokeAzDoRestMethodSplat - if ($response.value) { - $result += $response.value - } else { - $result += $response - } - if ($result) { - $result | ForEach-Object { - [PSCustomObject]@{ - CollectionUri = $CollectionUri - ProjectName = $ProjectName - RepoName = $RepoName - PullRequest = $PSItem - } - } - } - } - } -} From 1f8bdafe2fa41b57d04c19e0959ec3a03b7ce47f Mon Sep 17 00:00:00 2001 From: Bas Wijdenes Date: Fri, 28 Feb 2025 10:09:24 +0100 Subject: [PATCH 4/7] feat: Updated the naming convention and update .gitignore --- .gitignore | 1 + ...Get-PipelineRun.ps1 => Get-AzDoPipelineRun.ps1} | 14 ++++++++++---- ...New-PipelineRun.ps1 => New-AzDoPipelineRun.ps1} | 13 ++++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) rename AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/{Get-PipelineRun.ps1 => Get-AzDoPipelineRun.ps1} (89%) rename AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/{New-PipelineRun.ps1 => New-AzDoPipelineRun.ps1} (91%) diff --git a/.gitignore b/.gitignore index 78a4fbb7..d697ea38 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Don't check in the Output dir Output/ site/ +.vs diff --git a/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-AzDoPipelineRun.ps1 similarity index 89% rename from AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 rename to AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-AzDoPipelineRun.ps1 index 9dbf3534..b6968644 100644 --- a/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-PipelineRun.ps1 +++ b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/Get-AzDoPipelineRun.ps1 @@ -1,12 +1,14 @@ -function Get-PipelineRun { +function Get-AzDoPipelineRun { <# .SYNOPSIS Retrieves pipeline run information from Azure DevOps for a specified pipeline within a project. + .DESCRIPTION - The `Get-PipelineRun` function fetches details about one or more pipeline runs from an Azure DevOps project. + The `Get-AzDoPipelineRun` function fetches details about one or more pipeline runs from an Azure DevOps project. It requires the collection URI, project name, and pipeline ID. Optionally, specific run IDs can be provided to filter the results. The function uses the `Invoke-AzDoRestMethod` cmdlet to make the REST API call to Azure DevOps and returns the run details. + .EXAMPLE $getPipelineRunSplat = @{ CollectionUri = "https://dev.azure.com/YourOrg" @@ -14,8 +16,10 @@ function Get-PipelineRun { PipelineId = 123 } - Get-PipelineRun @getPipelineRunSplat + Get-AzDoPipelineRun @getPipelineRunSplat + Retrieves all runs for the specified pipeline in the given project. + .EXAMPLE $getPipelineRunSplat = @{ CollectionUri = "https://dev.azure.com/YourOrg" @@ -24,11 +28,13 @@ function Get-PipelineRun { RunId = 456 } - Get-PipelineRun @getPipelineRunSplat + Get-AzDoPipelineRun @getPipelineRunSplat Retrieves the details of the specified run (with ID 456) for the given pipeline. + .OUTPUTS System.Management.Automation.PSCustomObject + #> [CmdletBinding(SupportsShouldProcess)] param ( diff --git a/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-AzDoPipelineRun.ps1 similarity index 91% rename from AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 rename to AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-AzDoPipelineRun.ps1 index df2292a3..f052a2de 100644 --- a/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-PipelineRun.ps1 +++ b/AzureDevOpsPowerShell/Public/Api/Pipelines/Runs/New-AzDoPipelineRun.ps1 @@ -1,11 +1,13 @@ -function New-PipelineRun { +function New-AzDoPipelineRun { <# .SYNOPSIS Initiates a new run of an Azure DevOps pipeline. + .DESCRIPTION - The New-PipelineRun function starts a new run for a specified Azure DevOps pipeline. + The New-AzDoPipelineRun function starts a new run for a specified Azure DevOps pipeline. It supports various parameters to customize the pipeline run, including the collection URI, project name, pipeline ID, branch, parameters, preview run flag, and stages to skip. This function leverages the Azure DevOps REST API to trigger the pipeline run. + .EXAMPLE $newPipelineRunSplat = @{ CollectionUri = "https://dev.azure.com/organization" @@ -14,8 +16,9 @@ function New-PipelineRun { StagesToSkip = @("Stage1", "Stage2") } - New-PipelineRun @newPipelineRunSplat + New-AzDoPipelineRun @newPipelineRunSplat This command initiates a new run of the pipeline with ID 123 in the "SampleProject" project. + .EXAMPLE $newPipelineRunSplat = @{ CollectionUri = "https://dev.azure.com/organization" @@ -25,13 +28,13 @@ function New-PipelineRun { Parameters = @{param1 = "value1"; param2 = "value2"} } - New-PipelineRun @newPipelineRunSplat + New-AzDoPipelineRun @newPipelineRunSplat This command initiates a new run of the pipeline with ID 123 in the "SampleProject" project, targeting the "dev" branch. + .OUTPUTS System.Management.Automation.PSCustomObject Returns the response from the Azure DevOps REST API, which includes details of the pipeline run. -.NOTES #> [CmdletBinding(SupportsShouldProcess)] param ( From a587db5af3d91f70101b7261fa1ad50535d3dde5 Mon Sep 17 00:00:00 2001 From: Bas Wijdenes Date: Fri, 28 Feb 2025 10:45:12 +0100 Subject: [PATCH 5/7] fix: Update Pester configuration to exclude specific test paths --- .github/scripts/pester.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/pester.ps1 b/.github/scripts/pester.ps1 index 9dd6e67e..de649912 100644 --- a/.github/scripts/pester.ps1 +++ b/.github/scripts/pester.ps1 @@ -1,7 +1,9 @@ $config = New-PesterConfiguration $config.Run.Path = @('./tests/') -$config.Run.ExcludePath = @('./tests/pester.ps1', './tests/Indented.ScriptAnalyzerRules/') +$config.Run.ExcludePath = @('./tests/Indented.ScriptAnalyzerRules/') $config.Run.Exit = $true $config.Output.CIFormat = 'GithubActions' $config.Output.Verbosity = 'Detailed' Invoke-Pester -Configuration $config + +# './tests/pester.ps1', From 206928a398d48ede100a9f97ccfd5f9de08969e4 Mon Sep 17 00:00:00 2001 From: Bas Wijdenes Date: Fri, 28 Feb 2025 10:48:31 +0100 Subject: [PATCH 6/7] fix: Update Pester configuration to exclude additional test paths --- .github/scripts/pester.ps1 | 4 +- build.ps1 | 93 +++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/.github/scripts/pester.ps1 b/.github/scripts/pester.ps1 index de649912..9dd6e67e 100644 --- a/.github/scripts/pester.ps1 +++ b/.github/scripts/pester.ps1 @@ -1,9 +1,7 @@ $config = New-PesterConfiguration $config.Run.Path = @('./tests/') -$config.Run.ExcludePath = @('./tests/Indented.ScriptAnalyzerRules/') +$config.Run.ExcludePath = @('./tests/pester.ps1', './tests/Indented.ScriptAnalyzerRules/') $config.Run.Exit = $true $config.Output.CIFormat = 'GithubActions' $config.Output.Verbosity = 'Detailed' Invoke-Pester -Configuration $config - -# './tests/pester.ps1', diff --git a/build.ps1 b/build.ps1 index ac55a736..a8ef24f3 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,61 +1,72 @@ [cmdletbinding(DefaultParameterSetName = 'Task')] param( - # Build task(s) to execute - [parameter(ParameterSetName = 'task', position = 0)] - [string[]]$Task = 'default', + # Build task(s) to execute + [parameter(ParameterSetName = 'task', position = 0)] + [string[]]$Task = 'default', - # Bootstrap dependencies - [switch]$Bootstrap, + # Bootstrap dependencies + [switch]$Bootstrap, - # Calculate version - [switch]$CalculateVersion, + # Calculate version + [switch]$CalculateVersion, - # List available build tasks - [parameter(ParameterSetName = 'Help')] - [switch]$Help, + # List available build tasks + [parameter(ParameterSetName = 'Help')] + [switch]$Help, - # Optional properties to pass to psake - [hashtable]$Properties, + # Optional properties to pass to psake + [hashtable]$Properties, - # Optional parameters to pass to psake - [hashtable]$Parameters = @{ - "PSRepository" = 'PSGallery' - "PSRepositoryApiKey" = "" - "ScriptAnalysisEnabled" = $true - "TestEnabled" = $true - } + # Optional parameters to pass to psake + [hashtable]$Parameters = @{ + "PSRepository" = 'PSGallery' + "PSRepositoryApiKey" = "" + "ScriptAnalysisEnabled" = $true + "TestEnabled" = $true + } ) $ErrorActionPreference = 'Stop' - +'1' # Bootstrap dependencies if ($Bootstrap.IsPresent) { - Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null - Set-PSRepository -Name PSGallery -InstallationPolicy Trusted - if ((Test-Path -Path ./requirements.psd1)) { - if (-not (Get-Module -Name PSDepend -ListAvailable)) { - Install-Module -Name PSDepend -Repository PSGallery -Scope CurrentUser -Force - } - Import-Module -Name PSDepend -Verbose:$false - Invoke-PSDepend -Path './requirements.psd1' -Install -Import -Force -WarningAction SilentlyContinue - } else { - Write-Warning 'No [requirements.psd1] found. Skipping build dependency installation.' + '2' + Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null + '3' + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + '4' + if ((Test-Path -Path ./requirements.psd1)) { + '5' + if (-not (Get-Module -Name PSDepend -ListAvailable)) { + '6' + Install-Module -Name PSDepend -Repository PSGallery -Scope CurrentUser -Force } + '7' + Import-Module -Name PSDepend -Verbose:$false + '8' + Invoke-PSDepend -Path './requirements.psd1' -Install -Import -Force -WarningAction SilentlyContinue + } else { + Write-Warning 'No [requirements.psd1] found. Skipping build dependency installation.' + } } # Execute psake task(s) +'9' $psakeFile = './psakeFile.ps1' if ($PSCmdlet.ParameterSetName -eq 'Help') { - Get-PSakeScriptTasks -buildFile $psakeFile | - Format-Table -Property Name, Description, Alias, DependsOn + '10' + Get-PSakeScriptTasks -buildFile $psakeFile | + Format-Table -Property Name, Description, Alias, DependsOn } else { - Set-BuildEnvironment -Force - Invoke-psake -buildFile $psakeFile -taskList $Task -nologo -properties $Properties -parameters $Parameters - if ($psake.build_success) { - "Build complete" - exit 0 - } else { - Write-Error "Build not complete" - exit 1 - } + '11' + Set-BuildEnvironment -Force + '12' + Invoke-psake -buildFile $psakeFile -taskList $Task -nologo -properties $Properties -parameters $Parameters + if ($psake.build_success) { + "Build complete" + exit 0 + } else { + Write-Error "Build not complete" + exit 1 + } } From 1d1b4d8c3e246eb3924942c0ccd45ab051bb6d7f Mon Sep 17 00:00:00 2001 From: Bas Wijdenes Date: Fri, 28 Feb 2025 11:10:35 +0100 Subject: [PATCH 7/7] fix: Clean up build.ps1 by removing unnecessary debug output --- build.ps1 | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/build.ps1 b/build.ps1 index a8ef24f3..6d5a0af5 100644 --- a/build.ps1 +++ b/build.ps1 @@ -27,23 +27,16 @@ param( ) $ErrorActionPreference = 'Stop' -'1' + # Bootstrap dependencies if ($Bootstrap.IsPresent) { - '2' Get-PackageProvider -Name Nuget -ForceBootstrap | Out-Null - '3' Set-PSRepository -Name PSGallery -InstallationPolicy Trusted - '4' if ((Test-Path -Path ./requirements.psd1)) { - '5' if (-not (Get-Module -Name PSDepend -ListAvailable)) { - '6' Install-Module -Name PSDepend -Repository PSGallery -Scope CurrentUser -Force } - '7' Import-Module -Name PSDepend -Verbose:$false - '8' Invoke-PSDepend -Path './requirements.psd1' -Install -Import -Force -WarningAction SilentlyContinue } else { Write-Warning 'No [requirements.psd1] found. Skipping build dependency installation.' @@ -51,16 +44,12 @@ if ($Bootstrap.IsPresent) { } # Execute psake task(s) -'9' $psakeFile = './psakeFile.ps1' if ($PSCmdlet.ParameterSetName -eq 'Help') { - '10' Get-PSakeScriptTasks -buildFile $psakeFile | Format-Table -Property Name, Description, Alias, DependsOn } else { - '11' Set-BuildEnvironment -Force - '12' Invoke-psake -buildFile $psakeFile -taskList $Task -nologo -properties $Properties -parameters $Parameters if ($psake.build_success) { "Build complete"