Merge branch 'master' into doc-vs-packing

This commit is contained in:
Ken Bailey
2016-04-12 13:44:03 -06:00
17 changed files with 182 additions and 51 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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