/********* ** Private methods *********/ /// <summary>Get a sorted, parsed list of SMAPI downloads for the latest releases.</summary> private async Task <ReleaseVersion[]> GetReleaseVersionsAsync() { return(await this.Cache.GetOrCreateAsync("available-versions", async entry => { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); // get latest stable release GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false); // strip 'noinclude' blocks from release description if (release != null) { HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(release.Body); foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? Array.Empty <HtmlNode>()) { node.Remove(); } release.Body = doc.DocumentNode.InnerHtml.Trim(); } // get versions return this .ParseReleaseVersions(release) .OrderBy(p => p.Version) .ToArray(); })); }
/// <summary> /// Starts the installation of the release /// </summary> /// <param name="releaseindex">The index of the release as in the Releases List</param> /// <param name="installdir">The installation directory</param> internal void StartInstallation(int releaseindex, string installdir) { if (Settings.ShowLicense) { LicenseWindow licenseWindow = new LicenseWindow(); if (licenseWindow.ShowDialog() == true) { _window.bt_install.IsEnabled = false; _installrelease = Releases[releaseindex]; _window.WriteLog("Starting installation of release \"" + _installrelease.Tag + "\" to \"" + installdir + "\"..."); _installdir = installdir; DownloadAssets(); } else { _window.WriteLog("You need to accept the license to proceed with the installation."); } } else { _window.bt_install.IsEnabled = false; _installrelease = Releases[releaseindex]; _window.WriteLog("Starting installation of release \"" + _installrelease.Tag + "\" to \"" + installdir + "\"..."); _installdir = installdir; DownloadAssets(); } }
/********* ** Private methods *********/ /**** ** Event handlers ****/ /// <summary>The method invoked when the player loads the game.</summary> private void ReceiveGameLoaded() { // check for an updated version if (this.Config.CheckForUpdates) { Task.Factory.StartNew(() => { GameHelper.InterceptErrors("checking for a newer version", () => { using (ICumulativeLog log = this.GetTaskLog()) { log.Append("Lookup Anything checking for update... "); GitRelease release = UpdateHelper.GetLatestReleaseAsync("Pathoschild/LookupAnything").Result; if (release.IsNewerThan(this.CurrentVersion)) { log.AppendLine($"update to version {release.Name} available."); this.NewRelease = release; } else { log.AppendLine("no update available."); } } }); }); } }
/// <summary>Get metadata about a mod in the repository.</summary> /// <param name="id">The mod ID in this repository.</param> public override async Task <ModInfoModel> GetModInfoAsync(string id) { // validate ID format if (!id.Contains("/") || id.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != id.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase)) { return(new ModInfoModel($"The value '{id}' isn't a valid GitHub mod ID, must be a username and project name like 'Pathoschild/LookupAnything'.")); } // fetch info try { GitRelease release = await this.Client .GetAsync(string.Format(this.ReleaseUrlFormat, id)) .As <GitRelease>(); return(new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases")); } catch (ApiException ex) when(ex.Status == HttpStatusCode.NotFound) { return(new ModInfoModel("Found no mod with this ID.")); } catch (Exception ex) { return(new ModInfoModel(ex.ToString())); } }
/********* ** Public methods *********/ /// <summary>Construct an instance.</summary> /// <param name="release">The underlying GitHub release.</param> /// <param name="asset">The underlying download asset.</param> /// <param name="version">The SMAPI version.</param> /// <param name="isForDevs">Whether this is a 'for developers' download.</param> public ReleaseVersion(GitRelease release, GitAsset asset, ISemanticVersion version, bool isForDevs) { this.Release = release; this.Asset = asset; this.Version = version; this.IsForDevs = isForDevs; }
/// <summary>Get a parsed list of SMAPI downloads for a release.</summary> /// <param name="release">The GitHub release.</param> private IEnumerable <ReleaseVersion> ParseReleaseVersions(GitRelease release) { if (release?.Assets == null) { yield break; } foreach (GitAsset asset in release.Assets) { if (asset.FileName.StartsWith("Z_")) { continue; } Match match = Regex.Match(asset.FileName, @"SMAPI-(?<version>[\d\.]+(?:-.+)?)-installer(?<forDevs>-for-developers)?.zip"); if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion version)) { continue; } bool isBeta = version.IsPrerelease(); bool isForDevs = match.Groups["forDevs"].Success; yield return(new ReleaseVersion(release, asset, version, isBeta, isForDevs)); } }
/// <summary>Get metadata about a mod in the repository.</summary> /// <param name="id">The mod ID in this repository.</param> public override async Task <ModInfoModel> GetModInfoAsync(string id) { // validate ID format if (!id.Contains("/") || id.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != id.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase)) { return(new ModInfoModel($"The value '{id}' isn't a valid GitHub mod ID, must be a username and project name like 'Pathoschild/LookupAnything'.")); } // fetch info try { // get latest release GitRelease latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease : true); GitRelease preview = null; if (latest == null) { return(new ModInfoModel("Found no mod with this ID.")); } // get latest stable release (if not latest) if (latest.IsPrerelease) { preview = latest; latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease : false); } // return data return(new ModInfoModel(name: id, version: this.NormaliseVersion(latest?.Tag), previewVersion: this.NormaliseVersion(preview?.Tag), url: $"https://github.com/{id}/releases")); } catch (Exception ex) { return(new ModInfoModel(ex.ToString())); } }
/// <summary>Get the latest beta download for a SMAPI release.</summary> /// <param name="release">The SMAPI release.</param> private IndexVersionModel GetBetaDownload(GitRelease release) { // get download with the latest version SemanticVersionImpl latestVersion = null; string latestUrl = null; foreach (GitAsset asset in release.Assets ?? new GitAsset[0]) { // parse version Match versionMatch = Regex.Match(asset.FileName, @"SMAPI-([\d\.]+(?:-.+)?)-installer.zip"); if (!versionMatch.Success || !SemanticVersionImpl.TryParse(versionMatch.Groups[1].Value, out SemanticVersionImpl version)) { continue; } // save latest version if (latestVersion == null || latestVersion.CompareTo(version) < 0) { latestVersion = version; latestUrl = asset.DownloadUrl; } } // return if prerelease return(latestVersion?.Tag != null ? new IndexVersionModel(latestVersion.ToString(), release.Body, latestUrl, null) : null); }
private async void MTModulesVw_ItemSelected(object sender, EventArgs e) { ModuleItem moduleItem = SelectedModuleItem; bool hasAuthors = (moduleItem.Authors.Count > 0); UI.MTAuthorsTxt.Enabled = hasAuthors; UI.MTAuthorsTxt.DataSource = (hasAuthors ? SelectedModuleItem.Authors : null); GitRepository repository = (moduleItem.Repository); if (UI.MTReleasesBtn.Enabled = (repository != null)) { GitRelease currentRelease = (repository.LatestRelease ?? await repository.GetLatestReleaseAsync()); bool hasReleaseMatch = (currentRelease.GetVersion() == moduleItem.Version); if (currentRelease != null && !hasReleaseMatch) { List <GitRelease> releases = (repository.Releases ?? await repository.GetReleasesAsync()); foreach (GitRelease release in releases) { if (release.GetVersion() == moduleItem.Version) { hasReleaseMatch = true; currentRelease = release; break; } } } if (currentRelease != null && moduleItem == SelectedModuleItem) { if (hasReleaseMatch) { DisplayDownloads(currentRelease); } UI.MTDownloadLatestBtn.Enabled = (repository.LatestRelease.GetVersion() > moduleItem.Version); } } else { UI.MTDownloadsLbl.Text = "Downloads: 0"; UI.MTDownloadLatestBtn.Enabled = UI.MTReleasesBtn.Enabled = false; } }
/// <summary>Get metadata about a mod in the repository.</summary> /// <param name="id">The mod ID in this repository.</param> public override async Task <ModInfoModel> GetModInfoAsync(string id) { ModInfoModel result = new ModInfoModel().SetBasicInfo(id, $"https://github.com/{id}/releases"); // validate ID format if (!id.Contains("/") || id.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != id.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase)) { return(result.SetError(RemoteModStatus.DoesNotExist, $"The value '{id}' isn't a valid GitHub mod ID, must be a username and project name like 'Pathoschild/LookupAnything'.")); } // fetch info try { // fetch repo info GitRepo repository = await this.Client.GetRepositoryAsync(id); if (repository == null) { return(result.SetError(RemoteModStatus.DoesNotExist, "Found no GitHub repository for this ID.")); } result .SetBasicInfo(repository.FullName, $"{repository.WebUrl}/releases") .SetLicense(url: repository.License?.Url, name: repository.License?.SpdxId ?? repository.License?.Name); // get latest release (whether preview or stable) GitRelease latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease : true); if (latest == null) { return(result.SetError(RemoteModStatus.DoesNotExist, "Found no GitHub release for this ID.")); } // split stable/prerelease if applicable GitRelease preview = null; if (latest.IsPrerelease) { GitRelease release = await this.Client.GetLatestReleaseAsync(id, includePrerelease : false); if (release != null) { preview = latest; latest = release; } } // return data return(result.SetVersions(version: this.NormalizeVersion(latest.Tag), previewVersion: this.NormalizeVersion(preview?.Tag))); } catch (Exception ex) { return(result.SetError(RemoteModStatus.TemporaryError, ex.ToString())); } }
/// <summary>Get the for-developers download URL for a SMAPI release.</summary> /// <param name="release">The SMAPI release.</param> private string GetDevDownloadUrl(GitRelease release) { // get dev download URL foreach (GitAsset asset in release.Assets ?? new GitAsset[0]) { if (Regex.IsMatch(asset.FileName, @"SMAPI-[\d\.]+-installer-for-developers.zip")) { return(asset.DownloadUrl); } } // fallback just in case return("https://github.com/pathoschild/SMAPI/releases"); }
/// <summary>Get metadata about a mod in the repository.</summary> /// <param name="id">The mod ID in this repository.</param> public async Task <ModInfoModel> GetModInfoAsync(string id) { try { GitRelease release = await this.Client .GetAsync(string.Format(this.ReleaseUrlFormat, id)) .As <GitRelease>(); return(new ModInfoModel(id, release.Tag, $"https://github.com/{id}/releases")); } catch (Exception ex) { return(new ModInfoModel(ex.ToString())); } }
/********* ** Private methods *********/ /// <summary>Get a sorted, parsed list of SMAPI downloads for the latest releases.</summary> private async Task <ReleaseVersion[]> GetReleaseVersionsAsync() { return(await this.Cache.GetOrCreateAsync("available-versions", async entry => { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); // get latest release (whether preview or stable) GitRelease stableRelease = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true); // split stable/prerelease if applicable GitRelease betaRelease = null; if (stableRelease.IsPrerelease) { GitRelease result = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false); if (result != null) { betaRelease = stableRelease; stableRelease = result; } } // strip 'noinclude' blocks from release descriptions foreach (GitRelease release in new[] { stableRelease, betaRelease }) { if (release == null) { continue; } HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(release.Body); foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//*[@class='noinclude']")?.ToArray() ?? new HtmlNode[0]) { node.Remove(); } release.Body = doc.DocumentNode.InnerHtml.Trim(); } // get versions ReleaseVersion[] stableVersions = this.ParseReleaseVersions(stableRelease).ToArray(); ReleaseVersion[] betaVersions = this.ParseReleaseVersions(betaRelease).ToArray(); return stableVersions .Concat(betaVersions) .OrderBy(p => p.Version) .ToArray(); })); }
public async Task <ViewResult> Index() { // fetch latest SMAPI release GitRelease release = await this.Cache.GetOrCreateAsync("latest-smapi-release", async entry => { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); return(await this.GitHub.GetLatestReleaseAsync("Pathoschild/SMAPI")); }); string downloadUrl = this.GetMainDownloadUrl(release); string devDownloadUrl = this.GetDevDownloadUrl(release); // render view var model = new IndexModel(release.Name, release.Body, downloadUrl, devDownloadUrl); return(this.View(model)); }
/********* ** Private methods *********/ /// <summary>Asynchronously check for a new version of SMAPI, and print a message to the console if an update is available.</summary> private void CheckForUpdateAsync() { new Thread(() => { try { GitRelease release = UpdateHelper.GetLatestVersionAsync(Constants.GitHubRepository).Result; ISemanticVersion latestVersion = new SemanticVersion(release.Tag); if (latestVersion.IsNewerThan(Constants.ApiVersion)) { this.Monitor.Log($"You can update SMAPI from version {Constants.ApiVersion} to {latestVersion}", LogLevel.Alert); } } catch (Exception ex) { this.Monitor.Log($"Couldn't check for a new version of SMAPI. This won't affect your game, but you may not be notified of new versions if this keeps happening.\n{ex.GetLogSummary()}"); } }).Start(); }
private async void UI_Shown(object sender, EventArgs e) { UI.Shown -= UI_Shown; GitRelease latestRelease = await TanjiRepo.GetLatestReleaseAsync(); if (latestRelease != null) { LatestVersion = new Version( latestRelease.TagName.Substring(1)); UI.TanjiVersionTxt.IsLink = true; if (LatestVersion > LocalVersion && !latestRelease.IsPrerelease) { UI.TanjiVersionTxt.Text = "Update Found!"; } } }
/********* ** Private methods *********/ /// <summary>Get a sorted, parsed list of SMAPI downloads for the latest releases.</summary> private async Task <ReleaseVersion[]> GetReleaseVersionsAsync() { return(await this.Cache.GetOrCreateAsync("available-versions", async entry => { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); // get releases GitRelease stableRelease = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false); GitRelease betaRelease = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true); if (stableRelease.Tag == betaRelease.Tag) { betaRelease = null; } // get versions ReleaseVersion[] stableVersions = this.ParseReleaseVersions(stableRelease).ToArray(); ReleaseVersion[] betaVersions = this.ParseReleaseVersions(betaRelease).ToArray(); return stableVersions .Concat(betaVersions) .OrderBy(p => p.Version) .ToArray(); })); }
private async void UI_Shown(object sender, EventArgs e) { UI.Shown -= UI_Shown; await Task.Delay(225); GitRelease latestRelease = await TanjiRepo.GetLatestReleaseAsync(); if (latestRelease != null) { LatestVersion = new Version( latestRelease.TagName.Substring(1)); UI.TanjiVersionTxt.IsLink = true; if (LatestVersion > LocalVersion && !latestRelease.IsPrerelease) { UI.TanjiInfoTxt.Spring = true; UI.TanjiInfoTxt.Text = "Source Code"; UI.TanjiVersionTxt.Text = "Update Available!"; } } }
internal async void UpdateAsync() { var _ReleaseDeserializer = new DataContractJsonSerializer(typeof(GitRelease)); GitRelease _ReleaseGit = await DownloadJsonObjectAsync <GitRelease>( GitReleaseBase.GithubURI, _ReleaseDeserializer, "github").ConfigureAwait(false); if (_ReleaseGit == default) { DepictInfo = string.Format(CultureInfos, "更换服务器......请稍后"); _ReleaseGit = await DownloadJsonObjectAsync <GitRelease>( GitReleaseBase.GiteeURI, _ReleaseDeserializer, "gitee").ConfigureAwait(false); if (_ReleaseGit == default) { DepictInfo = string.Format(CultureInfos, "请检查网络或稍后再试!"); return; } } UpdateVersionCompareTo(_ReleaseGit.GetVersion()); }
public async Task <ViewResult> Index() { // fetch SMAPI releases IndexVersionModel stableVersion = await this.Cache.GetOrCreateAsync("stable-version", async entry => { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false); return(new IndexVersionModel(release.Name, release.Body, this.GetMainDownloadUrl(release), this.GetDevDownloadUrl(release))); }); IndexVersionModel betaVersion = await this.Cache.GetOrCreateAsync("beta-version", async entry => { entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true); return(release.IsPrerelease ? this.GetBetaDownload(release) : null); }); // render view var model = new IndexModel(stableVersion, betaVersion); return(this.View(model)); }
private void DisplayDownloads(GitRelease release) { UI.MTDownloadsLbl.Text = $"Downloads: {release.Assets[0].DownloadCount:n0}"; }
/// <summary> /// Fetches the different versions/releases from the Githup repo /// </summary> async void GetVersions() { _window.WriteLog("Trying to fetch releases from GitHub..."); _window.prog_loading.IsIndeterminate = true; using (var client = new WebClient()) { client.Headers.Add("user-agent", "GitInstaller"); //Save as _releasesjson so you don't have to fetch it again _releasesjson = await client.DownloadStringTaskAsync(_url.AbsoluteUri); _window.WriteLog("Fetched all releases from GitHub..."); //Create GitRelease objects from the _releasesjson string _window.WriteLog("Creating Release objects..."); JObject[] jobjs = JsonConvert.DeserializeObject <JObject[]>(_releasesjson); int idcount = 0; Releases.Clear(); _window.cb_versions.Items.Clear(); foreach (JObject job in jobjs) { GitRelease robj = new GitRelease(); robj.Id = idcount; robj.Name = job.Value <string>("name"); robj.Tag = job.Value <string>("tag_name"); robj.Body = job.Value <string>("body"); robj.GitUrl = job.Value <string>("html_url"); robj.IsPrerelease = job.Value <bool>("prerelease"); robj.CreationDate = job.Value <string>("created_at"); //Author JObject authorobj = job.Value <JObject>("author"); if (authorobj != null) { robj.AuthorName = authorobj.Value <string>("login"); robj.AuthorUrl = authorobj.Value <string>("html_url"); } //Assets JToken assets = job.Value <JToken>("assets"); foreach (JToken asset in assets.Children()) { GitAsset newasset = new GitAsset(); newasset.Filename = asset.Value <string>("name"); newasset.DownloadUrl = asset.Value <string>("browser_download_url"); newasset.Size = asset.Value <float>("size"); robj.Assets.Add(newasset); } if (Settings.Ignored_Tags.Length > 0) { foreach (string ignoredtags in Settings.Ignored_Tags) { if (!Utils.HasWildcard(robj.Tag, ignoredtags)) { idcount++; Releases.Add(robj); } } } else { idcount++; Releases.Add(robj); } } _window.cb_versions.SelectedIndex = 0; } _window.prog_loading.IsIndeterminate = false; if (Releases.Count < 1) { MessageBox.Show("No releases found for this repository! Can't progress with the installation...", "Warning!", MessageBoxButton.OK, MessageBoxImage.Warning); Environment.Exit(2); return; } _window.bt_install.IsEnabled = true; _window.WriteLog("Done creating release objects, " + Releases.Count + " releases added..."); _window.UpdateVersions(Settings.Preview); _window.UpdateChanges(Releases[0]); }