mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Remove a bunch of releasify code from Update.exe
This commit is contained in:
@@ -37,7 +37,7 @@ Copy-Item "$In\Win32\WriteZipToSetup.exe" -Destination "$Out"
|
||||
Copy-Item "$In\Win32\WriteZipToSetup.pdb" -Destination "$Out"
|
||||
|
||||
Copy-Item -Path "$PSScriptRoot\vendor\7zip\*" -Destination "$Out" -Recurse
|
||||
Copy-Item -Path "$PSScriptRoot\vendor\wix\*" -Destination "$Out" -Recurse
|
||||
# Copy-Item -Path "$PSScriptRoot\vendor\wix\*" -Destination "$Out" -Recurse
|
||||
Copy-Item "$PSScriptRoot\vendor\NuGet.exe" -Destination "$Out"
|
||||
Copy-Item "$PSScriptRoot\vendor\rcedit.exe" -Destination "$Out"
|
||||
Copy-Item "$PSScriptRoot\vendor\signtool.exe" -Destination "$Out"
|
||||
|
||||
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
@@ -7,8 +7,7 @@
|
||||
<Description>Squirrel</Description>
|
||||
<Title>Squirrel</Title>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>9</LangVersion>
|
||||
<AssemblyName>Squirrel.Lib</AssemblyName>
|
||||
<AssemblyName>SquirrelLib</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<LangVersion>9</LangVersion>
|
||||
<AssemblyName>Squirrel</AssemblyName>
|
||||
<ApplicationIcon>..\..\squirrel.ico</ApplicationIcon>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
|
||||
<!-- Publish -->
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<CompressionInSingleFile>true</CompressionInSingleFile>
|
||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
|
||||
<ApplicationIcon>squirrel.ico</ApplicationIcon>
|
||||
<AssemblyName>Squirrel</AssemblyName>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<CompressionInSingleFile>true</CompressionInSingleFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="squirrel.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Octokit" Version="0.50.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.2.0.0" name="Squirrel.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
|
||||
</assembly>
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Squirrel.Update
|
||||
{
|
||||
public static class CopStache
|
||||
{
|
||||
public static string Render(string template, Dictionary<string, string> identifiers)
|
||||
{
|
||||
var buf = new StringBuilder();
|
||||
|
||||
foreach (var line in template.Split('\n')) {
|
||||
identifiers["RandomGuid"] = (Guid.NewGuid()).ToString();
|
||||
|
||||
foreach (var key in identifiers.Keys) {
|
||||
buf.Replace("{{" + key + "}}", SecurityElement.Escape(identifiers[key]));
|
||||
}
|
||||
|
||||
buf.AppendLine(line);
|
||||
}
|
||||
|
||||
return buf.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,21 +17,15 @@ using Squirrel.Lib;
|
||||
|
||||
namespace Squirrel.Update
|
||||
{
|
||||
enum UpdateAction
|
||||
{
|
||||
Unset = 0, Install, Uninstall, Download, Update, Releasify, Shortcut,
|
||||
Deshortcut, ProcessStart, UpdateSelf, CheckForUpdate
|
||||
}
|
||||
|
||||
class Program : IEnableLogger
|
||||
{
|
||||
static StartupOption opt;
|
||||
static IFullLogger Log => SquirrelLocator.Current.GetService<ILogManager>().GetLogger(typeof(Program));
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
var pg = new Program();
|
||||
try {
|
||||
return pg.main(args);
|
||||
return main(args);
|
||||
} catch (Exception ex) {
|
||||
// NB: Normally this is a terrible idea but we want to make
|
||||
// sure Setup.exe above us gets the nonzero error code
|
||||
@@ -40,7 +34,7 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
int main(string[] args)
|
||||
static int main(string[] args)
|
||||
{
|
||||
try {
|
||||
opt = new StartupOption(args);
|
||||
@@ -68,9 +62,9 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
int executeCommandLine(string[] args)
|
||||
static int executeCommandLine(string[] args)
|
||||
{
|
||||
this.Log().Info("Starting Squirrel Updater: " + String.Join(" ", args));
|
||||
Log.Info("Starting Squirrel Updater: " + String.Join(" ", args));
|
||||
|
||||
if (args.Any(x => x.StartsWith("/squirrel", StringComparison.OrdinalIgnoreCase))) {
|
||||
// NB: We're marked as Squirrel-aware, but we don't want to do
|
||||
@@ -84,7 +78,6 @@ namespace Squirrel.Update
|
||||
}
|
||||
|
||||
switch (opt.updateAction) {
|
||||
#if !MONO
|
||||
case UpdateAction.Install:
|
||||
var progressSource = new ProgressSource();
|
||||
Install(opt.silentInstall, progressSource, Path.GetFullPath(opt.target)).Wait();
|
||||
@@ -105,7 +98,7 @@ namespace Squirrel.Update
|
||||
UpdateSelf().Wait();
|
||||
break;
|
||||
case UpdateAction.Shortcut:
|
||||
Shortcut(opt.target, opt.shortcutArgs, opt.processStartArgs, opt.setupIcon, opt.onlyUpdateShortcuts);
|
||||
Shortcut(opt.target, opt.shortcutArgs, opt.processStartArgs, opt.icon, opt.onlyUpdateShortcuts);
|
||||
break;
|
||||
case UpdateAction.Deshortcut:
|
||||
Deshortcut(opt.target, opt.shortcutArgs);
|
||||
@@ -113,22 +106,18 @@ namespace Squirrel.Update
|
||||
case UpdateAction.ProcessStart:
|
||||
ProcessStart(opt.processStart, opt.processStartArgs, opt.shouldWait);
|
||||
break;
|
||||
#endif
|
||||
case UpdateAction.Releasify:
|
||||
Releasify(opt.target, opt.releaseDir, opt.bootstrapperExe, opt.backgroundGif, opt.signingParameters, opt.baseUrl, opt.setupIcon, !opt.noMsi, opt.packageAs64Bit, opt.frameworkVersion, !opt.noDelta);
|
||||
break;
|
||||
}
|
||||
|
||||
this.Log().Info("Finished Squirrel Updater");
|
||||
Log.Info("Finished Squirrel Updater");
|
||||
return 0;
|
||||
}
|
||||
|
||||
public async Task Install(bool silentInstall, ProgressSource progressSource, string sourceDirectory = null)
|
||||
static async Task Install(bool silentInstall, ProgressSource progressSource, string sourceDirectory = null)
|
||||
{
|
||||
sourceDirectory = sourceDirectory ?? AssemblyRuntimeInfo.BaseDirectory;
|
||||
var releasesPath = Path.Combine(sourceDirectory, "RELEASES");
|
||||
|
||||
this.Log().Info("Starting install, writing to {0}", sourceDirectory);
|
||||
Log.Info("Starting install, writing to {0}", sourceDirectory);
|
||||
|
||||
if (!AssemblyRuntimeInfo.IsSingleFile) {
|
||||
// when doing a standard build, our executable has multiple files/dependencies.
|
||||
@@ -138,7 +127,7 @@ namespace Squirrel.Update
|
||||
}
|
||||
|
||||
if (!File.Exists(releasesPath)) {
|
||||
this.Log().Info("RELEASES doesn't exist, creating it at " + releasesPath);
|
||||
Log.Info("RELEASES doesn't exist, creating it at " + releasesPath);
|
||||
var nupkgs = (new DirectoryInfo(sourceDirectory)).GetFiles()
|
||||
.Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(x => ReleaseEntry.GenerateFromFile(x.FullName));
|
||||
@@ -150,42 +139,42 @@ namespace Squirrel.Update
|
||||
.First().PackageName;
|
||||
|
||||
using (var mgr = new UpdateManager(sourceDirectory, ourAppName)) {
|
||||
this.Log().Info("About to install to: " + mgr.RootAppDirectory);
|
||||
Log.Info("About to install to: " + mgr.RootAppDirectory);
|
||||
if (Directory.Exists(mgr.RootAppDirectory)) {
|
||||
this.Log().Warn("Install path {0} already exists, burning it to the ground", mgr.RootAppDirectory);
|
||||
Log.Warn("Install path {0} already exists, burning it to the ground", mgr.RootAppDirectory);
|
||||
|
||||
mgr.KillAllExecutablesBelongingToPackage();
|
||||
await Task.Delay(500);
|
||||
|
||||
await this.ErrorIfThrows(() => Utility.DeleteDirectory(mgr.RootAppDirectory),
|
||||
await Log.ErrorIfThrows(() => Utility.DeleteDirectory(mgr.RootAppDirectory),
|
||||
"Failed to remove existing directory on full install, is the app still running???");
|
||||
|
||||
this.ErrorIfThrows(() => Utility.Retry(() => Directory.CreateDirectory(mgr.RootAppDirectory), 3),
|
||||
Log.ErrorIfThrows(() => Utility.Retry(() => Directory.CreateDirectory(mgr.RootAppDirectory), 3),
|
||||
"Couldn't recreate app directory, perhaps Antivirus is blocking it");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(mgr.RootAppDirectory);
|
||||
|
||||
var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe");
|
||||
this.ErrorIfThrows(() => File.Copy(AssemblyRuntimeInfo.EntryExePath, updateTarget, true),
|
||||
Log.ErrorIfThrows(() => File.Copy(AssemblyRuntimeInfo.EntryExePath, updateTarget, true),
|
||||
"Failed to copy Update.exe to " + updateTarget);
|
||||
|
||||
await mgr.FullInstall(silentInstall, progressSource.Raise);
|
||||
|
||||
await this.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(),
|
||||
await Log.ErrorIfThrows(() => mgr.CreateUninstallerRegistryEntry(),
|
||||
"Failed to create uninstaller registry entry");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Update(string updateUrl, string appName = null)
|
||||
static async Task Update(string updateUrl, string appName = null)
|
||||
{
|
||||
appName = appName ?? getAppNameFromDirectory();
|
||||
|
||||
this.Log().Info("Starting update, downloading from " + updateUrl);
|
||||
Log.Info("Starting update, downloading from " + updateUrl);
|
||||
|
||||
using (var mgr = new UpdateManager(updateUrl, appName)) {
|
||||
bool ignoreDeltaUpdates = false;
|
||||
this.Log().Info("About to update to: " + mgr.RootAppDirectory);
|
||||
Log.Info("About to update to: " + mgr.RootAppDirectory);
|
||||
|
||||
retry:
|
||||
try {
|
||||
@@ -199,24 +188,24 @@ namespace Squirrel.Update
|
||||
await mgr.ApplyReleases(updateInfo, x => Console.WriteLine(UpdateManager.CalculateProgress(x, 30, 100)));
|
||||
} catch (Exception ex) {
|
||||
if (ignoreDeltaUpdates) {
|
||||
this.Log().ErrorException("Really couldn't apply updates!", ex);
|
||||
Log.ErrorException("Really couldn't apply updates!", ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
this.Log().WarnException("Failed to apply updates, falling back to full updates", ex);
|
||||
Log.WarnException("Failed to apply updates, falling back to full updates", ex);
|
||||
ignoreDeltaUpdates = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
var updateTarget = Path.Combine(mgr.RootAppDirectory, "Update.exe");
|
||||
|
||||
await this.ErrorIfThrows(() =>
|
||||
await Log.ErrorIfThrows(() =>
|
||||
mgr.CreateUninstallerRegistryEntry(),
|
||||
"Failed to create uninstaller registry entry");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateSelf()
|
||||
static async Task UpdateSelf()
|
||||
{
|
||||
waitForParentToExit();
|
||||
var src = AssemblyRuntimeInfo.EntryExePath;
|
||||
@@ -229,11 +218,11 @@ namespace Squirrel.Update
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<string> Download(string updateUrl, string appName = null)
|
||||
static async Task<string> Download(string updateUrl, string appName = null)
|
||||
{
|
||||
appName = appName ?? getAppNameFromDirectory();
|
||||
|
||||
this.Log().Info("Fetching update information, downloading from " + updateUrl);
|
||||
Log.Info("Fetching update information, downloading from " + updateUrl);
|
||||
using (var mgr = new UpdateManager(updateUrl, appName)) {
|
||||
var updateInfo = await mgr.CheckForUpdate(intention: UpdaterIntention.Update, progress: x => Console.WriteLine(x / 3));
|
||||
await mgr.DownloadReleases(updateInfo.ReleasesToApply, x => Console.WriteLine(33 + x / 3));
|
||||
@@ -253,11 +242,11 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> CheckForUpdate(string updateUrl, string appName = null)
|
||||
static async Task<string> CheckForUpdate(string updateUrl, string appName = null)
|
||||
{
|
||||
appName = appName ?? getAppNameFromDirectory();
|
||||
|
||||
this.Log().Info("Fetching update information, downloading from " + updateUrl);
|
||||
Log.Info("Fetching update information, downloading from " + updateUrl);
|
||||
using (var mgr = new UpdateManager(updateUrl, appName)) {
|
||||
var updateInfo = await mgr.CheckForUpdate(intention: UpdaterIntention.Update, progress: x => Console.WriteLine(x));
|
||||
var releaseNotes = updateInfo.FetchReleaseNotes();
|
||||
@@ -275,9 +264,9 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Uninstall(string appName = null)
|
||||
static async Task Uninstall(string appName = null)
|
||||
{
|
||||
this.Log().Info("Starting uninstall for app: " + appName);
|
||||
Log.Info("Starting uninstall for app: " + appName);
|
||||
|
||||
appName = appName ?? getAppNameFromDirectory();
|
||||
using (var mgr = new UpdateManager("", appName)) {
|
||||
@@ -294,172 +283,7 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
public void Releasify(string package, string targetDir = null, string bootstrapperExe = null, string backgroundGif = null, string signingOpts = null, string baseUrl = null, string setupIcon = null, bool generateMsi = true, bool packageAs64Bit = false, string frameworkVersion = null, bool generateDeltas = true)
|
||||
{
|
||||
ensureConsole();
|
||||
|
||||
if (baseUrl != null) {
|
||||
if (!Utility.IsHttpUrl(baseUrl)) {
|
||||
throw new Exception(string.Format("Invalid --baseUrl '{0}'. A base URL must start with http or https and be a valid URI.", baseUrl));
|
||||
}
|
||||
|
||||
if (!baseUrl.EndsWith("/")) {
|
||||
baseUrl += "/";
|
||||
}
|
||||
}
|
||||
|
||||
targetDir = targetDir ?? Path.Combine(".", "Releases");
|
||||
bootstrapperExe = bootstrapperExe ?? Path.Combine(".", "Setup.exe");
|
||||
|
||||
if (!Directory.Exists(targetDir)) {
|
||||
Directory.CreateDirectory(targetDir);
|
||||
}
|
||||
|
||||
if (!File.Exists(bootstrapperExe)) {
|
||||
bootstrapperExe = Path.Combine(
|
||||
AssemblyRuntimeInfo.BaseDirectory,
|
||||
"Setup.exe");
|
||||
}
|
||||
|
||||
if (!File.Exists(bootstrapperExe)) {
|
||||
this.Log().Error("Could not find Bootstrapper EXE. Please place it in the directory next to me, or use the bootstrapperExe command line option.");
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
this.Log().Info("Bootstrapper EXE found at:" + bootstrapperExe);
|
||||
|
||||
// validate that the provided "frameworkVersion" is supported by Setup.exe
|
||||
if (!String.IsNullOrWhiteSpace(frameworkVersion)) {
|
||||
var chkFrameworkResult = Utility.InvokeProcessAsync(bootstrapperExe, "--checkFramework " + frameworkVersion, CancellationToken.None).Result;
|
||||
if (chkFrameworkResult.Item1 != 0) {
|
||||
this.Log().Error($"Unsupported FrameworkVersion: '{frameworkVersion}'. {chkFrameworkResult.Item2}");
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
// copy input package to target output directory
|
||||
var di = new DirectoryInfo(targetDir);
|
||||
File.Copy(package, Path.Combine(di.FullName, Path.GetFileName(package)), true);
|
||||
|
||||
var allNuGetFiles = di.EnumerateFiles()
|
||||
.Where(x => x.Name.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var toProcess = allNuGetFiles.Where(x => !x.Name.Contains("-delta") && !x.Name.Contains("-full"));
|
||||
var processed = new List<string>();
|
||||
|
||||
var releaseFilePath = Path.Combine(di.FullName, "RELEASES");
|
||||
var previousReleases = new List<ReleaseEntry>();
|
||||
if (File.Exists(releaseFilePath)) {
|
||||
previousReleases.AddRange(ReleaseEntry.ParseReleaseFile(File.ReadAllText(releaseFilePath, Encoding.UTF8)));
|
||||
}
|
||||
|
||||
foreach (var file in toProcess) {
|
||||
this.Log().Info("Creating release package: " + file.FullName);
|
||||
|
||||
var rp = new ReleasePackage(file.FullName);
|
||||
rp.CreateReleasePackage(Path.Combine(di.FullName, rp.SuggestedReleaseFileName), contentsPostProcessHook: pkgPath => {
|
||||
|
||||
// create sub executable for all exe's in this package (except Squirrel!)
|
||||
new DirectoryInfo(pkgPath).GetAllFilesRecursively()
|
||||
.Where(x => x.Name.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
.Where(x => !x.Name.Contains("squirrel.exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
.Where(x => Utility.IsFileTopLevelInPackage(x.FullName, pkgPath))
|
||||
.Where(x => Utility.ExecutableUsesWin32Subsystem(x.FullName))
|
||||
.ForEachAsync(x => createExecutableStubForExe(x.FullName))
|
||||
.Wait();
|
||||
|
||||
// copy myself into the package so Squirrel can also be updated
|
||||
// how we find the lib dir is a huge hack here, but 'ReleasePackage' verifies there can only be one of these so it should be fine.
|
||||
var re = new Regex(@"lib[\\\/][^\\\/]*[\\\/]?", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
var libDir = Directory
|
||||
.EnumerateDirectories(pkgPath, "*", SearchOption.AllDirectories)
|
||||
.Where(d => re.IsMatch(d))
|
||||
.OrderBy(d => d.Length)
|
||||
.FirstOrDefault();
|
||||
File.Copy(getUpdateExePath(), Path.Combine(libDir, "Squirrel.exe"));
|
||||
|
||||
// sign all exe's in this package
|
||||
if (signingOpts == null) return;
|
||||
new DirectoryInfo(pkgPath).GetAllFilesRecursively()
|
||||
.Where(x => Utility.FileIsLikelyPEImage(x.Name))
|
||||
.ForEachAsync(async x => {
|
||||
if (isPEFileSigned(x.FullName)) {
|
||||
this.Log().Info("{0} is already signed, skipping", x.FullName);
|
||||
return;
|
||||
}
|
||||
|
||||
this.Log().Info("About to sign {0}", x.FullName);
|
||||
await signPEFile(x.FullName, signingOpts);
|
||||
}, 1)
|
||||
.Wait();
|
||||
});
|
||||
|
||||
processed.Add(rp.ReleasePackageFile);
|
||||
|
||||
var prev = ReleaseEntry.GetPreviousRelease(previousReleases, rp, targetDir);
|
||||
if (prev != null && generateDeltas) {
|
||||
var deltaBuilder = new DeltaPackageBuilder(null);
|
||||
|
||||
var dp = deltaBuilder.CreateDeltaPackage(prev, rp,
|
||||
Path.Combine(di.FullName, rp.SuggestedReleaseFileName.Replace("full", "delta")));
|
||||
processed.Insert(0, dp.InputPackageFile);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var file in toProcess) { File.Delete(file.FullName); }
|
||||
|
||||
var newReleaseEntries = processed
|
||||
.Select(packageFilename => ReleaseEntry.GenerateFromFile(packageFilename, baseUrl))
|
||||
.ToList();
|
||||
var distinctPreviousReleases = previousReleases
|
||||
.Where(x => !newReleaseEntries.Select(e => e.Version).Contains(x.Version));
|
||||
var releaseEntries = distinctPreviousReleases.Concat(newReleaseEntries).ToList();
|
||||
|
||||
ReleaseEntry.WriteReleaseFile(releaseEntries, releaseFilePath);
|
||||
|
||||
var targetSetupExe = Path.Combine(di.FullName, "Setup.exe");
|
||||
var newestFullRelease = releaseEntries.MaxBy(x => x.Version).Where(x => !x.IsDelta).First();
|
||||
|
||||
File.Copy(bootstrapperExe, targetSetupExe, true);
|
||||
var zipPath = createSetupEmbeddedZip(Path.Combine(di.FullName, newestFullRelease.Filename), di.FullName, signingOpts, setupIcon).Result;
|
||||
|
||||
var writeZipToSetup = Utility.FindHelperExecutable("WriteZipToSetup.exe");
|
||||
|
||||
try {
|
||||
string arguments = $"\"{targetSetupExe}\" \"{zipPath}\"";
|
||||
if (!String.IsNullOrWhiteSpace(frameworkVersion)) {
|
||||
arguments += $" --set-required-framework \"{frameworkVersion}\"";
|
||||
}
|
||||
if (!String.IsNullOrWhiteSpace(backgroundGif)) {
|
||||
arguments += $" --set-splash \"{Path.GetFullPath(backgroundGif)}\"";
|
||||
}
|
||||
|
||||
var result = Utility.InvokeProcessAsync(writeZipToSetup, arguments, CancellationToken.None).Result;
|
||||
if (result.Item1 != 0) throw new Exception("Failed to write Zip to Setup.exe!\n\n" + result.Item2);
|
||||
} catch (Exception ex) {
|
||||
this.Log().ErrorException("Failed to update Setup.exe with new Zip file", ex);
|
||||
throw;
|
||||
} finally {
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
|
||||
Utility.Retry(() =>
|
||||
setPEVersionInfoAndIcon(targetSetupExe, new ZipPackage(package), setupIcon).Wait());
|
||||
|
||||
if (signingOpts != null) {
|
||||
signPEFile(targetSetupExe, signingOpts).Wait();
|
||||
}
|
||||
|
||||
if (generateMsi) {
|
||||
createMsiPackage(targetSetupExe, new ZipPackage(package), packageAs64Bit).Wait();
|
||||
|
||||
if (signingOpts != null) {
|
||||
signPEFile(targetSetupExe.Replace(".exe", ".msi"), signingOpts).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Shortcut(string exeName, string shortcutArgs, string processStartArgs, string icon, bool onlyUpdate)
|
||||
static void Shortcut(string exeName, string shortcutArgs, string processStartArgs, string icon, bool onlyUpdate)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(exeName)) {
|
||||
ShowHelp();
|
||||
@@ -475,7 +299,7 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
public void Deshortcut(string exeName, string shortcutArgs)
|
||||
static void Deshortcut(string exeName, string shortcutArgs)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(exeName)) {
|
||||
ShowHelp();
|
||||
@@ -491,7 +315,7 @@ namespace Squirrel.Update
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessStart(string exeName, string arguments, bool shouldWait)
|
||||
static void ProcessStart(string exeName, string arguments, bool shouldWait)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(exeName)) {
|
||||
ShowHelp();
|
||||
@@ -519,7 +343,7 @@ namespace Squirrel.Update
|
||||
|
||||
// Check for the EXE name they want
|
||||
var targetExe = new FileInfo(Path.Combine(latestAppDir, exeName.Replace("%20", " ")));
|
||||
this.Log().Info("Want to launch '{0}'", targetExe);
|
||||
Log.Info("Want to launch '{0}'", targetExe);
|
||||
|
||||
// Check for path canonicalization attacks
|
||||
if (!targetExe.FullName.StartsWith(latestAppDir, StringComparison.Ordinal)) {
|
||||
@@ -527,27 +351,27 @@ namespace Squirrel.Update
|
||||
}
|
||||
|
||||
if (!targetExe.Exists) {
|
||||
this.Log().Error("File {0} doesn't exist in current release", targetExe);
|
||||
Log.Error("File {0} doesn't exist in current release", targetExe);
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
if (shouldWait) waitForParentToExit();
|
||||
|
||||
try {
|
||||
this.Log().Info("About to launch: '{0}': {1}", targetExe.FullName, arguments ?? "");
|
||||
Log.Info("About to launch: '{0}': {1}", targetExe.FullName, arguments ?? "");
|
||||
Process.Start(new ProcessStartInfo(targetExe.FullName, arguments ?? "") { WorkingDirectory = Path.GetDirectoryName(targetExe.FullName) });
|
||||
} catch (Exception ex) {
|
||||
this.Log().ErrorException("Failed to start process", ex);
|
||||
Log.ErrorException("Failed to start process", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowHelp()
|
||||
static void ShowHelp()
|
||||
{
|
||||
ensureConsole();
|
||||
opt.WriteOptionDescriptions();
|
||||
}
|
||||
|
||||
void waitForParentToExit()
|
||||
static void waitForParentToExit()
|
||||
{
|
||||
// Grab a handle the parent process
|
||||
var parentPid = NativeMethods.GetParentProcessId();
|
||||
@@ -557,242 +381,16 @@ namespace Squirrel.Update
|
||||
try {
|
||||
handle = NativeMethods.OpenProcess(ProcessAccess.Synchronize, false, parentPid);
|
||||
if (handle != IntPtr.Zero) {
|
||||
this.Log().Info("About to wait for parent PID {0}", parentPid);
|
||||
Log.Info("About to wait for parent PID {0}", parentPid);
|
||||
NativeMethods.WaitForSingleObject(handle, 0xFFFFFFFF /*INFINITE*/);
|
||||
} else {
|
||||
this.Log().Info("Parent PID {0} no longer valid - ignoring", parentPid);
|
||||
Log.Info("Parent PID {0} no longer valid - ignoring", parentPid);
|
||||
}
|
||||
} finally {
|
||||
if (handle != IntPtr.Zero) NativeMethods.CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
string getUpdateExePath()
|
||||
{
|
||||
//if (selfContained) {
|
||||
// var selfPath = Path.Combine(AssemblyRuntimeInfo.BaseDirectory, "UpdateSelfContained.exe");
|
||||
// if (!File.Exists(selfPath)) {
|
||||
// this.Log().Error("Could not find UpdateSelfContained.exe in base directory. Am I published?");
|
||||
// }
|
||||
// return selfPath;
|
||||
//}
|
||||
return AssemblyRuntimeInfo.EntryExePath;
|
||||
}
|
||||
|
||||
async Task<string> createSetupEmbeddedZip(string fullPackage, string releasesDir, string signingOpts, string setupIcon)
|
||||
{
|
||||
string tempPath;
|
||||
|
||||
this.Log().Info("Building embedded zip file for Setup.exe");
|
||||
using (Utility.WithTempDirectory(out tempPath, null)) {
|
||||
this.ErrorIfThrows(() => {
|
||||
File.Copy(getUpdateExePath(), Path.Combine(tempPath, "Update.exe"));
|
||||
File.Copy(fullPackage, Path.Combine(tempPath, Path.GetFileName(fullPackage)));
|
||||
}, "Failed to write package files to temp dir: " + tempPath);
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(setupIcon)) {
|
||||
this.ErrorIfThrows(() => {
|
||||
File.Copy(setupIcon, Path.Combine(tempPath, "setupIcon.ico"));
|
||||
}, "Failed to write icon to temp dir: " + tempPath);
|
||||
}
|
||||
|
||||
var releases = new[] { ReleaseEntry.GenerateFromFile(fullPackage) };
|
||||
ReleaseEntry.WriteReleaseFile(releases, Path.Combine(tempPath, "RELEASES"));
|
||||
|
||||
var target = Path.GetTempFileName();
|
||||
File.Delete(target);
|
||||
|
||||
// Sign Update.exe so that virus scanners don't think we're
|
||||
// pulling one over on them
|
||||
if (signingOpts != null) {
|
||||
var di = new DirectoryInfo(tempPath);
|
||||
|
||||
var files = di.EnumerateFiles()
|
||||
.Where(x => x.Name.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
.Select(x => x.FullName);
|
||||
|
||||
await files.ForEachAsync(x => signPEFile(x, signingOpts));
|
||||
}
|
||||
|
||||
this.ErrorIfThrows(() =>
|
||||
ZipFile.CreateFromDirectory(tempPath, target, CompressionLevel.Optimal, false),
|
||||
"Failed to create Zip file from directory: " + tempPath);
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
static async Task signPEFile(string exePath, string signingOpts)
|
||||
{
|
||||
// Try to find SignTool.exe
|
||||
var exe = @".\signtool.exe";
|
||||
if (!File.Exists(exe)) {
|
||||
exe = Path.Combine(AssemblyRuntimeInfo.BaseDirectory, "signtool.exe");
|
||||
|
||||
// Run down PATH and hope for the best
|
||||
if (!File.Exists(exe)) exe = "signtool.exe";
|
||||
}
|
||||
|
||||
var processResult = await Utility.InvokeProcessAsync(exe,
|
||||
String.Format("sign {0} \"{1}\"", signingOpts, exePath), CancellationToken.None);
|
||||
|
||||
if (processResult.Item1 != 0) {
|
||||
var optsWithPasswordHidden = new Regex(@"/p\s+\w+").Replace(signingOpts, "/p ********");
|
||||
var msg = String.Format("Failed to sign, command invoked was: '{0} sign {1} {2}'",
|
||||
exe, optsWithPasswordHidden, exePath);
|
||||
|
||||
throw new Exception(msg);
|
||||
} else {
|
||||
Console.WriteLine(processResult.Item2);
|
||||
}
|
||||
}
|
||||
bool isPEFileSigned(string path)
|
||||
{
|
||||
#if MONO
|
||||
return Path.GetExtension(path).Equals(".exe", StringComparison.OrdinalIgnoreCase);
|
||||
#else
|
||||
try {
|
||||
return AuthenticodeTools.IsTrusted(path);
|
||||
} catch (Exception ex) {
|
||||
this.Log().ErrorException("Failed to determine signing status for " + path, ex);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
async Task createExecutableStubForExe(string fullName)
|
||||
{
|
||||
var exe = Utility.FindHelperExecutable(@"StubExecutable.exe");
|
||||
|
||||
var target = Path.Combine(
|
||||
Path.GetDirectoryName(fullName),
|
||||
Path.GetFileNameWithoutExtension(fullName) + "_ExecutionStub.exe");
|
||||
|
||||
await Utility.CopyToAsync(exe, target);
|
||||
|
||||
await Utility.InvokeProcessAsync(
|
||||
Utility.FindHelperExecutable("WriteZipToSetup.exe"),
|
||||
String.Format("--copy-stub-resources \"{0}\" \"{1}\"", fullName, target),
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
static async Task setPEVersionInfoAndIcon(string exePath, IPackage package, string iconPath = null)
|
||||
{
|
||||
var realExePath = Path.GetFullPath(exePath);
|
||||
var company = String.Join(",", package.Authors);
|
||||
var verStrings = new Dictionary<string, string>() {
|
||||
{ "CompanyName", company },
|
||||
{ "LegalCopyright", package.Copyright ?? "Copyright © " + DateTime.Now.Year.ToString() + " " + company },
|
||||
{ "FileDescription", package.Summary ?? package.Description ?? "Installer for " + package.Id },
|
||||
{ "ProductName", package.Description ?? package.Summary ?? package.Id },
|
||||
};
|
||||
|
||||
var args = verStrings.Aggregate(new StringBuilder("\"" + realExePath + "\""), (acc, x) => { acc.AppendFormat(" --set-version-string \"{0}\" \"{1}\"", x.Key, x.Value); return acc; });
|
||||
args.AppendFormat(" --set-file-version {0} --set-product-version {0}", package.Version.ToString());
|
||||
if (iconPath != null) {
|
||||
args.AppendFormat(" --set-icon \"{0}\"", Path.GetFullPath(iconPath));
|
||||
}
|
||||
|
||||
// Try to find rcedit.exe
|
||||
string exe = Utility.FindHelperExecutable("rcedit.exe");
|
||||
|
||||
var processResult = await Utility.InvokeProcessAsync(exe, args.ToString(), CancellationToken.None);
|
||||
|
||||
if (processResult.Item1 != 0) {
|
||||
var msg = String.Format(
|
||||
"Failed to modify resources, command invoked was: '{0} {1}'\n\nOutput was:\n{2}",
|
||||
exe, args, processResult.Item2);
|
||||
|
||||
throw new Exception(msg);
|
||||
} else {
|
||||
Console.WriteLine(processResult.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
static async Task createMsiPackage(string setupExe, IPackage package, bool packageAs64Bit)
|
||||
{
|
||||
var pathToWix = pathToWixTools();
|
||||
var setupExeDir = Path.GetDirectoryName(setupExe);
|
||||
var company = String.Join(",", package.Authors);
|
||||
|
||||
var culture = CultureInfo.GetCultureInfo(package.Language ?? "").TextInfo.ANSICodePage;
|
||||
|
||||
|
||||
var templateText = File.ReadAllText(Path.Combine(pathToWix, "template.wxs"));
|
||||
var templateData = new Dictionary<string, string> {
|
||||
{ "Id", package.Id },
|
||||
{ "Title", package.Title },
|
||||
{ "Author", company },
|
||||
{ "Version", Regex.Replace(package.Version.ToString(), @"-.*$", "") },
|
||||
{ "Summary", package.Summary ?? package.Description ?? package.Id },
|
||||
{ "Codepage", $"{culture}" },
|
||||
{ "Platform", packageAs64Bit ? "x64" : "x86" },
|
||||
{ "ProgramFilesFolder", packageAs64Bit ? "ProgramFiles64Folder" : "ProgramFilesFolder" },
|
||||
{ "Win64YesNo", packageAs64Bit ? "yes" : "no" }
|
||||
};
|
||||
|
||||
// NB: We need some GUIDs that are based on the package ID, but unique (i.e.
|
||||
// "Unique but consistent").
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
templateData[String.Format("IdAsGuid{0}", i)] = Utility.CreateGuidFromHash(String.Format("{0}:{1}", package.Id, i)).ToString();
|
||||
}
|
||||
|
||||
var templateResult = CopStache.Render(templateText, templateData);
|
||||
|
||||
var wxsTarget = Path.Combine(setupExeDir, "Setup.wxs");
|
||||
File.WriteAllText(wxsTarget, templateResult, Encoding.UTF8);
|
||||
|
||||
var candleParams = String.Format("-nologo -ext WixNetFxExtension -out \"{0}\" \"{1}\"", wxsTarget.Replace(".wxs", ".wixobj"), wxsTarget);
|
||||
var processResult = await Utility.InvokeProcessAsync(
|
||||
Path.Combine(pathToWix, "candle.exe"), candleParams, CancellationToken.None, setupExeDir);
|
||||
|
||||
if (processResult.Item1 != 0) {
|
||||
var msg = String.Format(
|
||||
"Failed to compile WiX template, command invoked was: '{0} {1}'\n\nOutput was:\n{2}",
|
||||
"candle.exe", candleParams, processResult.Item2);
|
||||
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
var lightParams = String.Format("-ext WixNetFxExtension -sval -out \"{0}\" \"{1}\"", wxsTarget.Replace(".wxs", ".msi"), wxsTarget.Replace(".wxs", ".wixobj"));
|
||||
processResult = await Utility.InvokeProcessAsync(
|
||||
Path.Combine(pathToWix, "light.exe"), lightParams, CancellationToken.None, setupExeDir);
|
||||
|
||||
if (processResult.Item1 != 0) {
|
||||
var msg = String.Format(
|
||||
"Failed to link WiX template, command invoked was: '{0} {1}'\n\nOutput was:\n{2}",
|
||||
"light.exe", lightParams, processResult.Item2);
|
||||
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
var toDelete = new[] {
|
||||
wxsTarget,
|
||||
wxsTarget.Replace(".wxs", ".wixobj"),
|
||||
wxsTarget.Replace(".wxs", ".wixpdb"),
|
||||
};
|
||||
|
||||
await Utility.ForEachAsync(toDelete, x => Utility.DeleteFileHarder(x));
|
||||
}
|
||||
|
||||
static string pathToWixTools()
|
||||
{
|
||||
var ourPath = AssemblyRuntimeInfo.BaseDirectory;
|
||||
|
||||
// Same Directory? (i.e. released)
|
||||
if (File.Exists(Path.Combine(ourPath, "candle.exe"))) {
|
||||
return ourPath;
|
||||
}
|
||||
|
||||
// Debug Mode (i.e. in vendor)
|
||||
var debugPath = Path.Combine(ourPath, "..", "..", "..", "vendor", "wix", "candle.exe");
|
||||
if (File.Exists(debugPath)) {
|
||||
return Path.GetFullPath(debugPath);
|
||||
}
|
||||
|
||||
throw new Exception("WiX tools can't be found");
|
||||
}
|
||||
|
||||
static string getAppNameFromDirectory(string path = null)
|
||||
{
|
||||
path = path ?? AssemblyRuntimeInfo.BaseDirectory;
|
||||
|
||||
@@ -3,28 +3,24 @@ using System;
|
||||
|
||||
namespace Squirrel.Update
|
||||
{
|
||||
enum UpdateAction
|
||||
{
|
||||
Unset = 0, Install, Uninstall, Download, Update, Shortcut,
|
||||
Deshortcut, ProcessStart, UpdateSelf, CheckForUpdate
|
||||
}
|
||||
|
||||
internal class StartupOption
|
||||
{
|
||||
private readonly OptionSet optionSet;
|
||||
|
||||
internal bool silentInstall { get; private set; } = false;
|
||||
internal UpdateAction updateAction { get; private set; } = default(UpdateAction);
|
||||
internal string target { get; private set; } = default(string);
|
||||
internal string releaseDir { get; private set; } = default(string);
|
||||
internal string bootstrapperExe { get; private set; } = default(string);
|
||||
internal string backgroundGif { get; private set; } = default(string);
|
||||
internal string signingParameters { get; private set; } = default(string);
|
||||
internal string baseUrl { get; private set; } = default(string);
|
||||
//internal string baseUrl { get; private set; } = default(string);
|
||||
internal string processStart { get; private set; } = default(string);
|
||||
internal string processStartArgs { get; private set; } = default(string);
|
||||
internal string setupIcon { get; private set; } = default(string);
|
||||
internal string icon { get; private set; } = default(string);
|
||||
internal string shortcutArgs { get; private set; } = default(string);
|
||||
internal string frameworkVersion { get; private set; }
|
||||
internal bool shouldWait { get; private set; } = false;
|
||||
internal bool noMsi { get; private set; } = (Environment.OSVersion.Platform != PlatformID.Win32NT); // NB: WiX doesn't work under Mono / Wine
|
||||
internal bool packageAs64Bit { get; private set; } = false;
|
||||
internal bool noDelta { get; private set; } = false;
|
||||
internal bool onlyUpdateShortcuts { get; private set; } = false;
|
||||
|
||||
public StartupOption(string[] args)
|
||||
@@ -44,7 +40,6 @@ namespace Squirrel.Update
|
||||
{ "download=", "Download the releases specified by the URL and write new results to stdout as JSON", v => { updateAction = UpdateAction.Download; target = v; } },
|
||||
{ "checkForUpdate=", "Check for one available update and writes new results to stdout as JSON", v => { updateAction = UpdateAction.CheckForUpdate; target = v; } },
|
||||
{ "update=", "Update the application to the latest remote version specified by URL", v => { updateAction = UpdateAction.Update; target = v; } },
|
||||
{ "releasify=", "Update or generate a releases directory with a given NuGet package", v => { updateAction = UpdateAction.Releasify; target = v; } },
|
||||
{ "createShortcut=", "Create a shortcut for the given executable name", v => { updateAction = UpdateAction.Shortcut; target = v; } },
|
||||
{ "removeShortcut=", "Remove a shortcut for the given executable name", v => { updateAction = UpdateAction.Deshortcut; target = v; } },
|
||||
{ "updateSelf=", "Copy the currently executing Update.exe into the default location", v => { updateAction = UpdateAction.UpdateSelf; target = v; } },
|
||||
@@ -53,29 +48,16 @@ namespace Squirrel.Update
|
||||
"",
|
||||
"Options:",
|
||||
{ "h|?|help", "Display Help and exit", _ => {} },
|
||||
{ "r=|releaseDir=", "Path to a release directory to use with releasify", v => releaseDir = v},
|
||||
{ "bootstrapperExe=", "Path to the Setup.exe to use as a template", v => bootstrapperExe = v},
|
||||
{ "g=|loadingGif=|splashImage=", "Path to an animated GIF to be displayed during installation", v => backgroundGif = v},
|
||||
{ "i=|icon", "Path to an ICO file that will be used for icon shortcuts", v => icon = v},
|
||||
{ "setupIcon=", "Path to an ICO file that will be used for the Setup executable's icon", v => setupIcon = v},
|
||||
{ "n=|signWithParams=", "Sign the installer via SignTool.exe with the parameters given", v => signingParameters = v},
|
||||
{ "s|silent", "Silent install", _ => silentInstall = true},
|
||||
{ "b=|baseUrl=", "Provides a base URL to prefix the RELEASES file packages with", v => baseUrl = v, true},
|
||||
//{ "b=|baseUrl=", "Provides a base URL to prefix the RELEASES file packages with", v => baseUrl = v, true},
|
||||
{ "a=|process-start-args=", "Arguments that will be used when starting executable", v => processStartArgs = v, true},
|
||||
{ "l=|shortcut-locations=", "Comma-separated string of shortcut locations, e.g. 'Desktop,StartMenu'", v => shortcutArgs = v},
|
||||
{ "no-msi", "Don't generate an MSI package", v => noMsi = true},
|
||||
{ "no-delta", "Don't generate delta packages to save time", v => noDelta = true},
|
||||
{ "framework-version=", "Set the required .NET framework version, e.g. net461", v => frameworkVersion = v },
|
||||
{ "msi-win64", "Mark the MSI as 64-bit, which is useful in Enterprise deployment scenarios", _ => packageAs64Bit = true},
|
||||
{ "updateOnly", "Update shortcuts that already exist, rather than creating new ones", _ => onlyUpdateShortcuts = true},
|
||||
};
|
||||
|
||||
opts.Parse(args);
|
||||
|
||||
// NB: setupIcon and icon are just aliases for compatibility
|
||||
// reasons, because of a dumb breaking rename I made in 1.0.1
|
||||
setupIcon = setupIcon ?? icon;
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<LangVersion>9</LangVersion>
|
||||
<RootNamespace>Squirrel.Update</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>squirrel.ico</ApplicationIcon>
|
||||
<NoWarn>CA1416</NoWarn>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<LangVersion>9</LangVersion>
|
||||
<RootNamespace>Squirrel.Update</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>..\..\squirrel.ico</ApplicationIcon>
|
||||
<NoWarn>CA1416</NoWarn>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
|
||||
<!-- Publish -->
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
|
||||
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- If update.ico exists in project directory it will be used instead of the squirrel icon -->
|
||||
<PropertyGroup Condition="Exists('update.ico')">
|
||||
<ApplicationIcon>update.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<!-- If update.ico exists in project directory it will be used instead of the squirrel icon -->
|
||||
<PropertyGroup Condition="Exists('update.ico')">
|
||||
<ApplicationIcon>update.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Squirrel\Squirrel.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 137 KiB |
Reference in New Issue
Block a user