mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Refactor osx commands to be more similar to windows
This commit is contained in:
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
23
src/Velopack.Packaging.OSX/MachO.cs
Normal file
23
src/Velopack.Packaging.OSX/MachO.cs
Normal 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
@@ -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);
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user