re-arrange project directories; part 2

This commit is contained in:
Caelan Sayler
2022-05-06 12:30:07 +01:00
parent 44340cbb3a
commit bb973acb7d
25 changed files with 293 additions and 292 deletions

View File

@@ -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));
}
}
}

View File

@@ -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,

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)

View 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)]

View File

@@ -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();

View File

@@ -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)

View File

@@ -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>

View 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));
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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);
});
}
}
}

View File

@@ -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);

View File

@@ -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)