Skip to content

Instantly share code, notes, and snippets.

@rfennell
Last active April 23, 2024 12:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rfennell/5764fe4bb1372dd9a9e1d5a1c1bc05a0 to your computer and use it in GitHub Desktop.
Save rfennell/5764fe4bb1372dd9a9e1d5a1c1bc05a0 to your computer and use it in GitHub Desktop.
Copy repos from GitLab to Azure DevOps
<#
.SYNOPSIS
Copies GitLab repositories to Azure DevOps.
.DESCRIPTION
This script retrieves GitLab repositories using the provided GitLab token and copies them to an Azure DevOps project.
.PARAMETER gitlabtoken
Specifies the GitLab token to authenticate with the GitLab API.
Create a personal access token in GitLab by navigating to Profile > Edit Profile > Access Tokens.
Create a personal access token with read_api permissions
.PARAMETER azdoorg
Specifies the Azure DevOps organization e.g. for https://dev.azure.com/your-org this value is your-org
.PARAMETER azdoproject
Specifies the Azure DevOps project name
.PARAMETER gitlaburl
Specifies the based GitLab URL, only needs to be changed if using a on premise GitLab instance defaults to https://gitlab.com
.PARAMETER gitlabowned
List only projects owned by the authenticated user, defaults to true
.EXAMPLE
PS C:\> Copy-GitLabRepoToAzureDevOps -gitlabtoken "your-gitlab-token" -azdoorg "your-org" -azdoproject "your-project"
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
$gitlabtoken,
[Parameter(Mandatory = $true)]
$azdoorg,
[Parameter(Mandatory = $true)]
$azdoproject,
[Parameter(Mandatory = $false)]
$gitlaburl = "https://gitlab.com",
[Parameter(Mandatory = $false)]
$gitlabowned = $true
)
# Check PowerShell version
# We need PowerShell 7 for the correct version of invoke-restmethod
if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Host "This script requires PowerShell 7 or later. Please upgrade your PowerShell version." -ForegroundColor Red
exit
}
# we are using the Azure CLI to interact with Azure DevOps
# so make a simple list call to check if we are logged in
# it is done this way to avoid the need to pass a token to the script
# and the need for the slow login process on every run
# the alternative is to pass the token to the script and use it in the az command e.g
Write-Host "Checking Azure DevOps authentication" -ForegroundColor Green
# echo "######" | az devops login --organization https://dev.azure.com/contoso/
az repos list --org https://dev.azure.com/$AzDoOrg --project $azdoproject --output none
# check the exit code of the last command
if ($? -eq $false) {
Write-Host "Azure DevOps login failed, please login using 'az login --allow-no-subscriptions' then try again." -ForegroundColor Red
exit
}
Write-Host "Azure DevOps authentication check successful." -ForegroundColor Green
# this gets all the projects that can be seen by the authenticated GitLab user,
# if we don't specify the token, we will get all the public projects too
# this might not be correct for the on premise version of GitLab as we will want all projects/repos
$uri = "$gitlaburl/api/v4/projects?private_token=$gitlabtoken&owned=$gitlabowned&per_page=100"
Write-Host "Getting GitLab Projects owned by the authenticated GitLab user..."
$response = Invoke-RestMethod -Uri $uri -Method Get -ResponseHeadersVariable "ResponseHeaders"
# check if we have more than one page of results
while ($ResponseHeaders.'X-Next-Page' -le $ResponseHeaders.'X-Total-Pages') {
$pageuri = "$uri&page=$($ResponseHeaders.'X-Next-Page')"
$response += Invoke-RestMethod -Uri $pageuri -Method Get -ResponseHeadersVariable "ResponseHeaders"
}
Write-Host "Found $($response.count) projects (repos)"
# Loop across the projects to get the repos
foreach ($project in $response) {
# Access the project details
$projectId = $project.id
$projectName = $project.name
$projectUrl = $project.http_url_to_repo
# get the last portion of the URL for the repo name
$repoName = ($projectUrl.Split("/")[-1]).Replace(".git", "")
Write-Host "Project: $projectName ($projectId)"
# clean out our local workin folder
if (Test-Path $repoName) {
Write-Host "Local copy of repo '$repoName' already exists, deleting..." -ForegroundColor Yellow
Remove-Item -Recurse -Force $repoName
}
# check if the repo exist on the target Azure DevOps
# if it does this script will skip the import, there are other options
Write-Host "Checking if repo '$repoName' exists in Azure DevOps ..." -ForegroundColor Green
$repoExists = az repos show --repository $repoName --org https://dev.azure.com/$AzDoOrg --project $azdoproject --query "id" -o tsv
if ($repoExists) {
Write-Host "Repo '$repoName' already exists in Azure DevOps, skipping import." -ForegroundColor Yellow
}
else {
Write-Host "Create the new empty repo '$repoName' in Azure DevOps..." -ForegroundColor Green
$AzDoOutput = az repos create --name $repoName --org https://dev.azure.com/$AzDoOrg --project $azdoproject | ConvertFrom-Json
Write-Host "Cloning the GitLab repo URL: $projectUrl to local storage" -ForegroundColor Green
git clone --mirror $projectUrl.Replace("https://", "https://oauth2:$gitlabtoken@") $repoName
cd $repoName
Write-Host "Fetching tags for repo '$repoName'..." -ForegroundColor Green
git fetch --tags
Write-Host "Updating the ORIGIN remote to Azure DevOps $($AzDoOutput.remoteUrl)" -ForegroundColor Green
git remote rm origin
git remote add origin $AzDoOutput.remoteUrl
Write-Host "Pushing to Azure DevOps" -ForegroundColor Green
git push origin --all
git push --tags
cd ..
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment