summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SMAPI.Common/Models/ModInfoModel.cs12
-rw-r--r--src/SMAPI.ModBuildConfig/build/smapi.targets17
-rw-r--r--src/SMAPI.ModBuildConfig/package.nuspec3
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/StardewModdingAPI.Mods.ConsoleCommands.csproj4
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/manifest.json2
-rw-r--r--src/SMAPI.Mods.ConsoleCommands/packages.config2
-rw-r--r--src/SMAPI.Web/Controllers/IndexController.cs53
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs16
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs50
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs4
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs5
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs7
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs7
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs19
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs2
-rw-r--r--src/SMAPI.Web/StardewModdingAPI.Web.csproj14
-rw-r--r--src/SMAPI.Web/Startup.cs3
-rw-r--r--src/SMAPI.Web/ViewModels/IndexModel.cs28
-rw-r--r--src/SMAPI.Web/ViewModels/IndexVersionModel.cs41
-rw-r--r--src/SMAPI.Web/Views/Index/Index.cshtml35
-rw-r--r--src/SMAPI.Web/Views/LogParser/Index.cshtml34
-rw-r--r--src/SMAPI.Web/appsettings.json6
-rw-r--r--src/SMAPI/Constants.cs8
-rw-r--r--src/SMAPI/Framework/Content/AssetDataForImage.cs2
-rw-r--r--src/SMAPI/Framework/ContentCore.cs6
-rw-r--r--src/SMAPI/Metadata/CoreAssetPropagator.cs642
-rw-r--r--src/SMAPI/Metadata/CoreAssets.cs220
-rw-r--r--src/SMAPI/Program.cs31
-rw-r--r--src/SMAPI/StardewModdingAPI.csproj5
-rw-r--r--src/SMAPI/packages.config2
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