-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.Update.cs
More file actions
158 lines (141 loc) · 6.71 KB
/
Program.Update.cs
File metadata and controls
158 lines (141 loc) · 6.71 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace CSharpScriptRunner
{
static partial class Program
{
sealed class ReleaseInfo
{
[JsonPropertyName("tag_name")]
public string Version { get; set; }
[JsonPropertyName("html_url")]
public string Url { get; set; }
[JsonPropertyName("assets")]
public List<Asset> Assets { get; set; }
public sealed class Asset
{
[JsonPropertyName("browser_download_url")]
public string DownloadUrl { get; set; }
}
}
static async Task Update()
{
const string UpdateMutexName = "C371A9A2-6CBE-43DE-B834-AC8F73E47705";
using var mutex = new Mutex(true, UpdateMutexName);
if (!mutex.WaitOne(0, true))
{
WriteLineError($"{Verbs.Update} command is already running", ConsoleColor.Red);
return;
}
WriteLine("Checking for new version...");
using var httpClient = new HttpClient();
// httpClient.Timeout = Timeout.InfiniteTimeSpan;
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Add("User-Agent", nameof(CSharpScriptRunner));
var response = await httpClient.GetAsync(UpdateRequestUri).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var release = JsonSerializer.Deserialize<ReleaseInfo>(await response.Content.ReadAsByteArrayAsync());
var dstDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(CSharpScriptRunner), release.Version);
if (release.Version == BuildInfo.ReleaseTag)
{
foreach (var dir in Directory.EnumerateDirectories(Path.GetDirectoryName(dstDir)))
{
if (string.Equals(dir, dstDir, StringComparison.OrdinalIgnoreCase))
continue;
try { Directory.Delete(dir, true); }
catch { continue; }
}
WriteLine($"{nameof(CSharpScriptRunner)} is up-to-date ({BuildInfo.ReleaseTag})", ConsoleColor.Green);
return;
}
var tmpDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(CSharpScriptRunner), Guid.NewGuid().ToString());
if (Directory.Exists(dstDir))
{
WriteLine($"Newest version ({release.Version}) is already installed", ConsoleColor.Green);
return;
}
WriteLine($"New version ({release.Version}) available, downloading...");
var platform = BuildInfo.RuntimeIdentifier.Split('-').First();
var downloadUrl = release.Assets.Select(x => x.DownloadUrl).FirstOrDefault(x => x.EndsWith($"-{platform}.zip", StringComparison.OrdinalIgnoreCase));
if (downloadUrl == null)
{
WriteLineError("Operation failed", ConsoleColor.Red);
return;
}
httpClient.DefaultRequestHeaders.Accept.Clear();
response = await httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
using (var ms = new MemoryStream((int)response.Content.Headers.ContentLength))
{
using (var stream = await response.Content.ReadAsStreamAsync())
{
var buffer = new byte[(int)response.Content.Headers.ContentLength / 100 + 1];
var progress = string.Empty;
while (true)
{
var length = await stream.ReadAsync(buffer, 0, buffer.Length);
if (length < 1)
break;
await ms.WriteAsync(buffer, 0, length);
var newProgress = $"{(double)ms.Length / ms.Capacity,4:P0}";
if (newProgress != progress)
{
progress = newProgress;
try { Console.CursorLeft = 0; }
catch { }
Console.Write(progress);
}
}
Console.WriteLine();
}
ms.Seek(0, SeekOrigin.Begin);
using (var archive = new ZipArchive(ms, ZipArchiveMode.Read))
{
WriteLine("Extracting...");
var progress = string.Empty;
for (int i = 0; i < archive.Entries.Count; i++)
{
var entry = archive.Entries[i];
var parts = entry.FullName.Split('/');
parts = parts.Take(1).Append("bin").Concat(parts.Skip(1)).Prepend(tmpDir).ToArray();
var dstPath = Path.Combine(parts);
Directory.CreateDirectory(Path.GetDirectoryName(dstPath));
using var src = entry.Open();
using var dst = new FileStream(dstPath, FileMode.Create);
await src.CopyToAsync(dst);
var newProgress = $"{(double)(i + 1) / archive.Entries.Count,4:P0}";
if (newProgress != progress)
{
progress = newProgress;
try { Console.CursorLeft = 0; }
catch { }
Console.Write(progress);
}
}
Console.WriteLine();
}
}
Directory.Move(tmpDir, dstDir);
var installed = false;
var path = Path.Combine(dstDir, BuildInfo.RuntimeIdentifier.Split('-').Last(), "bin", Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName));
if (File.Exists(path))
{
using var process = Process.Start(new ProcessStartInfo { FileName = path, Arguments = "install inplace", UseShellExecute = false });
process.WaitForExit();
installed = process.ExitCode == (int)ErrorCodes.OK;
}
if (!installed)
Directory.Move(dstDir, tmpDir);
}
}
}