1
- using System . Net ;
1
+ using System . Diagnostics ;
2
+ using System . IO . Compression ;
3
+ using System . Net ;
4
+
5
+ using Microsoft . VisualBasic . FileIO ;
2
6
3
7
using Newtonsoft . Json ;
4
8
@@ -36,4 +40,88 @@ public static class GitHubUpdateManager {
36
40
string body = await response . Content . ReadAsStringAsync ( ) ;
37
41
return JsonConvert . DeserializeObject < GithubLatestReleaseJson > ( body ) ;
38
42
}
43
+
44
+ /// <summary>
45
+ /// Prepares to update this application before this application is closed.
46
+ /// </summary>
47
+ public static async Task PrepareUpdate ( ) {
48
+ // To prepare the update, we just need to back up our files
49
+ string ourFolder = Path . GetDirectoryName ( typeof ( GitHubUpdateManager ) . Assembly . Location ) ?? "" ;
50
+ string backupFolder = Path . Combine ( ourFolder , ".." , "backup" ) ;
51
+ await DeleteRetry ( backupFolder ) ;
52
+
53
+ Directory . CreateDirectory ( backupFolder ) ;
54
+ FileSystem . CopyDirectory ( ourFolder , backupFolder ) ;
55
+ }
56
+
57
+ /// <summary>
58
+ /// Runs this application from the backup folder to initiate the update on the installed folder.
59
+ /// </summary>
60
+ public static void ExitApplicationToUpdate ( ) {
61
+ // Since we have a backup folder from PrepareUpdate() we can just run the backup executable
62
+ string ourFolder = Path . GetDirectoryName ( typeof ( GitHubUpdateManager ) . Assembly . Location ) ?? "" ;
63
+ string backupFolder = Path . Combine ( ourFolder , ".." , "backup" ) ;
64
+ if ( ! Directory . Exists ( backupFolder ) ) {
65
+ return ;
66
+ }
67
+
68
+ string ourExecutable = $ "{ AppDomain . CurrentDomain . FriendlyName } .exe";
69
+
70
+ // we must pass the installation folder to the executable so it knows where to install
71
+ Process . Start ( Path . Combine ( backupFolder , ourExecutable ) , $ "--update \" { ourFolder } \" ") ;
72
+ Environment . Exit ( 0 ) ;
73
+ }
74
+
75
+ /// <summary>
76
+ /// Performs the application update. This involves downloading the latest release from GitHub, extracting its contents
77
+ /// to the installation folder, and closing the currently running application while running the new one.
78
+ /// </summary>
79
+ public static async Task PerformUpdateAndRestart ( string owner , string repo , string installFolder , string assetName ) {
80
+ // Delete the old install folder.
81
+ await DeleteRetry ( installFolder ) ;
82
+
83
+ // Get the latest version of the application from GitHub.
84
+ string ourFolder = Path . GetDirectoryName ( typeof ( GitHubUpdateManager ) . Assembly . Location ) ?? "" ;
85
+ string zipLocation = Path . Combine ( ourFolder , assetName ) ;
86
+ GithubLatestReleaseJson ? latestVersion = await GetLatestVersion ( owner , repo ) ;
87
+ using ( var client = new HttpClient ( ) ) {
88
+ using HttpResponseMessage response = await client . GetAsync ( $ "https://github.com/{ owner } /{ repo } /releases/download/{ latestVersion ? . name } /{ assetName } ") ;
89
+ await using Stream streamToReadFrom = await response . Content . ReadAsStreamAsync ( ) ;
90
+ await using var fileStream = new FileStream ( zipLocation , FileMode . Create ) ;
91
+ await streamToReadFrom . CopyToAsync ( fileStream ) ;
92
+ }
93
+
94
+ // Extract the zip file to the installation folder.
95
+ ZipFile . ExtractToDirectory ( zipLocation , installFolder ) ;
96
+
97
+ // Run the new version of the application.
98
+ Process . Start ( Path . Combine ( installFolder , $ "{ AppDomain . CurrentDomain . FriendlyName } .exe") , "--justUpdated" ) ;
99
+
100
+ // Close this version of the application.
101
+ Environment . Exit ( 0 ) ;
102
+ }
103
+
104
+ /// <summary>
105
+ /// Cleans up the previous update's files.
106
+ /// </summary>
107
+ public static async Task CleanupUpdate ( ) {
108
+ string ourFolder = Path . GetDirectoryName ( typeof ( GitHubUpdateManager ) . Assembly . Location ) ?? "" ;
109
+ string backupFolder = Path . Combine ( ourFolder , ".." , "backup" ) ;
110
+
111
+ await DeleteRetry ( backupFolder ) ;
112
+ }
113
+
114
+ /// <summary>
115
+ /// Retries deleting a folder multiple times.
116
+ /// </summary>
117
+ /// <param name="folder">The folder to delete.</param>
118
+ private static async Task DeleteRetry ( string folder ) {
119
+ await Retry . Execute < bool > ( ( ) => {
120
+ if ( Directory . Exists ( folder ) ) {
121
+ Directory . Delete ( folder , true ) ;
122
+ }
123
+
124
+ return Task . FromResult ( true ) ;
125
+ } , 30 , waitTime : TimeSpan . FromSeconds ( 1 ) ) ;
126
+ }
39
127
}
0 commit comments