diff --git a/.docs/Get-VSTeamStaleBranch.md b/.docs/Get-VSTeamStaleBranch.md new file mode 100644 index 000000000..857023eb2 --- /dev/null +++ b/.docs/Get-VSTeamStaleBranch.md @@ -0,0 +1,63 @@ + + +# Get-VSTeamStaleBranch + +## SYNOPSIS + + + +## SYNTAX + +## DESCRIPTION + +Retrieve Stale Branches + +You must call Set-VSTeamAccount before calling this function. + +## EXAMPLES + +### -------------------------- EXAMPLE 1 -------------------------- + +```PowerShell +PS C:\> Get-VSTeamStaleBranch +``` + +This will return all branches that have not been committed to within 90 days (default value) + +### -------------------------- EXAMPLE 2 -------------------------- + +```PowerShell +PS C:\> Get-VSTeamStaleBranch -top 5 | Format-Wide +``` + +This will return the top five Process Templates only showing their name + +## PARAMETERS + + + +### -RepositoryId + +Specifies the Repository Id to process + +```yaml +Type: Guid +Parameter Sets: ByRepositoryId +``` + +### -MaximumAgeDays + +The maximum number of days a branch has not been committed to rending it "stale" + +```yaml +Type: Int32 +Default value: 90 +``` + +## INPUTS + +## OUTPUTS + +## NOTES + +## RELATED LINKS diff --git a/.docs/synopsis/Get-VSTeamStaleBranch.md b/.docs/synopsis/Get-VSTeamStaleBranch.md new file mode 100644 index 000000000..eb176d6e8 --- /dev/null +++ b/.docs/synopsis/Get-VSTeamStaleBranch.md @@ -0,0 +1 @@ +Retrieve Stale Branches \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b2118eae4..98c803642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ ## 6.4.5 -All unit test now pass consistently. +Merged [Pull Request](https://github.com/DarqueWarrior/vsteam/pull/269) from [Michel Zehnder](https://github.com/MichelZ) which included the following: + +- Add Get-VSTeamGitStaleBranch to retrieve branches which have not been committed to recently (default: 90 days) + +- All unit test now pass consistently. Merged [Pull Request](https://github.com/DarqueWarrior/vsteam/pull/265) from [Michel Zehnder](https://github.com/MichelZ) which included the following: @@ -16,6 +20,7 @@ Merged [Pull Request](https://github.com/DarqueWarrior/vsteam/pull/273) from [Lu - Adds a new function Update-VSTeamAgent which allows to update the agent version + ## 6.4.4 Merged [Pull Request](https://github.com/DarqueWarrior/vsteam/pull/257) from [Michel Zehnder](https://github.com/MichelZ) which included the following: diff --git a/Source/Classes/VSTeamGitCommitRef.ps1 b/Source/Classes/VSTeamGitCommitRef.ps1 index 01b1f5cae..bd99445cb 100644 --- a/Source/Classes/VSTeamGitCommitRef.ps1 +++ b/Source/Classes/VSTeamGitCommitRef.ps1 @@ -15,7 +15,11 @@ class VSTeamGitCommitRef : VSTeamLeaf { $this.Committer = [VSTeamGitUserDate]::new($obj.committer, $ProjectName) $this.CommitId = $obj.commitId $this.Comment = $obj.comment - $this.RemoteUrl = $obj.remoteUrl + + if ($obj.PSobject.Properties.Name -contains "remoteurl") { + $this.RemoteUrl = $obj.remoteUrl + } + $this.Url = $obj.url $this._internalObj = $obj diff --git a/Source/Public/Get-VSTeamStaleBranch.ps1 b/Source/Public/Get-VSTeamStaleBranch.ps1 new file mode 100644 index 000000000..4997f431b --- /dev/null +++ b/Source/Public/Get-VSTeamStaleBranch.ps1 @@ -0,0 +1,89 @@ +function Get-VSTeamStaleBranch { + [CmdletBinding(DefaultParameterSetName="ByProjectId")] + param ( + [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true, ParameterSetName = "ByRepositoryId")] + [Alias("Id")] + [guid] $RepositoryId, + + [int] $MaximumAgeDays = 90 + ) + + DynamicParam { + try { [void] $RepositoryId } + catch { $RepositoryId = $null } + + if ($null -ne $RepositoryId) { + $dynamic = _buildProjectNameDynamicParam -ParameterSetName "ByRepositoryId" -Mandatory $true + } else { + $dynamic = _buildProjectNameDynamicParam -ParameterSetName "ByProjectId" -Mandatory $false + } + + $dynamic + } + + process { + # Bind the parameter to a friendly variable + $ProjectName = $PSBoundParameters["ProjectName"] + $maximumAge = (Get-Date).AddDays(-$MaximumAgeDays) + + try { + if ($RepositoryId) # Retrieve single repository + { + Write-Verbose "Retrieving Branches for Repository ID $RepositoryId in Project $ProjectName" + $repository = Get-VSTeamGitRepository -ProjectName $ProjectName -RepositoryId $RepositoryId + $branches = $repository | Get-VSTeamGitRef -Filter "heads" + foreach ($branch in $branches) + { + Write-Verbose "Processing Branch $($branch.RefName)" + $isStale = ((Get-VSTeamGitCommit -ProjectName $ProjectName -RepositoryId $RepositoryId -FromDate $maximumAge -Top 1) | Measure-Object).Count -ne 1 + if ($isStale) + { + Write-Verbose "Branch $($branch.RefName) is stale!" + $branchStats = Get-VSTeamGitStat -ProjectName $ProjectName -RepositoryId $RepositoryId -BranchName ($branch.RefName -replace 'refs/heads/', '') + + $object = + [PSCustomObject]@{ + ProjectName = $ProjectName + RepositoryName = $repository.Name + BranchName = ($branch.RefName -replace 'refs/heads/', '') + Creator = $branch.Creator + LastCommitId = $branchStats.commit.commitId + LastCommitter = $branchStats.commit.committer.name + LastCommitDate = $branchStats.commit.committer.date + Ahead = $branchStats.aheadCount + Behind = $branchStats.behindCount + } + + _applyTypes $object "Team.GitStaleBranch" + Write-Output $object + } + } + } elseif ($ProjectName) { # Retrieve whole project (recursive) + Write-Verbose "Retrieving Repositories for Project $ProjectName" + $repos = Get-VSTeamGitRepository -ProjectName $ProjectName + foreach ($repo in $repos) + { + $staleBranchesResult = Get-VSTeamStaleBranch -ProjectName $ProjectName -RepositoryId $repo.Id -MaximumAgeDays $MaximumAgeDays + foreach ($result in $staleBranchesResult) + { + Write-Output $result + } + } + } else { # Retrieve all projects (recursive) + Write-Verbose "Retrieving all Projects" + $projects = Get-VSTeamProject + foreach ($project in $projects) + { + $staleBranchesResult = Get-VSTeamStaleBranch -ProjectName $project.Name -MaximumAgeDays $MaximumAgeDays + foreach ($result in $staleBranchesResult) + { + Write-Output $result + } + } + } + } + catch { + throw $_ + } + } +} \ No newline at end of file diff --git a/Source/formats/Team.GitStaleBranch.ListView.ps1xml b/Source/formats/Team.GitStaleBranch.ListView.ps1xml new file mode 100644 index 000000000..02f7b91ce --- /dev/null +++ b/Source/formats/Team.GitStaleBranch.ListView.ps1xml @@ -0,0 +1,46 @@ + + + + + Team.GitStaleBranch.ListView + + Team.GitStaleBranch + + + + + + + ProjectName + + + RepositoryName + + + BranchName + + + Creator + + + LastCommitId + + + LastCommitter + + + LastCommitDate + + + Behind + + + Ahead + + + + + + + + \ No newline at end of file diff --git a/Source/formats/Team.GitStaleBranch.TableView.ps1xml b/Source/formats/Team.GitStaleBranch.TableView.ps1xml new file mode 100644 index 000000000..b33edec9b --- /dev/null +++ b/Source/formats/Team.GitStaleBranch.TableView.ps1xml @@ -0,0 +1,75 @@ + + + + + Team.GitStaleBranch.TableView + + Team.GitStaleBranch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ProjectName + + + RepositoryName + + + BranchName + + + Creator + + + LastCommitId + + + LastCommitter + + + LastCommitDate + + + Ahead + + + Behind + + + + + + + + \ No newline at end of file diff --git a/Source/formats/_formats.json b/Source/formats/_formats.json index 9cd65634f..d53b10489 100644 --- a/Source/formats/_formats.json +++ b/Source/formats/_formats.json @@ -1,7 +1,7 @@ -{ - "outputFile": "vsteam.format.ps1xml", - "fileType": "formats", - "files": [ - "*.ps1xml" - ] +{ + "outputFile": "vsteam.format.ps1xml", + "fileType": "formats", + "files": [ + "*.ps1xml" + ] } \ No newline at end of file diff --git a/Source/types/Team.GitStaleBranch.ps1xml b/Source/types/Team.GitStaleBranch.ps1xml new file mode 100644 index 000000000..feb631234 --- /dev/null +++ b/Source/types/Team.GitStaleBranch.ps1xml @@ -0,0 +1,27 @@ + + + + Team.GitStaleBranch + + + PSStandardMembers + + + DefaultDisplayPropertySet + + ProjectName + RepositoryName + BranchName + Creator + LastCommitId + LastCommitter + LastCommitDate + Ahead + Behind + + + + + + + \ No newline at end of file diff --git a/unit/test/Get-VSTeamStaleBranch.Tests.ps1 b/unit/test/Get-VSTeamStaleBranch.Tests.ps1 new file mode 100644 index 000000000..2cad11631 --- /dev/null +++ b/unit/test/Get-VSTeamStaleBranch.Tests.ps1 @@ -0,0 +1,168 @@ +Set-StrictMode -Version Latest + +InModuleScope VSTeam { + + # Set the account to use for testing. A normal user would do this + # using the Set-VSTeamAccount function. + [VSTeamVersions]::Account = 'https://dev.azure.com/test' + + $results = [PSCustomObject]@{ + Branch = [PSCustomObject]@{ + objectId = '6f365a7143e492e911c341451a734401bcacadfd' + name = 'refs/heads/master' + creator = [PSCustomObject]@{ + displayName = 'Microsoft.VisualStudio.Services.TFS' + id = '1' + uniqueName = 'some@email.com' + } + } + Creator = [PSCustomObject]@{ + displayName = 'Microsoft.VisualStudio.Services.TFS' + id = '1' + uniqueName = 'some@email.com' + } + CreationDate = "1010101" + ProjectName = "Test" + Repository = "repo" + LastCommit = "Block-SmbShareAccess" + LastCommitter = [PSCustomObject]@{ + displayName = 'Microsoft.VisualStudio.Services.TFS' + id = '1' + uniqueName = 'some@email.com' + } + } + + $singleRepository = [VSTeamGitRepository]::new([PSCustomObject]@{ + id = '00000000-0000-0000-0000-000000000000' + url = '' + sshUrl = '' + remoteUrl = '' + defaultBranch = '' + size = 0 + name = 'TestRepo' + project = [PSCustomObject]@{ + name = 'Project' + id = 1 + description = '' + url = '' + state = '' + revision = '' + visibility = '' + } + }, "Project") + + $refs = @([VSTeamRef]::new([PSCustomObject]@{ + objectId = '6f365a7143e492e911c341451a734401bcacadfd' + name = 'refs/heads/master' + creator = [PSCustomObject]@{ + displayName = 'Microsoft.VisualStudio.Services.TFS' + id = '1' + uniqueName = 'some@email.com' + } + }, "Project")) + + $commits = @( + [VSTeamGitCommitRef]::new( + [PSCustomObject]@{ + author = [PSCustomObject]@{ + date = '2019-02-19T15:12:01Z' + email = 'test@test.com' + name = 'Test User' + } + changeCounts = [PSCustomObject]@{ + Add = 2 + Delete = 0 + Edit = 1 + } + comment = 'Just a test commit' + commitId = '1234567890abcdef1234567890abcdef' + committer = [PSCustomObject]@{ + date = '2019-02-19T15:12:01Z' + email = 'test@test.com' + name = 'Test User' + } + remoteUrl = 'https://dev.azure.com/test/test/_git/test/commit/1234567890abcdef1234567890abcdef' + url = 'https://dev.azure.com/test/21AF684D-AFFB-4F9A-9D49-866EF24D6A4A/_apid/git/repositories/06E176BE-D3D2-41C2-AB34-5F4D79AEC86B/commits/1234567890abcdef1234567890abcdef' + }, "Project"), + [VSTeamGitCommitRef]::new( + [PSCustomObject]@{ + author = [PSCustomObject]@{ + date = '2019-02-20T01:00:01Z' + email = 'eample@example.com' + name = 'Example User' + } + changeCounts = [PSCustomObject]@{ + Add = 8 + Delete = 1 + Edit = 0 + } + comment = 'Just another test commit' + commitId = 'abcdef1234567890abcdef1234567890' + committer = [PSCustomObject]@{ + date = '2019-02-20T01:00:01Z' + email = 'eample@example.com' + name = 'Example User' + } + remoteUrl = 'https://dev.azure.com/test/test/_git/test/commit/abcdef1234567890abcdef1234567890' + url = 'https://dev.azure.com/test/21AF684D-AFFB-4F9A-9D49-866EF24D6A4A/_apid/git/repositories/06E176BE-D3D2-41C2-AB34-5F4D79AEC86B/commits/abcdef1234567890abcdef1234567890' + }, "Project") + ) + + $stats = [PSCustomObject]@{ + commit = [PSCustomObject]@{ + commitId = '67cae2b029dff7eb3dc062b49403aaedca5bad8d' + author = [PSCustomObject]@{ + name = '"Chuck Reinhart' + email = 'fabrikamfiber3@hotmail.com' + date = '2014-01-29T23:52:56Z' + } + committer = [PSCustomObject]@{ + name = '"Chuck Reinhart' + email = 'fabrikamfiber3@hotmail.com' + date = '2014-01-29T23:52:56Z' + } + comment = 'home page' + url = 'https://dev.azure.com/fabrikam/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/commits/67cae2b029dff7eb3dc062b49403aaedca5bad8d' + } + name = 'develop' + aheadCount = 1 + behindCount = 17 + isBaseVersion = $false + } + + _applyTypes $stats "VSTeam.GitStat" + + Describe "Git VSTS" { + # Mock the call to Get-Projects by the dynamic parameter for ProjectName + Mock Invoke-RestMethod { return @() } -ParameterFilter { + $Uri -like "*_apis/projects*" + } + + . "$PSScriptRoot\mocks\mockProjectNameDynamicParam.ps1" + + Context 'Get-VSTeamStaleBranch' { + Mock Get-VSTeamGitRepository { return $singleRepository } -ParameterFilter { $Id -eq "00000000-0000-0000-0000-000000000000" } -Verifiable + Mock Get-VSTeamGitRef { return $refs } -Verifiable + Mock Get-VSTeamGitCommit { return $commits } -Verifiable + Mock Get-VSTeamGitStat { return $stats } -Verifiable + + $staleBranches = Get-VSTeamStaleBranch -ProjectName Test -RepositoryId 00000000-0000-0000-0000-000000000000 + + It 'Should return stale branch' { + Assert-VerifiableMock + } + + It 'Should return 1 stale branch' { + $staleBranches | Should -HaveCount 1 + } + } + + Context 'Get-VSTeamStaleBranch by id throws' { + Mock Invoke-RestMethod { throw [System.Net.WebException] "Test Exception." } + + It 'Should return a single repo by id' { + { Get-VSTeamStaleBranch -ProjectName Test -RepositoryId 00000000-0000-0000-0000-000000000000 } | Should Throw + } + } + } +} \ No newline at end of file