From 61d6ec12daee843f758e5f828a713a72a767a94b Mon Sep 17 00:00:00 2001 From: Tyler Date: Tue, 18 Oct 2022 20:03:28 -0500 Subject: add detailed manifest validation errors at build time --- src/SMAPI.ModBuildConfig/DeployModTask.cs | 33 +++++++++++++++++++++- .../Framework/ModFileManager.cs | 12 -------- 2 files changed, 32 insertions(+), 13 deletions(-) (limited to 'src/SMAPI.ModBuildConfig') diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs index 88412d92..357e02b5 100644 --- a/src/SMAPI.ModBuildConfig/DeployModTask.cs +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -7,7 +7,10 @@ using System.Reflection; using System.Text.RegularExpressions; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Newtonsoft.Json; using StardewModdingAPI.ModBuildConfig.Framework; +using StardewModdingAPI.Toolkit.Serialization; +using StardewModdingAPI.Toolkit.Serialization.Models; using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.ModBuildConfig @@ -75,6 +78,34 @@ namespace StardewModdingAPI.ModBuildConfig this.Log.LogMessage(MessageImportance.High, $"[mod build package] Handling build with options {string.Join(", ", properties)}"); } + // check if manifest file exists + FileInfo manifestFile = new(Path.Combine(this.ProjectDir, "manifest.json")); + if (!manifestFile.Exists) + { + this.Log.LogError("[mod build package] The mod does not have a manifest.json file."); + return false; + } + + // check if the json is valid + Manifest manifest; + try + { + new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest); + } catch (JsonReaderException ex) + { + // log the inner exception, otherwise the message will be generic + Exception exToShow = ex.InnerException ?? ex; + this.Log.LogError($"[mod build package] Failed to parse manifest.json: {exToShow.Message}"); + return false; + } + + // validate the manifest's fields + if (!manifest.TryValidate(out string error)) + { + this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}"); + return false; + } + if (!this.EnableModDeploy && !this.EnableModZip) return true; // nothing to do @@ -101,7 +132,7 @@ namespace StardewModdingAPI.ModBuildConfig // create release zip if (this.EnableModZip) { - string zipName = this.EscapeInvalidFilenameCharacters($"{this.ModFolderName} {package.GetManifestVersion()}.zip"); + string zipName = this.EscapeInvalidFilenameCharacters($"{this.ModFolderName} {manifest.Version}.zip"); string zipPath = Path.Combine(this.ModZipPath, zipName); this.Log.LogMessage(MessageImportance.High, $"[mod build package] Generating the release zip at {zipPath}..."); diff --git a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs index 80955f67..00f3f439 100644 --- a/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs +++ b/src/SMAPI.ModBuildConfig/Framework/ModFileManager.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; -using StardewModdingAPI.Toolkit.Serialization; -using StardewModdingAPI.Toolkit.Serialization.Models; using StardewModdingAPI.Toolkit.Utilities; namespace StardewModdingAPI.ModBuildConfig.Framework @@ -113,16 +111,6 @@ namespace StardewModdingAPI.ModBuildConfig.Framework return new Dictionary(this.Files, StringComparer.OrdinalIgnoreCase); } - /// Get a semantic version from the mod manifest. - /// The manifest is missing or invalid. - public string GetManifestVersion() - { - if (!this.Files.TryGetValue(this.ManifestFileName, out FileInfo manifestFile) || !new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out Manifest manifest)) - throw new InvalidOperationException($"The mod does not have a {this.ManifestFileName} file."); // shouldn't happen since we validate in constructor - - return manifest.Version.ToString(); - } - /********* ** Private methods -- cgit From 346fddda670704c1458e42104ee7405fc1de7ccc Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 10 Nov 2022 23:27:38 -0500 Subject: move validation logic out of Manifest model This avoids tightly coupling higher logic to the implementation class, since we can validate the interface. --- src/SMAPI.ModBuildConfig/DeployModTask.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/SMAPI.ModBuildConfig') diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs index 357e02b5..70761a2f 100644 --- a/src/SMAPI.ModBuildConfig/DeployModTask.cs +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -9,6 +9,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Newtonsoft.Json; using StardewModdingAPI.ModBuildConfig.Framework; +using StardewModdingAPI.Toolkit.Framework; using StardewModdingAPI.Toolkit.Serialization; using StardewModdingAPI.Toolkit.Serialization.Models; using StardewModdingAPI.Toolkit.Utilities; @@ -91,7 +92,8 @@ namespace StardewModdingAPI.ModBuildConfig try { new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest); - } catch (JsonReaderException ex) + } + catch (JsonReaderException ex) { // log the inner exception, otherwise the message will be generic Exception exToShow = ex.InnerException ?? ex; @@ -100,7 +102,7 @@ namespace StardewModdingAPI.ModBuildConfig } // validate the manifest's fields - if (!manifest.TryValidate(out string error)) + if (!ManifestValidator.TryValidate(manifest, out string error)) { this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}"); return false; -- cgit From 6ee0d2f93d5d06556915f41e29621c59d9e9e551 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 10 Nov 2022 23:27:38 -0500 Subject: don't validate manifest if we're not deploying or zipping the mod That would break cases like unit test projects, which don't have a manifest.json file. --- src/SMAPI.ModBuildConfig/DeployModTask.cs | 58 +++++++++++++++++-------------- 1 file changed, 32 insertions(+), 26 deletions(-) (limited to 'src/SMAPI.ModBuildConfig') diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs index 70761a2f..1581b282 100644 --- a/src/SMAPI.ModBuildConfig/DeployModTask.cs +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -79,38 +79,44 @@ namespace StardewModdingAPI.ModBuildConfig this.Log.LogMessage(MessageImportance.High, $"[mod build package] Handling build with options {string.Join(", ", properties)}"); } - // check if manifest file exists - FileInfo manifestFile = new(Path.Combine(this.ProjectDir, "manifest.json")); - if (!manifestFile.Exists) - { - this.Log.LogError("[mod build package] The mod does not have a manifest.json file."); - return false; - } + // skip if nothing to do + // (This must be checked before the manifest validation, to allow cases like unit test projects.) + if (!this.EnableModDeploy && !this.EnableModZip) + return true; - // check if the json is valid + // validate the manifest file Manifest manifest; - try { - new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest); - } - catch (JsonReaderException ex) - { - // log the inner exception, otherwise the message will be generic - Exception exToShow = ex.InnerException ?? ex; - this.Log.LogError($"[mod build package] Failed to parse manifest.json: {exToShow.Message}"); - return false; - } + // check if manifest file exists + FileInfo manifestFile = new(Path.Combine(this.ProjectDir, "manifest.json")); + if (!manifestFile.Exists) + { + this.Log.LogError("[mod build package] The mod does not have a manifest.json file."); + return false; + } - // validate the manifest's fields - if (!ManifestValidator.TryValidate(manifest, out string error)) - { - this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}"); - return false; - } + // check if the json is valid + try + { + new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest); + } + catch (JsonReaderException ex) + { + // log the inner exception, otherwise the message will be generic + Exception exToShow = ex.InnerException ?? ex; + this.Log.LogError($"[mod build package] Failed to parse manifest.json: {exToShow.Message}"); + return false; + } - if (!this.EnableModDeploy && !this.EnableModZip) - return true; // nothing to do + // validate the manifest's fields + if (!ManifestValidator.TryValidate(manifest, out string error)) + { + this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}"); + return false; + } + } + // deploy files try { // parse extra DLLs to bundle -- cgit From 867afdd96ff8896dc81fdab204cf045713d32d91 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 10 Nov 2022 23:27:38 -0500 Subject: tweak new code --- src/SMAPI.ModBuildConfig/DeployModTask.cs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src/SMAPI.ModBuildConfig') diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs index 1581b282..3508a6db 100644 --- a/src/SMAPI.ModBuildConfig/DeployModTask.cs +++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs @@ -85,33 +85,30 @@ namespace StardewModdingAPI.ModBuildConfig return true; // validate the manifest file - Manifest manifest; + IManifest manifest; { - // check if manifest file exists - FileInfo manifestFile = new(Path.Combine(this.ProjectDir, "manifest.json")); - if (!manifestFile.Exists) - { - this.Log.LogError("[mod build package] The mod does not have a manifest.json file."); - return false; - } - - // check if the json is valid try { - new JsonHelper().ReadJsonFileIfExists(manifestFile.FullName, out manifest); + string manifestPath = Path.Combine(this.ProjectDir, "manifest.json"); + if (!new JsonHelper().ReadJsonFileIfExists(manifestPath, out Manifest rawManifest)) + { + this.Log.LogError("[mod build package] The mod's manifest.json file doesn't exist."); + return false; + } + manifest = rawManifest; } catch (JsonReaderException ex) { // log the inner exception, otherwise the message will be generic Exception exToShow = ex.InnerException ?? ex; - this.Log.LogError($"[mod build package] Failed to parse manifest.json: {exToShow.Message}"); + this.Log.LogError($"[mod build package] The mod's manifest.json file isn't valid JSON: {exToShow.Message}"); return false; } - // validate the manifest's fields - if (!ManifestValidator.TryValidate(manifest, out string error)) + // validate manifest fields + if (!ManifestValidator.TryValidateFields(manifest, out string error)) { - this.Log.LogError($"[mod build package] The mod manifest is invalid: {error}"); + this.Log.LogError($"[mod build package] The mod's manifest.json file is invalid: {error}"); return false; } } -- cgit