Last active
October 26, 2023 21:46
-
-
Save Ricky-G/6dc5cfc0de0eb1eb6cd25eb453cc5022 to your computer and use it in GitHub Desktop.
PowerShell script to Get a list of unique committers from Azure Devops && ADO Server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$tfsUrl = "http://your-tfs-server:8080/tfs" # Replace with your TFS server URL | |
$collection = "DefaultCollection" # Replace with your TFS collection name | |
$accessToken = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":your-personal-access-token")) #Replace with your Personal Access Token (NB : the colon is required at the beginning of the token before the pat itself) | |
$Headers = @{ | |
"Authorization" = $accessToken | |
"Content-Type" = "application/json" | |
} | |
# Get all projects | |
$pageSize = 100 | |
$skipCount = 0 | |
$projects = @() | |
do { | |
$pagedApiUrl = "$tfsUrl/$collection/_apis/projects?`$top=$pageSize&`$skip=$skipCount" | |
try { | |
$projectsResponse = Invoke-RestMethod -Uri $pagedApiUrl -Headers $Headers -ContentType 'application/json' -ErrorAction Stop | |
$fetchedProjects = $projectsResponse.value | |
} catch { | |
Write-Host "Error: The API call was not successful. Response:`n$($_.Exception.Response)" | |
exit | |
} | |
# Check if the response has the expected properties | |
if ($null -eq $fetchedProjects -or $fetchedProjects.Count -eq 0 -or (-not ($fetchedProjects[0].PSObject.Properties.Name -contains 'id'))) { | |
Write-Host "Error: The response for the first API call to get a list of all projects is not valid. Please check your Personal Access Token, remember the PAT token needs to have a : colon in front of it, please check it has that." | |
exit | |
} | |
$projects += $fetchedProjects | |
$skipCount += $pageSize | |
} while ($fetchedProjects.Count -eq $pageSize) | |
$uniqueCommitters = @{} | |
foreach ($project in $projects) { | |
$projectName = $project.name | |
$repoApiUrl = "$tfsUrl/$collection/$projectName/_apis/git/repositories" | |
$reposResponse = Invoke-RestMethod -Uri $repoApiUrl -Headers $Headers | |
$repos = $reposResponse.value | |
foreach ($repo in $repos) { | |
$repoId = $repo.id | |
$fromDate = (Get-Date).AddDays(-90).ToString("yyyy-MM-dd") | |
$commitsApiUrl = "$tfsUrl/$collection/$projectName/_apis/git/repositories/$repoId/commits?searchCriteria.fromDate=$fromDate" | |
$commitsResponse = Invoke-RestMethod -Uri $commitsApiUrl -Headers $Headers | |
$commits = $commitsResponse.value | |
foreach ($commit in $commits) { | |
$committer = $commit.committer.name | |
$uniqueCommitters[$committer] = $true | |
} | |
} | |
} | |
# Check if the uniqueCommitters hashtable is empty | |
if ($uniqueCommitters.Count -eq 0) { | |
Write-Host "No committers found for the given time frame" | |
} else { | |
# Output unique committers | |
$uniqueCommitters.Keys | Sort-Object | ForEach-Object { | |
Write-Host "Name: $_ | Email: $($uniqueCommitters[$_].Email) | Last Commit Date: $($uniqueCommitters[$_].LastCommitDate)" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$organization = "" #Your organization name here | |
$accessToken = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":your-pat-token")) #Replace with your Personal Access Token NB: the colon is required at the beginning of the token | |
$Headers = @{ | |
"Authorization" = $accessToken | |
"Content-Type" = "application/json" | |
} | |
# Get all projects | |
$projectApiUrl = "https://dev.azure.com/$organization/_apis/projects" | |
try { | |
$projectsResponse = Invoke-RestMethod -Uri $projectApiUrl -Headers $Headers -ContentType 'application/json' -ErrorAction Stop | |
$projects = $projectsResponse.value | |
} catch { | |
Write-Host "Error: The API call was not successful. Response:`n$($_.Exception.Response)" | |
exit | |
} | |
# Check if the response has the expected properties | |
if ($null -eq $projects -or $projects.Count -eq 0 -or (-not ($projects[0].PSObject.Properties.Name -contains 'id'))) { | |
Write-Host "Error: The response for the first API call to get a list of all projects is not valid. Please check your Personal Access Token." | |
exit | |
} | |
$uniqueCommitters = @{} | |
#Loop through each project | |
foreach ($project in $projects) { | |
$projectId = $project.id | |
$repoApiUrl = "https://dev.azure.com/$organization/$projectId/_apis/git/repositories" | |
$reposResponse = Invoke-RestMethod -Uri $repoApiUrl -Headers $Headers | |
$repos = $reposResponse.value | |
#Loop through each repo | |
foreach ($repo in $repos) { | |
$repoId = $repo.id | |
$fromDate = (Get-Date).AddDays(-90).ToString("yyyy-MM-dd") #Commiter history for the last 90 days | |
$commitsApiUrl = "https://dev.azure.com/$organization/$projectId/_apis/git/repositories/$repoId/commits?searchCriteria.fromDate=$fromDate" | |
$commitsResponse = Invoke-RestMethod -Uri $commitsApiUrl -Headers $Headers | |
$commits = $commitsResponse.value | |
foreach ($commit in $commits) { | |
$committer = $commit.committer.name | |
$committerEmail = $commit.committer.email | |
$lastCommitDate = $commit.committer.date | |
# For each committer, store their email and the date of their last commit in the hashtable | |
$uniqueCommitters[$committer] = @{ | |
Email = $committerEmail | |
LastCommitDate = $lastCommitDate | |
} | |
} | |
} | |
} | |
# Check if the uniqueCommitters hashtable is empty | |
if ($uniqueCommitters.Count -eq 0) { | |
Write-Host "No committers found for the given time frame" | |
} else { | |
# Output unique committers | |
$uniqueCommitters.Keys | Sort-Object | ForEach-Object { | |
Write-Host "Name: $_ | Email: $($uniqueCommitters[$_].Email) | Last Commit Date: $($uniqueCommitters[$_].LastCommitDate)" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When using these scripts, please make sure to have a colon : in front the PAT Token, this is because
In PowerShell, when using Basic Authentication, the Authorization header must be set as "Basic " followed by the Base64-encoded : or : string. In the case of Azure DevOps or ADO Server, the username part is not required, but the colon (:) is still needed to separate the username and password/token fields.
By including the colon in the encoded string, you effectively tell the system that the username is empty, and the Personal Access Token (PAT) is being used as the password.
When you encode the string without the colon, it is interpreted as if the entire token is the username, and the password field is left empty. This causes authentication to fail, as the system expects a token in the password field.
So, adding the colon in front of the PAT ensures that the PAT is interpreted correctly as the password, allowing authentication to proceed as expected.