For a client, we manage over 60 repositories with frontends and an API behind them. To realize some features, we need to update all repositories with the same change. That seems like a lot of work doesn’t it?
Clone 60 projects?#
I talked in previous posts about automating these kinds of changes. But for this case, we don’t want to clone all 60 repositories. We could go for a temp folder and clone them one by one and then clean them up. Another solution could be creating a yaml pipeline for it, that clones on the agent machine and loops over all repositories, but I wanted to keep the blame on the person of the initial change.
For this case I used GitHub copilot to help me brainstorm for solutions, another solution I did not follow was using a $gitClient
object in Powershell.
Push files#
In the underlying script, I can create a branch from the main branch and push the contents of the readme file. This is a simple example, but it could be any file. The script can push the file to the repository and create a pull request.
PushReadmeAzureDevOps.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
# Prompt the user to login and get the access token
# see https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity?toc=%2Fazure%2Fdevops%2Forganizations%2Fsecurity%2Ftoc.json&view=azure-devops#q-can-i-use-a-service-principal-or-managed-identity-with-azure-cli
$accessToken = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query "accessToken" --output tsv
if ($null -eq $accessToken) {
exit 1
}
$orgUrl = "https://dev.azure.com/MARTORG"
$project = "MARTPROJECT"
$repositoryId = "YOURREPO"
$readmeFilePath = "README.md"
$headers = @{
"Authorization" = ("Bearer {0}" -f $accessToken)
"Accept" = "application/json"
"Content-Type" = "application/json"
}
# Convert the README file to base64
$readmeContent = Get-Content -Path $readmeFilePath -Raw
$readmeBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($readmeContent))
Write-Host "README file content: $readmeContent"
# get current commit object id
$commitUrl = "$orgUrl/$project/_apis/git/repositories/$repositoryId/refs/heads/main?api-version=6.0"
$commitResponse = Invoke-RestMethod -Uri $commitUrl -Headers $headers -Method Get
Write-Host $commitResponse.Value
$commitObjectId = $commitResponse.Value.objectId
Write-Host "Current commit object id: $commitObjectId"
# Create the request body
$changes = @(
@{
changeType = "edit"
item = @{
path = "README.md"
}
newContent = @{
content = $readmeBase64
contentType = "base64encoded"
}
}
)
$requestBody = @{
refUpdates = @(
@{
name = "refs/heads/add-readme"
oldObjectId = $commitObjectId
}
)
commits = @(
@{
comment = "Adding README file"
changes = $changes
}
)
} | ConvertTo-Json -Depth 6
# Set the API endpoint
$apiUrl = "$orgUrl/$project/_apis/git/repositories/$repositoryId/pushes?api-version=6.0"
# Send the API request
$response = Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $requestBody
# Check the response
if ($response.pushId) {
Write-Host "README file pushed successfully."
} else {
Write-Host "Failed to push README file. Error: $($response.message)"
}
# create a pull request for myfirstbranch
$pullRequestUrl = "$orgUrl/$project/_apis/git/repositories/$repositoryId/pullrequests?api-version=6.0"
$pullRequestBody = @{
sourceRefName = "refs/heads/add-readme"
targetRefName = "refs/heads/main"
title = "Add README file"
description = "Adding README file"
} | ConvertTo-Json -Depth 6
$pullResponse = Invoke-RestMethod -Uri $pullRequestUrl -Method Post -Headers $headers -Body $pullRequestBody
if ($pullResponse.pullRequestId) {
Write-Host "Pull request created successfully."
} else {
Write-Host "Failed to create pull request. Error: $($pullResponse.message)"
}
|
Conclusion#
I am going to use this script to update files across all of our repositories. For this client, the code review is important, so the automation would end in a set pull request. In the next blog post, I am going to create a script that will create files to be pushed for every repository.