使用REST API下载Azure Devops存储库的存档



我正在寻找一种方法,使用REST-Api将我的azure-devops存储库下载为zip/tar文件。有一个使用控制台下载zip的规定,但我需要使用API。

类似于Github存储库档案下载(链接(

您可以⬇使用以下链接下载为Zip

https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repository}/items/items?path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=0&versionDescriptor[version]={branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true

替换{organization}{project}{repository}&{branch}与您的价值

有几种方法可以做到这一点。看看这个,你可以微调它:

$OrganizationName = "" # Fill this
$ProjectName = "" # Fill this
$Reponame = "" # Fill this
$user = "" # Fill this
$PAT = "" # Fill this
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$PAT)))
$headers = @{
'Authorization' = ("Basic {0}" -f $base64AuthInfo)
'Content-Type'  = 'application/json'
}
$BaseUriWithProject=https://dev.azure.com/$OrganizationName/$ProjectName
# Get Repository
$listRepoUri = "$BaseUriWithProject/_apis/git/repositories?api-version=6.0"
$allRepo = Invoke-RestMethod -Uri $listRepoUri -Method GET -Headers $headers -UseBasicParsing
$repo = $allRepo.value | Where-Object -FilterScript {$_.Name -match $Reponame } | Select-Object Id
if($null -eq $repo){
Write-Error "No Repo"
}
# Get all items in repo
$itemuri = "$BaseUriWithProject/_apis/git/repositories/$($repo.id)/items?recursionLevel=Full&includeContentMetadata=true&download=true&api-version=6.0"
$allItems = Invoke-RestMethod -Uri $itemuri -Method GET -Headers $headers -UseBasicParsing
$files = $allItems.value | Where-Object -FilterScript {$_.gitObjectType -eq 'blob'}
# Download items
$headers += @{"Accept"="application/zip"}
$uri = "$BaseUriWithProject/_apis/git/repositories/$($repo.id)/blobs?api-version=6.0"
$body = $files.objectId | ConvertTo-Json
Invoke-RestMethod -Uri $uri -Method POST -Headers $headers -UseBasicParsing -Body $body -OutFile "repo.zip"

添加到@Ahmed Magdy的答案:

GET: https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repository}/items/items?path=/&versionDescriptor[version]={branch}&resolveLfs=true&$format=zip&download=true

试用版&错误,我发现不需要versionDescriptor[versionOptions]versionDescriptor[versionType]pi版本参数。因此从上述URL中删除。

需要授权标头:

Authorization: Basic <TOKEN-STRING>

以下是获取TOKEN-STRING:的方法

TOKEN-STRING = base64-encode(":" + personal-access-token)

如果个人访问令牌是";abcxyz";,JS形式:

TOKEN-STRING = btoa(":abcxyz");

参考链接:https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=当前页面#使用你的代码

这是截至2021年11月的工作

存档repo不支持开箱即用。但这是可行的。请检查此

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
using Newtonsoft.Json;
using RestSharp;
namespace AzureDevopsBackupFunction
{
public static class BackupFunction
{
private const string version = "api-version=5.1";
[FunctionName("BackupFunction")]
public static async Task Run([TimerTrigger("0 0 20 * * *")]TimerInfo myTimer, ILogger log) //, RunOnStartup = true
{
log.LogInformation($"DevOps BackupFunction function starting execution at: {DateTime.Now}");
// configure connections
string storageAccountKey = Environment.GetEnvironmentVariable("storageAccountKey", EnvironmentVariableTarget.Process);
string storageName = Environment.GetEnvironmentVariable("storageName", EnvironmentVariableTarget.Process);
string token = Environment.GetEnvironmentVariable("token", EnvironmentVariableTarget.Process);
string organization = Environment.GetEnvironmentVariable("organization", EnvironmentVariableTarget.Process);
string storageConnection = $"DefaultEndpointsProtocol=https;AccountName={storageName};AccountKey={storageAccountKey};EndpointSuffix=core.windows.net";
string devopsURL = $"https://dev.azure.com/{organization}/";
// make API request to get all projects
string auth = "Basic " + Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(string.Format("{0}:{1}", "", token)));
var clientProjects = new RestClient($"{devopsURL}_apis/projects?{version}");
var requestProjects = new RestRequest(Method.GET);
requestProjects.AddHeader("Authorization", auth);
var responseProjects = clientProjects.Execute(requestProjects);
if(responseProjects.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new Exception("API Request failed: " + responseProjects.StatusCode + " " + responseProjects.ErrorMessage);
}
Projects projects = JsonConvert.DeserializeObject<Projects>(responseProjects.Content);
// connect to Azure Storage
var storageAccount = CloudStorageAccount.Parse(storageConnection);
var client = storageAccount.CreateCloudBlobClient();
var container = client.GetContainerReference("devopsbackup");
await container.CreateIfNotExistsAsync();

foreach (Project project in projects.value)
{
log.LogInformation(project.name);
// get repositories
var clientRepos = new RestClient($"{devopsURL}{project.name}/_apis/git/repositories?{version}");
var requestRepos = new RestRequest(Method.GET);
requestRepos.AddHeader("Authorization", auth);
var responseRepos = clientRepos.Execute(requestRepos);
Repos repos = JsonConvert.DeserializeObject<Repos>(responseRepos.Content);
foreach (Repo repo in repos.value)
{
log.LogInformation("Repo: " + repo.name);
// get file mapping
var clientItems = new RestClient($"{devopsURL}_apis/git/repositories/{repo.id}/items?recursionlevel=full&{version}");
var requestItems = new RestRequest(Method.GET);
requestItems.AddHeader("Authorization", auth);
var responseItems = clientItems.Execute(requestItems);
Items items = JsonConvert.DeserializeObject<Items>(responseItems.Content);
log.LogInformation("Items count: " + items.count);
if (items.count > 0)
{
// get files as zip
var clientBlob = new RestClient($"{devopsURL}_apis/git/repositories/{repo.id}/blobs?{version}");
var requestBlob = new RestRequest(Method.POST);
requestBlob.AddJsonBody(items.value.Where(itm => itm.gitObjectType == "blob").Select(itm => itm.objectId).ToList());
requestBlob.AddHeader("Authorization", auth);
requestBlob.AddHeader("Accept", "application/zip");
var zipfile = clientBlob.DownloadData(requestBlob); 
// upload blobs to Azure Storage
string name = $"{project.name}_{repo.name}_blob.zip";
var blob = container.GetBlockBlobReference(name);
await blob.DeleteIfExistsAsync();
await blob.UploadFromByteArrayAsync(zipfile, 0, zipfile.Length);
// upload file mapping
string namejson = $"{project.name}_{repo.name}_tree.json";
var blobjson = container.GetBlockBlobReference(name);
await blobjson.DeleteIfExistsAsync();
blobjson.Properties.ContentType = "application/json";
await blobjson.UploadTextAsync(responseItems.Content);
/* TODO:
* File mapping defines relationship between blob IDs and file names/paths.
* To reproduce a full file structure 
* 1. Recreate all folders for <item.isFolder>
* 2. Extract all other items to <item.path>
*/

}
}
}
log.LogInformation($"DevOps BackupFunction function finished at: {DateTime.Now}");
}
}
struct Project
{
public string name;
}
struct Projects
{
public List<Project> value;
}
struct Repo
{
public string id;
public string name;
}
struct Repos
{
public List<Repo> value;
}
struct Item
{
public string objectId;
public string gitObjectType;
public string commitId;
public string path;
public bool isFolder;
public string url;
}
struct Items
{
public int count;
public List<Item> value;
}
}

所有的功劳都归于此。AN也请考虑投票支持此功能请求

最新更新