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

时间:2020-09-04 11:25:47

标签: azure azure-devops

我一直在寻找一种使用REST Api将我的Azure devops存储库下载为zip / tar文件的方法。可以使用控制台下载zip,但我需要使用API​​进行下载。

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

3 个答案:

答案 0 :(得分:2)

您可以使用以下链接⬇下载为 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}

答案 1 :(得分:1)

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

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;
    }
}

所有功劳归于here。也请考虑对此feature request

进行投票

答案 2 :(得分:1)

几种方法。检查一下,您可以对其进行微调:

$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"