mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add macos command line bundler
This commit is contained in:
50
Squirrel.sln
50
Squirrel.sln
@@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squirrel.CommandLine", "src
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StubExecutable", "src\StubExecutable\StubExecutable.vcxproj", "{611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squirrel.CommandLine.OSX", "src\Squirrel.CommandLine.OSX\Squirrel.CommandLine.OSX.csproj", "{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
CIBuild|Any CPU = CIBuild|Any CPU
|
||||
@@ -390,6 +392,54 @@ Global
|
||||
{611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C}.Release|x64.Build.0 = Release|x64
|
||||
{611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C}.Release|x86.Build.0 = Release|Win32
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|x64.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|x64.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|x86.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.CIBuild|x86.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|x64.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|x64.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|x86.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Coverage|x86.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|x64.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Mono Release|x86.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5ECCE39A-56E3-4EA8-8F55-CE59979B6B6B}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
35
src/Squirrel.CommandLine.OSX/AppInfo.cs
Normal file
35
src/Squirrel.CommandLine.OSX/AppInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
internal class AppInfo
|
||||
{
|
||||
public string CFBundleName { get; set; }
|
||||
|
||||
public string CFBundleDisplayName { get; set; }
|
||||
|
||||
public string CFBundleIdentifier { get; set; }
|
||||
|
||||
public string CFBundleVersion { get; set; }
|
||||
|
||||
public string CFBundlePackageType { get; set; }
|
||||
|
||||
public string CFBundleSignature { get; set; }
|
||||
|
||||
public string CFBundleExecutable { get; set; }
|
||||
|
||||
public string CFBundleIconFile { get; set; }
|
||||
|
||||
public string CFBundleShortVersionString { get; set; }
|
||||
|
||||
public string NSPrincipalClass { get; set; }
|
||||
|
||||
public bool NSHighResolutionCapable { get; set; }
|
||||
|
||||
public bool? NSRequiresAquaSystemAppearance { get; private set; }
|
||||
}
|
||||
}
|
||||
73
src/Squirrel.CommandLine.OSX/Options.cs
Normal file
73
src/Squirrel.CommandLine.OSX/Options.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.NET.HostModel.AppHost;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
internal class BundleOptions : ValidatedOptionSet
|
||||
{
|
||||
public string packId { get; private set; }
|
||||
public string packTitle { get; private set; }
|
||||
public string packVersion { get; private set; }
|
||||
public string packDirectory { get; private set; }
|
||||
public string outputDirectory { get; private set; }
|
||||
public string icon { get; private set; }
|
||||
public string exeName { get; private set; }
|
||||
|
||||
public BundleOptions()
|
||||
{
|
||||
Add("o=|outputDir=", "The {DIRECTORY} to create the bundle", v => outputDirectory = v);
|
||||
Add("e=|exeName=", "The file name of the main executable", v => exeName = v);
|
||||
Add("u=|packId=", "Unique {ID} for bundle", v => packId = v);
|
||||
Add("v=|packVersion=", "Current {VERSION} for bundle", v => packVersion = v);
|
||||
Add("p=|packDir=", "{DIRECTORY} containing application files for bundle", v => packDirectory = v);
|
||||
Add("packTitle=", "Optional display/friendly {NAME} for bundle", v => packTitle = v);
|
||||
Add("i=|icon=", "{PATH} to the .icns file for this bundle", v => icon = v);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(packId), nameof(packVersion), nameof(packDirectory));
|
||||
Squirrel.NuGet.NugetUtil.ThrowIfInvalidNugetId(packId);
|
||||
Squirrel.NuGet.NugetUtil.ThrowIfVersionNotSemverCompliant(packVersion, false);
|
||||
IsValidDirectory(nameof(packDirectory), true);
|
||||
|
||||
IsRequired(nameof(icon));
|
||||
IsValidFile(nameof(icon), ".icns");
|
||||
|
||||
var exe = Path.Combine(packDirectory, exeName);
|
||||
if (!File.Exists(exe)) // || !MachOUtils.IsMachOImage(exe))
|
||||
throw new OptionValidationException($"Could not find mach-o executable at '{exe}'.");
|
||||
}
|
||||
}
|
||||
|
||||
internal class PackOptions : BaseOptions
|
||||
{
|
||||
public string package { get; set; }
|
||||
public bool noDelta { get; private set; }
|
||||
public string baseUrl { get; private set; }
|
||||
public bool includePdb { get; private set; }
|
||||
public string releaseNotes { get; private set; }
|
||||
|
||||
public PackOptions()
|
||||
{
|
||||
Add("b=|baseUrl=", "Provides a base URL to prefix the RELEASES file packages with", v => baseUrl = v, true);
|
||||
Add("p=|package=", "{PATH} to a '.app' directory to releasify", v => package = v);
|
||||
Add("noDelta", "Skip the generation of delta packages", v => noDelta = true);
|
||||
Add("includePdb", "Add *.pdb files to bundle", v => includePdb = true);
|
||||
Add("releaseNotes=", "{PATH} to file with markdown notes for version", v => releaseNotes = v);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(package));
|
||||
IsValidDirectory(nameof(package), true);
|
||||
if (!Utility.PathPartEndsWith(package, ".app"))
|
||||
throw new OptionValidationException("-p argument must point to a macos bundle directory ending in '.app'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
151
src/Squirrel.CommandLine.OSX/PlistWriter.cs
Normal file
151
src/Squirrel.CommandLine.OSX/PlistWriter.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
// https://raw.githubusercontent.com/egramtel/dotnet-bundle/master/DotNet.Bundle/PlistWriter.cs
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
internal class PlistWriter : IEnableLogger
|
||||
{
|
||||
private readonly AppInfo _task;
|
||||
private readonly string _outputDir;
|
||||
|
||||
private static readonly string[] ArrayTypeProperties = { "CFBundleURLSchemes" };
|
||||
private const char Separator = ';';
|
||||
public const string PlistFileName = "Info.plist";
|
||||
|
||||
public PlistWriter(AppInfo task, string outputDir)
|
||||
{
|
||||
_task = task;
|
||||
_outputDir = outputDir;
|
||||
}
|
||||
|
||||
public void Write()
|
||||
{
|
||||
var settings = new XmlWriterSettings {
|
||||
Indent = true,
|
||||
NewLineOnAttributes = false
|
||||
};
|
||||
|
||||
var path = Path.Combine(_outputDir, PlistFileName);
|
||||
|
||||
this.Log().Info($"Writing property list file: {path}");
|
||||
using (var xmlWriter = XmlWriter.Create(path, settings)) {
|
||||
xmlWriter.WriteStartDocument();
|
||||
|
||||
xmlWriter.WriteRaw(Environment.NewLine);
|
||||
xmlWriter.WriteRaw(
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
|
||||
xmlWriter.WriteRaw(Environment.NewLine);
|
||||
|
||||
xmlWriter.WriteStartElement("plist");
|
||||
xmlWriter.WriteAttributeString("version", "1.0");
|
||||
xmlWriter.WriteStartElement("dict");
|
||||
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleName), _task.CFBundleName);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleDisplayName), _task.CFBundleDisplayName);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleIdentifier), _task.CFBundleIdentifier);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleVersion), _task.CFBundleVersion);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundlePackageType), _task.CFBundlePackageType);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleSignature), _task.CFBundleSignature);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleExecutable), _task.CFBundleExecutable);
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleIconFile), Path.GetFileName(_task.CFBundleIconFile));
|
||||
WriteProperty(xmlWriter, nameof(_task.CFBundleShortVersionString), _task.CFBundleShortVersionString);
|
||||
WriteProperty(xmlWriter, nameof(_task.NSPrincipalClass), _task.NSPrincipalClass);
|
||||
WriteProperty(xmlWriter, nameof(_task.NSHighResolutionCapable), _task.NSHighResolutionCapable);
|
||||
|
||||
if (_task.NSRequiresAquaSystemAppearance.HasValue) {
|
||||
WriteProperty(xmlWriter, nameof(_task.NSRequiresAquaSystemAppearance), _task.NSRequiresAquaSystemAppearance.Value);
|
||||
}
|
||||
|
||||
//if (_task.CFBundleURLTypes.Length != 0) {
|
||||
// WriteProperty(xmlWriter, nameof(_task.CFBundleURLTypes), _task.CFBundleURLTypes);
|
||||
//}
|
||||
|
||||
xmlWriter.WriteEndElement();
|
||||
xmlWriter.WriteEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteProperty(XmlWriter xmlWriter, string name, string value)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(value)) {
|
||||
xmlWriter.WriteStartElement("key");
|
||||
xmlWriter.WriteString(name);
|
||||
xmlWriter.WriteEndElement();
|
||||
|
||||
xmlWriter.WriteStartElement("string");
|
||||
xmlWriter.WriteString(value);
|
||||
xmlWriter.WriteEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteProperty(XmlWriter xmlWriter, string name, bool value)
|
||||
{
|
||||
xmlWriter.WriteStartElement("key");
|
||||
xmlWriter.WriteString(name);
|
||||
xmlWriter.WriteEndElement();
|
||||
|
||||
if (value) {
|
||||
xmlWriter.WriteStartElement("true");
|
||||
} else {
|
||||
xmlWriter.WriteStartElement("false");
|
||||
}
|
||||
|
||||
xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
private void WriteProperty(XmlWriter xmlWriter, string name, string[] values)
|
||||
{
|
||||
if (values.Length != 0) {
|
||||
xmlWriter.WriteStartElement("key");
|
||||
xmlWriter.WriteString(name);
|
||||
xmlWriter.WriteEndElement();
|
||||
|
||||
xmlWriter.WriteStartElement("array");
|
||||
foreach (var value in values) {
|
||||
if (!string.IsNullOrEmpty(value)) {
|
||||
xmlWriter.WriteStartElement("string");
|
||||
xmlWriter.WriteString(value);
|
||||
xmlWriter.WriteEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
xmlWriter.WriteEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
//private void WriteProperty(XmlWriter xmlWriter, string name, ITaskItem[] values)
|
||||
//{
|
||||
// xmlWriter.WriteStartElement("key");
|
||||
// xmlWriter.WriteString(name);
|
||||
// xmlWriter.WriteEndElement();
|
||||
|
||||
// xmlWriter.WriteStartElement("array");
|
||||
|
||||
// foreach (var value in values) {
|
||||
// xmlWriter.WriteStartElement("dict");
|
||||
// var metadataDictionary = value.CloneCustomMetadata();
|
||||
|
||||
// foreach (DictionaryEntry entry in metadataDictionary) {
|
||||
// var dictValue = entry.Value.ToString();
|
||||
// var dictKey = entry.Key.ToString();
|
||||
|
||||
// if (dictValue.Contains(Separator.ToString()) || ArrayTypeProperties.Contains(dictKey)) //array
|
||||
// {
|
||||
// WriteProperty(xmlWriter, dictKey, dictValue.Split(Separator));
|
||||
// } else {
|
||||
// WriteProperty(xmlWriter, dictKey, dictValue);
|
||||
// }
|
||||
// }
|
||||
|
||||
// xmlWriter.WriteEndElement(); //End dict
|
||||
// }
|
||||
|
||||
// xmlWriter.WriteEndElement(); //End outside array
|
||||
//}
|
||||
}
|
||||
}
|
||||
133
src/Squirrel.CommandLine.OSX/Program.cs
Normal file
133
src/Squirrel.CommandLine.OSX/Program.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Claunia.PropertyList;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
|
||||
|
||||
static string TempDir => Utility.GetDefaultTempDirectory(null);
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
var commands = new CommandSet {
|
||||
"[ Package Authoring ]",
|
||||
{ "bundle", "Reads a build directory and creates a OSX '.app' bundle", new BundleOptions(), Bundle },
|
||||
{ "pack", "Convert a '.app' bundle into a Squirrel release", new PackOptions(), Pack },
|
||||
};
|
||||
|
||||
return SquirrelHost.Run(args, commands);
|
||||
}
|
||||
|
||||
private static void Pack(PackOptions options)
|
||||
{
|
||||
var targetDir = options.releaseDir ?? Path.Combine(".", "Releases");
|
||||
var di = new DirectoryInfo(targetDir);
|
||||
if (!Directory.Exists(targetDir)) {
|
||||
Directory.CreateDirectory(targetDir);
|
||||
}
|
||||
|
||||
//var builder = new StructureBuilder(options.package);
|
||||
var plistPath = Path.Combine(options.package, "Contents", PlistWriter.PlistFileName);
|
||||
NSDictionary plist = (NSDictionary) PropertyListParser.Parse(plistPath);
|
||||
|
||||
var _ = Utility.GetTempDir(TempDir, out var tmp);
|
||||
|
||||
var packId = plist.ObjectForKey("CFBundleIdentifier").ToString();
|
||||
var packVersion = plist.ObjectForKey("CFBundleVersion").ToString();
|
||||
var packTitle = plist.ObjectForKey("CFBundleName").ToString();
|
||||
|
||||
var nupkgPath = NugetConsole.CreatePackageFromMetadata(
|
||||
tmp, options.package, packId, packTitle, packTitle,
|
||||
packVersion, options.releaseNotes, options.includePdb);
|
||||
|
||||
var releaseFilePath = Path.Combine(di.FullName, "RELEASES");
|
||||
var previousReleases = new List<ReleaseEntry>();
|
||||
if (File.Exists(releaseFilePath)) {
|
||||
previousReleases.AddRange(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8)));
|
||||
}
|
||||
|
||||
var rp = new ReleasePackageBuilder(nupkgPath);
|
||||
//var newPkgPath = rp.CreateReleasePackage(TempDir, Path.Combine(options.releaseDir, rp.SuggestedReleaseFileName), contentsPostProcessHook: (pkgPath, zpkg) => {
|
||||
// var nuspecPath = Directory.GetFiles(pkgPath, "*.nuspec", SearchOption.TopDirectoryOnly)
|
||||
// .ContextualSingle("package", "*.nuspec", "top level directory");
|
||||
// var libDir = Directory.GetDirectories(Path.Combine(pkgPath, "lib"))
|
||||
// .ContextualSingle("package", "'lib' folder");
|
||||
// var contentsDir = Path.Combine(libDir, "Contents");
|
||||
// File.Copy(nuspecPath, Path.Combine(contentsDir, "current.version"));
|
||||
//});
|
||||
|
||||
// we are not currently making any modifications to the package
|
||||
// so we can just copy it to the right place. uncomment the above otherwise.
|
||||
var newPkgPath = Path.Combine(targetDir, rp.SuggestedReleaseFileName);
|
||||
File.Move(rp.InputPackageFile, newPkgPath);
|
||||
|
||||
var prev = ReleasePackageBuilder.GetPreviousRelease(previousReleases, rp, targetDir);
|
||||
if (prev != null && !options.noDelta) {
|
||||
var deltaBuilder = new DeltaPackageBuilder();
|
||||
var dp = deltaBuilder.CreateDeltaPackage(prev, rp,
|
||||
Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta")), TempDir);
|
||||
}
|
||||
|
||||
ReleaseEntry.WriteReleaseFile(previousReleases.Concat(new [] { ReleaseEntry.GenerateFromFile(newPkgPath) }), releaseFilePath);
|
||||
|
||||
Log.Info("Done");
|
||||
}
|
||||
|
||||
private static void Bundle(BundleOptions options)
|
||||
{
|
||||
var info = new AppInfo {
|
||||
CFBundleName = options.packTitle ?? options.packId,
|
||||
CFBundleDisplayName = options.packTitle ?? options.packId,
|
||||
CFBundleExecutable = options.exeName,
|
||||
CFBundleIdentifier = options.packId,
|
||||
CFBundlePackageType = "APPL",
|
||||
CFBundleShortVersionString = options.packVersion,
|
||||
CFBundleVersion = options.packVersion,
|
||||
CFBundleSignature = "????",
|
||||
NSPrincipalClass = "NSApplication",
|
||||
NSHighResolutionCapable = true,
|
||||
CFBundleIconFile = Path.GetFileName(options.icon),
|
||||
};
|
||||
|
||||
Log.Info("Creating .app directory structure");
|
||||
var builder = new StructureBuilder(options.packId, options.outputDirectory);
|
||||
builder.Build();
|
||||
|
||||
Log.Info("Writing Info.plist");
|
||||
var plist = new PlistWriter(info, builder.ContentsDirectory);
|
||||
plist.Write();
|
||||
|
||||
Log.Info("Copying resources");
|
||||
File.Copy(options.icon, Path.Combine(builder.ResourcesDirectory, Path.GetFileName(options.icon)));
|
||||
|
||||
Log.Info("Copying application files");
|
||||
CopyFiles(new DirectoryInfo(options.packDirectory), new DirectoryInfo(builder.MacosDirectory));
|
||||
|
||||
Log.Info("MacOS application bundle (.app) created at: " + builder.AppDirectory);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/Squirrel.CommandLine.OSX/Squirrel.CommandLine.OSX.csproj
Normal file
26
src/Squirrel.CommandLine.OSX/Squirrel.CommandLine.OSX.csproj
Normal file
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<RootNamespace>Squirrel.CommandLine</RootNamespace>
|
||||
<AssemblyName>SquirrelMac</AssemblyName>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<DebugType>embedded</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<PathMap>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=./</PathMap>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="plist-cil" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Squirrel.CommandLine\Squirrel.CommandLine.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
51
src/Squirrel.CommandLine.OSX/StructureBuilder.cs
Normal file
51
src/Squirrel.CommandLine.OSX/StructureBuilder.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
// https://github.com/egramtel/dotnet-bundle/blob/master/DotNet.Bundle/StructureBuilder.cs
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
public class StructureBuilder : IEnableLogger
|
||||
{
|
||||
private readonly string _id;
|
||||
private readonly string _outputDir;
|
||||
private readonly string _appDir;
|
||||
|
||||
public StructureBuilder(string appDir)
|
||||
{
|
||||
_appDir = appDir;
|
||||
}
|
||||
|
||||
public StructureBuilder(string id, string outputDir)
|
||||
{
|
||||
_id = id;
|
||||
_outputDir = outputDir;
|
||||
}
|
||||
|
||||
public string AppDirectory => _appDir ?? Path.Combine(Path.Combine(_outputDir, _id + ".app"));
|
||||
|
||||
public string ContentsDirectory => Path.Combine(AppDirectory, "Contents");
|
||||
|
||||
public string MacosDirectory => Path.Combine(ContentsDirectory, "MacOS");
|
||||
|
||||
public string ResourcesDirectory => Path.Combine(ContentsDirectory, "Resources");
|
||||
|
||||
public void Build()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_outputDir))
|
||||
throw new NotSupportedException();
|
||||
|
||||
Directory.CreateDirectory(_outputDir);
|
||||
|
||||
if (Directory.Exists(AppDirectory)) {
|
||||
Directory.Delete(AppDirectory, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(AppDirectory);
|
||||
Directory.CreateDirectory(ContentsDirectory);
|
||||
Directory.CreateDirectory(MacosDirectory);
|
||||
Directory.CreateDirectory(ResourcesDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ namespace Squirrel.CommandLine
|
||||
{
|
||||
IsRequired(nameof(packId), nameof(packVersion), nameof(packDirectory));
|
||||
Squirrel.NuGet.NugetUtil.ThrowIfInvalidNugetId(packId);
|
||||
Squirrel.NuGet.NugetUtil.ThrowIfVersionNotSemverCompliant(packVersion);
|
||||
Squirrel.NuGet.NugetUtil.ThrowIfVersionNotSemverCompliant(packVersion, true);
|
||||
IsValidDirectory(nameof(packDirectory), true);
|
||||
IsValidFile(nameof(releaseNotes));
|
||||
base.ValidateInternal(false);
|
||||
|
||||
@@ -8,136 +8,35 @@ using System.Security;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Mono.Options;
|
||||
using NuGet.Versioning;
|
||||
using Squirrel.NuGet;
|
||||
using Squirrel.SimpleSplat;
|
||||
using Squirrel.CommandLine.Sync;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
class Program : IEnableLogger
|
||||
{
|
||||
#pragma warning disable CS0436 // Type conflicts with imported type
|
||||
public static string DisplayVersion => ThisAssembly.AssemblyInformationalVersion + (ThisAssembly.IsPublicRelease ? "" : " (prerelease)");
|
||||
public static string FileVersion => ThisAssembly.AssemblyFileVersion;
|
||||
#pragma warning restore CS0436 // Type conflicts with imported type
|
||||
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
|
||||
|
||||
public static string TempDir => Utility.GetDefaultTempDirectory(null);
|
||||
static string TempDir => Utility.GetDefaultTempDirectory(null);
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
var logger = new ConsoleLogger();
|
||||
SquirrelLocator.CurrentMutable.Register(() => logger, typeof(ILogger));
|
||||
|
||||
bool help = false;
|
||||
bool verbose = false;
|
||||
var globalOptions = new OptionSet() {
|
||||
{ "h|?|help", "Ignores all other arguments and shows help text", _ => help = true },
|
||||
{ "verbose", "Print extra diagnostic logging", _ => verbose = true },
|
||||
};
|
||||
|
||||
var exeName = Path.GetFileName(SquirrelRuntimeInfo.EntryExePath);
|
||||
string sqUsage =
|
||||
$"Squirrel {DisplayVersion}, tool for creating and deploying Squirrel releases" + Environment.NewLine +
|
||||
$"Usage: {exeName} [verb] [--option:value]";
|
||||
|
||||
var commands = new CommandSet {
|
||||
"",
|
||||
sqUsage,
|
||||
"",
|
||||
"[ Global Options ]",
|
||||
globalOptions.GetHelpText().TrimEnd(),
|
||||
"",
|
||||
"[ Package Authoring ]",
|
||||
{ "pack", "Creates a Squirrel release from a folder containing application files", new PackOptions(), Pack },
|
||||
{ "releasify", "Take an existing nuget package and convert it into a Squirrel release", new ReleasifyOptions(), Releasify },
|
||||
"",
|
||||
"[ Package Deployment / Syncing ]",
|
||||
{ "b2-down", "Download recent releases from BackBlaze B2", new SyncBackblazeOptions(), o => Download(new BackblazeRepository(o)) },
|
||||
{ "b2-up", "Upload releases to BackBlaze B2", new SyncBackblazeOptions(), o => Upload(new BackblazeRepository(o)) },
|
||||
{ "http-down", "Download recent releases from an HTTP source", new SyncHttpOptions(), o => Download(new SimpleWebRepository(o)) },
|
||||
{ "github-down", "Download recent releases from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) },
|
||||
{ "s3-down", "Download recent releases from a S3 bucket", new SyncS3Options(), o => Download(new S3Repository(o)) },
|
||||
{ "s3-up", "Upload recent releases to a S3 bucket", new SyncS3Options(), o => Upload(new S3Repository(o)) },
|
||||
//"",
|
||||
//"[ Examples ]",
|
||||
//$" {exeName} pack ",
|
||||
//$" ",
|
||||
};
|
||||
|
||||
try {
|
||||
globalOptions.Parse(args);
|
||||
|
||||
if (verbose) {
|
||||
logger.Level = LogLevel.Debug;
|
||||
}
|
||||
|
||||
if (help) {
|
||||
commands.WriteHelp();
|
||||
return 0;
|
||||
} else {
|
||||
// parse cli and run command
|
||||
commands.Execute(args);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (Exception ex) when (ex is OptionValidationException || ex is OptionException) {
|
||||
// if the arguments fail to validate, print argument help
|
||||
Console.WriteLine();
|
||||
logger.Write(ex.Message, LogLevel.Error);
|
||||
commands.WriteHelp();
|
||||
Console.WriteLine();
|
||||
logger.Write(ex.Message, LogLevel.Error);
|
||||
return -1;
|
||||
} catch (Exception ex) {
|
||||
// for other errors, just print the error and short usage instructions
|
||||
Console.WriteLine();
|
||||
logger.Write(ex.ToString(), LogLevel.Error);
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(sqUsage);
|
||||
Console.WriteLine($" > '{exeName} -h' to see program help.");
|
||||
return -1;
|
||||
}
|
||||
return SquirrelHost.Run(args, commands);
|
||||
}
|
||||
|
||||
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
|
||||
|
||||
static void Upload<T>(T repo) where T : IPackageRepository => repo.UploadMissingPackages().Wait();
|
||||
|
||||
static void Download<T>(T repo) where T : IPackageRepository => repo.DownloadRecentPackages().Wait();
|
||||
|
||||
static void Pack(PackOptions options)
|
||||
{
|
||||
var releaseNotesText = String.IsNullOrEmpty(options.releaseNotes)
|
||||
? "" // no releaseNotes
|
||||
: $"<releaseNotes>{SecurityElement.Escape(File.ReadAllText(options.releaseNotes))}</releaseNotes>";
|
||||
|
||||
using (Utility.GetTempDir(TempDir, out var tmp)) {
|
||||
string nuspec = $@"
|
||||
<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>{options.packId}</id>
|
||||
<title>{options.packTitle ?? options.packId}</title>
|
||||
<description>{options.packTitle ?? options.packId}</description>
|
||||
<authors>{options.packAuthors ?? options.packId}</authors>
|
||||
<version>{options.packVersion}</version>
|
||||
{releaseNotesText}
|
||||
</metadata>
|
||||
<files>
|
||||
<file src=""**"" target=""lib\native\"" exclude=""{(options.includePdb ? "" : "*.pdb;")}*.nupkg;*.vshost.*""/>
|
||||
</files>
|
||||
</package>
|
||||
".Trim();
|
||||
var nuspecPath = Path.Combine(tmp, options.packId + ".nuspec");
|
||||
File.WriteAllText(nuspecPath, nuspec);
|
||||
|
||||
new NugetConsole().Pack(nuspecPath, options.packDirectory, tmp);
|
||||
|
||||
var nupkgPath = Directory.EnumerateFiles(tmp).Where(f => f.EndsWith(".nupkg")).FirstOrDefault();
|
||||
if (nupkgPath == null)
|
||||
throw new Exception($"Failed to generate nupkg, unspecified error");
|
||||
var nupkgPath = NugetConsole.CreatePackageFromMetadata(
|
||||
tmp, options.packDirectory, options.packId, options.packTitle,
|
||||
options.packAuthors, options.packVersion, options.releaseNotes, options.includePdb);
|
||||
|
||||
options.package = nupkgPath;
|
||||
Releasify(options);
|
||||
@@ -237,7 +136,7 @@ namespace Squirrel.CommandLine
|
||||
// warning if the installed SquirrelLib version is not the same as Squirrel.exe
|
||||
StringFileInfo sqLib = null;
|
||||
try {
|
||||
var myFileVersion = new NuGetVersion(FileVersion).Version;
|
||||
var myFileVersion = new NuGetVersion(SquirrelHost.FileVersion).Version;
|
||||
sqLib = Directory.EnumerateFiles(libDir, "SquirrelLib.dll")
|
||||
.Select(f => { StringFileInfo.ReadVersionInfo(f, out var fi); return fi; })
|
||||
.FirstOrDefault(fi => fi.FileVersion != myFileVersion);
|
||||
@@ -247,7 +146,7 @@ namespace Squirrel.CommandLine
|
||||
if (sqLib != null) {
|
||||
Log.Warn(
|
||||
$"SquirrelLib.dll {sqLib.FileVersion} is installed in provided package, " +
|
||||
$"but current Squirrel.exe version is {DisplayVersion} ({FileVersion}). " +
|
||||
$"but current Squirrel.exe version is {SquirrelHost.DisplayVersion} ({SquirrelHost.FileVersion}). " +
|
||||
$"The LIB version and CLI tool version must be the same to build releases " +
|
||||
$"or the application may fail to update properly.");
|
||||
}
|
||||
@@ -457,33 +356,4 @@ namespace Squirrel.CommandLine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConsoleLogger : ILogger
|
||||
{
|
||||
public LogLevel Level { get; set; } = LogLevel.Info;
|
||||
|
||||
private readonly object gate = new object();
|
||||
|
||||
private readonly string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
|
||||
public void Write(string message, LogLevel logLevel)
|
||||
{
|
||||
if (logLevel < Level) {
|
||||
return;
|
||||
}
|
||||
|
||||
message = message.Replace(localAppData, "%localappdata%", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
lock (gate) {
|
||||
string lvl = logLevel.ToString().Substring(0, 4).ToUpper();
|
||||
if (logLevel == LogLevel.Error || logLevel == LogLevel.Fatal) {
|
||||
Utility.ConsoleWriteWithColor($"[{lvl}] {message}{Environment.NewLine}", ConsoleColor.Red);
|
||||
} else if (logLevel == LogLevel.Warn) {
|
||||
Utility.ConsoleWriteWithColor($"[{lvl}] {message}{Environment.NewLine}", ConsoleColor.Yellow);
|
||||
} else {
|
||||
Console.WriteLine($"[{lvl}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
src/Squirrel.CommandLine/ConsoleLogger.cs
Normal file
42
src/Squirrel.CommandLine/ConsoleLogger.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
class ConsoleLogger : ILogger
|
||||
{
|
||||
public LogLevel Level { get; set; } = LogLevel.Info;
|
||||
|
||||
private readonly object gate = new object();
|
||||
|
||||
private readonly string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
|
||||
public void Write(string message, LogLevel logLevel)
|
||||
{
|
||||
if (logLevel < Level) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SquirrelRuntimeInfo.IsWindows)
|
||||
message = message.Replace(localAppData, "%localappdata%", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
lock (gate) {
|
||||
string lvl = logLevel.ToString().Substring(0, 4).ToUpper();
|
||||
if (logLevel == LogLevel.Error || logLevel == LogLevel.Fatal) {
|
||||
Utility.ConsoleWriteWithColor($"[{lvl}] {message}{Environment.NewLine}", ConsoleColor.Red);
|
||||
} else if (logLevel == LogLevel.Warn) {
|
||||
Utility.ConsoleWriteWithColor($"[{lvl}] {message}{Environment.NewLine}", ConsoleColor.Yellow);
|
||||
} else {
|
||||
Console.WriteLine($"[{lvl}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ILogger RegisterLogger()
|
||||
{
|
||||
var logger = new ConsoleLogger();
|
||||
SquirrelLocator.CurrentMutable.Register(() => logger, typeof(ILogger));
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Threading.Tasks;
|
||||
using NuGet.Commands;
|
||||
using Squirrel.SimpleSplat;
|
||||
@@ -9,6 +11,43 @@ namespace Squirrel.CommandLine
|
||||
{
|
||||
internal class NugetConsole : NG.ILogger, IEnableLogger
|
||||
{
|
||||
public static string CreatePackageFromMetadata(
|
||||
string tempDir, string packDir, string packId, string packTitle, string packAuthors,
|
||||
string packVersion, string releaseNotes, bool includePdb)
|
||||
{
|
||||
var releaseNotesText = String.IsNullOrEmpty(releaseNotes)
|
||||
? "" // no releaseNotes
|
||||
: $"<releaseNotes>{SecurityElement.Escape(File.ReadAllText(releaseNotes))}</releaseNotes>";
|
||||
|
||||
string nuspec = $@"
|
||||
<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>{packId}</id>
|
||||
<title>{packTitle ?? packId}</title>
|
||||
<description>{packTitle ?? packId}</description>
|
||||
<authors>{packAuthors ?? packId}</authors>
|
||||
<version>{packVersion}</version>
|
||||
{releaseNotesText}
|
||||
</metadata>
|
||||
<files>
|
||||
<file src=""**"" target=""lib\native\"" exclude=""{(includePdb ? "" : "*.pdb;")}*.nupkg;*.vshost.*""/>
|
||||
</files>
|
||||
</package>
|
||||
".Trim();
|
||||
|
||||
var nuspecPath = Path.Combine(tempDir, packId + ".nuspec");
|
||||
File.WriteAllText(nuspecPath, nuspec);
|
||||
|
||||
new NugetConsole().Pack(nuspecPath, packDir, tempDir);
|
||||
|
||||
var nupkgPath = Directory.EnumerateFiles(tempDir).Where(f => f.EndsWith(".nupkg")).FirstOrDefault();
|
||||
if (nupkgPath == null)
|
||||
throw new Exception($"Failed to generate nupkg, unspecified error");
|
||||
|
||||
return nupkgPath;
|
||||
}
|
||||
|
||||
public void Pack(string nuspecPath, string baseDirectory, string outputDirectory)
|
||||
{
|
||||
this.Log().Info($"Starting to package '{nuspecPath}'");
|
||||
|
||||
@@ -2,5 +2,13 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.Tests, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("SquirrelMac, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine.OSX, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine.Windows, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Update, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("UpdateMac, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Update.Windows, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Update.OSX, PublicKey=" + SNK.SHA1)]
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace Squirrel.CommandLine
|
||||
|
||||
public SemanticVersion Version => ReleaseEntry.ParseEntryFileName(InputPackageFile).Version;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal string CreateReleasePackage(string temporaryDirectory, string outputFile, Func<string, string> releaseNotesProcessor = null, Action<string, ZipPackage> contentsPostProcessHook = null)
|
||||
{
|
||||
Contract.Requires(!String.IsNullOrEmpty(outputFile));
|
||||
@@ -62,7 +61,7 @@ namespace Squirrel.CommandLine
|
||||
// we don't really care that they aren't valid
|
||||
if (!ModeDetector.InUnitTestRunner()) {
|
||||
// verify that the .nuspec version is semver compliant
|
||||
NugetUtil.ThrowIfVersionNotSemverCompliant(package.Version.ToString());
|
||||
NugetUtil.ThrowIfVersionNotSemverCompliant(package.Version.ToString(), true);
|
||||
|
||||
// verify that the suggested filename can be round-tripped as an assurance
|
||||
// someone won't run across an edge case and install a broken app somehow
|
||||
|
||||
100
src/Squirrel.CommandLine/SquirrelHost.cs
Normal file
100
src/Squirrel.CommandLine/SquirrelHost.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Mono.Options;
|
||||
using Squirrel.CommandLine.Sync;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
internal class SquirrelHost
|
||||
{
|
||||
#pragma warning disable CS0436 // Type conflicts with imported type
|
||||
public static string DisplayVersion => ThisAssembly.AssemblyInformationalVersion + (ThisAssembly.IsPublicRelease ? "" : " (prerelease)");
|
||||
public static string FileVersion => ThisAssembly.AssemblyFileVersion;
|
||||
#pragma warning restore CS0436 // Type conflicts with imported type
|
||||
|
||||
public static int Run(string[] args, CommandSet packageCommands)
|
||||
{
|
||||
var logger = ConsoleLogger.RegisterLogger();
|
||||
|
||||
bool help = false;
|
||||
bool verbose = false;
|
||||
var globalOptions = new OptionSet() {
|
||||
{ "h|?|help", "Ignores all other arguments and shows help text", _ => help = true },
|
||||
{ "verbose", "Print extra diagnostic logging", _ => verbose = true },
|
||||
};
|
||||
|
||||
var exeName = Path.GetFileName(SquirrelRuntimeInfo.EntryExePath);
|
||||
string sqUsage =
|
||||
$"Squirrel {DisplayVersion}, tool for creating and deploying Squirrel releases" + Environment.NewLine +
|
||||
$"Usage: {exeName} [verb] [--option:value]";
|
||||
|
||||
var commands = new CommandSet {
|
||||
"",
|
||||
sqUsage,
|
||||
"",
|
||||
"[ Global Options ]",
|
||||
globalOptions.GetHelpText().TrimEnd(),
|
||||
"",
|
||||
packageCommands,
|
||||
//"[ Package Authoring ]",
|
||||
//{ "pack", "Creates a Squirrel release from a folder containing application files", new PackOptions(), Pack },
|
||||
//{ "releasify", "Take an existing nuget package and convert it into a Squirrel release", new ReleasifyOptions(), Releasify },
|
||||
"",
|
||||
"[ Package Deployment / Syncing ]",
|
||||
{ "b2-down", "Download recent releases from BackBlaze B2", new SyncBackblazeOptions(), o => Download(new BackblazeRepository(o)) },
|
||||
{ "b2-up", "Upload releases to BackBlaze B2", new SyncBackblazeOptions(), o => Upload(new BackblazeRepository(o)) },
|
||||
{ "http-down", "Download recent releases from an HTTP source", new SyncHttpOptions(), o => Download(new SimpleWebRepository(o)) },
|
||||
{ "github-down", "Download recent releases from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) },
|
||||
{ "s3-down", "Download recent releases from a S3 bucket", new SyncS3Options(), o => Download(new S3Repository(o)) },
|
||||
{ "s3-up", "Upload recent releases to a S3 bucket", new SyncS3Options(), o => Upload(new S3Repository(o)) },
|
||||
//"",
|
||||
//"[ Examples ]",
|
||||
//$" {exeName} pack ",
|
||||
//$" ",
|
||||
};
|
||||
|
||||
try {
|
||||
globalOptions.Parse(args);
|
||||
|
||||
if (verbose) {
|
||||
logger.Level = LogLevel.Debug;
|
||||
}
|
||||
|
||||
if (help) {
|
||||
commands.WriteHelp();
|
||||
return 0;
|
||||
} else {
|
||||
// parse cli and run command
|
||||
commands.Execute(args);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} catch (Exception ex) when (ex is OptionValidationException || ex is OptionException) {
|
||||
// if the arguments fail to validate, print argument help
|
||||
Console.WriteLine();
|
||||
logger.Write(ex.Message, LogLevel.Error);
|
||||
commands.WriteHelp();
|
||||
Console.WriteLine();
|
||||
logger.Write(ex.Message, LogLevel.Error);
|
||||
return -1;
|
||||
} catch (Exception ex) {
|
||||
// for other errors, just print the error and short usage instructions
|
||||
Console.WriteLine();
|
||||
logger.Write(ex.ToString(), LogLevel.Error);
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(sqUsage);
|
||||
Console.WriteLine($" > '{exeName} -h' to see program help.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void Upload<T>(T repo) where T : IPackageRepository => repo.UploadMissingPackages().GetAwaiter().GetResult();
|
||||
|
||||
static void Download<T>(T repo) where T : IPackageRepository => repo.DownloadRecentPackages().GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
@@ -185,6 +185,11 @@ namespace Squirrel.CommandLine
|
||||
this.Add(new CommandAction<T>(command, description, options, action));
|
||||
}
|
||||
|
||||
public void Add(CommandSet commands)
|
||||
{
|
||||
AddRange(commands);
|
||||
}
|
||||
|
||||
public virtual void Execute(string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Squirrel
|
||||
|
||||
public static bool PathPartEndsWith(string part1, string endsWith)
|
||||
{
|
||||
return part1.StartsWith(endsWith, SquirrelRuntimeInfo.PathStringComparison);
|
||||
return part1.EndsWith(endsWith, SquirrelRuntimeInfo.PathStringComparison);
|
||||
}
|
||||
|
||||
public static bool FileHasExtension(string filePath, string extension)
|
||||
@@ -378,19 +378,26 @@ namespace Squirrel
|
||||
}));
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static string GetDefaultTempDirectory(string localAppDirectory)
|
||||
{
|
||||
string tempDir;
|
||||
|
||||
if (SquirrelRuntimeInfo.IsOSX) {
|
||||
tempDir = "/tmp/squirrel";
|
||||
} else if (SquirrelRuntimeInfo.IsWindows) {
|
||||
#if DEBUG
|
||||
const string TEMP_ENV_VAR = "CLOWD_SQUIRREL_TEMP_DEBUG";
|
||||
const string TEMP_DIR_NAME = "SquirrelClowdTempDebug";
|
||||
const string TEMP_ENV_VAR = "CLOWD_SQUIRREL_TEMP_DEBUG";
|
||||
const string TEMP_DIR_NAME = "SquirrelClowdTempDebug";
|
||||
#else
|
||||
const string TEMP_ENV_VAR = "CLOWD_SQUIRREL_TEMP";
|
||||
const string TEMP_DIR_NAME = "SquirrelClowdTemp";
|
||||
const string TEMP_ENV_VAR = "CLOWD_SQUIRREL_TEMP";
|
||||
const string TEMP_DIR_NAME = "SquirrelClowdTemp";
|
||||
#endif
|
||||
|
||||
var tempDir = Environment.GetEnvironmentVariable(TEMP_ENV_VAR);
|
||||
tempDir = tempDir ?? Path.Combine(localAppDirectory ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), TEMP_DIR_NAME);
|
||||
tempDir = Environment.GetEnvironmentVariable(TEMP_ENV_VAR);
|
||||
tempDir = tempDir ?? Path.Combine(localAppDirectory ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), TEMP_DIR_NAME);
|
||||
} else {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
var di = new DirectoryInfo(tempDir);
|
||||
if (!di.Exists) di.Create();
|
||||
|
||||
@@ -27,12 +27,16 @@ namespace Squirrel.NuGet
|
||||
throw new ArgumentException($"Invalid package Id '{id}', it must contain only alphanumeric characters, underscores, dashes, and dots.");
|
||||
}
|
||||
|
||||
public static void ThrowIfVersionNotSemverCompliant(string version)
|
||||
public static void ThrowIfVersionNotSemverCompliant(string version, bool allowTags)
|
||||
{
|
||||
if (SemanticVersion.TryParse(version, out var parsed)) {
|
||||
if (parsed < new SemanticVersion(0, 0, 1)) {
|
||||
throw new Exception($"Invalid package version '{version}', it must be >= 0.0.1.");
|
||||
}
|
||||
|
||||
if (!allowTags && (parsed.HasMetadata || parsed.IsPrerelease)) {
|
||||
throw new Exception($"Invalid package version '{version}', metadata/pre-release tags are not permitted.");
|
||||
}
|
||||
} else {
|
||||
throw new Exception($"Invalid package version '{version}', it must be a 3-part SemVer2 compliant version string.");
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@ using System.Runtime.InteropServices;
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.Tests, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("SquirrelMac, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine.OSX, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine.Windows, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Update, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("UpdateMac, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Update.Windows, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Update.OSX, PublicKey=" + SNK.SHA1)]
|
||||
|
||||
internal static class SNK
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ using InteropArchitecture = System.Runtime.InteropServices.Architecture;
|
||||
|
||||
namespace System.Runtime.Versioning
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
|
||||
internal class SupportedOSPlatformGuardAttribute : Attribute
|
||||
{
|
||||
public SupportedOSPlatformGuardAttribute(string platformName) { }
|
||||
@@ -29,6 +30,7 @@ namespace System.Runtime.Versioning
|
||||
|
||||
namespace System.Runtime.Versioning
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
|
||||
internal class SupportedOSPlatformAttribute : Attribute
|
||||
{
|
||||
public SupportedOSPlatformAttribute(string platformName) { }
|
||||
|
||||
Reference in New Issue
Block a user