Csq is compiling

This commit is contained in:
Caelan Sayler
2023-12-15 11:59:43 +00:00
parent 123f91ea1d
commit 4226aa8fe7
19 changed files with 806 additions and 596 deletions

View File

@@ -1,5 +1,7 @@
using System.Text.RegularExpressions;
using NuGet.Common;
using NuGet.Versioning;
using Squirrel.NuGet;
namespace Squirrel.Csq.Commands;
@@ -36,7 +38,7 @@ internal static class SystemCommandLineExtensions
public static CliOption<T> SetDefault<T>(this CliOption<T> option, T defaultValue)
{
option.SetDefault(defaultValue);
option.DefaultValueFactory = (r) => defaultValue;
return option;
}
@@ -273,10 +275,9 @@ internal static class SystemCommandLineExtensions
{
for (var i = 0; i < result.Tokens.Count; i++) {
var framework = result.Tokens[i].Value;
try {
Runtimes.ParseDependencyString(framework);
} catch (Exception e) {
result.AddError(e.Message);
bool valid = framework.Split(",").Select(Runtimes.GetRuntimeByName).All(x => x != null);
if (!valid) {
result.AddError($"Invalid target dependency string: {framework}.");
}
}
}

View File

@@ -1,4 +1,6 @@

using Squirrel.Packaging;
namespace Squirrel.Csq.Commands;
public class SigningCommand : BaseCommand

View File

@@ -0,0 +1,112 @@
using System.Runtime.Versioning;
using Squirrel.Csq.Commands;
using Squirrel.Deployment;
using Squirrel.Packaging.OSX;
namespace Squirrel.Csq.Compat;
public class EmbeddedRunner : ICommandRunner
{
private readonly ILogger _logger;
public EmbeddedRunner(ILogger logger)
{
_logger = logger;
}
[SupportedOSPlatform("osx")]
public Task ExecuteBundleOsx(BundleOsxCommand command)
{
var options = new BundleOsxOptions {
BundleId = command.BundleId,
PackAuthors = command.PackAuthors,
EntryExecutableName = command.EntryExecutableName,
Icon = command.Icon,
PackDirectory = command.PackDirectory,
PackId = command.PackId,
PackTitle = command.PackTitle,
PackVersion = command.PackVersion,
ReleaseDir = command.GetReleaseDirectory(),
};
new OsxCommands(_logger).Bundle(options);
return Task.CompletedTask;
}
public Task ExecuteGithubDownload(GitHubDownloadCommand command)
{
var options = new GitHubDownloadOptions {
Pre = command.Pre,
ReleaseDir = command.GetReleaseDirectory(),
RepoUrl = command.RepoUrl,
Token = command.Token,
};
return new GitHubRepository(_logger).DownloadRecentPackages(options);
}
public Task ExecuteGithubUpload(GitHubUploadCommand command)
{
var options = new GitHubUploadOptions {
ReleaseDir = command.GetReleaseDirectory(),
RepoUrl = command.RepoUrl,
Token = command.Token,
Publish = command.Publish,
ReleaseName = command.ReleaseName,
};
return new GitHubRepository(_logger).UploadMissingPackages(options);
}
public Task ExecuteHttpDownload(HttpDownloadCommand command)
{
var options = new HttpDownloadOptions {
ReleaseDir = command.GetReleaseDirectory(),
Url = command.Url,
};
return new SimpleWebRepository(_logger).DownloadRecentPackages(options);
}
public Task ExecutePackWindows(PackWindowsCommand command)
{
throw new NotImplementedException();
}
[SupportedOSPlatform("osx")]
public Task ExecuteReleasifyOsx(ReleasifyOsxCommand command)
{
throw new NotImplementedException();
}
public Task ExecuteReleasifyWindows(ReleasifyWindowsCommand command)
{
throw new NotImplementedException();
}
public Task ExecuteS3Download(S3DownloadCommand command)
{
var options = new S3Options {
Bucket = command.Bucket,
Endpoint = command.Endpoint,
KeyId = command.KeyId,
PathPrefix = command.PathPrefix,
Region = command.Region,
ReleaseDir = command.GetReleaseDirectory(),
Secret = command.Secret,
};
return new S3Repository(_logger).DownloadRecentPackages(options);
}
public Task ExecuteS3Upload(S3UploadCommand command)
{
var options = new S3UploadOptions {
Bucket = command.Bucket,
Endpoint = command.Endpoint,
KeyId = command.KeyId,
PathPrefix = command.PathPrefix,
Region = command.Region,
ReleaseDir = command.GetReleaseDirectory(),
Secret = command.Secret,
KeepMaxReleases = command.KeepMaxReleases,
Overwrite = command.Overwrite,
};
return new S3Repository(_logger).UploadMissingPackages(options);
}
}

View File

@@ -0,0 +1,17 @@
using System.Runtime.Versioning;
using Squirrel.Csq.Commands;
namespace Squirrel.Csq.Compat;
public interface ICommandRunner
{
public Task ExecuteGithubDownload(GitHubDownloadCommand command);
public Task ExecuteGithubUpload(GitHubUploadCommand command);
public Task ExecuteHttpDownload(HttpDownloadCommand command);
public Task ExecuteS3Download(S3DownloadCommand command);
public Task ExecuteS3Upload(S3UploadCommand command);
public Task ExecuteBundleOsx(BundleOsxCommand command);
public Task ExecuteReleasifyOsx(ReleasifyOsxCommand command);
public Task ExecuteReleasifyWindows(ReleasifyWindowsCommand command);
public Task ExecutePackWindows(PackWindowsCommand command);
}

View File

@@ -0,0 +1,9 @@
using Squirrel.Csq.Commands;
namespace Squirrel.Csq.Compat;
public interface IRunnerFactory
{
public Task CreateAndExecuteAsync<T>(string commandName, T options) where T : BaseCommand;
public Task<ICommandRunner> CreateAsync();
}

View File

@@ -0,0 +1,105 @@
using Microsoft.Extensions.Configuration;
using Squirrel.Csq.Commands;
using Squirrel.Csq.Updates;
namespace Squirrel.Csq.Compat;
public class RunnerFactory : IRunnerFactory
{
private const string CLOWD_PACKAGE_NAME = "Clowd.Squirrel";
private readonly ILogger _logger;
private readonly FileSystemInfo _solution;
private readonly IConfiguration _config;
public RunnerFactory(ILogger logger, FileSystemInfo solution, IConfiguration config)
{
_logger = logger;
_solution = solution;
this._config = config;
}
public async Task CreateAndExecuteAsync<T>(string commandName, T options) where T : BaseCommand
{
var runner = await CreateAsync();
var method = typeof(ICommandRunner).GetMethod(commandName);
await (Task) method.Invoke(runner, new object[] { options });
}
public async Task<ICommandRunner> CreateAsync()
{
if (_config.GetValue<bool?>("SKIP_UPDATE_CHECK") != true) {
var updateCheck = new UpdateChecker(_logger);
await updateCheck.CheckForUpdates();
}
var solutionDir = FindSolutionDirectory(_solution?.FullName);
if (solutionDir is null) {
throw new Exception($"Could not find '.sln'. Specify solution or solution directory with '--solution='.");
}
var version = new SquirrelVersionLocator(_logger).Search(solutionDir, CLOWD_PACKAGE_NAME);
if (version.Major == 4) {
var myVer = SquirrelRuntimeInfo.SquirrelNugetVersion;
if (version != myVer) {
_logger.Warn($"Installed SDK is {version}, while csq is {myVer}, this is not recommended.");
}
return new EmbeddedRunner(_logger);
}
if (version.Major == 2 && version.Minor > 7) {
_logger.Warn("Running in V2 compatibility mode. Not all features may be available.");
Dictionary<string, string> packageSearchPaths = new();
var nugetPackagesDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
packageSearchPaths.Add("nuget user profile cache", Path.Combine(nugetPackagesDir, CLOWD_PACKAGE_NAME.ToLower(), "{0}", "tools"));
packageSearchPaths.Add("visual studio packages cache", Path.Combine(solutionDir, "packages", CLOWD_PACKAGE_NAME + ".{0}", "tools"));
string squirrelExe = null;
foreach (var kvp in packageSearchPaths) {
var path = String.Format(kvp.Value, version);
if (Directory.Exists(path)) {
_logger.Debug($"Found {CLOWD_PACKAGE_NAME} {version} from {kvp.Key}");
var toolExePath = Path.Combine(path, "Squirrel.exe");
if (File.Exists(toolExePath)) {
squirrelExe = toolExePath;
break;
}
}
}
if (squirrelExe is null) {
throw new Exception($"Could not find {CLOWD_PACKAGE_NAME} {version} Squirrel.exe");
}
return new V2CompatRunner(_logger, squirrelExe);
}
throw new NotSupportedException($"Squirrel {version} is installed in this project, but not supported by this version of Csq. Supported versions are [> v2.8] and [> v4.0]");
}
private string FindSolutionDirectory(string slnArgument)
{
if (!String.IsNullOrWhiteSpace(slnArgument)) {
if (File.Exists(slnArgument) && slnArgument.EndsWith(".sln", StringComparison.InvariantCultureIgnoreCase)) {
// we were given a sln file as argument
return Path.GetDirectoryName(Path.GetFullPath(slnArgument));
}
if (Directory.Exists(slnArgument) && Directory.EnumerateFiles(slnArgument, "*.sln").Any()) {
return Path.GetFullPath(slnArgument);
}
}
// try to find the solution directory from cwd
var cwd = Environment.CurrentDirectory;
var slnSearchDirs = new string[] {
cwd,
Path.Combine(cwd, ".."),
Path.Combine(cwd, "..", ".."),
};
return slnSearchDirs.FirstOrDefault(d => Directory.EnumerateFiles(d, "*.sln").Any());
}
}

View File

@@ -0,0 +1,85 @@
using System.Xml.Linq;
using Microsoft.Build.Construction;
using NuGet.Versioning;
namespace Squirrel.Csq.Compat;
public class SquirrelVersionLocator
{
private readonly ILogger _logger;
public SquirrelVersionLocator(ILogger logger)
{
_logger = logger;
}
public NuGetVersion Search(string solutionDir, string packageName)
{
var dependencies = GetPackageVersionsFromDir(solutionDir, packageName).Distinct().ToArray();
if (dependencies.Length == 0) {
throw new Exception($"{packageName} nuget package was not found installed in solution.");
}
if (dependencies.Length > 1) {
throw new Exception($"Found multiple versions of {packageName} installed in solution ({string.Join(", ", dependencies)}). " +
$"Please consolidate to a single version.'");
}
var targetVersion = dependencies.Single();
return NuGetVersion.Parse(targetVersion);
}
IEnumerable<string> GetPackageVersionsFromDir(string rootDir, string packageName)
{
// old-style framework packages.config
foreach (var packagesFile in EnumerateFilesUntilSpecificDepth(rootDir, "packages.config", 3)) {
using var xmlStream = File.OpenRead(packagesFile);
var xdoc = XDocument.Load(xmlStream);
var sqel = xdoc.Root?.Elements().FirstOrDefault(e => e.Attribute("id")?.Value == packageName);
var ver = sqel?.Attribute("version");
if (ver == null) continue;
_logger.Debug($"{packageName} {ver.Value} referenced in {packagesFile}");
if (ver.Value.Contains('*'))
throw new Exception(
$"Wildcard versions are not supported in packages.config. Remove wildcard or upgrade csproj format to use PackageReference.");
yield return ver.Value;
}
// new-style csproj PackageReference
foreach (var projFile in EnumerateFilesUntilSpecificDepth(rootDir, "*.csproj", 3)) {
var proj = ProjectRootElement.Open(projFile);
if (proj == null) continue;
ProjectItemElement item = proj.Items.FirstOrDefault(i => i.ItemType == "PackageReference" && i.Include == packageName);
if (item == null) continue;
var version = item.Children.FirstOrDefault(x => x.ElementName == "Version") as ProjectMetadataElement;
if (version?.Value == null) continue;
_logger.Debug($"{packageName} {version.Value} referenced in {projFile}");
yield return version.Value;
}
}
static IEnumerable<string> EnumerateFilesUntilSpecificDepth(string rootPath, string searchPattern, int maxDepth, int currentDepth = 0)
{
var files = Directory.EnumerateFiles(rootPath, searchPattern, SearchOption.TopDirectoryOnly);
foreach (var f in files) {
yield return f;
}
if (currentDepth < maxDepth) {
foreach (var dir in Directory.EnumerateDirectories(rootPath)) {
foreach (var file in EnumerateFilesUntilSpecificDepth(dir, searchPattern, maxDepth, currentDepth + 1)) {
yield return file;
}
}
}
}
}

View File

@@ -0,0 +1,275 @@
using System.Diagnostics;
using Squirrel.Csq.Commands;
namespace Squirrel.Csq.Compat;
public class V2CompatRunner : ICommandRunner
{
private readonly ILogger _logger;
private readonly string _squirrelExePath;
private readonly EmbeddedRunner _embedded;
public V2CompatRunner(ILogger logger, string squirrelExePath)
{
_logger = logger;
_squirrelExePath = squirrelExePath;
_embedded = new EmbeddedRunner(logger);
}
public async Task ExecutePackWindows(PackWindowsCommand command)
{
if (!SquirrelRuntimeInfo.IsWindows || command.TargetRuntime.BaseRID != RuntimeOs.Windows) {
throw new NotSupportedException("Squirrel v2.x is only supported on/for Windows.");
}
string msi = null;
if (command.BuildMsi) {
msi = command.TargetRuntime.Architecture.ToString();
}
var options = new PackOptions {
releaseDir = command.GetReleaseDirectory().FullName,
package = command.Package,
baseUrl = command.BaseUrl,
framework = command.Runtimes,
splashImage = command.SplashImage,
icon = command.Icon,
appIcon = command.AppIcon,
noDelta = command.NoDelta,
allowUnaware = false,
msi = msi,
signParams = command.SignParameters,
signTemplate = command.SignTemplate,
packId = command.PackId,
includePdb = command.IncludePdb,
packAuthors = command.PackAuthors,
packDirectory = command.PackDirectory,
packTitle = command.PackTitle,
packVersion = command.PackVersion,
releaseNotes = command.ReleaseNotes,
};
var args = new List<string> { "pack" };
options.AddArgs(args);
_logger.Debug($"Running V2 Squirrel.exe: '{_squirrelExePath} {String.Join(" ", args)}'");
await Process.Start(_squirrelExePath, args).WaitForExitAsync();
}
public async Task ExecuteReleasifyWindows(ReleasifyWindowsCommand command)
{
if (!SquirrelRuntimeInfo.IsWindows || command.TargetRuntime.BaseRID != RuntimeOs.Windows) {
throw new NotSupportedException("Squirrel v2.x is only supported on/for Windows.");
}
string msi = null;
if (command.BuildMsi) {
msi = command.TargetRuntime.Architecture.ToString();
}
var options = new ReleasifyOptions {
releaseDir = command.GetReleaseDirectory().FullName,
package = command.Package,
baseUrl = command.BaseUrl,
framework = command.Runtimes,
splashImage = command.SplashImage,
icon = command.Icon,
appIcon = command.AppIcon,
noDelta = command.NoDelta,
allowUnaware = false,
msi = msi,
signParams = command.SignParameters,
signTemplate = command.SignTemplate,
};
var args = new List<string> { "releasify" };
options.AddArgs(args);
_logger.Debug($"Running V2 Squirrel.exe: '{_squirrelExePath} {String.Join(" ", args)}'");
await Process.Start(_squirrelExePath, args).WaitForExitAsync();
}
public Task ExecuteBundleOsx(BundleOsxCommand command)
{
throw new NotSupportedException("Squirrel v2.x is only supported on/for Windows.");
}
public Task ExecuteReleasifyOsx(ReleasifyOsxCommand command)
{
throw new NotSupportedException("Squirrel v2.x is only supported on/for Windows.");
}
public Task ExecuteGithubDownload(GitHubDownloadCommand command)
{
return ((ICommandRunner) _embedded).ExecuteGithubDownload(command);
}
public Task ExecuteGithubUpload(GitHubUploadCommand command)
{
return ((ICommandRunner) _embedded).ExecuteGithubUpload(command);
}
public Task ExecuteHttpDownload(HttpDownloadCommand command)
{
return ((ICommandRunner) _embedded).ExecuteHttpDownload(command);
}
public Task ExecuteS3Download(S3DownloadCommand command)
{
return ((ICommandRunner) _embedded).ExecuteS3Download(command);
}
public Task ExecuteS3Upload(S3UploadCommand command)
{
return ((ICommandRunner) _embedded).ExecuteS3Upload(command);
}
private abstract class BaseOptions
{
public string releaseDir { get; set; }
public virtual void AddArgs(List<string> args)
{
if (!String.IsNullOrWhiteSpace(releaseDir)) {
args.Add("--releaseDir");
args.Add(releaseDir);
}
}
}
private class SigningOptions : BaseOptions
{
public string signParams { get; set; }
public string signTemplate { get; set; }
public override void AddArgs(List<string> args)
{
base.AddArgs(args);
if (!String.IsNullOrWhiteSpace(signParams)) {
args.Add("--signParams");
args.Add(signParams);
}
if (!String.IsNullOrWhiteSpace(signTemplate)) {
args.Add("--signTemplate");
args.Add(signTemplate);
}
}
}
private class ReleasifyOptions : SigningOptions
{
public string package { get; set; }
public string baseUrl { get; set; }
public string framework { get; set; }
public string splashImage { get; set; }
public string icon { get; set; }
public string appIcon { get; set; }
public bool noDelta { get; set; }
public bool allowUnaware { get; set; }
public string msi { get; set; }
public string debugSetupExe { get; set; }
public override void AddArgs(List<string> args)
{
base.AddArgs(args);
if (!String.IsNullOrWhiteSpace(package)) {
args.Add("--package");
args.Add(package);
}
if (!String.IsNullOrWhiteSpace(baseUrl)) {
args.Add("--baseUrl");
args.Add(baseUrl);
}
if (!String.IsNullOrWhiteSpace(framework)) {
args.Add("--framework");
args.Add(framework);
}
if (!String.IsNullOrWhiteSpace(splashImage)) {
args.Add("--splashImage");
args.Add(splashImage);
}
if (!String.IsNullOrWhiteSpace(icon)) {
args.Add("--icon");
args.Add(icon);
}
if (!String.IsNullOrWhiteSpace(appIcon)) {
args.Add("--appIcon");
args.Add(appIcon);
}
if (noDelta) {
args.Add("--noDelta");
}
if (allowUnaware) {
args.Add("--allowUnaware");
}
if (!String.IsNullOrWhiteSpace(msi)) {
args.Add("--msi");
args.Add(msi);
}
if (!String.IsNullOrWhiteSpace(debugSetupExe)) {
args.Add("--debugSetupExe");
args.Add(debugSetupExe);
}
}
}
private class PackOptions : ReleasifyOptions
{
public string packId { get; set; }
public string packTitle { get; set; }
public string packVersion { get; set; }
public string packAuthors { get; set; }
public string packDirectory { get; set; }
public bool includePdb { get; set; }
public string releaseNotes { get; set; }
public override void AddArgs(List<string> args)
{
base.AddArgs(args);
if (!String.IsNullOrWhiteSpace(packId)) {
args.Add("--packId");
args.Add(packId);
}
if (!String.IsNullOrWhiteSpace(packTitle)) {
args.Add("--packTitle");
args.Add(packTitle);
}
if (!String.IsNullOrWhiteSpace(packVersion)) {
args.Add("--packVersion");
args.Add(packVersion);
}
if (!String.IsNullOrWhiteSpace(packAuthors)) {
args.Add("--packAuthors");
args.Add(packAuthors);
}
if (!String.IsNullOrWhiteSpace(packDirectory)) {
args.Add("--packDir");
args.Add(packDirectory);
}
if (includePdb) {
args.Add("--includePdb");
}
if (!String.IsNullOrWhiteSpace(releaseNotes)) {
args.Add("--releaseNotes");
args.Add(releaseNotes);
}
}
}
}

View File

@@ -1,76 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Packaging.Core;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
namespace Squirrel.Tool
{
public class NugetDownloader
{
private readonly ILogger _logger;
private readonly PackageSource _packageSource;
private readonly SourceRepository _sourceRepository;
private readonly SourceCacheContext _sourceCacheContext;
public NugetDownloader(ILogger logger)
{
_logger = logger;
_packageSource = new PackageSource("https://api.nuget.org/v3/index.json", "NuGet.org");
_sourceRepository = new SourceRepository(_packageSource, Repository.Provider.GetCoreV3());
_sourceCacheContext = new SourceCacheContext();
}
public async Task<IPackageSearchMetadata> GetPackageMetadata(string packageName, string version, CancellationToken cancellationToken)
{
PackageMetadataResource packageMetadataResource = _sourceRepository.GetResource<PackageMetadataResource>();
FindPackageByIdResource packageByIdResource = _sourceRepository.GetResource<FindPackageByIdResource>();
IPackageSearchMetadata package = null;
var prerelease = version?.Equals("pre", StringComparison.InvariantCultureIgnoreCase) == true;
if (version is null || version.Equals("latest", StringComparison.InvariantCultureIgnoreCase) || prerelease) {
// get latest (or prerelease) version
IEnumerable<IPackageSearchMetadata> metadata = await packageMetadataResource
.GetMetadataAsync(packageName, true, true, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
package = metadata
.Where(x => x.IsListed)
.Where(x => prerelease || !x.Identity.Version.IsPrerelease)
.OrderByDescending(x => x.Identity.Version)
.FirstOrDefault();
} else {
// resolve version ranges and wildcards
var versions = await packageByIdResource.GetAllVersionsAsync(packageName, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
var resolved = versions.FindBestMatch(VersionRange.Parse(version), version => version);
// get exact version
var packageIdentity = new PackageIdentity(packageName, resolved);
package = await packageMetadataResource
.GetMetadataAsync(packageIdentity, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
}
if (package is null) {
throw new Exception($"Unable to locate {packageName} {version} on NuGet.org");
}
return package;
}
public async Task DownloadPackageToStream(IPackageSearchMetadata package, Stream targetStream, CancellationToken cancellationToken)
{
FindPackageByIdResource packageByIdResource = _sourceRepository.GetResource<FindPackageByIdResource>();
await packageByIdResource
.CopyNupkgToStreamAsync(package.Identity.Id, package.Identity.Version, targetStream, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
}
}
}

View File

@@ -1,56 +0,0 @@
using System.Threading.Tasks;
using NugetLevel = NuGet.Common.LogLevel;
using NugetLogger = NuGet.Common.ILogger;
using NugetMessage = NuGet.Common.ILogMessage;
namespace Squirrel.CommandLine
{
class NullNugetLogger : NugetLogger
{
void NugetLogger.LogDebug(string data)
{
}
void NugetLogger.LogVerbose(string data)
{
}
void NugetLogger.LogInformation(string data)
{
}
void NugetLogger.LogMinimal(string data)
{
}
void NugetLogger.LogWarning(string data)
{
}
void NugetLogger.LogError(string data)
{
}
void NugetLogger.LogInformationSummary(string data)
{
}
void NugetLogger.Log(NugetLevel level, string data)
{
}
Task NugetLogger.LogAsync(NugetLevel level, string data)
{
return Task.CompletedTask;
}
void NugetLogger.Log(NugetMessage message)
{
}
Task NugetLogger.LogAsync(NugetMessage message)
{
return Task.CompletedTask;
}
}
}

View File

@@ -1,306 +0,0 @@
//using System;
//using System.Collections.Generic;
//using System.CommandLine;
//using System.CommandLine.Builder;
//using System.CommandLine.Invocation;
//using System.CommandLine.Parsing;
//using System.Diagnostics;
//using System.IO;
//using System.Linq;
//using System.Threading;
//using System.Threading.Tasks;
//using System.Xml;
//using System.Xml.Linq;
//using Microsoft.Build.Construction;
//using Microsoft.Extensions.Configuration;
//using Microsoft.Extensions.Hosting;
//using Squirrel.CommandLine;
//namespace Squirrel.Tool
//{
// class Program
// {
// const string CLOWD_PACKAGE_NAME = "Clowd.Squirrel";
// private static ConsoleLogger _logger;
// private static CliOption<string> CsqVersion { get; }
// = new CliOption<string>("--csq-version");
// private static CliOption<FileSystemInfo> CsqSolutionPath { get; }
// = new CliOption<FileSystemInfo>(new[] { "--csq-sln", "--csq-solution" }).ExistingOnly();
// private static CliOption<bool> Verbose { get; }
// = new CliOption<bool>("--verbose");
// static Task<int> Main(string[] args)
// {
// HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
// builder.Environment.ContentRootPath = Directory.GetCurrentDirectory();
// builder.Configuration.AddJsonFile("hostsettings.json", optional: true);
// builder.Configuration.AddEnvironmentVariables(prefix: "PREFIX_");
// builder.Configuration.AddCommandLine(args);
// _logger = ConsoleLogger.RegisterLogger();
// System.CommandLine.Hosting.HostingExtensions.
// CliRootCommand rootCommand = new CliRootCommand() {
// CsqVersion,
// CsqSolutionPath,
// Verbose
// };
// rootCommand.TreatUnmatchedTokensAsErrors = false;
// rootCommand.SetHandler(MainInner);
// ParseResult parseResult = rootCommand.Parse(inargs);
// CommandLineBuilder builder = new CommandLineBuilder(rootCommand);
// if (parseResult.Directives.Contains("local")) {
// builder.UseDefaults();
// } else {
// builder
// .UseParseErrorReporting()
// .UseExceptionHandler()
// .CancelOnProcessTermination();
// }
// return builder.Build().InvokeAsync(inargs);
// }
// static async Task MainInner(InvocationContext context)
// {
// bool verbose = context.ParseResult.GetValueForOption(Verbose);
// FileSystemInfo explicitSolutionPath = context.ParseResult.GetValueForOption(CsqSolutionPath);
// string explicitSquirrelVersion = context.ParseResult.GetValueForOption(CsqVersion);
// // we want to forward the --verbose argument to Squirrel, too.
// var verboseArgs = verbose ? new string[] { "--verbose" } : Array.Empty<string>();
// string[] restArgs = context.ParseResult.UnmatchedTokens
// .Concat(verboseArgs)
// .ToArray();
// if (verbose) {
// _logger.Level = LogLevel.Debug;
// }
// context.Console.WriteLine($"Squirrel Locator 'csq' {SquirrelRuntimeInfo.SquirrelDisplayVersion}");
// _logger.Write($"Entry EXE: {SquirrelRuntimeInfo.EntryExePath}", LogLevel.Debug);
// CancellationToken cancellationToken = context.GetCancellationToken();
// await CheckForUpdates(cancellationToken).ConfigureAwait(false);
//#if DEBUG && false
// var devcsproj = Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "src", "Squirrel.CommandLine", "Squirrel.CommandLine.csproj");
// var devargs = new[] { "run", "--no-build", "-v", "q", "--project", devcsproj, "--" }.Concat(restArgs).ToArray();
// context.ExitCode = RunProcess("dotnet", devargs);
// return;
//#endif
// var solutionDir = FindSolutionDirectory(explicitSolutionPath?.FullName);
// var nugetPackagesDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
// var cacheDir = Path.GetFullPath(solutionDir is null ? ".squirrel" : Path.Combine(solutionDir, ".squirrel"));
// Dictionary<string, string> packageSearchPaths = new();
// packageSearchPaths.Add("nuget user profile cache", Path.Combine(nugetPackagesDir, CLOWD_PACKAGE_NAME.ToLower(), "{0}", "tools"));
// if (solutionDir != null)
// packageSearchPaths.Add("visual studio packages cache", Path.Combine(solutionDir, "packages", CLOWD_PACKAGE_NAME + ".{0}", "tools"));
// packageSearchPaths.Add("squirrel cache", Path.Combine(cacheDir, "{0}", "tools"));
// async Task<int> runSquirrel(string version, CancellationToken cancellationToken)
// {
// foreach (var kvp in packageSearchPaths) {
// var path = String.Format(kvp.Value, version);
// if (Directory.Exists(path)) {
// _logger.Write($"Running {CLOWD_PACKAGE_NAME} {version} from {kvp.Key}", LogLevel.Info);
// return RunCsqFromPath(path, restArgs);
// }
// }
// // we did not find it locally on first pass, search for the package online
// var dl = new NugetDownloader(_logger);
// var package = await dl.GetPackageMetadata(CLOWD_PACKAGE_NAME, version, cancellationToken).ConfigureAwait(false);
// // search one more time now that we've potentially resolved the nuget version
// foreach (var kvp in packageSearchPaths) {
// var path = String.Format(kvp.Value, package.Identity.Version);
// if (Directory.Exists(path)) {
// _logger.Write($"Running {CLOWD_PACKAGE_NAME} {package.Identity.Version} from {kvp.Key}", LogLevel.Info);
// return RunCsqFromPath(path, restArgs);
// }
// }
// // let's try to download it from NuGet.org
// var versionDir = Path.Combine(cacheDir, package.Identity.Version.ToString());
// Directory.CreateDirectory(cacheDir);
// Directory.CreateDirectory(versionDir);
// _logger.Write($"Downloading {package.Identity} from NuGet.", LogLevel.Info);
// var filePath = Path.Combine(versionDir, package.Identity + ".nupkg");
// using (var fs = File.Create(filePath)) {
// await dl.DownloadPackageToStream(package, fs, cancellationToken).ConfigureAwait(false);
// }
// EasyZip.ExtractZipToDirectory(filePath, versionDir);
// var toolsPath = Path.Combine(versionDir, "tools");
// return RunCsqFromPath(toolsPath, restArgs);
// }
// if (explicitSquirrelVersion != null) {
// context.ExitCode = await runSquirrel(explicitSquirrelVersion, cancellationToken).ConfigureAwait(false);
// return;
// }
// if (solutionDir is null) {
// throw new Exception($"Could not find '.sln'. Specify solution with '{CsqSolutionPath.Aliases.First()}=', or specify version of squirrel to use with '{CsqVersion.Aliases.First()}='.");
// }
// _logger.Write("Solution dir found at: " + solutionDir, LogLevel.Debug);
// // TODO actually read the SLN file rather than just searching for all .csproj files
// var dependencies = GetPackageVersionsFromDir(solutionDir, CLOWD_PACKAGE_NAME).Distinct().ToArray();
// if (dependencies.Length == 0) {
// throw new Exception($"{CLOWD_PACKAGE_NAME} nuget package was not found installed in solution.");
// }
// if (dependencies.Length > 1) {
// throw new Exception($"Found multiple versions of {CLOWD_PACKAGE_NAME} installed in solution ({string.Join(", ", dependencies)}). " +
// $"Please consolidate the following to a single version, or specify the version to use with '{CsqVersion.Aliases.First()}='");
// }
// var targetVersion = dependencies.Single();
// context.ExitCode = await runSquirrel(targetVersion, cancellationToken).ConfigureAwait(false);
// }
// static async Task CheckForUpdates(CancellationToken cancellationToken)
// {
// try {
// var myVer = SquirrelRuntimeInfo.SquirrelNugetVersion;
// var dl = new NugetDownloader(new NullNugetLogger());
// var package = await dl.GetPackageMetadata("csq", (myVer.IsPrerelease || myVer.HasMetadata) ? "pre" : "latest", cancellationToken).ConfigureAwait(false);
// if (package.Identity.Version > myVer)
// _logger.Write($"There is a new version of csq available ({package.Identity.Version})", LogLevel.Warn);
// } catch { }
// }
// static string FindSolutionDirectory(string slnArgument)
// {
// if (!String.IsNullOrWhiteSpace(slnArgument)) {
// if (File.Exists(slnArgument) && slnArgument.EndsWith(".sln", StringComparison.InvariantCultureIgnoreCase)) {
// // we were given a sln file as argument
// return Path.GetDirectoryName(Path.GetFullPath(slnArgument));
// }
// if (Directory.Exists(slnArgument) && Directory.EnumerateFiles(slnArgument, "*.sln").Any()) {
// return Path.GetFullPath(slnArgument);
// }
// }
// // try to find the solution directory from cwd
// var cwd = Environment.CurrentDirectory;
// var slnSearchDirs = new string[] {
// cwd,
// Path.Combine(cwd, ".."),
// Path.Combine(cwd, "..", ".."),
// };
// return slnSearchDirs.FirstOrDefault(d => Directory.EnumerateFiles(d, "*.sln").Any());
// }
// static int RunCsqFromPath(string toolRootPath, string[] args)
// {
// // > v3.0.170
// if (File.Exists(Path.Combine(toolRootPath, "Squirrel.CommandLine.runtimeconfig.json"))) {
// var cliPath = Path.Combine(toolRootPath, "Squirrel.CommandLine.dll");
// var dnargs = new[] { cliPath }.Concat(args).ToArray();
// _logger.Write("running dotnet " + String.Join(" ", dnargs), LogLevel.Debug);
// return RunProcess("dotnet", dnargs);
// }
// // v3.0 - v3.0.170
// var toolDllPath = Path.Combine(toolRootPath, "csq.dll");
// if (File.Exists(toolDllPath)) {
// var dnargs = new[] { toolDllPath, "--csq-embedded" }.Concat(args).ToArray();
// _logger.Write("running dotnet " + String.Join(" ", dnargs), LogLevel.Debug);
// return RunProcess("dotnet", dnargs);
// }
// // < v3.0
// var toolExePath = Path.Combine(toolRootPath, "Squirrel.exe");
// if (File.Exists(toolExePath)) {
// if (!SquirrelRuntimeInfo.IsWindows)
// throw new NotSupportedException(
// $"Squirrel at '{toolRootPath}' does not support this operating system. Please update the package version to >= 3.0");
// _logger.Write("running " + toolExePath + " " + String.Join(" ", args), LogLevel.Debug);
// return RunProcess(toolExePath, args);
// }
// throw new Exception("Unable to locate Squirrel at: " + toolRootPath);
// }
// static int RunProcess(string path, string[] args)
// {
// var p = Process.Start(path, args);
// p.WaitForExit();
// return p.ExitCode;
// }
// static IEnumerable<string> GetPackageVersionsFromDir(string rootDir, string packageName)
// {
// // old-style framework packages.config
// foreach (var packagesFile in EnumerateFilesUntilSpecificDepth(rootDir, "packages.config", 3)) {
// using var xmlStream = File.OpenRead(packagesFile);
// using var xmlReader = new XmlTextReader(xmlStream);
// var xdoc = XDocument.Load(xmlReader);
// var sqel = xdoc.Root?.Elements().FirstOrDefault(e => e.Attribute("id")?.Value == packageName);
// var ver = sqel?.Attribute("version");
// if (ver == null) continue;
// _logger.Write($"{packageName} {ver.Value} referenced in {packagesFile}", LogLevel.Debug);
// if (ver.Value.Contains('*'))
// throw new Exception(
// $"Wildcard versions are not supported in packages.config. Remove wildcard or upgrade csproj format to use PackageReference.");
// yield return ver.Value;
// }
// // new-style csproj PackageReference
// foreach (var projFile in EnumerateFilesUntilSpecificDepth(rootDir, "*.csproj", 3)) {
// var proj = ProjectRootElement.Open(projFile);
// if (proj == null) continue;
// ProjectItemElement item = proj.Items.FirstOrDefault(i => i.ItemType == "PackageReference" && i.Include == packageName);
// if (item == null) continue;
// var version = item.Children.FirstOrDefault(x => x.ElementName == "Version") as ProjectMetadataElement;
// if (version?.Value == null) continue;
// _logger.Write($"{packageName} {version.Value} referenced in {projFile}", LogLevel.Debug);
// yield return version.Value;
// }
// }
// static IEnumerable<string> EnumerateFilesUntilSpecificDepth(string rootPath, string searchPattern, int maxDepth, int currentDepth = 0)
// {
// var files = Directory.EnumerateFiles(rootPath, searchPattern, SearchOption.TopDirectoryOnly);
// foreach (var f in files) {
// yield return f;
// }
// if (currentDepth < maxDepth) {
// foreach (var dir in Directory.EnumerateDirectories(rootPath)) {
// foreach (var file in EnumerateFilesUntilSpecificDepth(dir, searchPattern, maxDepth, currentDepth + 1)) {
// yield return file;
// }
// }
// }
// }
// }
//}

View File

@@ -5,3 +5,4 @@ global using System.CommandLine.Parsing;
global using System.IO;
global using System.Linq;
global using System.Threading.Tasks;
global using Microsoft.Extensions.Logging;

View File

@@ -3,33 +3,44 @@ using Serilog.Events;
using Serilog;
using Microsoft.Extensions.Configuration;
using Squirrel.Csq.Commands;
using Squirrel.Deployment;
using Microsoft.Extensions.DependencyInjection;
using Squirrel.Csq.Updates;
using Squirrel.Csq.Compat;
namespace Squirrel.Csq;
public class Program
{
public static CliOption<string> TargetRuntime { get; }
= new CliOption<string>("runtime", "-r", "--runtime", "The target runtime to build packages for.")
= new CliOption<string>("--runtime", "-r")
.SetDescription("The target runtime to build packages for.")
.SetArgumentHelpName("RID")
.MustBeSupportedRid()
.SetRequired();
public static CliOption<bool> VerboseOption { get; }
= new CliOption<bool>("--verbose", "Print diagnostic messages.");
= new CliOption<bool>("--verbose")
.SetDescription("Print diagnostic messages.");
public static Task<int> Main(string[] args)
private static CliOption<FileSystemInfo> CsqSolutionPath { get; }
= new CliOption<FileSystemInfo>("--solution")
.SetDescription("Explicit path to project solution (.sln)")
.AcceptExistingOnly();
private static IServiceProvider Provider { get; set; }
public static async Task<int> Main(string[] args)
{
CliRootCommand platformRootCommand = new CliRootCommand() {
TargetRuntime,
VerboseOption,
CsqSolutionPath,
};
platformRootCommand.TreatUnmatchedTokensAsErrors = false;
ParseResult parseResult = platformRootCommand.Parse(args);
var runtime = RID.Parse(parseResult.GetValue(TargetRuntime) ?? SquirrelRuntimeInfo.SystemOs.GetOsShortName());
var solutionPath = parseResult.GetValue(CsqSolutionPath);
bool verbose = parseResult.GetValue(VerboseOption);
var builder = Host.CreateEmptyApplicationBuilder(new HostApplicationBuilderSettings {
@@ -39,77 +50,68 @@ public class Program
Configuration = new ConfigurationManager(),
});
var minLevel = verbose ? LogEventLevel.Debug : LogEventLevel.Information;
builder.Services.AddSingleton<IRunnerFactory>(s => new RunnerFactory(s.GetRequiredService<Microsoft.Extensions.Logging.ILogger>(), solutionPath, s.GetRequiredService<IConfiguration>()));
builder.Configuration.AddEnvironmentVariables("CSQ_");
var minLevel = verbose ? LogEventLevel.Debug : LogEventLevel.Information;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Is(minLevel)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.WriteTo.Console()
.CreateLogger();
builder.Logging.AddSerilog();
var host = builder.Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
Provider = host.Services;
var logger = host.Services.GetRequiredService<Microsoft.Extensions.Logging.ILogger<Program>>();
CliRootCommand rootCommand = new CliRootCommand($"Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion} for creating and distributing Squirrel releases.");
rootCommand.Options.Add(TargetRuntime);
rootCommand.Options.Add(VerboseOption);
CliRootCommand rootCommand = new CliRootCommand($"Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion} for creating and distributing Squirrel releases.") {
TargetRuntime,
VerboseOption,
CsqSolutionPath,
};
switch (runtime.BaseRID) {
case RuntimeOs.Windows:
if (!SquirrelRuntimeInfo.IsWindows)
logger.Warn("Cross-compiling will cause some commands and options of Squirrel to be unavailable.");
Add(rootCommand, new PackWindowsCommand(), Windows.Commands.Pack);
Add(rootCommand, new ReleasifyWindowsCommand(), Windows.Commands.Releasify);
Add(rootCommand, new PackWindowsCommand(), nameof(ICommandRunner.ExecutePackWindows));
Add(rootCommand, new ReleasifyWindowsCommand(), nameof(ICommandRunner.ExecuteReleasifyWindows));
break;
case RuntimeOs.OSX:
if (!SquirrelRuntimeInfo.IsOSX)
throw new InvalidOperationException("Cannot create OSX packages on non-OSX platforms.");
Add(rootCommand, new BundleOsxCommand(), OSX.Commands.Bundle);
Add(rootCommand, new ReleasifyOsxCommand(), OSX.Commands.Releasify);
throw new NotSupportedException("Cannot create OSX packages on non-OSX platforms.");
Add(rootCommand, new BundleOsxCommand(), nameof(ICommandRunner.ExecuteBundleOsx));
Add(rootCommand, new ReleasifyOsxCommand(), nameof(ICommandRunner.ExecuteReleasifyOsx));
break;
default:
throw new NotSupportedException("Unsupported OS platform: " + runtime.BaseRID.GetOsLongName());
}
CliCommand downloadCommand = new CliCommand("download", "Download's the latest release from a remote update source.");
Add(downloadCommand, new HttpDownloadCommand(), options => SimpleWebRepository.DownloadRecentPackages(options));
Add(downloadCommand, new S3DownloadCommand(), options => S3Repository.DownloadRecentPackages(options));
Add(downloadCommand, new GitHubDownloadCommand(), options => GitHubRepository.DownloadRecentPackages(options));
Add(downloadCommand, new HttpDownloadCommand(), nameof(ICommandRunner.ExecuteHttpDownload));
Add(downloadCommand, new S3DownloadCommand(), nameof(ICommandRunner.ExecuteS3Download));
Add(downloadCommand, new GitHubDownloadCommand(), nameof(ICommandRunner.ExecuteGithubDownload));
rootCommand.Add(downloadCommand);
var uploadCommand = new CliCommand("upload", "Upload local package(s) to a remote update source.");
Add(uploadCommand, new S3UploadCommand(), options => S3Repository.UploadMissingPackages(options));
Add(uploadCommand, new GitHubUploadCommand(), options => GitHubRepository.UploadMissingPackages(options));
Add(uploadCommand, new S3UploadCommand(), nameof(ICommandRunner.ExecuteS3Upload));
Add(uploadCommand, new GitHubUploadCommand(), nameof(ICommandRunner.ExecuteGithubUpload));
rootCommand.Add(uploadCommand);
var cli = new CliConfiguration(rootCommand);
return cli.InvokeAsync(args);
return await cli.InvokeAsync(args);
}
private static CliCommand Add<T>(CliCommand parent, T command, Action<T> execute)
where T : BaseCommand
{
command.SetAction((ctx) => {
command.SetProperties(ctx);
command.TargetRuntime = RID.Parse(ctx.GetValue(TargetRuntime));
execute(command);
});
parent.Subcommands.Add(command);
return command;
}
private static CliCommand Add<T>(CliCommand parent, T command, Func<T, Task> execute)
private static CliCommand Add<T>(CliCommand parent, T command, string commandName)
where T : BaseCommand
{
command.SetAction((ctx, token) => {
command.SetProperties(ctx);
command.TargetRuntime = RID.Parse(ctx.GetValue(TargetRuntime));
return execute(command);
var factory = Provider.GetRequiredService<IRunnerFactory>();
return factory.CreateAndExecuteAsync(commandName, command);
});
parent.Subcommands.Add(command);
return command;

View File

@@ -14,6 +14,7 @@
<Description>A .NET Core Tool that uses the Squirrel framework to create installers and update packages for dotnet applications.</Description>
<PackageIcon>Clowd_200.png</PackageIcon>
<LangVersion>latest</LangVersion>
<NoWarn>$(NoWarn);CA2007;CS8002</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -27,6 +28,7 @@
<PackageReference Include="Serilog.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.23407.1" />
<PackageReference Include="System.Xml.XDocument" Version="4.3.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,114 +0,0 @@
using System;
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Threading.Tasks;
using Squirrel.CommandLine.Commands;
using Squirrel.CommandLine.Sync;
using Squirrel.SimpleSplat;
namespace Squirrel.CommandLine
{
public class SquirrelHost
{
public static Option<string> TargetRuntime { get; }
= new Option<string>(new[] { "-r", "--runtime" }, "The target runtime to build packages for.")
.SetArgumentHelpName("RID")
.MustBeSupportedRid()
.SetRequired();
public static Option<bool> VerboseOption { get; }
= new Option<bool>("--verbose", "Print diagnostic messages.");
public static Option<string[]> AddSearchPathOption { get; }
= new Option<string[]>("--addSearchPath", "Add additional search directories when looking for helper exe's.")
.SetArgumentHelpName("DIR");
public static int Main(string[] args)
{
var logger = ConsoleLogger.RegisterLogger();
RootCommand platformRootCommand = new RootCommand() {
TargetRuntime,
VerboseOption,
AddSearchPathOption,
};
platformRootCommand.TreatUnmatchedTokensAsErrors = false;
ParseResult parseResult = platformRootCommand.Parse(args);
bool verbose = parseResult.GetValueForOption(VerboseOption);
if (parseResult.GetValueForOption(AddSearchPathOption) is { } searchPath) {
foreach (var v in searchPath) {
HelperFile.AddSearchPath(v);
}
}
RootCommand rootCommand = new RootCommand($"Squirrel {SquirrelRuntimeInfo.SquirrelDisplayVersion} for creating and distributing Squirrel releases.");
rootCommand.AddGlobalOption(TargetRuntime);
rootCommand.AddGlobalOption(VerboseOption);
rootCommand.AddGlobalOption(AddSearchPathOption);
var runtime = RID.Parse(parseResult.GetValueForOption(TargetRuntime) ?? SquirrelRuntimeInfo.SystemOs.GetOsShortName());
switch (runtime.BaseRID) {
case RuntimeOs.Windows:
if (!SquirrelRuntimeInfo.IsWindows)
logger.Write("Cross-compiling will cause some commands and options of Squirrel to be unavailable.", LogLevel.Warn);
Add(rootCommand, new PackWindowsCommand(), Windows.Commands.Pack);
Add(rootCommand, new ReleasifyWindowsCommand(), Windows.Commands.Releasify);
// temporarily, upload only supports windows.
Command uploadCommand = new Command("upload", "Upload local package(s) to a remote update source.");
Add(uploadCommand, new S3UploadCommand(), options => S3Repository.UploadMissingPackages(options));
Add(uploadCommand, new GitHubUploadCommand(), options => GitHubRepository.UploadMissingPackages(options));
rootCommand.Add(uploadCommand);
break;
case RuntimeOs.OSX:
if (!SquirrelRuntimeInfo.IsOSX)
logger.Write("Cross-compiling will cause some commands and options of Squirrel to be unavailable.", LogLevel.Warn);
Add(rootCommand, new BundleOsxCommand(), OSX.Commands.Bundle);
Add(rootCommand, new ReleasifyOsxCommand(), OSX.Commands.Releasify);
break;
default:
throw new NotSupportedException("Unsupported OS platform: " + runtime.BaseRID.GetOsLongName());
}
if (verbose) {
logger.Level = LogLevel.Debug;
}
Command downloadCommand = new Command("download", "Download's the latest release from a remote update source.");
Add(downloadCommand, new HttpDownloadCommand(), options => SimpleWebRepository.DownloadRecentPackages(options));
Add(downloadCommand, new S3DownloadCommand(), options => S3Repository.DownloadRecentPackages(options));
Add(downloadCommand, new GitHubDownloadCommand(), options => GitHubRepository.DownloadRecentPackages(options));
rootCommand.Add(downloadCommand);
return rootCommand.Invoke(args);
}
private static Command Add<T>(Command parent, T command, Action<T> execute)
where T : BaseCommand
{
command.SetHandler((ctx) => {
command.SetProperties(ctx.ParseResult);
command.TargetRuntime = RID.Parse(ctx.ParseResult.GetValueForOption(TargetRuntime));
execute(command);
});
parent.AddCommand(command);
return command;
}
private static Command Add<T>(Command parent, T command, Func<T, Task> execute)
where T : BaseCommand
{
command.SetHandler((ctx) => {
command.SetProperties(ctx.ParseResult);
command.TargetRuntime = RID.Parse(ctx.ParseResult.GetValueForOption(TargetRuntime));
return execute(command);
});
parent.AddCommand(command);
return command;
}
}
}

View File

@@ -0,0 +1,69 @@
using System.Threading;
using NuGet.Configuration;
using NuGet.Packaging.Core;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using NugetLogger = NuGet.Common.ILogger;
namespace Squirrel.Csq.Updates;
public class NugetDownloader
{
private readonly NugetLogger _logger;
private readonly PackageSource _packageSource;
private readonly SourceRepository _sourceRepository;
private readonly SourceCacheContext _sourceCacheContext;
public NugetDownloader(NugetLogger logger)
{
_logger = logger;
_packageSource = new PackageSource("https://api.nuget.org/v3/index.json", "NuGet.org");
_sourceRepository = new SourceRepository(_packageSource, Repository.Provider.GetCoreV3());
_sourceCacheContext = new SourceCacheContext();
}
public async Task<IPackageSearchMetadata> GetPackageMetadata(string packageName, string version, CancellationToken cancellationToken)
{
PackageMetadataResource packageMetadataResource = _sourceRepository.GetResource<PackageMetadataResource>();
FindPackageByIdResource packageByIdResource = _sourceRepository.GetResource<FindPackageByIdResource>();
IPackageSearchMetadata package = null;
var prerelease = version?.Equals("pre", StringComparison.InvariantCultureIgnoreCase) == true;
if (version is null || version.Equals("latest", StringComparison.InvariantCultureIgnoreCase) || prerelease) {
// get latest (or prerelease) version
IEnumerable<IPackageSearchMetadata> metadata = await packageMetadataResource
.GetMetadataAsync(packageName, true, true, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
package = metadata
.Where(x => x.IsListed)
.Where(x => prerelease || !x.Identity.Version.IsPrerelease)
.OrderByDescending(x => x.Identity.Version)
.FirstOrDefault();
} else {
// resolve version ranges and wildcards
var versions = await packageByIdResource.GetAllVersionsAsync(packageName, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
var resolved = versions.FindBestMatch(VersionRange.Parse(version), version => version);
// get exact version
var packageIdentity = new PackageIdentity(packageName, resolved);
package = await packageMetadataResource
.GetMetadataAsync(packageIdentity, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
}
if (package is null) {
throw new Exception($"Unable to locate {packageName} {version} on NuGet.org");
}
return package;
}
public async Task DownloadPackageToStream(IPackageSearchMetadata package, Stream targetStream, CancellationToken cancellationToken)
{
FindPackageByIdResource packageByIdResource = _sourceRepository.GetResource<FindPackageByIdResource>();
await packageByIdResource
.CopyNupkgToStreamAsync(package.Identity.Id, package.Identity.Version, targetStream, _sourceCacheContext, _logger, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -0,0 +1,54 @@
using NugetLevel = NuGet.Common.LogLevel;
using NugetLogger = NuGet.Common.ILogger;
using NugetMessage = NuGet.Common.ILogMessage;
namespace Squirrel.Csq.Updates;
class NullNugetLogger : NugetLogger
{
void NugetLogger.LogDebug(string data)
{
}
void NugetLogger.LogVerbose(string data)
{
}
void NugetLogger.LogInformation(string data)
{
}
void NugetLogger.LogMinimal(string data)
{
}
void NugetLogger.LogWarning(string data)
{
}
void NugetLogger.LogError(string data)
{
}
void NugetLogger.LogInformationSummary(string data)
{
}
void NugetLogger.Log(NugetLevel level, string data)
{
}
Task NugetLogger.LogAsync(NugetLevel level, string data)
{
return Task.CompletedTask;
}
void NugetLogger.Log(NugetMessage message)
{
}
Task NugetLogger.LogAsync(NugetMessage message)
{
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,28 @@
using System.Threading;
using Squirrel.Packaging;
namespace Squirrel.Csq.Updates;
public class UpdateChecker
{
private readonly ILogger _logger;
public UpdateChecker(ILogger logger)
{
_logger = logger;
}
public async Task CheckForUpdates()
{
try {
var cancel = new CancellationTokenSource(3000);
var myVer = SquirrelRuntimeInfo.SquirrelNugetVersion;
var dl = new NugetDownloader(new NugetLoggingWrapper(_logger));
var package = await dl.GetPackageMetadata("csq", (myVer.IsPrerelease || myVer.HasMetadata) ? "pre" : "latest", cancel.Token).ConfigureAwait(false);
if (package.Identity.Version > myVer)
_logger.Warn($"There is a new version of csq available ({package.Identity.Version})");
} catch (Exception ex) {
_logger.Debug(ex, "Failed to check for updates.");
}
}
}

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "3.0",
"version": "4.0",
"gitCommitIdShortFixedLength": 7,
"publicReleaseRefSpec": [
"^refs/heads/master$"