mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
re-arrange project directories; part 2
This commit is contained in:
@@ -12,18 +12,6 @@ using Squirrel.SimpleSplat;
|
||||
|
||||
namespace SquirrelCli
|
||||
{
|
||||
internal abstract class BaseOptions : ValidatedOptionSet
|
||||
{
|
||||
public string releaseDir { get; private set; } = ".\\Releases";
|
||||
|
||||
protected static IFullLogger Log = SquirrelLocator.CurrentMutable.GetService<ILogManager>().GetLogger(typeof(BaseOptions));
|
||||
|
||||
public BaseOptions()
|
||||
{
|
||||
Add("r=|releaseDir=", "Output {DIRECTORY} for releasified packages", v => releaseDir = v);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SigningOptions : BaseOptions
|
||||
{
|
||||
public string signParams { get; private set; }
|
||||
@@ -186,100 +174,4 @@ namespace SquirrelCli
|
||||
base.ValidateInternal(false);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncBackblazeOptions : BaseOptions
|
||||
{
|
||||
public string b2KeyId { get; private set; }
|
||||
public string b2AppKey { get; private set; }
|
||||
public string b2BucketId { get; private set; }
|
||||
|
||||
public SyncBackblazeOptions()
|
||||
{
|
||||
Add("b2BucketId=", v => b2BucketId = v);
|
||||
Add("b2keyid=", v => b2KeyId = v);
|
||||
Add("b2key=", v => b2AppKey = v);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(b2KeyId), nameof(b2AppKey), nameof(b2BucketId));
|
||||
Log.Warn("Provider 'b2' is being deprecated and will no longer be updated.");
|
||||
Log.Warn("The replacement is using the 's3' provider with BackBlaze B2 using the '--endpoint' option.");
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncS3Options : BaseOptions
|
||||
{
|
||||
public string keyId { get; private set; }
|
||||
public string secret { get; private set; }
|
||||
public string region { get; private set; }
|
||||
public string endpoint { get; private set; }
|
||||
public string bucket { get; private set; }
|
||||
public string pathPrefix { get; private set; }
|
||||
public bool overwrite { get; private set; }
|
||||
public int keepMaxReleases { get; private set; }
|
||||
|
||||
public SyncS3Options()
|
||||
{
|
||||
Add("keyId=", "Authentication {IDENTIFIER} or access key", v => keyId = v);
|
||||
Add("secret=", "Authentication secret {KEY}", v => secret = v);
|
||||
Add("region=", "AWS service {REGION} (eg. us-west-1)", v => region = v);
|
||||
Add("endpoint=", "Custom service {URL} (backblaze, digital ocean, etc)", v => endpoint = v);
|
||||
Add("bucket=", "{NAME} of the S3 bucket", v => bucket = v);
|
||||
Add("pathPrefix=", "A sub-folder {PATH} used for files in the bucket, for creating release channels (eg. 'stable' or 'dev')", v => pathPrefix = v);
|
||||
Add("overwrite", "Replace existing files if source has changed", v => overwrite = true);
|
||||
Add("keepMaxReleases=", "Applies a retention policy during upload which keeps only the specified {NUMBER} of old versions",
|
||||
v => keepMaxReleases = ParseIntArg(nameof(keepMaxReleases), v));
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(secret), nameof(keyId), nameof(bucket));
|
||||
|
||||
if ((region == null) == (endpoint == null)) {
|
||||
throw new OptionValidationException("One of 'region' and 'endpoint' arguments is required and are also mutually exclusive. Specify only one of these. ");
|
||||
}
|
||||
|
||||
if (region != null) {
|
||||
var r = Amazon.RegionEndpoint.GetBySystemName(region);
|
||||
if (r.DisplayName == "Unknown")
|
||||
Log.Warn($"Region '{region}' lookup failed, is this a valid AWS region?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncHttpOptions : BaseOptions
|
||||
{
|
||||
public string url { get; private set; }
|
||||
public SyncHttpOptions()
|
||||
{
|
||||
Add("url=", "Base url to the http location with hosted releases", v => url = v);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(url));
|
||||
IsValidUrl(nameof(url));
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncGithubOptions : BaseOptions
|
||||
{
|
||||
public string repoUrl { get; private set; }
|
||||
public string token { get; private set; }
|
||||
public bool pre { get; private set; }
|
||||
|
||||
public SyncGithubOptions()
|
||||
{
|
||||
Add("repoUrl=", "Full url to the github repository\nexample: 'https://github.com/myname/myrepo'", v => repoUrl = v);
|
||||
Add("token=", "OAuth token to use as login credentials", v => token = v);
|
||||
Add("pre", "Fetch the latest pre-release instead of stable", v => pre = true);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(repoUrl));
|
||||
IsValidUrl(nameof(repoUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ using Squirrel;
|
||||
using Squirrel.Json;
|
||||
using Squirrel.Lib;
|
||||
using Squirrel.NuGet;
|
||||
using Squirrel.Shared;
|
||||
using Squirrel.CommandLine;
|
||||
using Squirrel.SimpleSplat;
|
||||
using SquirrelCli.Sources;
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace SquirrelCli
|
||||
foreach (var file in toProcess) {
|
||||
Log.Info("Creating release for package: " + file.FullName);
|
||||
|
||||
var rp = new ReleasePackage(file.FullName);
|
||||
var rp = new ReleasePackageBuilder(file.FullName);
|
||||
rp.CreateReleasePackage(TempDir, Path.Combine(di.FullName, rp.SuggestedReleaseFileName), contentsPostProcessHook: (pkgPath, zpkg) => {
|
||||
var nuspecPath = Directory.GetFiles(pkgPath, "*.nuspec", SearchOption.TopDirectoryOnly)
|
||||
.ContextualSingle("package", "*.nuspec", "top level directory");
|
||||
@@ -347,7 +347,7 @@ namespace SquirrelCli
|
||||
|
||||
processed.Add(rp.ReleasePackageFile);
|
||||
|
||||
var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir);
|
||||
var prev = ReleasePackageBuilder.GetPreviousRelease(previousReleases, rp, targetDir);
|
||||
if (prev != null && generateDeltas) {
|
||||
var deltaBuilder = new DeltaPackageBuilder();
|
||||
var dp = deltaBuilder.CreateDeltaPackage(prev, rp,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>Squirrel</AssemblyName>
|
||||
<RootNamespace>Squirrel</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>..\..\squirrel.ico</ApplicationIcon>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
@@ -12,7 +13,6 @@
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<DebugType>embedded</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<RootNamespace>Squirrel</RootNamespace>
|
||||
<PathMap>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=./</PathMap>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
@@ -21,15 +21,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.8.16" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.IO" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="B2Net" Version="0.7.5" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||
<PackageReference Include="NuGet.Commands" Version="6.1.0" />
|
||||
<PackageReference Include="PeNet" Version="2.9.7" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Squirrel
|
||||
{
|
||||
internal class DeltaPackageBuilder : IEnableLogger
|
||||
{
|
||||
public ReleasePackage CreateDeltaPackage(ReleasePackage basePackage, ReleasePackage newPackage, string outputFile, string tempDirectory)
|
||||
public ReleasePackageBuilder CreateDeltaPackage(ReleasePackageBuilder basePackage, ReleasePackageBuilder newPackage, string outputFile, string tempDirectory)
|
||||
{
|
||||
Contract.Requires(basePackage != null);
|
||||
Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile));
|
||||
@@ -164,7 +164,7 @@ namespace Squirrel
|
||||
|
||||
printProcessed(newLibFiles.Length, baseLibFiles.Count);
|
||||
|
||||
ReleasePackage.addDeltaFilesToContentTypes(tempInfo.FullName);
|
||||
ReleasePackageBuilder.addDeltaFilesToContentTypes(tempInfo.FullName);
|
||||
EasyZip.CreateZipFromDirectory(outputFile, tempInfo.FullName);
|
||||
|
||||
this.Log().Info(
|
||||
@@ -173,7 +173,7 @@ namespace Squirrel
|
||||
".");
|
||||
}
|
||||
|
||||
return new ReleasePackage(outputFile);
|
||||
return new ReleasePackageBuilder(outputFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,10 @@ namespace Squirrel
|
||||
public static string StubExecutablePath => FindHelperFile("StubExecutable.exe");
|
||||
public static string SingleFileHostPath => FindHelperFile("singlefilehost.exe");
|
||||
public static string WixTemplatePath => FindHelperFile("template.wxs");
|
||||
public static string SevenZipPath => FindHelperFile("7z.exe");
|
||||
public static string SignToolPath => FindHelperFile("signtool.exe");
|
||||
|
||||
// private so we don't expose paths to internal tools. these should be exposed as a helper function
|
||||
private static string SevenZipPath => FindHelperFile("7z.exe");
|
||||
private static string RceditPath => FindHelperFile("rcedit.exe");
|
||||
private static string WixCandlePath => FindHelperFile("candle.exe");
|
||||
private static string WixLightPath => FindHelperFile("light.exe");
|
||||
@@ -133,6 +133,43 @@ namespace Squirrel
|
||||
return InvokeAndThrowIfNonZero(RceditPath, args);
|
||||
}
|
||||
|
||||
//private static string _7zPath;
|
||||
|
||||
//private static async Task<string> Get7zPath()
|
||||
//{
|
||||
// if (_7zPath != null) return _7zPath;
|
||||
|
||||
// var findCommand = SquirrelRuntimeInfo.IsWindows ? "where" : "which";
|
||||
|
||||
// // search for the 7z or 7za on the path
|
||||
// var result = await Utility.InvokeProcessUnsafeAsync(Utility.CreateProcessStartInfo(findCommand, "7z"), CancellationToken.None).ConfigureAwait(false);
|
||||
// if (result.ExitCode == 0) {
|
||||
// _7zPath = "7z";
|
||||
// return _7zPath;
|
||||
// }
|
||||
|
||||
// result = await Utility.InvokeProcessUnsafeAsync(Utility.CreateProcessStartInfo(findCommand, "7za"), CancellationToken.None).ConfigureAwait(false);
|
||||
// if (result.ExitCode == 0) {
|
||||
// _7zPath = "7za";
|
||||
// return _7zPath;
|
||||
// }
|
||||
|
||||
// // we only bundle the windows version currently
|
||||
// if (SquirrelRuntimeInfo.IsWindows) {
|
||||
// _7zPath = HelperExe.SevenZipPath;
|
||||
// return _7zPath;
|
||||
// }
|
||||
|
||||
// return null;
|
||||
//}
|
||||
|
||||
public static async Task CompressLzma7z(string zipFilePath, string inFolder)
|
||||
{
|
||||
Log.Info($"Compressing '{inFolder}' to '{zipFilePath}' using 7z (LZMA)...");
|
||||
var args = new string[] { "a", zipFilePath, "-tzip", "-m0=LZMA", "-aoa", "-y", "*" };
|
||||
await InvokeAndThrowIfNonZero(SevenZipPath, args, inFolder).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async Task InvokeAndThrowIfNonZero(string exePath, IEnumerable<string> args, string workingDir = null)
|
||||
{
|
||||
var result = await Utility.InvokeProcessAsync(exePath, args, CancellationToken.None, workingDir).ConfigureAwait(false);
|
||||
@@ -1,4 +1,4 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
// If updating HostModel, mark the ResourceUpdater.cs class as partial so these functions can get mixed in
|
||||
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Microsoft.NET.HostModel
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
|
||||
#endif
|
||||
[SupportedOSPlatform("windows")]
|
||||
public partial class ResourceUpdater
|
||||
{
|
||||
public ResourceUpdater(string peFile, bool bDeleteExistingResources)
|
||||
|
||||
6
src/Squirrel.CommandLine/Properties/AssemblyInfo.cs
Normal file
6
src/Squirrel.CommandLine/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: InternalsVisibleTo("Squirrel, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Squirrel.CommandLine.Windows, PublicKey=" + SNK.SHA1)]
|
||||
@@ -13,6 +13,7 @@ using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Readers;
|
||||
using NuGet.Versioning;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Squirrel
|
||||
{
|
||||
@@ -24,9 +25,9 @@ namespace Squirrel
|
||||
SemanticVersion Version { get; }
|
||||
}
|
||||
|
||||
internal class ReleasePackage : IEnableLogger, IReleasePackage
|
||||
internal class ReleasePackageBuilder : IEnableLogger, IReleasePackage
|
||||
{
|
||||
public ReleasePackage(string inputPackageFile, bool isReleasePackage = false)
|
||||
public ReleasePackageBuilder(string inputPackageFile, bool isReleasePackage = false)
|
||||
{
|
||||
InputPackageFile = inputPackageFile;
|
||||
|
||||
@@ -123,6 +124,21 @@ namespace Squirrel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a list of releases and a specified release package, returns the release package
|
||||
/// directly previous to the specified version.
|
||||
/// </summary>
|
||||
internal static ReleasePackageBuilder GetPreviousRelease(IEnumerable<ReleaseEntry> releaseEntries, IReleasePackage package, string targetDir)
|
||||
{
|
||||
if (releaseEntries == null || !releaseEntries.Any()) return null;
|
||||
return releaseEntries
|
||||
.Where(x => x.IsDelta == false)
|
||||
.Where(x => x.Version < package.Version)
|
||||
.OrderByDescending(x => x.Version)
|
||||
.Select(x => new ReleasePackageBuilder(Path.Combine(targetDir, x.Filename), true))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
static Task extractZipWithEscaping(string zipFilePath, string outFolder)
|
||||
{
|
||||
return Task.Run(() => {
|
||||
@@ -148,79 +164,6 @@ namespace Squirrel
|
||||
});
|
||||
}
|
||||
|
||||
public static Task ExtractZipForInstall(string zipFilePath, string outFolder, string rootPackageFolder)
|
||||
{
|
||||
return ExtractZipForInstall(zipFilePath, outFolder, rootPackageFolder, x => { });
|
||||
}
|
||||
|
||||
public static Task ExtractZipForInstall(string zipFilePath, string outFolder, string rootPackageFolder, Action<int> progress)
|
||||
{
|
||||
var re = new Regex(@"lib[\\\/][^\\\/]*[\\\/]", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
|
||||
return Task.Run(() => {
|
||||
using (var za = ZipArchive.Open(zipFilePath))
|
||||
using (var reader = za.ExtractAllEntries()) {
|
||||
var totalItems = za.Entries.Count;
|
||||
var currentItem = 0;
|
||||
|
||||
while (reader.MoveToNextEntry()) {
|
||||
// Report progress early since we might be need to continue for non-matches
|
||||
currentItem++;
|
||||
var percentage = (currentItem * 100d) / totalItems;
|
||||
progress((int) percentage);
|
||||
|
||||
// extract .nuspec to app directory as 'current.version'
|
||||
if (Utility.FileHasExtension(reader.Entry.Key, NugetUtil.ManifestExtension)) {
|
||||
Utility.Retry(() => reader.WriteEntryToFile(Path.Combine(outFolder, "current.version")));
|
||||
continue;
|
||||
}
|
||||
|
||||
var parts = reader.Entry.Key.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
|
||||
var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts);
|
||||
|
||||
if (!re.IsMatch(decoded)) continue;
|
||||
decoded = re.Replace(decoded, "", 1);
|
||||
|
||||
var fullTargetFile = Path.Combine(outFolder, decoded);
|
||||
var fullTargetDir = Path.GetDirectoryName(fullTargetFile);
|
||||
Directory.CreateDirectory(fullTargetDir);
|
||||
|
||||
var failureIsOkay = false;
|
||||
if (!reader.Entry.IsDirectory && decoded.Contains("_ExecutionStub.exe")) {
|
||||
// NB: On upgrade, many of these stubs will be in-use, nbd tho.
|
||||
failureIsOkay = true;
|
||||
|
||||
fullTargetFile = Path.Combine(
|
||||
rootPackageFolder,
|
||||
Path.GetFileName(decoded).Replace("_ExecutionStub.exe", ".exe"));
|
||||
|
||||
LogHost.Default.Info("Rigging execution stub for {0} to {1}", decoded, fullTargetFile);
|
||||
}
|
||||
|
||||
if (Utility.PathPartEquals(parts.Last(), "app.ico")) {
|
||||
failureIsOkay = true;
|
||||
fullTargetFile = Path.Combine(rootPackageFolder, "app.ico");
|
||||
}
|
||||
|
||||
try {
|
||||
Utility.Retry(() => {
|
||||
if (reader.Entry.IsDirectory) {
|
||||
Directory.CreateDirectory(fullTargetFile);
|
||||
} else {
|
||||
reader.WriteEntryToFile(fullTargetFile);
|
||||
}
|
||||
}, 5);
|
||||
} catch (Exception e) {
|
||||
if (!failureIsOkay) throw;
|
||||
LogHost.Default.WarnException("Can't write execution stub, probably in use", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress(100);
|
||||
});
|
||||
}
|
||||
|
||||
void renderReleaseNotesMarkdown(string specPath, Func<string, string> releaseNotesProcessor)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
@@ -1,12 +1,14 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using Microsoft.NET.HostModel;
|
||||
using Microsoft.NET.HostModel.AppHost;
|
||||
|
||||
namespace Squirrel.Shared
|
||||
namespace Squirrel.CommandLine
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static class SetupBundle
|
||||
{
|
||||
public static bool IsBundle(string setupPath, out long bundleOffset, out long bundleLength)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -9,4 +9,15 @@
|
||||
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.8.16" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.IO" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="B2Net" Version="0.7.5" />
|
||||
<PackageReference Include="NuGet.Commands" Version="6.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
117
src/Squirrel.CommandLine/SyncOptions.cs
Normal file
117
src/Squirrel.CommandLine/SyncOptions.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace SquirrelCli
|
||||
{
|
||||
internal abstract class BaseOptions : ValidatedOptionSet
|
||||
{
|
||||
public string releaseDir { get; private set; } = ".\\Releases";
|
||||
|
||||
protected static IFullLogger Log = SquirrelLocator.CurrentMutable.GetService<ILogManager>().GetLogger(typeof(BaseOptions));
|
||||
|
||||
public BaseOptions()
|
||||
{
|
||||
Add("r=|releaseDir=", "Output {DIRECTORY} for releasified packages", v => releaseDir = v);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncBackblazeOptions : BaseOptions
|
||||
{
|
||||
public string b2KeyId { get; private set; }
|
||||
public string b2AppKey { get; private set; }
|
||||
public string b2BucketId { get; private set; }
|
||||
|
||||
public SyncBackblazeOptions()
|
||||
{
|
||||
Add("b2BucketId=", v => b2BucketId = v);
|
||||
Add("b2keyid=", v => b2KeyId = v);
|
||||
Add("b2key=", v => b2AppKey = v);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(b2KeyId), nameof(b2AppKey), nameof(b2BucketId));
|
||||
Log.Warn("Provider 'b2' is being deprecated and will no longer be updated.");
|
||||
Log.Warn("The replacement is using the 's3' provider with BackBlaze B2 using the '--endpoint' option.");
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncS3Options : BaseOptions
|
||||
{
|
||||
public string keyId { get; private set; }
|
||||
public string secret { get; private set; }
|
||||
public string region { get; private set; }
|
||||
public string endpoint { get; private set; }
|
||||
public string bucket { get; private set; }
|
||||
public string pathPrefix { get; private set; }
|
||||
public bool overwrite { get; private set; }
|
||||
public int keepMaxReleases { get; private set; }
|
||||
|
||||
public SyncS3Options()
|
||||
{
|
||||
Add("keyId=", "Authentication {IDENTIFIER} or access key", v => keyId = v);
|
||||
Add("secret=", "Authentication secret {KEY}", v => secret = v);
|
||||
Add("region=", "AWS service {REGION} (eg. us-west-1)", v => region = v);
|
||||
Add("endpoint=", "Custom service {URL} (backblaze, digital ocean, etc)", v => endpoint = v);
|
||||
Add("bucket=", "{NAME} of the S3 bucket", v => bucket = v);
|
||||
Add("pathPrefix=", "A sub-folder {PATH} used for files in the bucket, for creating release channels (eg. 'stable' or 'dev')", v => pathPrefix = v);
|
||||
Add("overwrite", "Replace existing files if source has changed", v => overwrite = true);
|
||||
Add("keepMaxReleases=", "Applies a retention policy during upload which keeps only the specified {NUMBER} of old versions",
|
||||
v => keepMaxReleases = ParseIntArg(nameof(keepMaxReleases), v));
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(secret), nameof(keyId), nameof(bucket));
|
||||
|
||||
if ((region == null) == (endpoint == null)) {
|
||||
throw new OptionValidationException("One of 'region' and 'endpoint' arguments is required and are also mutually exclusive. Specify only one of these. ");
|
||||
}
|
||||
|
||||
if (region != null) {
|
||||
var r = Amazon.RegionEndpoint.GetBySystemName(region);
|
||||
if (r.DisplayName == "Unknown")
|
||||
Log.Warn($"Region '{region}' lookup failed, is this a valid AWS region?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncHttpOptions : BaseOptions
|
||||
{
|
||||
public string url { get; private set; }
|
||||
public SyncHttpOptions()
|
||||
{
|
||||
Add("url=", "Base url to the http location with hosted releases", v => url = v);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(url));
|
||||
IsValidUrl(nameof(url));
|
||||
}
|
||||
}
|
||||
|
||||
internal class SyncGithubOptions : BaseOptions
|
||||
{
|
||||
public string repoUrl { get; private set; }
|
||||
public string token { get; private set; }
|
||||
public bool pre { get; private set; }
|
||||
|
||||
public SyncGithubOptions()
|
||||
{
|
||||
Add("repoUrl=", "Full url to the github repository\nexample: 'https://github.com/myname/myrepo'", v => repoUrl = v);
|
||||
Add("token=", "OAuth token to use as login credentials", v => token = v);
|
||||
Add("pre", "Fetch the latest pre-release instead of stable", v => pre = true);
|
||||
}
|
||||
|
||||
public override void Validate()
|
||||
{
|
||||
IsRequired(nameof(repoUrl));
|
||||
IsValidUrl(nameof(repoUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,68 +33,11 @@ namespace Squirrel
|
||||
|
||||
public static void CreateZipFromDirectory(string outputFile, string directoryToCompress)
|
||||
{
|
||||
// 7z is much faster, and produces much better compression results
|
||||
// so we will use it if it is available
|
||||
if (Compress7z(outputFile, directoryToCompress).GetAwaiter().GetResult())
|
||||
return;
|
||||
|
||||
Log.Info($"Compressing '{directoryToCompress}' to '{outputFile}' using SharpCompress...");
|
||||
using var archive = ZipArchive.Create();
|
||||
archive.DeflateCompressionLevel = CompressionLevel.BestSpeed;
|
||||
archive.AddAllFromDirectory(directoryToCompress);
|
||||
archive.SaveTo(outputFile, CompressionType.Deflate);
|
||||
}
|
||||
|
||||
private static string _7zPath;
|
||||
|
||||
private static async Task<string> Get7zPath()
|
||||
{
|
||||
if (_7zPath != null) return _7zPath;
|
||||
|
||||
var findCommand = SquirrelRuntimeInfo.IsWindows ? "where" : "which";
|
||||
|
||||
// search for the 7z or 7za on the path
|
||||
var result = await Utility.InvokeProcessUnsafeAsync(Utility.CreateProcessStartInfo(findCommand, "7z"), CancellationToken.None).ConfigureAwait(false);
|
||||
if (result.ExitCode == 0) {
|
||||
_7zPath = "7z";
|
||||
return _7zPath;
|
||||
}
|
||||
|
||||
result = await Utility.InvokeProcessUnsafeAsync(Utility.CreateProcessStartInfo(findCommand, "7za"), CancellationToken.None).ConfigureAwait(false);
|
||||
if (result.ExitCode == 0) {
|
||||
_7zPath = "7za";
|
||||
return _7zPath;
|
||||
}
|
||||
|
||||
// we only bundle the windows version currently
|
||||
if (SquirrelRuntimeInfo.IsWindows) {
|
||||
_7zPath = HelperExe.SevenZipPath;
|
||||
return _7zPath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task<bool> Compress7z(string zipFilePath, string inFolder)
|
||||
{
|
||||
var path = await Get7zPath();
|
||||
|
||||
if (path == null) {
|
||||
Log.Warn("7z not found on path. Will fallback to SharpCompress.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.Info($"Compressing '{inFolder}' to '{zipFilePath}' using 7z (LZMA)...");
|
||||
try {
|
||||
var args = String.Format("a \"{0}\" -tzip -m0=LZMA -aoa -y *", zipFilePath);
|
||||
var psi = Utility.CreateProcessStartInfo(path, args, inFolder);
|
||||
var result = await Utility.InvokeProcessUnsafeAsync(psi, CancellationToken.None).ConfigureAwait(false);
|
||||
if (result.ExitCode != 0) throw new Exception(result.StdOutput);
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
Log.Warn("Unable to create archive with 7z.exe\n" + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using NuGet.Versioning;
|
||||
using Squirrel.Lib;
|
||||
using Squirrel.NuGet;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
@@ -15,6 +16,7 @@ namespace Squirrel.Lib
|
||||
/// <summary>
|
||||
/// Provides access to NTFS junction points in .Net.
|
||||
/// </summary>
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal static class JunctionPoint
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Readers;
|
||||
using Squirrel.SimpleSplat;
|
||||
|
||||
namespace Squirrel.NuGet
|
||||
{
|
||||
@@ -84,5 +89,74 @@ namespace Squirrel.NuGet
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
public static Task ExtractZipReleaseForInstall(string zipFilePath, string outFolder, string rootPackageFolder, Action<int> progress)
|
||||
{
|
||||
var re = new Regex(@"lib[\\\/][^\\\/]*[\\\/]", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
|
||||
return Task.Run(() => {
|
||||
using (var za = ZipArchive.Open(zipFilePath))
|
||||
using (var reader = za.ExtractAllEntries()) {
|
||||
var totalItems = za.Entries.Count;
|
||||
var currentItem = 0;
|
||||
|
||||
while (reader.MoveToNextEntry()) {
|
||||
// Report progress early since we might be need to continue for non-matches
|
||||
currentItem++;
|
||||
var percentage = (currentItem * 100d) / totalItems;
|
||||
progress((int) percentage);
|
||||
|
||||
// extract .nuspec to app directory as 'current.version'
|
||||
if (Utility.FileHasExtension(reader.Entry.Key, NugetUtil.ManifestExtension)) {
|
||||
Utility.Retry(() => reader.WriteEntryToFile(Path.Combine(outFolder, "current.version")));
|
||||
continue;
|
||||
}
|
||||
|
||||
var parts = reader.Entry.Key.Split('\\', '/').Select(x => Uri.UnescapeDataString(x));
|
||||
var decoded = String.Join(Path.DirectorySeparatorChar.ToString(), parts);
|
||||
|
||||
if (!re.IsMatch(decoded)) continue;
|
||||
decoded = re.Replace(decoded, "", 1);
|
||||
|
||||
var fullTargetFile = Path.Combine(outFolder, decoded);
|
||||
var fullTargetDir = Path.GetDirectoryName(fullTargetFile);
|
||||
Directory.CreateDirectory(fullTargetDir);
|
||||
|
||||
var failureIsOkay = false;
|
||||
if (!reader.Entry.IsDirectory && decoded.Contains("_ExecutionStub.exe")) {
|
||||
// NB: On upgrade, many of these stubs will be in-use, nbd tho.
|
||||
failureIsOkay = true;
|
||||
|
||||
fullTargetFile = Path.Combine(
|
||||
rootPackageFolder,
|
||||
Path.GetFileName(decoded).Replace("_ExecutionStub.exe", ".exe"));
|
||||
|
||||
LogHost.Default.Info("Rigging execution stub for {0} to {1}", decoded, fullTargetFile);
|
||||
}
|
||||
|
||||
if (Utility.PathPartEquals(parts.Last(), "app.ico")) {
|
||||
failureIsOkay = true;
|
||||
fullTargetFile = Path.Combine(rootPackageFolder, "app.ico");
|
||||
}
|
||||
|
||||
try {
|
||||
Utility.Retry(() => {
|
||||
if (reader.Entry.IsDirectory) {
|
||||
Directory.CreateDirectory(fullTargetFile);
|
||||
} else {
|
||||
reader.WriteEntryToFile(fullTargetFile);
|
||||
}
|
||||
}, 5);
|
||||
} catch (Exception e) {
|
||||
if (!failureIsOkay) throw;
|
||||
LogHost.Default.WarnException("Can't write execution stub, probably in use", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress(100);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,21 +363,6 @@ namespace Squirrel
|
||||
return Filename.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a list of releases and a specified release package, returns the release package
|
||||
/// directly previous to the specified version.
|
||||
/// </summary>
|
||||
internal static ReleasePackage GetPreviousRelease(IEnumerable<ReleaseEntry> releaseEntries, IReleasePackage package, string targetDir)
|
||||
{
|
||||
if (releaseEntries == null || !releaseEntries.Any()) return null;
|
||||
return releaseEntries
|
||||
.Where(x => x.IsDelta == false)
|
||||
.Where(x => x.Version < package.Version)
|
||||
.OrderByDescending(x => x.Version)
|
||||
.Select(x => new ReleasePackage(Path.Combine(targetDir, x.Filename), true))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
static readonly Regex _suffixRegex = new Regex(@"(-full|-delta)?\.nupkg$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
static readonly Regex _versionStartRegex = new Regex(@"[\.-](0|[1-9]\d*)\.(0|[1-9]\d*)($|[^\d])", RegexOptions.Compiled);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using Squirrel.SimpleSplat;
|
||||
using System.Threading;
|
||||
using Squirrel.Bsdiff;
|
||||
using System.Runtime.Versioning;
|
||||
using Squirrel.NuGet;
|
||||
|
||||
namespace Squirrel
|
||||
{
|
||||
@@ -144,7 +145,7 @@ namespace Squirrel
|
||||
target.Create();
|
||||
|
||||
this.Log().Info("Writing files to app directory: {0}", target.FullName);
|
||||
await ReleasePackage.ExtractZipForInstall(
|
||||
await ZipPackage.ExtractZipReleaseForInstall(
|
||||
Path.Combine(updateInfo.PackageDirectory, release.Filename),
|
||||
target.FullName,
|
||||
_config.RootAppDir,
|
||||
@@ -185,22 +186,22 @@ namespace Squirrel
|
||||
//
|
||||
|
||||
// Smash together our base full package and the nearest delta
|
||||
var ret = await Task.Run(() => {
|
||||
var basePkg = new ReleasePackage(Path.Combine(_config.PackagesDir, currentVersion.Filename));
|
||||
var deltaPkg = new ReleasePackage(Path.Combine(_config.PackagesDir, releasesToApply.First().Filename));
|
||||
var outputPackageZip = await Task.Run(() => {
|
||||
var basePkg = Path.Combine(_config.PackagesDir, currentVersion.Filename);
|
||||
var deltaPkg = Path.Combine(_config.PackagesDir, releasesToApply.First().Filename);
|
||||
|
||||
return ApplyDeltaPackage(basePkg, deltaPkg,
|
||||
Regex.Replace(deltaPkg.InputPackageFile, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant),
|
||||
Regex.Replace(deltaPkg, @"-delta.nupkg$", ".nupkg", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant),
|
||||
x => progress.ReportReleaseProgress(x));
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
progress.FinishRelease();
|
||||
|
||||
if (releasesToApply.Count() == 1) {
|
||||
return ReleaseEntry.GenerateFromFile(ret.InputPackageFile);
|
||||
return ReleaseEntry.GenerateFromFile(outputPackageZip);
|
||||
}
|
||||
|
||||
var fi = new FileInfo(ret.InputPackageFile);
|
||||
var fi = new FileInfo(outputPackageZip);
|
||||
var entry = ReleaseEntry.GenerateFromFile(fi.OpenRead(), fi.Name);
|
||||
|
||||
// Recursively combine the rest of them
|
||||
@@ -360,19 +361,19 @@ namespace Squirrel
|
||||
return File.Exists(Path.Combine(appFolderPath, ".dead"));
|
||||
}
|
||||
|
||||
internal ReleasePackage ApplyDeltaPackage(ReleasePackage basePackage, ReleasePackage deltaPackage, string outputFile, Action<int> progress = null)
|
||||
internal string ApplyDeltaPackage(string basePackageZip, string deltaPackageZip, string outputFile, Action<int> progress = null)
|
||||
{
|
||||
progress = progress ?? (x => { });
|
||||
|
||||
Contract.Requires(deltaPackage != null);
|
||||
Contract.Requires(deltaPackageZip != null);
|
||||
Contract.Requires(!String.IsNullOrEmpty(outputFile) && !File.Exists(outputFile));
|
||||
|
||||
using (Utility.GetTempDir(_config.TempDir, out var deltaPath))
|
||||
using (Utility.GetTempDir(_config.TempDir, out var workingPath)) {
|
||||
EasyZip.ExtractZipToDirectory(deltaPackage.InputPackageFile, deltaPath);
|
||||
EasyZip.ExtractZipToDirectory(deltaPackageZip, deltaPath);
|
||||
progress(25);
|
||||
|
||||
EasyZip.ExtractZipToDirectory(basePackage.InputPackageFile, workingPath);
|
||||
EasyZip.ExtractZipToDirectory(basePackageZip, workingPath);
|
||||
progress(50);
|
||||
|
||||
var pathsVisited = new List<string>();
|
||||
@@ -422,7 +423,7 @@ namespace Squirrel
|
||||
progress(100);
|
||||
}
|
||||
|
||||
return new ReleasePackage(outputFile);
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
void applyDiffToFile(string deltaPath, string relativeFilePath, string workingDirectory)
|
||||
|
||||
Reference in New Issue
Block a user