Last active
April 30, 2024 15:25
-
-
Save bondarenkod/46a8ef534dc5234fea27b0a1ec754a39 to your computer and use it in GitHub Desktop.
Azure DevOps Git Submodules Authentication, VSTS git submodules
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
<# | |
.SYNOPSIS | |
Invokes authentication for Git submodules within a repository. | |
.DESCRIPTION | |
The Invoke-GitSubmodulesAuthentication function is used to authenticate Git submodules within a repository. It searches for .gitmodules files within the specified repository path and restores the submodules using the provided Personal Access Token (PAT). | |
.PARAMETER pat | |
The Personal Access Token (PAT) used for authentication. | |
.PARAMETER gitModulesSearchingDepth | |
The depth to search for .gitmodules files within the repository. Default value is 5. | |
.PARAMETER recursionLimit | |
The recursion limit for restoring submodules. Default value is 7. | |
.PARAMETER repoPath | |
The path to the repository. If not provided, the current location is used. | |
.EXAMPLE | |
Invoke-GitSubmodulesAuthentication -pat "your_personal_access_token" | |
This example invokes authentication for Git submodules within the current repository using the provided Personal Access Token. | |
#> | |
function Invoke-GitSubmodulesAuthentication { | |
param( | |
[string]$pat, | |
[int]$gitModulesSearchingDepth = 5, | |
[int]$recursionLimit = 7, | |
[string]$repoPath = (Get-Location) | |
) | |
# check pat | |
if ([string]::IsNullOrEmpty($pat)) { | |
Write-Error "PAT token is not provided" | |
return | |
} | |
Invoke-ValidateAzurePATToken -patToken $pat | |
# Get all top-level directories within the specified path | |
$topLevelDirs = Get-ChildItem -Path $repoPath -Directory | |
# Iterate over each directory to check for .gitmodules file | |
foreach ($dir in $topLevelDirs) { | |
$gitmodulesPath = Join-Path -Path $dir.FullName -ChildPath ".gitmodules" | |
# Check if the .gitmodules file exists in the directory | |
if (Test-Path -Path $gitmodulesPath) { | |
# Output the directory path that contains a .gitmodules file | |
Write-Output "found .gitmodules file in $($dir.FullName)" | |
RestoreSubmodules $pat $dir.FullName $recursionLimit $false | |
} | |
} | |
} | |
function RestoreSubmodules { | |
param ( | |
[string]$pat, | |
[string]$repoDirectory, | |
[int]$recursionLimit, | |
[bool]$isRecursion = $true | |
) | |
Write-Host "This function does not work with nested submodules in submodules, should be updated when needed!" | |
Write-Warning "This function does not work with nested submodules in submodules, should be updated when needed!" | |
try { | |
Push-Location $repoDirectory | |
# check if the directory exists | |
if (-not (Test-Path $repoDirectory)) { | |
Write-Error "The directory '$repoDirectory' does not exist" | |
return | |
} | |
Write-Host "Restoring submodules in '$repoDirectory'" | |
$gitSms = Get-GitSubmodules -repoPath $repoDirectory | |
Write-Host "Found submodules in $gitSms" | |
# check if we have submodules | |
if ($gitSms.Count -eq 0) { | |
if ($isRecursion -eq $false) { | |
# exit with error | |
Write-Error "No submodules found in '$repoDirectory'" | |
} | |
Write-Host "No submodules found in '$repoDirectory'" | |
} | |
# verify submodules | |
foreach ($gitsm in $gitSms) { | |
Write-Host "found submodule at '$($gitsm.RelativePath)'with url: '$($gitsm.Url)'" | |
# check if .git exists in the submodule, if not - remove the directory and init | |
$smDir = Join-Path -Path $repoDirectory -ChildPath $($gitsm.RelativePath) | |
if ($true -eq (Test-Path -Path $smDir)) { | |
Write-Host "Checking if submodule directory contains any files..." | |
# get all the top files and folders from the submodule files, in some cases, | |
# the submodule directory may be empty, the checkout will fail in this case | |
# this issue can happen at self-hosted agents | |
$smFiles = Get-ChildItem -Path $smDir -Force | |
if ($smFiles.Count -le 1) { | |
# need to remove the submodule directory from the .git/modules directory | |
$gitModulePath = Join-Path -Path $repoDirectory -ChildPath ".git/modules/$($gitsm.RelativePath)" | |
if ((Test-Path -Path $gitModulePath)) { | |
Write-Host "Removing submodule from .git/modules directory: '$smDir'..." | |
Remove-Item -Path $gitModulePath -Force -Recurse | |
} | |
Write-Host "Removing submodule directory '$smDir'..." | |
Remove-Item -Path $smDir -Force -Recurse | |
} | |
} | |
} | |
# read gitmodules file | |
$gitmodulesPath = Join-Path -Path $repoDirectory -ChildPath ".gitmodules" | |
$content = Get-Content -Path $gitmodulesPath | |
$replacementMap = @{} | |
# inject PAT token | |
foreach ($gitsm in $gitSms) { | |
# if the URL contains the PAT token, skip it | |
if ($gitsm.Url -match $pat) { | |
continue | |
} | |
# if the URL contains the @ symbol, this if format #1: https://[email protected]/ORG/PROJECT/_git/REPO | |
if ($gitsm.Url -match "@") { | |
# Extract the username from the URL | |
$user = $gitsm.Url -split '@' | Select-Object -First 1 -Skip 0 | |
$user = $user -replace 'https://', '' | |
# Replace the first occurrence of username with 'username:PAT' | |
$newUrl = $gitsm.Url -replace "https://$user@", "https://$($pat)@" | |
$replacementMap[$gitsm.Url] = $newUrl | |
} | |
else { | |
# if the URL does not contain the @ symbol, this is format #2: https://dev.azure.com/ORG/PROJECT/_git/REPO | |
$replacementMap[$gitsm.Url] = $gitsm.Url -replace "https://", "https://$($pat)@" | |
} | |
} | |
# print the replacement map | |
foreach ($key in $replacementMap.Keys) { | |
Write-Host "Replacing '$key' with '$($replacementMap[$key])'" | |
$content = $content -replace $key, $replacementMap[$key] | |
} | |
# write the content back to the file | |
Set-Content -Path $gitmodulesPath -Value $content | |
Write-Host "Setting up git config..." | |
Write-Host "git config --global credential.interactive false" | |
git config credential.interactive false | |
# git config --global credential.interactive false | |
# git config --global credential.azreposCredentialType oauth | |
git submodule sync | |
$gitCommand = "git submodule update --init --depth 1" | |
Write-Host "Executing command: '$gitCommand'" | |
Invoke-Expression $gitCommand | |
# TBD - add recursion here for nested submodules, dont forget to update the warning message and -1 for recursionLimit | |
} | |
catch { | |
<#Do this if a terminating exception happens#> | |
} | |
finally { | |
Pop-Location | |
} | |
} | |
# RestoreSubmodules -pat "token" -repoDirectory "D:\src\repo" -recursionLimit 2 | |
function Invoke-ValidateAzurePATToken { | |
param ( | |
[string]$patToken, | |
[string]$organization = $null, # This gets the organization URL from the pipeline environment | |
[string]$apiVersion = '6.0' # API version, update if necessary | |
) | |
if (-not $organization) { | |
$organization = "${env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}" | |
} | |
$organization = $organization.TrimEnd('/') | |
$apiUrl = "$organization/_apis/projects?api-version=$apiVersion" | |
Write-Host "Validating PAT token..." | |
Write-Host "URL: $apiUrl" | |
$res = Test-VstsPATPatToken -url $apiUrl -personalAccessToken $patToken | |
if ($res -eq $true) { | |
Write-Output "PAT token is valid" | |
} | |
else { | |
Write-Error "PAT token is invalid" | |
} | |
} | |
function Test-VstsPATPatToken { | |
param ( | |
[string]$url, | |
[string]$personalAccessToken | |
) | |
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($personalAccessToken)")) | |
try { | |
$response = Invoke-RestMethod -Uri $url -Headers @{Authorization = ("Basic $base64AuthInfo") } -Method Get -StatusCodeVariable "statusCode" | |
Write-Host "Response status code: $statusCode" | |
if ($statusCode -ne 200) { | |
Write-Host "Failed to validate PAT token. Status code: $statusCode" | |
return $false | |
} | |
else { | |
Write-Host "Successfully validated PAT token. Status code: $statusCode" | |
} | |
Write-Host "Response: $response" | |
return $true | |
} | |
catch { | |
Write-Host "Failed to validate PAT token. Status code: $($_.Exception.Response.StatusCode.Value__) Error: $($_.Exception.Message)" | |
return $false | |
} | |
} | |
# $token = "1" | |
# Invoke-ValidateAzurePATToken -patToken $token -organization "https://ORG.visualstudio.com/" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment