mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Merge branch 'master' into doc-vs-packing
This commit is contained in:
@@ -22,6 +22,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionLevel", "SolutionLe
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\SolutionAssemblyInfo.cs = src\SolutionAssemblyInfo.cs
|
||||
src\Squirrel.nuspec = src\Squirrel.nuspec
|
||||
vendor\wix\template.wxs = vendor\wix\template.wxs
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "vendor\nuget\src\Core\Core.csproj", "{F879F274-EFA0-4157-8404-33A19B4E6AEC}"
|
||||
|
||||
BIN
docs/artwork/Squirrel-Logo-Square.png
Normal file
BIN
docs/artwork/Squirrel-Logo-Square.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
13
docs/faq.md
13
docs/faq.md
@@ -11,6 +11,14 @@ Frequently Asked Questions for Squirrel.Windows, organized by area below.
|
||||
Yes, you can package a non-c# application in the same manner as described in the Getting Started guide. For additional customization, see [custom squirrel events for non-c# apps](using/custom-squirrel-events-non-CS.md).
|
||||
1. **How do I migrate a ClickOnce app to Squirrel?**
|
||||
You may want to look into the [ClickOnceToSquirrelMigrator](https://github.com/flagbug/ClickOnceToSquirrelMigrator) migration helper.
|
||||
1. **How can I determine if my app is a Squirrel app? I provide a squirrel and non-squirrel install version and want to know which is running.**
|
||||
You can check for the `Update.exe` in the parent directory to determine if the app is using Squirrel ([see #574](https://github.com/Squirrel/Squirrel.Windows/issues/574#issuecomment-176043311)).
|
||||
|
||||
```
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
var updateDotExe = Path.Combine(Path.GetDirectoryName(assembly.Location), '..', 'Update.exe');
|
||||
var isInstalled = File.Exists(updateDotExe);
|
||||
```
|
||||
|
||||
## Packaging
|
||||
|
||||
@@ -43,7 +51,10 @@ This program is blocked by group policy. For more information, contact your syst
|
||||
The best course of action is to request that executables for Squirrel and your application be whitelisted by your corporate overlords.
|
||||
4. **No Shortcuts are Created for my Application**
|
||||
Verify that the NuGet Package Metadata `id` property doesn't have a [space or \[dot\]](https://github.com/Squirrel/Squirrel.Windows/issues/530) in it.
|
||||
|
||||
5. **Can I use a different name for the `Setup.exe` install application?**
|
||||
Yes, you can rename the `Setup.exe` to what ever you wish (e.g., `MyAppSetup.exe`) ([see #611](https://github.com/Squirrel/Squirrel.Windows/issues/611))
|
||||
6. **Virus scanner is returning false positives on `MyApp.exe` or `Update.exe`. What can I do?**
|
||||
[Application Signing](using/application-signing.md) will help. In addition, you can submit false positives to the various antivirus authors (e.g., [Symantec](https://submit.symantec.com/false_positive/), [Microsoft](https://www.microsoft.com/security/portal/Submission/Submit.aspx), [AVG](http://www.avg.com/submit-sample), [Comodo](https://www.comodo.com/home/internet-security/submit.php), [McAfee](https://support.mcafeesaas.com/MCAFEE/_cs/AnswerDetail.aspx?aid=65), [List of Submission Locations](http://www.techsupportalert.com/content/how-report-malware-or-false-positives-multiple-antivirus-vendors.htm), [see #218](https://github.com/Squirrel/Squirrel.Windows/issues/218#issuecomment-166406180)).
|
||||
|
||||
## Updating
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ The first step in preparing the application for distribution is to build the app
|
||||
|
||||
Squirrel uses [NuGet](https://www.NuGet.org/) for bundling application files and various application properties (e.g., application name, version, description) in a single release package.
|
||||
|
||||
Section [NuGet Package Metadata](../using/nuget-package-metadata.md) provides additional details on using NuGet and `.nuspec` files to automate the packing of your application. We will be going through the process using the [NuGet Package Explorer](https://npe.codeplex.com/) to manually create a NuGet package.
|
||||
Section [NuGet Package Metadata](../using/nuget-package-metadata.md) provides additional details on using NuGet and `.nuspec` files to automate the packing of your application. We will be going through the process using the [NuGet Package Explorer](https://github.com/NuGetPackageExplorer/NuGetPackageExplorer) to manually create a NuGet package.
|
||||
|
||||
1. **Creating a New NuGet Package** - the first step is to create a new NuGet package.
|
||||
2. **Edit Metadata** - update package metadata for MyApp.
|
||||
@@ -76,4 +76,4 @@ The `Squirrel --releasify` command completes the following:
|
||||
|
||||
---
|
||||
| Previous: [1. Integrating](1-integrating.md) | Next: [3. Distributing](3-distributing.md)|
|
||||
|:---|:---|
|
||||
|:---|:---|
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
| [docs](..) / [using](.) / application-signing.md
|
||||
|:---|
|
||||
|
||||
|
||||
| [docs](..) / [using](.) / application-signing.md
|
||||
|:---|
|
||||
|
||||
|
||||
# Application Signing
|
||||
|
||||
Signing your installer with a valid code signing certificate is one of the most important things that you need to do for production apps. Both IE SmartScreen as well as virus scanning software will give a significant amount of "points" to apps that are signed correctly, preventing your users from getting scary dialogs.
|
||||
|
||||
Acquire a code-signing certificate - it's recommended to get a Windows Error Reporting-compatible certificate, see this MSDN article for more information, then pass the -n parameter, which are the parameters you would pass to `signtool.exe sign` to sign the app.
|
||||
|
||||
Squirrel makes signing easy, as it signs all of your application's executables *as well* as the final generated Setup.exe.
|
||||
|
||||
An example invocation including both of these features would be something like:
|
||||
|
||||
~~~powershell
|
||||
PM> Squirrel --releasify MyApp.1.0.0.nupkg -n "/a /f CodeCert.pfx /p MySecretCertPassword"
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
## See Also
|
||||
* [Squirrel Command Line](squirrel-command-line.md) - command line options for `Squirrel --releasify`
|
||||
|
||||
|
||||
---
|
||||
| Return: [Table of Contents](../readme.md) |
|
||||
|----|
|
||||
|
||||
|
||||
|
||||
|
||||
Signing your installer with a valid code signing certificate is one of the most important things that you need to do for production apps. Both IE SmartScreen as well as virus scanning software will give a significant amount of "points" to apps that are signed correctly, preventing your users from getting scary dialogs.
|
||||
|
||||
Acquire a code-signing certificate - it's recommended to get a Windows Error Reporting-compatible certificate, see this [MSDN article](https://msdn.microsoft.com/library/windows/hardware/hh801887.aspx) for more information, then pass the -n parameter, which are the parameters you would pass to `signtool.exe sign` to sign the app.
|
||||
|
||||
Squirrel makes signing easy, as it signs all of your application's executables *as well* as the final generated Setup.exe.
|
||||
|
||||
An example invocation including both of these features would be something like:
|
||||
|
||||
~~~powershell
|
||||
PM> Squirrel --releasify MyApp.1.0.0.nupkg -n "/a /f CodeCert.pfx /p MySecretCertPassword"
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
## See Also
|
||||
* [Squirrel Command Line](squirrel-command-line.md) - command line options for `Squirrel --releasify`
|
||||
|
||||
|
||||
---
|
||||
| Return: [Table of Contents](../readme.md) |
|
||||
|----|
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ using (var mgr = UpdateManager.GitHubUpdateManager("https://github.com/myuser/my
|
||||
}
|
||||
~~~
|
||||
|
||||
**Important:** Make sure your url doesn't end in a forward slash ("/"). Adding the trailing forward slash will cause it to fail with a 404 error ([see #641](https://github.com/Squirrel/Squirrel.Windows/issues/641#issuecomment-201478324)).
|
||||
|
||||
**Tip:** You can also specify that the update manager should use `prerelease` for updating (see method signature for details).
|
||||
|
||||
**Source:** See [Issue #442](https://github.com/Squirrel/Squirrel.Windows/issues/442) for more information.
|
||||
|
||||
@@ -23,7 +23,7 @@ The installation root really only needs to consist of two types of folders:
|
||||
* **App Folders** - the "installed" application files for a given version of MyApp.
|
||||
|
||||
```
|
||||
\%LocalAppData%
|
||||
\%LocalAppData%\MyApp
|
||||
\packages
|
||||
MyApp-1.0.0.nupkg
|
||||
MyApp-1.0.1-delta.nupkg
|
||||
|
||||
@@ -11,6 +11,7 @@ The following tools can simplify and/or automate the packaging process.
|
||||
* [Visual Studio Build Packaging](visual-studio-packaging.md) - integrating NuGet packaging into your visual studio build process.
|
||||
* [OctoPack](octopack.md) - steps to use OctoPack to build the source NuGet package to provide to `squirrel --releasify`.
|
||||
* [Auto.Squirrel Package Manager](https://github.com/tenacious/Auto.Squirrel) - tool to fully automatize the application deploy, from build to upload the updated files.
|
||||
* [Paket](http://fsprojects.github.io/Paket/template-files.html) - dependency manager for .NET and mono projects, which is designed to work well with NuGet packages and also enables referencing files directly from Git repositories or any HTTP resource.
|
||||
|
||||
|
||||
## See Also
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<owners>Paul Betts</owners>
|
||||
<licenseUrl>https://github.com/squirrel/Squirrel.Windows/blob/master/COPYING</licenseUrl>
|
||||
<projectUrl>https://github.com/squirrel/Squirrel.Windows</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/Squirrel/Squirrel.Windows/master/docs/artwork/Squirrel-Logo-Square.png</iconUrl>
|
||||
|
||||
<dependencies>
|
||||
<dependency id="DeltaCompressionDotNet" version="1.0.0" />
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Squirrel
|
||||
var msDelta = new MsDeltaCompression();
|
||||
try {
|
||||
msDelta.CreateDelta(baseFileListing[relativePath], targetFile.FullName, targetFile.FullName + ".diff");
|
||||
} catch (Win32Exception) {
|
||||
} catch (Exception) {
|
||||
this.Log().Warn("We couldn't create a delta for {0}, attempting to create bsdiff", targetFile.Name);
|
||||
|
||||
var of = default(FileStream);
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Squirrel
|
||||
using (Utility.WithTempDirectory(out tempPath, null)) {
|
||||
var tempDir = new DirectoryInfo(tempPath);
|
||||
|
||||
extractZipDecoded(InputPackageFile, tempPath);
|
||||
ExtractZipDecoded(InputPackageFile, tempPath);
|
||||
|
||||
this.Log().Info("Extracting dependent packages: [{0}]", String.Join(",", dependencies.Select(x => x.Id)));
|
||||
extractDependentPackages(dependencies, tempDir, targetFramework);
|
||||
@@ -158,7 +158,7 @@ namespace Squirrel
|
||||
|
||||
// nupkg file %-encodes zip entry names. This method decodes entry names before writing to disk.
|
||||
// We must do this, or PathTooLongException may be thrown for some unicode entry names.
|
||||
void extractZipDecoded(string zipFilePath, string outFolder)
|
||||
public static void ExtractZipDecoded(string zipFilePath, string outFolder, string directoryFilter = null)
|
||||
{
|
||||
var zf = new ZipFile(zipFilePath);
|
||||
|
||||
@@ -172,12 +172,15 @@ namespace Squirrel
|
||||
|
||||
var fullZipToPath = Path.Combine(outFolder, entryFileName);
|
||||
var directoryName = Path.GetDirectoryName(fullZipToPath);
|
||||
if (directoryName.Length > 0) {
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
var directoryFilter_ = new NameFilter(directoryFilter);
|
||||
if (directoryFilter_.IsMatch(directoryName)) {
|
||||
if (directoryName.Length > 0) {
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
using (FileStream streamWriter = File.Create(fullZipToPath)) {
|
||||
StreamUtils.Copy(zipStream, streamWriter, buffer);
|
||||
using (FileStream streamWriter = File.Create(fullZipToPath)) {
|
||||
StreamUtils.Copy(zipStream, streamWriter, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
zf.Close();
|
||||
|
||||
@@ -289,7 +289,6 @@ namespace Squirrel
|
||||
Task<string> installPackageToAppDir(UpdateInfo updateInfo, ReleaseEntry release)
|
||||
{
|
||||
return Task.Run(async () => {
|
||||
var zipper = new FastZip();
|
||||
var target = getDirectoryForRelease(release.Version);
|
||||
|
||||
// NB: This might happen if we got killed partially through applying the release
|
||||
@@ -301,9 +300,8 @@ namespace Squirrel
|
||||
target.Create();
|
||||
|
||||
this.Log().Info("Writing files to app directory: {0}", target.FullName);
|
||||
zipper.ExtractZip(
|
||||
Path.Combine(updateInfo.PackageDirectory, release.Filename),
|
||||
target.FullName, FastZip.Overwrite.Always, (o) => true, null, @"lib", true);
|
||||
ReleasePackage.ExtractZipDecoded(Path.Combine(updateInfo.PackageDirectory, release.Filename),
|
||||
target.FullName, @"lib");
|
||||
|
||||
// Move all of the files out of the lib/ dirs in the NuGet package
|
||||
// into our target App directory.
|
||||
|
||||
@@ -584,6 +584,82 @@ namespace Squirrel
|
||||
MOVEFILE_CREATE_HARDLINK = 0x00000010,
|
||||
MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
|
||||
}
|
||||
|
||||
public static Guid CreateGuidFromHash(string text)
|
||||
{
|
||||
return CreateGuidFromHash(text, Utility.IsoOidNamespace);
|
||||
}
|
||||
|
||||
|
||||
public static Guid CreateGuidFromHash(string text, Guid namespaceId)
|
||||
{
|
||||
// convert the name to a sequence of octets (as defined by the standard
|
||||
// or conventions of its namespace) (step 3)
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(text);
|
||||
|
||||
// convert the namespace UUID to network order (step 3)
|
||||
byte[] namespaceBytes = namespaceId.ToByteArray();
|
||||
SwapByteOrder(namespaceBytes);
|
||||
|
||||
// comput the hash of the name space ID concatenated with the
|
||||
// name (step 4)
|
||||
byte[] hash;
|
||||
using (var algorithm = SHA1.Create()) {
|
||||
algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0);
|
||||
algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length);
|
||||
hash = algorithm.Hash;
|
||||
}
|
||||
|
||||
// most bytes from the hash are copied straight to the bytes of
|
||||
// the new GUID (steps 5-7, 9, 11-12)
|
||||
var newGuid = new byte[16];
|
||||
Array.Copy(hash, 0, newGuid, 0, 16);
|
||||
|
||||
// set the four most significant bits (bits 12 through 15) of
|
||||
// the time_hi_and_version field to the appropriate 4-bit
|
||||
// version number from Section 4.1.3 (step 8)
|
||||
newGuid[6] = (byte)((newGuid[6] & 0x0F) | (5 << 4));
|
||||
|
||||
// set the two most significant bits (bits 6 and 7) of the
|
||||
// clock_seq_hi_and_reserved to zero and one, respectively
|
||||
// (step 10)
|
||||
newGuid[8] = (byte)((newGuid[8] & 0x3F) | 0x80);
|
||||
|
||||
// convert the resulting UUID to local byte order (step 13)
|
||||
SwapByteOrder(newGuid);
|
||||
return new Guid(newGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for fully-qualified domain names (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for URLs (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for ISO OIDs (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
// Converts a GUID (expressed as a byte array) to/from network order (MSB-first).
|
||||
static void SwapByteOrder(byte[] guid)
|
||||
{
|
||||
SwapBytes(guid, 0, 3);
|
||||
SwapBytes(guid, 1, 2);
|
||||
SwapBytes(guid, 4, 5);
|
||||
SwapBytes(guid, 6, 7);
|
||||
}
|
||||
|
||||
static void SwapBytes(byte[] guid, int left, int right)
|
||||
{
|
||||
byte temp = guid[left];
|
||||
guid[left] = guid[right];
|
||||
guid[right] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static unsafe class UnsafeUtility
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace Squirrel.Update
|
||||
this.Log().Warn("Install path {0} already exists, burning it to the ground", mgr.RootAppDirectory);
|
||||
|
||||
mgr.KillAllExecutablesBelongingToPackage();
|
||||
await Task.Delay(250);
|
||||
await Task.Delay(500);
|
||||
|
||||
await this.ErrorIfThrows(() => Utility.DeleteDirectory(mgr.RootAppDirectory),
|
||||
"Failed to remove existing directory on full install, is the app still running???");
|
||||
@@ -655,13 +655,21 @@ namespace Squirrel.Update
|
||||
var company = String.Join(",", package.Authors);
|
||||
|
||||
var templateText = File.ReadAllText(Path.Combine(pathToWix, "template.wxs"));
|
||||
var templateResult = CopStache.Render(templateText, new Dictionary<string, string> {
|
||||
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 },
|
||||
});
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -111,6 +111,36 @@ namespace Squirrel.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SpecialCharactersInitialInstallTest()
|
||||
{
|
||||
string tempDir;
|
||||
using (Utility.WithTempDirectory(out tempDir))
|
||||
{
|
||||
var remotePackageDir = Directory.CreateDirectory(Path.Combine(tempDir, "remotePackages"));
|
||||
var localAppDir = Path.Combine(tempDir, "theApp");
|
||||
|
||||
new[] {
|
||||
"SpecialCharacters-0.1.0-full.nupkg",
|
||||
}.ForEach(x => File.Copy(IntegrationTestHelper.GetPath("fixtures", x), Path.Combine(remotePackageDir.FullName, x)));
|
||||
|
||||
using (var fixture = new UpdateManager(remotePackageDir.FullName, "theApp", tempDir))
|
||||
{
|
||||
await fixture.FullInstall();
|
||||
}
|
||||
|
||||
var releasePath = Path.Combine(localAppDir, "packages", "RELEASES");
|
||||
File.Exists(releasePath).ShouldBeTrue();
|
||||
|
||||
var entries = ReleaseEntry.ParseReleaseFile(File.ReadAllText(releasePath, Encoding.UTF8));
|
||||
entries.Count().ShouldEqual(1);
|
||||
|
||||
new[] {
|
||||
"file space name.txt"
|
||||
}.ForEach(x => File.Exists(Path.Combine(localAppDir, "app-0.1.0", x)).ShouldBeTrue());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WhenBothFilesAreInSyncNoUpdatesAreApplied()
|
||||
{
|
||||
|
||||
BIN
test/fixtures/SpecialCharacters-0.1.0-full.nupkg
vendored
Normal file
BIN
test/fixtures/SpecialCharacters-0.1.0-full.nupkg
vendored
Normal file
Binary file not shown.
6
vendor/wix/template.wxs
vendored
6
vendor/wix/template.wxs
vendored
@@ -1,5 +1,5 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" xmlns:netfx="http://schemas.microsoft.com/wix/NetFxExtension">
|
||||
<Product Id="*" Name="{{Title}} Machine-Wide Installer" Language="1033" Version="{{Version}}" UpgradeCode="{{RandomGuid}}" Manufacturer="{{Author}}">
|
||||
<Product Id="*" Name="{{Title}} Machine-Wide Installer" Language="1033" Version="{{Version}}" UpgradeCode="{{IdAsGuid1}}" Manufacturer="{{Author}}">
|
||||
|
||||
<Package Description="#Description" Comments="Comments" InstallerVersion="200" Compressed="yes"/>
|
||||
<Media Id="1" Cabinet="contents.cab" EmbedCab="yes" CompressionLevel="high"/>
|
||||
@@ -17,13 +17,13 @@
|
||||
</Directory>
|
||||
|
||||
<DirectoryRef Id="APPLICATIONROOTDIRECTORY">
|
||||
<Component Id="{{Id}}.exe" Guid="{{RandomGuid}}">
|
||||
<Component Id="{{Id}}.exe" Guid="{{IdAsGuid2}}">
|
||||
<File Id="{{Id}}.exe" Name="{{Id}}.exe" Source="./Setup.exe" KeyPath="yes" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<DirectoryRef Id="TARGETDIR">
|
||||
<Component Id="RegistryEntries" Guid="{{RandomGuid}}">
|
||||
<Component Id="RegistryEntries" Guid="{{IdAsGuid3}}">
|
||||
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Run">
|
||||
<RegistryValue Type="expandable" Name="{{Id}}MachineInstaller" Value="%ProgramFiles%\{{Title}} Installer\{{Id}}.exe --checkInstall" />
|
||||
</RegistryKey>
|
||||
|
||||
Reference in New Issue
Block a user