1- using System . Net ;
1+ using System . Diagnostics ;
2+ using System . IO . Compression ;
3+ using System . Net ;
4+
5+ using Microsoft . VisualBasic . FileIO ;
26
37using Newtonsoft . Json ;
48
@@ -36,4 +40,88 @@ public static class GitHubUpdateManager {
3640 string body = await response . Content . ReadAsStringAsync ( ) ;
3741 return JsonConvert . DeserializeObject < GithubLatestReleaseJson > ( body ) ;
3842 }
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+ }
39127}
0 commit comments