diff options
Diffstat (limited to 'src')
31 files changed, 947 insertions, 335 deletions
diff --git a/src/SMAPI.Common/Models/ModInfoModel.cs b/src/SMAPI.Common/Models/ModInfoModel.cs index 48305cb8..48df235a 100644 --- a/src/SMAPI.Common/Models/ModInfoModel.cs +++ b/src/SMAPI.Common/Models/ModInfoModel.cs @@ -9,9 +9,12 @@ namespace StardewModdingAPI.Common.Models /// <summary>The mod name.</summary> public string Name { get; set; } - /// <summary>The mod's semantic version number.</summary> + /// <summary>The semantic version for the mod's latest release.</summary> public string Version { get; set; } + /// <summary>The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</summary> + public string PreviewVersion { get; set; } + /// <summary>The mod's web URL.</summary> public string Url { get; set; } @@ -28,16 +31,17 @@ namespace StardewModdingAPI.Common.Models // needed for JSON deserialising } - /// <summary>Construct an instance.</summary> /// <param name="name">The mod name.</param> - /// <param name="version">The mod's semantic version number.</param> + /// <param name="version">The semantic version for the mod's latest release.</param> + /// <param name="previewVersion">The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</param> /// <param name="url">The mod's web URL.</param> /// <param name="error">The error message indicating why the mod is invalid (if applicable).</param> - public ModInfoModel(string name, string version, string url, string error = null) + public ModInfoModel(string name, string version, string url, string previewVersion = null, string error = null) { this.Name = name; this.Version = version; + this.PreviewVersion = previewVersion; this.Url = url; this.Error = error; // mainly initialised here for the JSON deserialiser } diff --git a/src/SMAPI.ModBuildConfig/build/smapi.targets b/src/SMAPI.ModBuildConfig/build/smapi.targets index 7e8bbfc3..d2e37101 100644 --- a/src/SMAPI.ModBuildConfig/build/smapi.targets +++ b/src/SMAPI.ModBuildConfig/build/smapi.targets @@ -19,9 +19,14 @@ <!-- set default settings --> <ModFolderName Condition="'$(ModFolderName)' == ''">$(MSBuildProjectName)</ModFolderName> + <ModUnitTests Condition="'$(ModUnitTests)' == ''">False</ModUnitTests> <ModZipPath Condition="'$(ModZipPath)' == ''">$(TargetDir)</ModZipPath> <EnableModDeploy Condition="'$(EnableModDeploy)' == ''">True</EnableModDeploy> <EnableModZip Condition="'$(EnableModZip)' == ''">True</EnableModZip> + + <!-- disable mod deploy in unit test project --> + <EnableModDeploy Condition="'$(ModUnitTests)' == true">False</EnableModDeploy> + <EnableModZip Condition="'$(ModUnitTests)' == true">False</EnableModZip> </PropertyGroup> <!-- find platform + game path --> @@ -57,32 +62,40 @@ <ItemGroup> <Reference Include="Microsoft.Xna.Framework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="Microsoft.Xna.Framework.Game, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="Microsoft.Xna.Framework.Graphics, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="Microsoft.Xna.Framework.Xact, Version=4.0.0.0, Culture=neutral, PublicKeyToken=842cf8be1de50553, processorArchitecture=x86"> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="Netcode" Condition="Exists('$(GamePath)\Netcode.dll')"> <HintPath>$(GamePath)\Netcode.dll</HintPath> <Private>False</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="Stardew Valley"> <HintPath>$(GamePath)\Stardew Valley.exe</HintPath> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="StardewModdingAPI"> <HintPath>$(GamePath)\StardewModdingAPI.exe</HintPath> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86"> <HintPath>$(GamePath)\xTile.dll</HintPath> <Private>false</Private> <SpecificVersion>False</SpecificVersion> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> </ItemGroup> @@ -100,18 +113,22 @@ <HintPath>$(GamePath)\MonoGame.Framework.dll</HintPath> <Private>false</Private> <SpecificVersion>False</SpecificVersion> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="StardewValley"> <HintPath>$(GamePath)\StardewValley.exe</HintPath> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="StardewModdingAPI"> <HintPath>$(GamePath)\StardewModdingAPI.exe</HintPath> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> <Reference Include="xTile"> <HintPath>$(GamePath)\xTile.dll</HintPath> <Private>false</Private> + <Private Condition="$(ModUnitTests)">true</Private> </Reference> </ItemGroup> </Otherwise> diff --git a/src/SMAPI.ModBuildConfig/package.nuspec b/src/SMAPI.ModBuildConfig/package.nuspec index 8393ab61..d24e15be 100644 --- a/src/SMAPI.ModBuildConfig/package.nuspec +++ b/src/SMAPI.ModBuildConfig/package.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"> <metadata> <id>Pathoschild.Stardew.ModBuildConfig</id> - <version>2.0.3-alpha20180307</version> + <version>2.0.3-alpha20180325</version> <title>Build package for SMAPI mods</title> <authors>Pathoschild</authors> <owners>Pathoschild</owners> @@ -29,6 +29,7 @@ 2.0.3: - Added support for Stardew Valley 1.3. + - Added support for unit test projects. </releaseNotes> </metadata> <files> diff --git a/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj b/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj index a5b89a33..d1f72c6c 100644 --- a/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj +++ b/src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj @@ -37,8 +37,8 @@ </PropertyGroup> <ItemGroup> <Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> - <HintPath>..\packages\Newtonsoft.Json.11.0.1-beta3\lib\net45\Newtonsoft.Json.dll</HintPath> - <Private>False</Private> + <HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath> + <Private>True</Private> </Reference> <Reference Include="System" /> <Reference Include="System.Xml" /> diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index 785af01a..a56cf66d 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,7 +1,7 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "2.5.3", + "Version": "2.5.4", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll" diff --git a/src/SMAPI.Mods.ConsoleCommands/packages.config b/src/SMAPI.Mods.ConsoleCommands/packages.config index a0f76c34..c8b3ae63 100644 --- a/src/SMAPI.Mods.ConsoleCommands/packages.config +++ b/src/SMAPI.Mods.ConsoleCommands/packages.config @@ -1,4 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> <packages> - <package id="Newtonsoft.Json" version="11.0.1-beta3" targetFramework="net45" /> + <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs index 5d45118f..0464e50a 100644 --- a/src/SMAPI.Web/Controllers/IndexController.cs +++ b/src/SMAPI.Web/Controllers/IndexController.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; +using StardewModdingAPI.Common; using StardewModdingAPI.Web.Framework.Clients.GitHub; using StardewModdingAPI.Web.ViewModels; @@ -23,7 +24,10 @@ namespace StardewModdingAPI.Web.Controllers private readonly IGitHubClient GitHub; /// <summary>The cache time for release info.</summary> - private readonly TimeSpan CacheTime = TimeSpan.FromMinutes(5); + private readonly TimeSpan CacheTime = TimeSpan.FromSeconds(1); + + /// <summary>The GitHub repository name to check for update.</summary> + private readonly string RepositoryName = "Pathoschild/SMAPI"; /********* @@ -42,17 +46,24 @@ namespace StardewModdingAPI.Web.Controllers [HttpGet] public async Task<ViewResult> Index() { - // fetch latest SMAPI release - GitRelease release = await this.Cache.GetOrCreateAsync("latest-smapi-release", async entry => + // 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); - return await this.GitHub.GetLatestReleaseAsync("Pathoschild/SMAPI"); + GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: true); + return release.IsPrerelease + ? this.GetBetaDownload(release) + : null; }); - string downloadUrl = this.GetMainDownloadUrl(release); - string devDownloadUrl = this.GetDevDownloadUrl(release); // render view - var model = new IndexModel(release.Name, release.Body, downloadUrl, devDownloadUrl); + var model = new IndexModel(stableVersion, betaVersion); return this.View(model); } @@ -89,5 +100,33 @@ namespace StardewModdingAPI.Web.Controllers // fallback just in case return "https://github.com/pathoschild/SMAPI/releases"; } + + /// <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; + } } } diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index abae7db7..24517263 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>The cache in which to store mod metadata.</summary> private readonly IMemoryCache Cache; - /// <summary>The number of minutes update checks should be cached before refetching them.</summary> - private readonly int CacheMinutes; + /// <summary>The number of minutes successful update checks should be cached before refetching them.</summary> + private readonly int SuccessCacheMinutes; + + /// <summary>The number of minutes failed update checks should be cached before refetching them.</summary> + private readonly int ErrorCacheMinutes; /// <summary>A regex which matches SMAPI-style semantic version.</summary> private readonly string VersionRegex; @@ -50,7 +53,8 @@ namespace StardewModdingAPI.Web.Controllers ModUpdateCheckConfig config = configProvider.Value; this.Cache = cache; - this.CacheMinutes = config.CacheMinutes; + this.SuccessCacheMinutes = config.SuccessCacheMinutes; + this.ErrorCacheMinutes = config.ErrorCacheMinutes; this.VersionRegex = config.SemanticVersionRegex; this.Repositories = new IModRepository[] @@ -115,13 +119,13 @@ namespace StardewModdingAPI.Web.Controllers if (info.Error == null) { if (info.Version == null) - info = new ModInfoModel(info.Name, info.Version, info.Url, "Mod has no version number."); + info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: "Mod has no version number."); if (!allowInvalidVersions && !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)) - info = new ModInfoModel(info.Name, info.Version, info.Url, $"Mod has invalid semantic version '{info.Version}'."); + info = new ModInfoModel(name: info.Name, version: info.Version, url: info.Url, error: $"Mod has invalid semantic version '{info.Version}'."); } // cache & return - entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes); + entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(info.Error == null ? this.SuccessCacheMinutes : this.ErrorCacheMinutes); return info; }); } diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs index 0b205660..4abe0737 100644 --- a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs +++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Net; using System.Threading.Tasks; using Pathoschild.Http.Client; @@ -11,8 +12,11 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub /********* ** Properties *********/ - /// <summary>The URL for a GitHub releases API query excluding the base URL, where {0} is the repository owner and name.</summary> - private readonly string ReleaseUrlFormat; + /// <summary>The URL for a GitHub API query for the latest stable release, excluding the base URL, where {0} is the organisation and project name.</summary> + private readonly string StableReleaseUrlFormat; + + /// <summary>The URL for a GitHub API query for the latest release (including prerelease), excluding the base URL, where {0} is the organisation and project name.</summary> + private readonly string AnyReleaseUrlFormat; /// <summary>The underlying HTTP client.</summary> private readonly IClient Client; @@ -23,14 +27,16 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub *********/ /// <summary>Construct an instance.</summary> /// <param name="baseUrl">The base URL for the GitHub API.</param> - /// <param name="releaseUrlFormat">The URL for a GitHub releases API query excluding the <paramref name="baseUrl"/>, where {0} is the repository owner and name.</param> + /// <param name="stableReleaseUrlFormat">The URL for a GitHub API query for the latest stable release, excluding the <paramref name="baseUrl"/>, where {0} is the organisation and project name.</param> + /// <param name="anyReleaseUrlFormat">The URL for a GitHub API query for the latest release (including prerelease), excluding the <paramref name="baseUrl"/>, where {0} is the organisation and project name.</param> /// <param name="userAgent">The user agent for the API client.</param> /// <param name="acceptHeader">The Accept header value expected by the GitHub API.</param> /// <param name="username">The username with which to authenticate to the GitHub API.</param> /// <param name="password">The password with which to authenticate to the GitHub API.</param> - public GitHubClient(string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password) + public GitHubClient(string baseUrl, string stableReleaseUrlFormat, string anyReleaseUrlFormat, string userAgent, string acceptHeader, string username, string password) { - this.ReleaseUrlFormat = releaseUrlFormat; + this.StableReleaseUrlFormat = stableReleaseUrlFormat; + this.AnyReleaseUrlFormat = anyReleaseUrlFormat; this.Client = new FluentClient(baseUrl) .SetUserAgent(userAgent) @@ -41,18 +47,23 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub /// <summary>Get the latest release for a GitHub repository.</summary> /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param> - /// <returns>Returns the latest release if found, else <c>null</c>.</returns> - public async Task<GitRelease> GetLatestReleaseAsync(string repo) + /// <param name="includePrerelease">Whether to return a prerelease version if it's latest.</param> + /// <returns>Returns the release if found, else <c>null</c>.</returns> + public async Task<GitRelease> GetLatestReleaseAsync(string repo, bool includePrerelease = false) { - // validate key format - if (!repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase)) - throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo)); - - // fetch info + this.AssetKeyFormat(repo); try { + if (includePrerelease) + { + GitRelease[] results = await this.Client + .GetAsync(string.Format(this.AnyReleaseUrlFormat, repo)) + .AsArray<GitRelease>(); + return results.FirstOrDefault(); + } + return await this.Client - .GetAsync(string.Format(this.ReleaseUrlFormat, repo)) + .GetAsync(string.Format(this.StableReleaseUrlFormat, repo)) .As<GitRelease>(); } catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound) @@ -66,5 +77,18 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub { this.Client?.Dispose(); } + + + /********* + ** Private methods + *********/ + /// <summary>Assert that a repository key is formatted correctly.</summary> + /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param> + /// <exception cref="ArgumentException">The repository key is invalid.</exception> + private void AssetKeyFormat(string repo) + { + if (repo == null || !repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase)) + throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo)); + } } } diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs index b944088d..827374fb 100644 --- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs +++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs @@ -19,6 +19,10 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub /// <summary>The Markdown description for the release.</summary> public string Body { get; set; } + /// <summary>Whether this is a prerelease version.</summary> + [JsonProperty("prerelease")] + public bool IsPrerelease { get; set; } + /// <summary>The attached files.</summary> public GitAsset[] Assets { get; set; } } diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs index 6e8eadff..9519c26f 100644 --- a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs +++ b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs @@ -11,7 +11,8 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub *********/ /// <summary>Get the latest release for a GitHub repository.</summary> /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param> - /// <returns>Returns the latest release if found, else <c>null</c>.</returns> - Task<GitRelease> GetLatestReleaseAsync(string repo); + /// <param name="includePrerelease">Whether to return a prerelease version if it's latest.</param> + /// <returns>Returns the release if found, else <c>null</c>.</returns> + Task<GitRelease> GetLatestReleaseAsync(string repo, bool includePrerelease = false); } } diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs index 61219414..de6c024a 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs @@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// <summary>The base URL for the GitHub API.</summary> public string GitHubBaseUrl { get; set; } - /// <summary>The URL for a GitHub API latest-release query excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary> - public string GitHubReleaseUrlFormat { get; set; } + /// <summary>The URL for a GitHub API query for the latest stable release, excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary> + public string GitHubStableReleaseUrlFormat { get; set; } + + /// <summary>The URL for a GitHub API query for the latest release (including prerelease), excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary> + public string GitHubAnyReleaseUrlFormat { get; set; } /// <summary>The Accept header value expected by the GitHub API.</summary> public string GitHubAcceptHeader { get; set; } diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs index 58c3a100..fc3b7dc2 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs @@ -6,8 +6,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /********* ** Accessors *********/ - /// <summary>The number of minutes update checks should be cached before refetching them.</summary> - public int CacheMinutes { get; set; } + /// <summary>The number of minutes successful update checks should be cached before refetching them.</summary> + public int SuccessCacheMinutes { get; set; } + + /// <summary>The number of minutes failed update checks should be cached before refetching them.</summary> + public int ErrorCacheMinutes { get; set; } /// <summary>A regex which matches SMAPI-style semantic version.</summary> /// <remarks>Derived from SMAPI's SemanticVersion implementation.</remarks> diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs index 266055a6..3e5a4272 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs @@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories return new ModInfoModel("Found no mod with this ID."); // create model - return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url); + return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url); } catch (Exception ex) { diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs index 7bad6127..59eb8cd1 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs @@ -38,10 +38,21 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories // fetch info try { - GitRelease release = await this.Client.GetLatestReleaseAsync(id); - return release != null - ? new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases") - : new ModInfoModel("Found no mod with this ID."); + // 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) { diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs index e1dc0fcc..6411ad4c 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs @@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories return new ModInfoModel("Found no mod with this ID."); if (mod.Error != null) return new ModInfoModel(mod.Error); - return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url); + return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url); } catch (Exception ex) { diff --git a/src/SMAPI.Web/StardewModdingAPI.Web.csproj b/src/SMAPI.Web/StardewModdingAPI.Web.csproj index 19198503..e2eee8a8 100644 --- a/src/SMAPI.Web/StardewModdingAPI.Web.csproj +++ b/src/SMAPI.Web/StardewModdingAPI.Web.csproj @@ -10,13 +10,13 @@ </ItemGroup> <ItemGroup> - <Pac |
