-
Notifications
You must be signed in to change notification settings - Fork 868
/
GitManager.cs
126 lines (108 loc) Β· 4.76 KB
/
GitManager.cs
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Agent.Sdk;
using Microsoft.VisualStudio.Services.Agent.Util;
namespace Microsoft.VisualStudio.Services.Agent.Worker
{
[ServiceLocator(Default = typeof(GitManager))]
public interface IGitManager : IAgentService
{
Task DownloadAsync(IExecutionContext executionContext, string version = GitManager.defaultGitVersion);
}
public class GitManager : AgentService, IGitManager
{
private const int timeout = 180;
private const int defaultFileStreamBufferSize = 4096;
private const int retryDelay = 10000;
private const int retryLimit = 3;
public const string defaultGitVersion = "2.39.4";
public async Task DownloadAsync(IExecutionContext executionContext, string version = defaultGitVersion)
{
Trace.Entering();
ArgUtil.NotNull(executionContext, nameof(executionContext));
ArgUtil.NotNullOrEmpty(version, nameof(version));
Uri gitUrl = GitStore.GetDownloadUrl(version);
var gitFileName = gitUrl.Segments[^1];
var externalsFolder = HostContext.GetDirectory(WellKnownDirectory.Externals);
var gitExternalsPath = Path.Combine(externalsFolder, $"git-{version}");
var gitPath = Path.Combine(gitExternalsPath, gitFileName);
if (File.Exists(gitPath))
{
executionContext.Debug($"Git instance {gitFileName} already exists.");
return;
}
var tempDirectory = Path.Combine(externalsFolder, "git_download_temp");
Directory.CreateDirectory(tempDirectory);
var downloadGitPath = Path.ChangeExtension(Path.Combine(tempDirectory, gitFileName), ".completed");
if (File.Exists(downloadGitPath))
{
executionContext.Debug($"Git intance {version} already downloaded.");
return;
}
Trace.Info($@"Git zip file will be downloaded and saved as ""{downloadGitPath}""");
int retryCount = 0;
while (true)
{
using CancellationTokenSource downloadToken = new(TimeSpan.FromSeconds(timeout));
using var downloadCancellation = CancellationTokenSource.CreateLinkedTokenSource(downloadToken.Token, executionContext.CancellationToken);
try
{
using HttpClient client = new();
using Stream stream = await client.GetStreamAsync(gitUrl);
using FileStream fs = new(downloadGitPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: defaultFileStreamBufferSize, useAsync: true);
await stream.CopyToAsync(fs);
Trace.Info("Finished Git downloading.");
await fs.FlushAsync(downloadCancellation.Token);
fs.Close();
break;
}
catch (OperationCanceledException) when (executionContext.CancellationToken.IsCancellationRequested)
{
Trace.Info($"Git download has been cancelled.");
throw;
}
catch (Exception ex)
{
retryCount++;
Trace.Info("Failed to download Git");
Trace.Error(ex);
if (retryCount > retryLimit)
{
Trace.Info($"Retry limit to download Git has been reached.");
break;
}
else
{
Trace.Info("Retry Git download in 10 seconds.");
await Task.Delay(retryDelay, executionContext.CancellationToken);
}
}
}
try
{
ZipFile.ExtractToDirectory(downloadGitPath, gitExternalsPath);
File.WriteAllText(downloadGitPath, DateTime.UtcNow.ToString());
Trace.Info("Git has been extracted and cleaned up");
}
catch (Exception ex)
{
Trace.Error(ex);
}
}
}
internal class GitStore
{
private static readonly string baseUrl = "https://vstsagenttools.blob.core.windows.net/tools/mingit";
private static readonly string bit = PlatformUtil.BuiltOnX86 ? "32" : "64";
internal static Uri GetDownloadUrl(string version = GitManager.defaultGitVersion)
{
return new Uri($"{baseUrl}/{version}/MinGit-{version}-{bit}-bit.zip");
}
}
}