/// <summary> /// Commits pre-release to GitHub converting it to Latest Release. /// </summary> /// <param name="settings">Zombie Settings.</param> public async void PushReleaseToGitHub(ZombieSettings settings) { try { var client = new GitHubClient(new ProductHeaderValue("Zombie")); var tokenAuth = new Credentials(settings.AccessToken); client.Credentials = tokenAuth; var segments = GitHubUtils.ParseUrl(settings.Address); var unused = await client.Repository.Release.Edit(segments["owner"], segments["repo"], settings.LatestRelease.Id, new ReleaseUpdate { Body = settings.LatestRelease.Body, Draft = false, Name = settings.LatestRelease.Name, Prerelease = false, TagName = settings.LatestRelease.TagName, TargetCommitish = "master" }); Messenger.Default.Send(new UpdateStatus { Message = "Zombie changed your Release!" }); } catch (Exception e) { _logger.Fatal(e.Message); Messenger.Default.Send(new UpdateStatus { Message = "Failed to push updates to your release!" }); } }
/// <summary> /// Downloads latest pre-release from GitHub. /// </summary> /// <param name="settings">Zombie Settings.</param> public async void DownloadPreRelease(ZombieSettings settings) { var segments = GitHubUtils.ParseUrl(settings.Address); var client = new GitHubClient(new ProductHeaderValue("Zombie")); var tokenAuth = new Credentials(settings.AccessToken); client.Credentials = tokenAuth; Release prerelease = null; try { var releases = await client.Repository.Release.GetAll(segments["owner"], segments["repo"], ApiOptions.None); if (releases.Any()) { prerelease = releases.OrderBy(x => x.PublishedAt).FirstOrDefault(x => x.Prerelease); } if (prerelease == null) { Messenger.Default.Send(new PrereleaseDownloaded { Status = PrereleaseStatus.Failed, Settings = null }); return; } } catch (Exception e) { _logger.Fatal("Failed to retrieve Pre-Release from GitHub. " + e.Message); return; } settings.LatestRelease = new ReleaseObject(prerelease); Messenger.Default.Send(new PrereleaseDownloaded { Status = PrereleaseStatus.Found, Settings = settings }); }
/// <summary> /// Method that retrieves the latest release from GitHub. /// </summary> /// <param name="settings">Zombie Settings to be used to retrieve latest Release.</param> public static async void GetLatestRelease(ZombieSettings settings) { if (IsProcessRunning("Revit")) { _logger.Error("Update failed. Revit is running."); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Revit is running."); return; } if (string.IsNullOrWhiteSpace(settings?.AccessToken) || string.IsNullOrWhiteSpace(settings.Address)) { var a = string.IsNullOrWhiteSpace(settings.Address) ? "Not found" : "Exists"; _logger.Error($"Connection failed! Address: {a}"); return; } var segments = GitHubUtils.ParseUrl(settings.Address); var client = new GitHubClient(new ProductHeaderValue("Zombie")); var tokenAuth = new Credentials(settings.AccessToken); client.Credentials = tokenAuth; Release release; try { release = await client.Repository.Release.GetLatest(segments["owner"], segments["repo"]); var currentVersion = RegistryUtils.GetZombieVersion(); if (!release.Assets.Any() || new Version(release.TagName).CompareTo(new Version(currentVersion)) <= 0) { PublishGuiUpdate(Program.Settings, Status.UpToDate, "Your release is up to date! Version: " + currentVersion); return; } } catch (Exception e) { _logger.Fatal("Failed to retrieve Release from GitHub. " + e.Message); return; } // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update in progress. Please do not launch Revit."); var dir = FileUtils.GetZombieDownloadsDirectory(); var downloaded = 0; foreach (var asset in release.Assets) { var filePath = Path.Combine(dir, asset.Name); if (GitHubUtils.DownloadAssets(settings, asset.Url, filePath)) { downloaded++; } } if (downloaded != release.Assets.Count) { _logger.Error("Failed to download assets!"); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Check your internet connection and try again."); return; } // (Konrad) Let's get updated settings, they might be local, or remote. // We need latest settings since there might be changes to the target locations. ZombieSettings newSettings; if (File.Exists(settings.SettingsLocation)) { if (!SettingsUtils.TryGetStoredSettings(settings.SettingsLocation, out newSettings)) { _logger.Error("Could not get latest local Zombie Settings!"); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Zombie Settings not found."); return; } } else { if (!SettingsUtils.TryGetRemoteSettings(settings.SettingsLocation, out newSettings)) { _logger.Error("Could not get latest remote Zombie Settings!"); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Zombie Settings not found."); return; } } // (Konrad) Let's make sure that we own the files that we are trying to override var fileStreams = new Dictionary <string, FileStream>(); foreach (var loc in newSettings.DestinationAssets.OrderByDescending(x => (int)x.LocationType)) { foreach (var asset in loc.Assets) { if (asset.IsArchive()) { // (Konrad) Use old settings if (LockAllContents(loc.LocationType == LocationType.Trash ? settings : newSettings, asset, loc.DirectoryPath, loc.LocationType, out var zippedStreams)) { fileStreams = fileStreams .Concat(zippedStreams) .GroupBy(x => x.Key) .ToDictionary(x => x.Key, x => x.First().Value); continue; } ReleaseStreams(fileStreams); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Revit is potentially running preventing an update."); return; } if (loc.LocationType != LocationType.Trash) { // (Konrad) Make sure that destination folder exists. if (!Directory.Exists(loc.DirectoryPath)) { FileUtils.CreateDirectory(loc.DirectoryPath); } } var to = Path.Combine(loc.DirectoryPath, asset.Name); if (!File.Exists(to)) { continue; } try { var fs = new FileStream(to, FileMode.Open, FileAccess.ReadWrite, FileShare.None); fileStreams.Add(to, fs); } catch (Exception e) { _logger.Fatal(e.Message); ReleaseStreams(fileStreams); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed...due to unknown reasons. Sorry about that."); return; } } } // (Konrad) Move assets to target locations. // We sort the locations list so that Trash (3) is first. // This should make sure that we delete first, then move. // Could be important with Zipped contents and overriding. foreach (var loc in newSettings.DestinationAssets.OrderByDescending(x => (int)x.LocationType)) { if (loc.LocationType == LocationType.Trash) { // (Konrad) Let's remove these files. foreach (var asset in loc.Assets) { if (asset.IsArchive()) { if (DeleteZipContents(asset, loc.DirectoryPath, fileStreams)) { continue; } ReleaseStreams(fileStreams); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Revit is potentially running preventing an update."); return; } var to = Path.Combine(loc.DirectoryPath, asset.Name); if (fileStreams.ContainsKey(to)) { // make sure that file is not locked var stream = fileStreams[to]; stream?.Close(); } if (FileUtils.DeleteFile(to)) { continue; } ReleaseStreams(fileStreams); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Revit is potentially running preventing an update."); return; } } else { // (Konrad) Let's copy these files. foreach (var asset in loc.Assets) { if (asset.IsArchive()) { if (ExtractToDirectory(asset, loc.DirectoryPath, fileStreams)) { continue; } ReleaseStreams(fileStreams); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Revit is potentially running preventing an update."); return; } var from = Path.Combine(dir, asset.Name); var to = Path.Combine(loc.DirectoryPath, asset.Name); if (fileStreams.ContainsKey(to)) { // make sure that file is not locked var stream = fileStreams[to]; stream?.Close(); } // (Konrad) Make sure that directory exists. if (!Directory.Exists(Path.GetDirectoryName(to))) { FileUtils.CreateDirectory(Path.GetDirectoryName(to)); } if (FileUtils.Copy(from, to)) { continue; } ReleaseStreams(fileStreams); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update failed. Revit is potentially running preventing an update."); return; } } } // (Konrad) Remove temporary assets if (!FileUtils.DeleteDirectory(dir)) { // (Konrad) Cleanup failed but we can continue. _logger.Error("Could not remove temporary download assets!"); } // (Konrad) This is important! ReleaseStreams(fileStreams); // (Konrad) We need to store the current version for comparison on next update RegistryUtils.SetZombieVersion(release.TagName); // (Konrad) Settings need to be updated with the latest one just downloaded newSettings.LatestRelease = new ReleaseObject(release); if (!newSettings.StoreSettings) { newSettings.AccessToken = Program.Settings.AccessToken; newSettings.SettingsLocation = Program.Settings.SettingsLocation; } Program.Settings = newSettings; // (Konrad) Publish to any open GUIs PublishGuiUpdate(newSettings, Status.Succeeded, "Successfully updated to Version: " + release.TagName); // (Konrad) Since release was not up to date. Let's show a notification to user that update is in progress. PublishGuiUpdate(Program.Settings, Status.Notification, "Update succeeded. Go ahead and launch Revit to see what's new."); }
/// <summary> /// Commits Zombie Settings to GitHub overriding existing file. /// </summary> /// <param name="settings">Zombie Settings.</param> public async void PushSettingsToGitHub(ZombieSettings settings) { try { var segments = GitHubUtils.ParseUrl(settings.SettingsLocation); var client = new GitHubClient(new ProductHeaderValue("Zombie")); var tokenAuth = new Credentials(settings.AccessToken); client.Credentials = tokenAuth; var contents = await client.Repository.Content.GetAllContents(segments["owner"], segments["repo"], segments["file"]); if (!contents.Any()) { Messenger.Default.Send(new UpdateStatus { Message = "Could not get contents of the repo!" }); return; } var sha = string.Empty; foreach (var rc in contents) { if (rc.Name != segments["file"]) { continue; } sha = rc.Sha; break; } if (string.IsNullOrEmpty(sha)) { Messenger.Default.Send(new UpdateStatus { Message = "Could not get valid SHA for the ZombieSettings!" }); return; } var jsonSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, CheckAdditionalContent = true, Formatting = Formatting.Indented }; settings.ShouldSerialize = false; var json = JsonConvert.SerializeObject(settings, jsonSettings); var unused = await client.Repository.Content.UpdateFile(segments["owner"], segments["repo"], segments["file"], new UpdateFileRequest("Zombie changed you!", json, sha, "master", true)); Messenger.Default.Send(new UpdateStatus { Message = "Zombie changed your settings!" }); } catch (Exception e) { _logger.Fatal(e.Message); Messenger.Default.Send(new UpdateStatus { Message = "Failed to push update to your Zombie Settings!" }); } }