Refactor osx commands to be more similar to windows

This commit is contained in:
caesay
2024-01-01 10:45:46 +00:00
parent e20f7a5e45
commit 90fb5cd38d
14 changed files with 92 additions and 5669 deletions

View File

@@ -2,10 +2,6 @@
internal class AppInfo
{
public string SQPackId { get; set; }
public string SQPackAuthors { get; set; }
public string CFBundleName { get; set; }
public string CFBundleDisplayName { get; set; }

View File

@@ -14,7 +14,7 @@ public class OsxBundleCommandRunner
_logger = logger;
}
public void Bundle(OsxBundleOptions options)
public string Bundle(OsxBundleOptions options)
{
var icon = options.Icon;
var packId = options.PackId;
@@ -28,7 +28,7 @@ public class OsxBundleCommandRunner
_logger.Info("Generating new '.app' bundle from a directory of application files.");
var mainExePath = Path.Combine(packDirectory, exeName);
if (!File.Exists(mainExePath))// || !PlatformUtil.IsMachOImage(mainExePath))
if (!File.Exists(mainExePath) || !MachO.IsMachOImage(mainExePath))
throw new ArgumentException($"--exeName '{mainExePath}' does not exist or is not a mach-o executable.");
var appleId = $"com.{packAuthors ?? packId}.{packId}";
@@ -36,8 +36,8 @@ public class OsxBundleCommandRunner
var appleSafeVersion = NuGetVersion.Parse(packVersion).Version.ToString();
var info = new AppInfo {
SQPackId = packId,
SQPackAuthors = packAuthors,
// SQPackId = packId,
// SQPackAuthors = packAuthors,
CFBundleName = packTitle ?? packId,
//CFBundleDisplayName = packTitle ?? packId,
CFBundleExecutable = exeName,
@@ -71,5 +71,7 @@ public class OsxBundleCommandRunner
Utility.CopyFiles(new DirectoryInfo(packDirectory), new DirectoryInfo(builder.MacosDirectory));
_logger.Info("Bundle created successfully: " + builder.AppDirectory);
return builder.AppDirectory;
}
}

View File

@@ -5,16 +5,16 @@ using NuGet.Versioning;
namespace Velopack.Packaging.OSX.Commands;
public class OsxReleasifyCommandRunner
public class OsxPackCommandRunner
{
private readonly ILogger _logger;
public OsxReleasifyCommandRunner(ILogger logger)
public OsxPackCommandRunner(ILogger logger)
{
_logger = logger;
}
public void Releasify(OsxReleasifyOptions options)
public void Releasify(OsxPackOptions options)
{
var releaseDir = options.ReleaseDir;
@@ -41,54 +41,32 @@ public class OsxReleasifyCommandRunner
throw new ArgumentException(message);
}
var appBundlePath = options.BundleDirectory;
_logger.Info("Creating application from app bundle at: " + appBundlePath);
string appBundlePath = options.PackDirectory;
if (!options.PackDirectory.EndsWith(".app", StringComparison.OrdinalIgnoreCase)) {
appBundlePath = new OsxBundleCommandRunner(_logger).Bundle(options);
}
_logger.Info("Parsing app Info.plist");
var contentsDir = Path.Combine(appBundlePath, "Contents");
_logger.Info("Creating release from app bundle at: " + appBundlePath);
if (!Directory.Exists(contentsDir))
throw new Exception("Invalid bundle structure (missing Contents dir)");
var structure = new StructureBuilder(appBundlePath);
var plistPath = Path.Combine(contentsDir, "Info.plist");
if (!File.Exists(plistPath))
throw new Exception("Invalid bundle structure (missing Info.plist)");
var rootDict = (NSDictionary) PropertyListParser.Parse(plistPath);
var packId = rootDict.ObjectForKey(nameof(AppInfo.SQPackId))?.ToString();
if (string.IsNullOrWhiteSpace(packId))
packId = rootDict.ObjectForKey(nameof(AppInfo.CFBundleIdentifier))?.ToString();
var packAuthors = rootDict.ObjectForKey(nameof(AppInfo.SQPackAuthors))?.ToString();
if (string.IsNullOrWhiteSpace(packAuthors))
packAuthors = packId;
var packTitle = rootDict.ObjectForKey(nameof(AppInfo.CFBundleName))?.ToString();
var packVersion = rootDict.ObjectForKey(nameof(AppInfo.CFBundleVersion))?.ToString();
if (string.IsNullOrWhiteSpace(packId))
throw new InvalidOperationException($"Invalid CFBundleIdentifier in Info.plist: '{packId}'");
if (string.IsNullOrWhiteSpace(packTitle))
throw new InvalidOperationException($"Invalid CFBundleName in Info.plist: '{packTitle}'");
if (string.IsNullOrWhiteSpace(packVersion) || !NuGetVersion.TryParse(packVersion, out var _))
throw new InvalidOperationException($"Invalid CFBundleVersion in Info.plist: '{packVersion}'");
_logger.Info($"Package valid: '{packId}', Name: '{packTitle}', Version: {packVersion}");
var packId = options.PackId;
var packTitle = options.PackTitle;
var packAuthors = options.PackAuthors;
var packVersion = options.PackVersion;
_logger.Info("Adding Squirrel resources to bundle.");
var nuspecText = NugetConsole.CreateNuspec(
packId, packTitle, packAuthors, packVersion, options.ReleaseNotes, options.IncludePdb);
var nuspecPath = Path.Combine(contentsDir, Utility.SpecVersionFileName);
var nuspecPath = Path.Combine(structure.ContentsDirectory, Utility.SpecVersionFileName);
var helper = new HelperExe(_logger);
// nuspec and UpdateMac need to be in contents dir or this package can't update
File.WriteAllText(nuspecPath, nuspecText);
File.Copy(helper.UpdateMacPath, Path.Combine(contentsDir, "UpdateMac"), true);
File.Copy(helper.UpdateMacPath, Path.Combine(structure.ContentsDirectory, "UpdateMac"), true);
var zipPath = Path.Combine(releaseDir.FullName, $"{packId}-{options.TargetRuntime.ToDisplay(RidDisplayType.NoVersion)}.zip");
var zipPath = Path.Combine(releaseDir.FullName, $"{options.PackId}-{options.TargetRuntime.ToDisplay(RidDisplayType.NoVersion)}.zip");
if (File.Exists(zipPath)) File.Delete(zipPath);
// code signing all mach-o binaries
@@ -104,7 +82,7 @@ public class OsxReleasifyCommandRunner
}
// create a portable zip package from signed/notarized bundle
_logger.Info("Creating final application artifact (zip)");
_logger.Info("Creating final application artifact (ditto zip)");
helper.CreateDittoZip(appBundlePath, zipPath);
// create release / delta from notarized .app
@@ -141,27 +119,23 @@ public class OsxReleasifyCommandRunner
// create installer package, sign and notarize
if (!options.NoPackage) {
if (VelopackRuntimeInfo.IsOSX) {
var pkgPath = Path.Combine(releaseDir.FullName, $"{packId}-Setup-[{options.TargetRuntime.ToDisplay(RidDisplayType.NoVersion)}].pkg");
var pkgPath = Path.Combine(releaseDir.FullName, $"{packId}-Setup-[{options.TargetRuntime.ToDisplay(RidDisplayType.NoVersion)}].pkg");
Dictionary<string, string> pkgContent = new() {
{"welcome", options.PackageWelcome },
{"license", options.PackageLicense },
{"readme", options.PackageReadme },
{"conclusion", options.PackageConclusion },
};
Dictionary<string, string> pkgContent = new() {
{"welcome", options.PackageWelcome },
{"license", options.PackageLicense },
{"readme", options.PackageReadme },
{"conclusion", options.PackageConclusion },
};
helper.CreateInstallerPkg(appBundlePath, packTitle, pkgContent, pkgPath, options.SigningInstallIdentity);
if (!string.IsNullOrEmpty(options.SigningInstallIdentity) && !string.IsNullOrEmpty(options.NotaryProfile)) {
helper.Notarize(pkgPath, options.NotaryProfile);
helper.Staple(pkgPath);
helper.SpctlAssessInstaller(pkgPath);
} else {
_logger.Warn("Package installer (.pkg) will not be Notarized. " +
"This is supported with the --signInstallIdentity and --notaryProfile arguments.");
}
helper.CreateInstallerPkg(appBundlePath, packTitle, pkgContent, pkgPath, options.SigningInstallIdentity);
if (!string.IsNullOrEmpty(options.SigningInstallIdentity) && !string.IsNullOrEmpty(options.NotaryProfile)) {
helper.Notarize(pkgPath, options.NotaryProfile);
helper.Staple(pkgPath);
helper.SpctlAssessInstaller(pkgPath);
} else {
_logger.Warn("Package installer (.pkg) will not be created - this is only supported on OSX.");
_logger.Warn("Package installer (.pkg) will not be Notarized. " +
"This is supported with the --signInstallIdentity and --notaryProfile arguments.");
}
}

View File

@@ -1,13 +1,9 @@
namespace Velopack.Packaging.OSX.Commands;
public class OsxReleasifyOptions
public class OsxPackOptions : OsxBundleOptions
{
public DirectoryInfo ReleaseDir { get; set; }
public RID TargetRuntime { get; set; }
public string BundleDirectory { get; set; }
public bool IncludePdb { get; set; }
public string ReleaseNotes { get; set; }

View File

@@ -0,0 +1,23 @@
namespace Velopack.Packaging.OSX;
public class MachO
{
private enum MagicMachO : uint
{
MH_MAGIC = 0xfeedface,
MH_CIGAM = 0xcefaedfe,
MH_MAGIC_64 = 0xfeedfacf,
MH_CIGAM_64 = 0xcffaedfe
}
public static bool IsMachOImage(string filePath)
{
using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) {
if (reader.BaseStream.Length < 256) // Header size
return false;
uint magic = reader.ReadUInt32();
return Enum.IsDefined(typeof(MagicMachO), magic);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -43,11 +43,11 @@ internal class PlistWriter
xmlWriter.WriteAttributeString("version", "1.0");
xmlWriter.WriteStartElement("dict");
if (!String.IsNullOrEmpty(_task.SQPackId))
WriteProperty(xmlWriter, nameof(_task.SQPackId), _task.SQPackId);
if (!String.IsNullOrEmpty(_task.SQPackAuthors))
WriteProperty(xmlWriter, nameof(_task.SQPackAuthors), _task.SQPackAuthors);
// if (!String.IsNullOrEmpty(_task.SQPackId))
// WriteProperty(xmlWriter, nameof(_task.SQPackId), _task.SQPackId);
//
// if (!String.IsNullOrEmpty(_task.SQPackAuthors))
// WriteProperty(xmlWriter, nameof(_task.SQPackAuthors), _task.SQPackAuthors);
if (!String.IsNullOrEmpty(_task.CFBundleDisplayName))
WriteProperty(xmlWriter, nameof(_task.CFBundleDisplayName), _task.CFBundleDisplayName);

View File

@@ -1,6 +1,6 @@
namespace Velopack.Vpk.Commands;
public class OsxBundleCommand : OutputCommand
public class OsxBundleCommand : PlatformCommand
{
public string PackId { get; private set; }
@@ -17,9 +17,13 @@ public class OsxBundleCommand : OutputCommand
public string Icon { get; private set; }
public string BundleId { get; private set; }
public OsxBundleCommand()
: base("bundle", "Create's an OSX .app bundle from a folder containing application files.")
: this("bundle", "Create's an OSX .app bundle from a folder containing application files.")
{}
public OsxBundleCommand(string name, string description)
: base(name, description)
{
AddOption<string>((v) => PackId = v, "--packId", "-u")
.SetDescription("Unique Id for application bundle.")

View File

@@ -2,12 +2,8 @@
namespace Velopack.Vpk.Commands;
public class OsxReleasifyCommand : PlatformCommand
public class OsxPackCommand : OsxBundleCommand
{
public string BundleDirectory { get; private set; }
public bool IncludePdb { get; private set; }
public string ReleaseNotes { get; private set; }
public DeltaMode Delta { get; private set; }
@@ -32,19 +28,9 @@ public class OsxReleasifyCommand : PlatformCommand
public string Channel { get; private set; }
public OsxReleasifyCommand()
: base("releasify", "Converts an application bundle into a release and installer.")
public OsxPackCommand()
: base("pack", "Converts application files into a release and installer.")
{
AddOption<DirectoryInfo>((v) => BundleDirectory = v.ToFullNameOrNull(), "-b", "--bundle")
.SetDescription("The bundle to convert into a release.")
.SetArgumentHelpName("PATH")
.MustNotBeEmpty()
.RequiresExtension(".app")
.SetRequired();
AddOption<bool>((v) => IncludePdb = v, "--includePdb")
.SetDescription("Add *.pdb files to release package.");
AddOption<FileInfo>((v) => ReleaseNotes = v.ToFullNameOrNull(), "--releaseNotes")
.SetDescription("File with markdown-formatted notes for this version.")
.SetArgumentHelpName("PATH")

View File

@@ -1,9 +1,7 @@

using Velopack.Packaging;
namespace Velopack.Vpk.Commands;
public class WindowsPackCommand : WindowsReleasifyCommand, INugetPackCommand
public class WindowsPackCommand : WindowsReleasifyCommand
{
public string PackId { get; private set; }
@@ -15,8 +13,6 @@ public class WindowsPackCommand : WindowsReleasifyCommand, INugetPackCommand
public string PackTitle { get; private set; }
public bool IncludePdb { get; private set; }
public string ReleaseNotes { get; private set; }
public WindowsPackCommand()
@@ -49,9 +45,6 @@ public class WindowsPackCommand : WindowsReleasifyCommand, INugetPackCommand
.SetDescription("Display/friendly name for application.")
.SetArgumentHelpName("NAME");
AddOption<bool>((v) => IncludePdb = v, "--includePdb")
.SetDescription("Add *.pdb files to release package");
AddOption<FileInfo>((v) => ReleaseNotes = v.ToFullNameOrNull(), "--releaseNotes")
.SetDescription("File with markdown-formatted notes for this version.")
.SetArgumentHelpName("PATH")

View File

@@ -35,13 +35,20 @@ public class EmbeddedRunner : ICommandRunner
}
[SupportedOSPlatform("osx")]
public virtual Task ExecuteReleasifyOsx(OsxReleasifyCommand command)
public virtual Task ExecutePackOsx(OsxPackCommand command)
{
var options = new OsxReleasifyOptions {
var options = new OsxPackOptions {
BundleId = command.BundleId,
PackAuthors = command.PackAuthors,
EntryExecutableName = command.EntryExecutableName,
Icon = command.Icon,
PackDirectory = command.PackDirectory,
PackId = command.PackId,
PackTitle = command.PackTitle,
PackVersion = command.PackVersion,
TargetRuntime = command.GetRid(),
ReleaseDir = command.GetReleaseDirectory(),
BundleDirectory = command.BundleDirectory,
IncludePdb = command.IncludePdb,
IncludePdb = false,
DeltaMode = command.Delta,
NoPackage = command.NoPackage,
NotaryProfile = command.NotaryProfile,
@@ -54,7 +61,7 @@ public class EmbeddedRunner : ICommandRunner
SigningEntitlements = command.SigningEntitlements,
SigningInstallIdentity = command.SigningInstallIdentity,
};
new OsxReleasifyCommandRunner(_logger).Releasify(options);
new OsxPackCommandRunner(_logger).Releasify(options);
return Task.CompletedTask;
}
@@ -66,7 +73,7 @@ public class EmbeddedRunner : ICommandRunner
Package = command.Package,
Icon = command.Icon,
DeltaMode = command.Delta,
IncludePdb = command.IncludePdb,
IncludePdb = false,
SignParameters = command.SignParameters,
EntryExecutableName = command.EntryExecutableName,
PackAuthors = command.PackAuthors,

View File

@@ -11,7 +11,7 @@ public interface ICommandRunner
public Task ExecuteS3Download(S3DownloadCommand command);
public Task ExecuteS3Upload(S3UploadCommand command);
public Task ExecuteBundleOsx(OsxBundleCommand command);
public Task ExecuteReleasifyOsx(OsxReleasifyCommand command);
public Task ExecutePackOsx(OsxPackCommand command);
public Task ExecuteReleasifyWindows(WindowsReleasifyCommand command);
public Task ExecutePackWindows(WindowsPackCommand command);
public Task ExecuteDeltaGen(DeltaGenCommand command);

View File

@@ -58,11 +58,10 @@ public class Program
switch (VelopackRuntimeInfo.SystemOs) {
case RuntimeOs.Windows:
Add(rootCommand, new WindowsPackCommand(), nameof(ICommandRunner.ExecutePackWindows));
Add(rootCommand, new WindowsReleasifyCommand(), nameof(ICommandRunner.ExecuteReleasifyWindows));
break;
case RuntimeOs.OSX:
Add(rootCommand, new OsxBundleCommand(), nameof(ICommandRunner.ExecuteBundleOsx));
Add(rootCommand, new OsxReleasifyCommand(), nameof(ICommandRunner.ExecuteReleasifyOsx));
Add(rootCommand, new OsxPackCommand(), nameof(ICommandRunner.ExecutePackOsx));
break;
default:
throw new NotSupportedException("Unsupported OS platform: " + VelopackRuntimeInfo.SystemOs.GetOsLongName());

View File

@@ -359,17 +359,6 @@ public class PackWindowsCommandTests : ReleaseCommandTests<WindowsPackCommand>
Assert.Equal("Me,mysel,I", command.PackAuthors);
}
[Fact]
public void IncludePdb_BareOption_SetsFlag()
{
var command = new WindowsPackCommand();
string cli = GetRequiredDefaultOptions() + "--includePdb";
ParseResult parseResult = command.ParseAndApply(cli);
Assert.True(command.IncludePdb);
}
[Fact]
public void ReleaseNotes_WithExistingFile_ParsesValue()
{