summaryrefslogtreecommitdiff
path: root/src/SMAPI.ModBuildConfig/DeployModTask.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.ModBuildConfig/DeployModTask.cs')
-rw-r--r--src/SMAPI.ModBuildConfig/DeployModTask.cs154
1 files changed, 154 insertions, 0 deletions
diff --git a/src/SMAPI.ModBuildConfig/DeployModTask.cs b/src/SMAPI.ModBuildConfig/DeployModTask.cs
new file mode 100644
index 00000000..a5725a81
--- /dev/null
+++ b/src/SMAPI.ModBuildConfig/DeployModTask.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using StardewModdingAPI.ModBuildConfig.Framework;
+
+namespace StardewModdingAPI.ModBuildConfig
+{
+ /// <summary>A build task which deploys the mod files and prepares a release zip.</summary>
+ public class DeployModTask : Task
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The name of the mod folder.</summary>
+ [Required]
+ public string ModFolderName { get; set; }
+
+ /// <summary>The absolute or relative path to the folder which should contain the generated zip file.</summary>
+ [Required]
+ public string ModZipPath { get; set; }
+
+ /// <summary>The folder containing the project files.</summary>
+ [Required]
+ public string ProjectDir { get; set; }
+
+ /// <summary>The folder containing the build output.</summary>
+ [Required]
+ public string TargetDir { get; set; }
+
+ /// <summary>The folder containing the game files.</summary>
+ [Required]
+ public string GameDir { get; set; }
+
+ /// <summary>Whether to enable copying the mod files into the game's Mods folder.</summary>
+ [Required]
+ public bool EnableModDeploy { get; set; }
+
+ /// <summary>Whether to enable the release zip.</summary>
+ [Required]
+ public bool EnableModZip { get; set; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>When overridden in a derived class, executes the task.</summary>
+ /// <returns>true if the task successfully executed; otherwise, false.</returns>
+ public override bool Execute()
+ {
+ if (!this.EnableModDeploy && !this.EnableModZip)
+ return true; // nothing to do
+
+ try
+ {
+ // get mod info
+ ModFileManager package = new ModFileManager(this.ProjectDir, this.TargetDir);
+
+ // deploy mod files
+ if (this.EnableModDeploy)
+ {
+ string outputPath = Path.Combine(this.GameDir, "Mods", this.EscapeInvalidFilenameCharacters(this.ModFolderName));
+ this.Log.LogMessage(MessageImportance.High, $"The mod build package is copying the mod files to {outputPath}...");
+ this.CreateModFolder(package.GetFiles(), outputPath);
+ }
+
+ // create release zip
+ if (this.EnableModZip)
+ {
+ this.Log.LogMessage(MessageImportance.High, $"The mod build package is generating a release zip at {this.ModZipPath} for {this.ModFolderName}...");
+ this.CreateReleaseZip(package.GetFiles(), this.ModFolderName, package.GetManifestVersion(), this.ModZipPath);
+ }
+
+ return true;
+ }
+ catch (UserErrorException ex)
+ {
+ this.Log.LogErrorFromException(ex);
+ return false;
+ }
+ catch (Exception ex)
+ {
+ this.Log.LogError($"The mod build package failed trying to deploy the mod.\n{ex}");
+ return false;
+ }
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Copy the mod files into the game's mod folder.</summary>
+ /// <param name="files">The files to include.</param>
+ /// <param name="modFolderPath">The folder path to create with the mod files.</param>
+ private void CreateModFolder(IDictionary<string, FileInfo> files, string modFolderPath)
+ {
+ foreach (var entry in files)
+ {
+ string fromPath = entry.Value.FullName;
+ string toPath = Path.Combine(modFolderPath, entry.Key);
+
+ // ReSharper disable once AssignNullToNotNullAttribute -- not applicable in this context
+ Directory.CreateDirectory(Path.GetDirectoryName(toPath));
+
+ File.Copy(fromPath, toPath, overwrite: true);
+ }
+ }
+
+ /// <summary>Create a release zip in the recommended format for uploading to mod sites.</summary>
+ /// <param name="files">The files to include.</param>
+ /// <param name="modName">The name of the mod.</param>
+ /// <param name="modVersion">The mod version string.</param>
+ /// <param name="outputFolderPath">The absolute or relative path to the folder which should contain the generated zip file.</param>
+ private void CreateReleaseZip(IDictionary<string, FileInfo> files, string modName, string modVersion, string outputFolderPath)
+ {
+ // get names
+ string zipName = this.EscapeInvalidFilenameCharacters($"{modName} {modVersion}.zip");
+ string folderName = this.EscapeInvalidFilenameCharacters(modName);
+ string zipPath = Path.Combine(outputFolderPath, zipName);
+
+ // create zip file
+ Directory.CreateDirectory(outputFolderPath);
+ using (Stream zipStream = new FileStream(zipPath, FileMode.Create, FileAccess.Write))
+ using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
+ {
+ foreach (var fileEntry in files)
+ {
+ string relativePath = fileEntry.Key;
+ FileInfo file = fileEntry.Value;
+
+ // get file info
+ string filePath = file.FullName;
+ string entryName = folderName + '/' + relativePath.Replace(Path.DirectorySeparatorChar, '/');
+
+ // add to zip
+ using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
+ using (Stream fileStreamInZip = archive.CreateEntry(entryName).Open())
+ fileStream.CopyTo(fileStreamInZip);
+ }
+ }
+ }
+
+ /// <summary>Get a copy of a filename with all invalid filename characters substituted.</summary>
+ /// <param name="name">The filename.</param>
+ private string EscapeInvalidFilenameCharacters(string name)
+ {
+ foreach (char invalidChar in Path.GetInvalidFileNameChars())
+ name = name.Replace(invalidChar, '.');
+ return name;
+ }
+ }
+}