diff --git a/src/Squirrel.CommandLine/OSX/Commands.cs b/src/Squirrel.CommandLine/OSX/Commands.cs index 0cb58f2ee..4108c378b 100644 --- a/src/Squirrel.CommandLine/OSX/Commands.cs +++ b/src/Squirrel.CommandLine/OSX/Commands.cs @@ -47,7 +47,7 @@ private static void Pack(PackOptions options) Log.Info("Copying app to release directory"); if (Directory.Exists(appBundlePath)) Utility.DeleteFileOrDirectoryHard(appBundlePath); Directory.CreateDirectory(appBundlePath); - CopyFiles(new DirectoryInfo(options.packDirectory), new DirectoryInfo(appBundlePath)); + Utility.CopyFiles(new DirectoryInfo(options.packDirectory), new DirectoryInfo(appBundlePath)); } else { Log.Info("Pack directory is not a bundle. Will generate new '.app' bundle from a directory of application files."); @@ -98,7 +98,7 @@ private static void Pack(PackOptions options) File.Copy(options.icon, Path.Combine(builder.ResourcesDirectory, Path.GetFileName(options.icon))); Log.Info("Copying application files into new '.app' bundle"); - CopyFiles(new DirectoryInfo(options.packDirectory), new DirectoryInfo(builder.MacosDirectory)); + Utility.CopyFiles(new DirectoryInfo(options.packDirectory), new DirectoryInfo(builder.MacosDirectory)); appBundlePath = builder.AppDirectory; } @@ -133,7 +133,7 @@ private static void Pack(PackOptions options) HelperExe.CreateDittoZip(appBundlePath, zipPath); HelperExe.Notarize(zipPath, options.notaryProfile); HelperExe.Staple(appBundlePath); - + // re-create the zip from the app with the stapled notarization File.Delete(zipPath); HelperExe.CreateDittoZip(appBundlePath, zipPath); @@ -181,20 +181,5 @@ private static void Pack(PackOptions options) Log.Info("Done."); } - - private static void CopyFiles(DirectoryInfo source, DirectoryInfo target) - { - Directory.CreateDirectory(target.FullName); - - foreach (var fileInfo in source.GetFiles()) { - var path = Path.Combine(target.FullName, fileInfo.Name); - fileInfo.CopyTo(path, true); - } - - foreach (var sourceSubDir in source.GetDirectories()) { - var targetSubDir = target.CreateSubdirectory(sourceSubDir.Name); - CopyFiles(sourceSubDir, targetSubDir); - } - } } } \ No newline at end of file diff --git a/src/Squirrel.CommandLine/OSX/HelperExe.cs b/src/Squirrel.CommandLine/OSX/HelperExe.cs index b5700e11c..60dd2765a 100644 --- a/src/Squirrel.CommandLine/OSX/HelperExe.cs +++ b/src/Squirrel.CommandLine/OSX/HelperExe.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.Versioning; using System.Threading; using Newtonsoft.Json; @@ -52,22 +53,54 @@ public static void CreateInstallerPkg(string appBundlePath, string pkgOutputPath { Log.Info($"Creating installer '.pkg' for app at '{appBundlePath}'"); - var args = new List { - "--install-location", "~/Applications", - "--component", appBundlePath, + using var _1 = Utility.GetTempDirectory(out var tmp); + using var _2 = Utility.GetTempDirectory(out var tmpPayload1); + using var _3 = Utility.GetTempDirectory(out var tmpPayload2); + + // copy .app to tmp folder + var bundleName = Path.GetFileName(appBundlePath); + var tmpBundlePath = Path.Combine(tmpPayload1, bundleName); + Utility.CopyFiles(new DirectoryInfo(appBundlePath), new DirectoryInfo(tmpBundlePath)); + + // generate non-relocatable pkg + var pkgPlistPath = Path.Combine(tmp, "tmp.plist"); + InvokeAndThrowIfNonZero("pkgbuild", new[] { "--analyze", "--root", tmpPayload1, pkgPlistPath }, null); + InvokeAndThrowIfNonZero("plutil", new[] { "-replace", "BundleIsRelocatable", "-bool", "NO", pkgPlistPath }, null); + + var pkg1Path = Path.Combine(tmpPayload2, "1.pkg"); + string[] args1 = { + "--root", tmpPayload1, + "--component-plist", pkgPlistPath, + "--install-location", "/Applications", + pkg1Path, }; + + InvokeAndThrowIfNonZero("pkgbuild", args1, null); + // create product package that installs to home dir + var distributionPath = Path.Combine(tmp, "distribution.xml"); + InvokeAndThrowIfNonZero("productbuild", new[] { "--synthesize", "--package", pkg1Path, distributionPath }, null); + + // disable local system installation and build final package + var distXml = File.ReadAllLines(distributionPath).ToList(); + distXml.Insert(2, ""); + File.WriteAllLines(distributionPath, distXml); + + List args2 = new () { + "--distribution", distributionPath, + "--package-path", tmpPayload2, + pkgOutputPath + }; + if (!String.IsNullOrEmpty(signIdentity)) { - args.Add("--sign"); - args.Add(signIdentity); + args2.Add("--sign"); + args2.Add(signIdentity); } else { Log.Warn("No Installer signing identity provided. The '.pkg' will not be signed."); } - - args.Add(pkgOutputPath); - - InvokeAndThrowIfNonZero("pkgbuild", args, null); - + + InvokeAndThrowIfNonZero("productbuild", args2, null); + Log.Info("Installer created successfully"); } diff --git a/src/Squirrel/Internal/Utility.cs b/src/Squirrel/Internal/Utility.cs index 90d68b744..07e184e5a 100644 --- a/src/Squirrel/Internal/Utility.cs +++ b/src/Squirrel/Internal/Utility.cs @@ -781,5 +781,20 @@ public static NuspecManifest ReadManifestFromVersionDir(string appVersionDir) return null; } + + public static void CopyFiles(DirectoryInfo source, DirectoryInfo target) + { + Directory.CreateDirectory(target.FullName); + + foreach (var fileInfo in source.GetFiles()) { + var path = Path.Combine(target.FullName, fileInfo.Name); + fileInfo.CopyTo(path, true); + } + + foreach (var sourceSubDir in source.GetDirectories()) { + var targetSubDir = target.CreateSubdirectory(sourceSubDir.Name); + CopyFiles(sourceSubDir, targetSubDir); + } + } } } \ No newline at end of file