Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
Add support for customizing the installer with content pages
Browse files Browse the repository at this point in the history
  • Loading branch information
caesay committed Jul 6, 2022
1 parent 70c6198 commit 336919b
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 9 deletions.
6 changes: 3 additions & 3 deletions src/Squirrel.CommandLine/OSX/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ private static void Pack(PackOptions options)
if (!File.Exists(Path.Combine(contentsDir, "Info.plist")))
throw new Exception("Invalid bundle structure (missing Info.plist)");

var pkgTitle = options.packTitle ?? options.packId;
var nuspecText = NugetConsole.CreateNuspec(
options.packId, options.packTitle, options.packAuthors, options.packVersion, options.releaseNotes, options.includePdb, "osx");

options.packId, pkgTitle, options.packAuthors, options.packVersion, options.releaseNotes, options.includePdb, "osx");
var nuspecPath = Path.Combine(contentsDir, Utility.SpecVersionFileName);

// nuspec and UpdateMac need to be in contents dir or this package can't update
Expand Down Expand Up @@ -179,7 +179,7 @@ private static void Pack(PackOptions options)
// create installer package, sign and notarize
if (SquirrelRuntimeInfo.IsOSX) {
var pkgPath = Path.Combine(releaseDir.FullName, options.packId + ".pkg");
HelperExe.CreateInstallerPkg(appBundlePath, pkgPath, options.signInstallIdentity);
HelperExe.CreateInstallerPkg(appBundlePath, pkgTitle, options.pkgContent, pkgPath, options.signInstallIdentity);
if (!String.IsNullOrEmpty(options.signInstallIdentity) && !String.IsNullOrEmpty(options.notaryProfile)) {
HelperExe.Notarize(pkgPath, options.notaryProfile);
HelperExe.Staple(pkgPath);
Expand Down
27 changes: 21 additions & 6 deletions src/Squirrel.CommandLine/OSX/HelperExe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Security;
using System.Threading;
using Newtonsoft.Json;

Expand Down Expand Up @@ -58,10 +59,11 @@ public static void SpctlAssess(string filePath)
}

[SupportedOSPlatform("osx")]
public static void CreateInstallerPkg(string appBundlePath, string pkgOutputPath, string signIdentity)
public static void CreateInstallerPkg(string appBundlePath, string appTitle, KeyValuePair<string, string>[] extraContent,
string pkgOutputPath, string signIdentity)
{
// https://matthew-brett.github.io/docosx/flat_packages.html

Log.Info($"Creating installer '.pkg' for app at '{appBundlePath}'");

if (File.Exists(pkgOutputPath)) File.Delete(pkgOutputPath);
Expand All @@ -70,12 +72,13 @@ public static void CreateInstallerPkg(string appBundlePath, string pkgOutputPath
using var _2 = Utility.GetTempDirectory(out var tmpPayload1);
using var _3 = Utility.GetTempDirectory(out var tmpPayload2);
using var _4 = Utility.GetTempDirectory(out var tmpScripts);
using var _5 = Utility.GetTempDirectory(out var tmpResources);

// copy .app to tmp folder
var bundleName = Path.GetFileName(appBundlePath);
var tmpBundlePath = Path.Combine(tmpPayload1, bundleName);
Utility.CopyFiles(new DirectoryInfo(appBundlePath), new DirectoryInfo(tmpBundlePath));

// create postinstall scripts to open app after install
// https://stackoverflow.com/questions/35619036/open-app-after-installation-from-pkg-file-in-mac
var postinstall = Path.Combine(tmpScripts, "postinstall");
Expand All @@ -97,19 +100,31 @@ public static void CreateInstallerPkg(string appBundlePath, string pkgOutputPath
};

InvokeAndThrowIfNonZero("pkgbuild", args1, null);
// create product package that installs to home dir

// create final product package that contains app component
var distributionPath = Path.Combine(tmp, "distribution.xml");
InvokeAndThrowIfNonZero("productbuild", new[] { "--synthesize", "--package", pkg1Path, distributionPath }, null);

// disable local system installation and build final package
// https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html
var distXml = File.ReadAllLines(distributionPath).ToList();

distXml.Insert(2, $"<title>{SecurityElement.Escape(appTitle)}</title>");

// disable local system installation (install to home dir)
distXml.Insert(2, "<domains enable_anywhere=\"false\" enable_currentUserHome=\"true\" enable_localSystem=\"false\" />");
File.WriteAllLines(distributionPath, distXml);

// add extra landing content (eg. license, readme)
foreach (var kvp in extraContent) {
var fileName = Path.GetFileName(kvp.Value);
File.Copy(kvp.Value, Path.Combine(tmpResources, fileName));
distXml.Insert(2, $"<{kvp.Key} file=\"{fileName}\" />");
}

List<string> args2 = new() {
"--distribution", distributionPath,
"--package-path", tmpPayload2,
"--resources", tmpResources,
pkgOutputPath
};

Expand Down
22 changes: 22 additions & 0 deletions src/Squirrel.CommandLine/OSX/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ internal class PackOptions : BaseOptions
public string signEntitlements { get; private set; }
public string notaryProfile { get; private set; }
public string appleId { get; private set; }
public KeyValuePair<string, string>[] pkgContent => _pkgContent.ToArray();

private Dictionary<string, string> _pkgContent = new Dictionary<string, string>();

public PackOptions()
{
Expand All @@ -40,6 +43,8 @@ public PackOptions()
Add("i=|icon=", "{PATH} to the .icns file for this bundle", v => icon = v);
Add("noDelta", "Skip the generation of delta packages", v => noDelta = true);
Add("appleId", "Override the apple bundle ID for generated bundles", v => appleId = v);
Add("noPkg", "Skip generating a .pkg installer", v => appleId = v);
Add("pkgContent=", "Add content files (eg. readme, license) to pkg installer.", (v1, v2) => _pkgContent.Add(v1, v2));

if (SquirrelRuntimeInfo.IsOSX) {
Add("signAppIdentity=", "The {SUBJECT} name of the cert to use for app code signing", v => signAppIdentity = v);
Expand All @@ -56,6 +61,23 @@ public override void Validate()
NuGet.NugetUtil.ThrowIfInvalidNugetId(packId);
NuGet.NugetUtil.ThrowIfVersionNotSemverCompliant(packVersion);
IsValidDirectory(nameof(packDirectory), true);

var validContentKeys = new string[] {
"welcome",
"readme",
"license",
"conclusion",
};

foreach (var kvp in _pkgContent) {
if (!validContentKeys.Contains(kvp.Key)) {
throw new OptionValidationException($"Invalid pkgContent key: {kvp.Key}. Must be one of: " + string.Join(", ", validContentKeys));
}

if (!File.Exists(kvp.Value)) {
throw new OptionValidationException("pkgContent file not found: " + kvp.Value);
}
}
}
}
}

0 comments on commit 336919b

Please sign in to comment.