First version of the 'csq' tool

This commit is contained in:
Caelan Sayler
2022-05-22 17:28:10 +01:00
parent 426a71171a
commit 804c8af0e3
10 changed files with 157 additions and 141 deletions

View File

@@ -32,6 +32,10 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Update.OSX", "src\Update.OSX\Update.OSX.csproj", "{A63B2CDA-5ECC-461C-9B1F-54CF4709ACBD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squirrel.Tool", "src\Squirrel.Tool\Squirrel.Tool.csproj", "{9E769C7E-A54C-4844-8362-727D37BB1578}"
ProjectSection(ProjectDependencies) = postProject
{6B406985-B2E1-4FED-A405-BD0694D68E93} = {6B406985-B2E1-4FED-A405-BD0694D68E93}
{611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C} = {611A03D4-4CDE-4DA0-B151-DE6FAFFB8B8C}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -33,6 +33,7 @@ namespace Squirrel.CommandLine
AddSearchPath(SquirrelRuntimeInfo.BaseDirectory, "..", "..", "..", "vendor", "wix");
#endif
AddSearchPath(SquirrelRuntimeInfo.BaseDirectory, "bin");
AddSearchPath(SquirrelRuntimeInfo.BaseDirectory, "wix");
}
public static void AddSearchPath(params string[] pathParts)
@@ -81,8 +82,7 @@ namespace Squirrel.CommandLine
.Where(d => !String.IsNullOrEmpty(d))
.Distinct()
.Select(d => Path.Combine(d, toFind))
.Where(d => File.Exists(d) || (File.Exists(d + ".exe") && SquirrelRuntimeInfo.IsWindows))
.Select(d => File.Exists(d + ".exe") ? d + ".exe" : d)
.Where(d => File.Exists(d))
.Select(Path.GetFullPath);
if (predicate != null)

View File

@@ -1,5 +1,4 @@
// See https://aka.ms/new-console-template for more information
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -11,21 +10,19 @@ using Squirrel.SimpleSplat;
namespace Squirrel.CommandLine.OSX
{
[SupportedOSPlatform("osx")]
class Program
class CommandsOSX
{
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(CommandsOSX));
public static int MainOSX(string[] args)
public static CommandSet GetCommands()
{
var commands = new CommandSet {
return new CommandSet {
"[ Package Authoring ]",
{ "bundle", "Convert a build directory into a OSX '.app' bundle", new BundleOptions(), Bundle },
{ "pack", "Create a Squirrel release from a '.app' bundle", new PackOptions(), Pack },
};
return SquirrelHost.Run(args, commands);
}
private static void Pack(PackOptions options)
{
var targetDir = options.releaseDir ?? Path.Combine(".", "Releases");

View File

@@ -1,18 +0,0 @@
using System;
namespace Squirrel.CommandLine
{
public class Program
{
public static int Main(string[] args)
{
if (SquirrelRuntimeInfo.IsWindows)
return Windows.Program.MainWindows(args);
if (SquirrelRuntimeInfo.IsOSX)
return OSX.Program.MainOSX(args);
throw new NotSupportedException("Unsupported OS: " + SquirrelRuntimeInfo.SystemOsName);
}
}
}

View File

@@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
<AssemblyName>SquirrelCli</AssemblyName>
<NoWarn>$(NoWarn);CA2007</NoWarn>
</PropertyGroup>

View File

@@ -10,55 +10,70 @@ using Squirrel.SimpleSplat;
namespace Squirrel.CommandLine
{
internal class SquirrelHost
public 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)
public static int Main(string[] args)
{
var logger = ConsoleLogger.RegisterLogger();
bool help = false;
bool verbose = false;
string xplat = null;
var globalOptions = new OptionSet() {
{ "h|?|help", "Ignores all other arguments and shows help text", _ => help = true },
{ "verbose", "Print extra diagnostic logging", _ => verbose = true },
{ "x|xplat=", "Select {PLATFORM} to cross-compile for (eg. win, osx)", v => xplat = v },
{ "verbose", "Print all diagnostic messages", _ => verbose = true },
};
var exeName = Path.GetFileName(SquirrelRuntimeInfo.EntryExePath);
string sqUsage =
$"Squirrel {DisplayVersion}, tool for creating and deploying Squirrel releases" + Environment.NewLine +
$"Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion}, 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 ]",
{ "s3-down", "Download releases from S3 compatible API", new SyncS3Options(), o => Download(new S3Repository(o)) },
{ "s3-up", "Upload releases to S3 compatible API", new SyncS3Options(), o => Upload(new S3Repository(o)) },
{ "http-down", "Download releases from an HTTP source", new SyncHttpOptions(), o => Download(new SimpleWebRepository(o)) },
{ "github-down", "Download releases from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) },
//"",
//"[ Examples ]",
//$" {exeName} pack ",
//$" ",
};
try {
globalOptions.Parse(args);
if (xplat == null)
xplat = SquirrelRuntimeInfo.SystemOsName;
CommandSet packageCommands;
switch (xplat.ToLower()) {
case "win":
case "windows":
if (!SquirrelRuntimeInfo.IsWindows)
logger.Write("Cross-compiling will cause some features of Squirrel to be disabled.", LogLevel.Warn);
packageCommands = Windows.CommandsWindows.GetCommands();
break;
case "mac":
case "osx":
case "macos":
if (!SquirrelRuntimeInfo.IsOSX)
logger.Write("Cross-compiling will cause some features of Squirrel to be disabled.", LogLevel.Warn);
packageCommands = OSX.CommandsOSX.GetCommands();
break;
default:
throw new NotSupportedException("Unsupported OS platform: " + xplat);
}
var commands = new CommandSet {
"",
sqUsage,
"",
"[ Global Options ]",
globalOptions.GetHelpText().TrimEnd(),
"",
packageCommands,
"",
"[ Package Deployment / Syncing ]",
{ "s3-down", "Download releases from S3 compatible API", new SyncS3Options(), o => Download(new S3Repository(o)) },
{ "s3-up", "Upload releases to S3 compatible API", new SyncS3Options(), o => Upload(new S3Repository(o)) },
{ "http-down", "Download releases from an HTTP source", new SyncHttpOptions(), o => Download(new SimpleWebRepository(o)) },
{ "github-down", "Download releases from GitHub", new SyncGithubOptions(), o => Download(new GitHubRepository(o)) },
};
if (verbose) {
logger.Level = LogLevel.Debug;
}
@@ -66,20 +81,21 @@ namespace Squirrel.CommandLine
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;
try {
// 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();
@@ -95,4 +111,4 @@ namespace Squirrel.CommandLine
static void Download<T>(T repo) where T : IPackageRepository => repo.DownloadRecentPackages().GetAwaiter().GetResult();
}
}
}

View File

@@ -16,21 +16,19 @@ using Squirrel.SimpleSplat;
namespace Squirrel.CommandLine.Windows
{
[SupportedOSPlatform("windows")]
class Program : IEnableLogger
class CommandsWindows : IEnableLogger
{
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(CommandsWindows));
public static int MainWindows(string[] args)
public static CommandSet GetCommands()
{
var commands = new CommandSet {
return new CommandSet {
"[ 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 },
};
return SquirrelHost.Run(args, commands);
}
static void Pack(PackOptions options)
{
using (Utility.GetTempDirectory(out var tmp)) {
@@ -136,7 +134,7 @@ namespace Squirrel.CommandLine.Windows
// warning if the installed SquirrelLib version is not the same as Squirrel.exe
StringFileInfo sqLib = null;
try {
var myFileVersion = new NuGetVersion(SquirrelHost.FileVersion).Version;
var myFileVersion = new NuGetVersion(SquirrelRuntimeInfo.SquirrelFileVersion).Version;
sqLib = Directory.EnumerateFiles(libDir, "SquirrelLib.dll")
.Select(f => { StringFileInfo.ReadVersionInfo(f, out var fi); return fi; })
.FirstOrDefault(fi => fi.FileVersion != myFileVersion);
@@ -146,7 +144,7 @@ namespace Squirrel.CommandLine.Windows
if (sqLib != null) {
Log.Warn(
$"SquirrelLib.dll {sqLib.FileVersion} is installed in provided package, " +
$"but current Squirrel.exe version is {SquirrelHost.DisplayVersion} ({SquirrelHost.FileVersion}). " +
$"but current Squirrel.exe version is {SquirrelRuntimeInfo.SquirrelDisplayVersion} ({SquirrelRuntimeInfo.SquirrelFileVersion}). " +
$"The LIB version and CLI tool version must be the same to build releases " +
$"or the application may fail to update properly.");
}

View File

@@ -1,75 +1,79 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using Microsoft.Build.Construction;
using Squirrel.CommandLine;
namespace Squirrel.Tool
{
class Program
{
static void Main(string[] args)
static int Main(string[] args)
{
var packageName = "Clowd.Squirrel";
// var dependencies = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.sln", SearchOption.TopDirectoryOnly)
// .SelectMany(GetProjectsFromSln)
// .Distinct()
// .SelectMany(proj => GetSquirrelVersionsFromProject(packageName, proj))
// .Distinct()
// .ToArray();
var dependencies = GetSquirrelVersionsFromProject(packageName);
if (dependencies.Length == 0)
throw new Exception("Clowd.Squirrel package was not found to be installed in the current solution.");
if (dependencies.Length > 1)
throw new Exception("Found multiple versions of Clowd.Squirrel installed in current solution. " +
"Please consolidate to a single version: " + string.Join(", ", dependencies));
var toolExecutable = SquirrelRuntimeInfo.SystemOsName switch {
"windows" => "Squirrel.exe",
"osx" => "SquirrelMac",
_ => throw new NotSupportedException("OS not supported: " + SquirrelRuntimeInfo.SystemOsName),
};
if (args.Contains("--csq-embedded-only")) {
return SquirrelHost.Main(args);
}
var packages = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
var toolPath = Path.Combine(packages, packageName.ToLower(), dependencies.First(), "tools", toolExecutable);
Console.WriteLine($"Squirrel Locator {SquirrelRuntimeInfo.SquirrelDisplayVersion}");
Process.Start(toolPath, args);
var packageName = "Clowd.Squirrel";
var dependencies = GetPackageVersionsFromCurrentDir(packageName).Distinct().ToArray();
if (dependencies.Length == 0) {
Console.WriteLine("Clowd.Squirrel package was not found to be installed in the current working dir/project.");
Console.WriteLine($"Using bundled Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion}");
return SquirrelHost.Main(args);
}
if (dependencies.Length > 1) {
throw new Exception("Found multiple versions of Clowd.Squirrel installed in current working dir/project. " +
"Please consolidate to a single version: " + string.Join(", ", dependencies));
}
var packages = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
var targetVersion = dependencies.First();
Console.WriteLine("Attempting to locate Squirrel " + targetVersion + " (installed in current working dir)");
var dllName = "csq.dll";
var exeName = "Squirrel.exe";
var toolDllPath = Path.Combine(packages, packageName.ToLower(), targetVersion, "tools", dllName);
var toolExePath = Path.Combine(packages, packageName.ToLower(), targetVersion, "tools", exeName);
Process p;
if (File.Exists(toolDllPath)) {
Console.WriteLine("Running at: " + toolDllPath);
p = Process.Start("dotnet", new[] { dllName, "--csq-embedded-only" }.Concat(args));
} else if (File.Exists(toolExePath)) {
if (!SquirrelRuntimeInfo.IsWindows)
throw new NotSupportedException($"The installed version {targetVersion} does not support this operating system. Please update.");
Console.WriteLine("Running at: " + toolExePath);
p = Process.Start(toolExePath, args);
} else {
throw new Exception("Unable to locate Squirrel " + targetVersion);
}
p.WaitForExit();
return p.ExitCode;
}
// static string[] GetProjectsFromSln(string solutionFile)
// {
// var result = ProcessUtil.InvokeProcess("dotnet", new[] { "sln", solutionFile, "list" }, null, CancellationToken.None);
// var proj = result.StdOutput
// .Split('\r', '\n')
// .Select(s => s.TrimEnd())
// .Where(s => s.EndsWith(".csproj", StringComparison.InvariantCultureIgnoreCase))
// .Select(s => s.Trim())
// .ToArray();
//
// return proj;
// }
static string[] GetSquirrelVersionsFromProject(string packageName)
static IEnumerable<string> GetPackageVersionsFromCurrentDir(string packageName)
{
//dotnet list "$PSScriptRoot\src\Clowd\Clowd.csproj" package
var result = ProcessUtil.InvokeProcess("dotnet", new[] { "list", "package" }, null, CancellationToken.None);
Console.WriteLine(result.StdOutput);
var escapedName = Regex.Escape(packageName);
var matches = Regex.Matches(result.StdOutput, $@"(?m){escapedName}.*\s(\d{{1,3}}\.\d{{1,3}}\.\d.*?)$");
foreach (var projFile in Directory.EnumerateFiles(Environment.CurrentDirectory, "*.csproj", SearchOption.AllDirectories)) {
var proj = ProjectRootElement.Open(projFile);
if (proj == null) continue;
if (matches.Count == 0)
return new string[0];
ProjectItemElement item = proj.Items.FirstOrDefault(i => i.ItemType == "PackageReference" && i.Include == packageName);
if (item == null) continue;
var outp = matches.Select(m => m.Groups[1].Value.Trim()).Distinct().ToArray();
Console.WriteLine(String.Join(", ", outp));
Console.WriteLine(String.Join(", ", outp));
Console.WriteLine(String.Join(", ", outp));
return outp;
var version = item.Children.FirstOrDefault(x => x.ElementName == "Version") as ProjectMetadataElement;
if (version == null) continue;
yield return version.Value;
}
}
}
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>true</IsPackable>
<AssemblyName>csq</AssemblyName>
<PackageId>csq</PackageId>
@@ -19,9 +19,19 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>A .NET Core Tool that uses the Squirrel framework to create installers and update packages for dotnet applications.</Description>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\vendor\**" Pack="true" PackagePath="tools\net6.0\any\" />
<None Include="..\..\build\$(Configuration)\Win32\Setup.exe" Pack="true" PackagePath="tools\net6.0\any\" />
<None Include="..\..\build\$(Configuration)\Win32\StubExecutable.exe" Pack="true" PackagePath="tools\net6.0\any\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
<ProjectReference Include="..\Squirrel.CommandLine\Squirrel.CommandLine.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="16.9.0" />
</ItemGroup>
</Project>

View File

@@ -66,6 +66,12 @@ namespace Squirrel
/// </summary>
public static class SquirrelRuntimeInfo
{
/// <summary> The current compiled Squirrel display version. </summary>
public static string SquirrelDisplayVersion => ThisAssembly.AssemblyInformationalVersion + (ThisAssembly.IsPublicRelease ? "" : " (prerelease)");
/// <summary> The current compiled Squirrel assembly file version. </summary>
public static string SquirrelFileVersion => ThisAssembly.AssemblyFileVersion;
/// <summary> The path on disk of the entry assembly. </summary>
public static string EntryExePath { get; }