diff --git a/Fake.sln b/Fake.sln index d817991c01a..be03269ca1d 100644 --- a/Fake.sln +++ b/Fake.sln @@ -170,6 +170,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Tools.Octo", "src\app\ EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Core.Vault", "src\app\Fake.Core.Vault\Fake.Core.Vault.fsproj", "{AAAF92C5-C40D-40B8-84BA-137DF0E98B56}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Installer.Squirrel", "src\app\Fake.Installer.Squirrel\Fake.Installer.Squirrel.fsproj", "{3DEF2E95-4BF8-413F-930E-32103A39364A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1056,6 +1058,18 @@ Global {AAAF92C5-C40D-40B8-84BA-137DF0E98B56}.Release|x64.Build.0 = Release|Any CPU {AAAF92C5-C40D-40B8-84BA-137DF0E98B56}.Release|x86.ActiveCfg = Release|Any CPU {AAAF92C5-C40D-40B8-84BA-137DF0E98B56}.Release|x86.Build.0 = Release|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Debug|x64.ActiveCfg = Debug|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Debug|x64.Build.0 = Debug|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Debug|x86.ActiveCfg = Debug|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Debug|x86.Build.0 = Debug|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|Any CPU.Build.0 = Release|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x64.ActiveCfg = Release|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x64.Build.0 = Release|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x86.ActiveCfg = Release|Any CPU + {3DEF2E95-4BF8-413F-930E-32103A39364A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1136,6 +1150,7 @@ Global {E656E3E2-18D2-4D8E-935C-200DB00E1F19} = {7BFFAE76-DEE9-417A-A79B-6A6644C4553A} {F1641150-B89D-40B7-A3BE-9DC357410FDA} = {7BFFAE76-DEE9-417A-A79B-6A6644C4553A} {AAAF92C5-C40D-40B8-84BA-137DF0E98B56} = {901F162F-8925-4390-89C5-9EE2C343F744} + {3DEF2E95-4BF8-413F-930E-32103A39364A} = {901F162F-8925-4390-89C5-9EE2C343F744} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {058A0C5E-2216-4306-8AFB-0AE28320C26A} diff --git a/build.fsx b/build.fsx index 8017b5dbba1..fc774d63960 100644 --- a/build.fsx +++ b/build.fsx @@ -302,6 +302,7 @@ let dotnetAssemblyInfos = "Fake.DotNet.Testing.XUnit2", "Running xunit test runner" "Fake.DotNet.Xamarin", "Running Xamarin builds" "Fake.Installer.InnoSetup", "Creating installers with InnoSetup" + "Fake.Installer.Squirrel", "Squirrel for windows Squirrel.exe tool helper" "Fake.Installer.Wix", "WiX helper to create msi installers" "Fake.IO.FileSystem", "Core Filesystem utilities and globbing support" "Fake.IO.Zip", "Core Zip functionality" @@ -522,7 +523,7 @@ Target.create "GenerateDocs" (fun _ -> ProjectParameters = ("api-docs-prefix", "/apidocs/v5/legacy/") :: ("CurrentPage", "APIReference") :: projInfo SourceRepository = githubLink + "/blob/master" }) else - buildLegacyFromDocsDir legacyLayoutRoots (apidocsDir @@ "v5/legacy") "/apidocs/v5/legacy/" "/blob/master" ("packages/docslegacyv5/FAKE/tools") + buildLegacyFromDocsDir legacyLayoutRoots (apidocsDir @@ "v5/legacy") "/apidocs/v5/legacy/" "/blob/master" ("packages/docslegacyv5/FAKE/tools") // FAKE 4 legacy documentation buildLegacyFromDocsDir fake4LayoutRoots (apidocsDir @@ "v4") "/apidocs/v4/" "/blob/hotfix_fake4" ("packages/docslegacyv4/FAKE/tools") @@ -844,14 +845,14 @@ Target.create "CheckReleaseSecrets" (fun _ -> Target.create "DotNetCoreCreateDebianPackage" (fun _ -> let runtime = "linux-x64" - let targetFramework = "netcoreapp2.1" - let args = + let targetFramework = "netcoreapp2.1" + let args = [ - sprintf "/t:%s" "Restore;CreateDeb" + sprintf "/t:%s" "Restore;CreateDeb" sprintf "/p:TargetFramework=%s" targetFramework sprintf "/p:CustomTarget=%s" "CreateDeb" sprintf "/p:RuntimeIdentifier=%s" runtime - sprintf "/p:Configuration=%s" "Release" + sprintf "/p:Configuration=%s" "Release" sprintf "/p:PackageVersion=%s" simpleVersion ] |> String.concat " " let result = @@ -862,7 +863,7 @@ Target.create "DotNetCoreCreateDebianPackage" (fun _ -> if result.OK |> not then failwith "Debian package creation failed" - + let fileName = sprintf "fake-cli.%s.%s.deb" simpleVersion runtime let sourceFile = sprintf "src/app/fake-cli/bin/Release/%s/%s/%s" targetFramework runtime fileName Directory.ensure nugetDncDir diff --git a/help/markdown/fake-installer-squirrel.md b/help/markdown/fake-installer-squirrel.md new file mode 100644 index 00000000000..ef950eac639 --- /dev/null +++ b/help/markdown/fake-installer-squirrel.md @@ -0,0 +1,25 @@ +# Releasifying NuGet packages with Squirrel + +
+
INFO
+

This documentation is for FAKE version 5.0 or later.

+
+ +[Squirrel.Windows](https://github.com/Squirrel/Squirrel.Windows) is an installation an auto-update system for Windows desktop apps. + +[API-Reference](apidocs/v5/fake-installer-squirrel.html) + +## Squirrel.exe CLI tool + +This module is a wrapper around the [Squirrel.exe](https://github.com/Squirrel/Squirrel.Windows/blob/master/docs/using/squirrel-command-line.md) CLI tool that is available in the [squirrel.windows](https://www.nuget.org/packages/squirrel.windows/) NuGet package. + +## Releasify + +[Releasifying](https://github.com/Squirrel/Squirrel.Windows/blob/master/docs/getting-started/2-packaging.md#releasifying) create all files necessary for release from a NuGet package + +```fsharp +open Fake.Installer + +Squirrel.releasify "./my.nupkg" (fun p -> { p with ReleaseDir = "./squirrel_release") +``` + diff --git a/help/templates/template.cshtml b/help/templates/template.cshtml index 190d2423a35..8b98ce302ae 100644 --- a/help/templates/template.cshtml +++ b/help/templates/template.cshtml @@ -156,6 +156,7 @@ Installer diff --git a/src/app/Fake.Installer.Squirrel/AssemblyInfo.fs b/src/app/Fake.Installer.Squirrel/AssemblyInfo.fs new file mode 100644 index 00000000000..56d4a8197db --- /dev/null +++ b/src/app/Fake.Installer.Squirrel/AssemblyInfo.fs @@ -0,0 +1,17 @@ +// Auto-Generated by FAKE; do not edit +namespace System +open System.Reflection + +[] +[] +[] +[] +[] +do () + +module internal AssemblyVersionInformation = + let [] AssemblyTitle = "FAKE - F# Make Squirrel for windows Squirrel.exe tool helper" + let [] AssemblyProduct = "FAKE - F# Make" + let [] AssemblyVersion = "5.3.1" + let [] AssemblyInformationalVersion = "5.3.1-alpha" + let [] AssemblyFileVersion = "5.3.1" diff --git a/src/app/Fake.Installer.Squirrel/Fake.Installer.Squirrel.fsproj b/src/app/Fake.Installer.Squirrel/Fake.Installer.Squirrel.fsproj new file mode 100644 index 00000000000..3b420946c0c --- /dev/null +++ b/src/app/Fake.Installer.Squirrel/Fake.Installer.Squirrel.fsproj @@ -0,0 +1,24 @@ + + + net46;netstandard1.6;netstandard2.0 + $(DefineConstants);NO_DOTNETCORE_BOOTSTRAP + Fake.Installer.Squirrel + Library + + + $(DefineConstants);NETSTANDARD + + + $(DefineConstants);RELEASE + + + + + + + + + + + + diff --git a/src/app/Fake.Installer.Squirrel/Squirrel.fs b/src/app/Fake.Installer.Squirrel/Squirrel.fs new file mode 100644 index 00000000000..fbe3e28f366 --- /dev/null +++ b/src/app/Fake.Installer.Squirrel/Squirrel.fs @@ -0,0 +1,142 @@ +/// Contains types and utility functions related to creating [Squirrel](https://github.com/Squirrel/Squirrel.Windows) installer. +[] +module Fake.Installer.Squirrel + +open Fake.Core +open Fake.IO +open Fake.IO.FileSystemOperators +open Fake.IO.Globbing +open System +open System.IO +open System.Text + +/// The [Squirrel](https://github.com/Squirrel/Squirrel.Windows) Console Parameters type. +/// +/// For reference, see: [Squirrel Command Line Options](https://github.com/Squirrel/Squirrel.Windows/blob/master/docs/advanced-releasify.md) +type ReleasifyParams = { + /// The output directory for the generated installer + ReleaseDir : string + + /// The working directory. + WorkingDir : string option + + /// The full path to an optional setup.exe template + BootstrapperExe : string option + + /// The full path to an optional animated gif to be displayed during installation + LoadingGif : string option + + /// The full path to an optional icon, which will be used for the generated installer. + SetupIcon : string option + + /// Do not create an MSI file + NoMsi : bool + + /// The path to Squirrel: `squirrel.exe` + ToolPath : string + + /// Maximum time to allow Squirrel to run before being killed. + TimeOut : TimeSpan + + /// Sign the installer with signtool.exe + SignExecutable : bool option + + /// The code signing certificate to be used for signing + SigningKeyFile : string option + + /// The secret key for the code signing certificate + SigningSecret : string option +} + +let internal defaultParams = lazy( + let toolname = "Squirrel.exe" + { ReleaseDir = "" + WorkingDir = None + BootstrapperExe = None + LoadingGif = None + SetupIcon = None + NoMsi = false + ToolPath = Tools.findToolInSubPath toolname ( Directory.GetCurrentDirectory() "tools" "Squirrel") + TimeOut = TimeSpan.FromMinutes 10. + SignExecutable = None + SigningKeyFile = None + SigningSecret = None }) + +let private createSigningArgs (parameters : ReleasifyParams) = + new StringBuilder() + |> StringBuilder.appendWithoutQuotes "--signWithParams=\"" + |> StringBuilder.appendWithoutQuotes "/a" + |> StringBuilder.appendIfSome parameters.SigningKeyFile (sprintf "/f %s") + |> StringBuilder.appendIfSome parameters.SigningSecret (sprintf "/p %s") + |> StringBuilder.appendWithoutQuotes "\"" + |> StringBuilder.toText + +let internal buildSquirrelArgs parameters nugetPackage = + new StringBuilder() + |> StringBuilder.appendIfNotNullOrEmpty nugetPackage "--releasify=" + |> StringBuilder.appendIfNotNullOrEmpty parameters.ReleaseDir "--releaseDir=" + |> StringBuilder.appendIfSome parameters.LoadingGif (sprintf "\"--loadingGif=%s\"") + |> StringBuilder.appendIfSome parameters.SetupIcon (sprintf "\"--setupIcon=%s\"") + |> StringBuilder.appendIfTrue parameters.NoMsi "--no-msi" + |> StringBuilder.appendIfSome parameters.BootstrapperExe (sprintf "\"--bootstrapperExe=%s\"") + |> StringBuilder.appendIfSome parameters.SignExecutable (fun _ -> createSigningArgs parameters) + |> StringBuilder.toText + +module internal ResultHandling = + let (|OK|Failure|) = function + | 0 -> OK + | x -> Failure x + + let buildErrorMessage = function + | OK -> None + | Failure errorCode -> + Some (sprintf "Squirrel reported an error (Error Code %d)" errorCode) + + let failBuildIfSquirrelReportedError = + buildErrorMessage + >> Option.iter failwith + +/// Creates a Squirrel installer for given NuGet package +/// Will fail if Squirrel terminates with non-zero exit code. +/// +/// ## Parameters +/// +/// - `setParams` - Function used to manipulate the default `SquirrelParams` value. +/// - `nugetPackage` - The package to create an installer for +/// +/// ## Sample usage +/// +/// Target.create "CreatePackage" (fun _ -> +/// Squirrel.releasify "./my.nupkg" (fun p -> { p with ReleaseDir = "./squirrel_release") +/// ) +/// +/// ## Defaults for setParams +/// +/// - `ReleaseDir` - `""` +/// - `WorkingDir` - `None` +/// - `BootstrapperExe` - `None` +/// - `LoadingGif` - `None` +/// - `SetupIcon` - `None` +/// - `NoMsi` - `false` +/// - `ToolPath` - The `squirrel.exe` path if it exists in a subdirectory of the current directory. +/// - `TimeOut` - 10 minutes +/// - `SignExecutable` - `None` +/// - `SigningKeyFile` - `None` +/// - `SigningSecret` - `None` +let releasify (nugetPackage: string) (setParams: ReleasifyParams -> ReleasifyParams): unit = + use __ = Trace.traceTask "Squirrel" nugetPackage + let parameters = defaultParams.Value |> setParams + let args = buildSquirrelArgs parameters nugetPackage + Trace.tracefn "%s" args + + let result = + Process.execSimple + (fun info -> { info with + FileName = parameters.ToolPath + WorkingDirectory = defaultArg parameters.WorkingDir "." + Arguments = args}) + parameters.TimeOut + + ResultHandling.failBuildIfSquirrelReportedError result + + __.MarkSuccess() diff --git a/src/app/Fake.Installer.Squirrel/paket.references b/src/app/Fake.Installer.Squirrel/paket.references new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/legacy/FakeLib/SquirrelHelper.fs b/src/legacy/FakeLib/SquirrelHelper.fs index 04c463c9461..bc3b16ed027 100644 --- a/src/legacy/FakeLib/SquirrelHelper.fs +++ b/src/legacy/FakeLib/SquirrelHelper.fs @@ -1,5 +1,5 @@ /// Contains types and utility functions related to creating [Squirrel](https://github.com/Squirrel/Squirrel.Windows) installer. -[] +[] module Fake.Squirrel open Fake @@ -11,7 +11,7 @@ open System.Text /// /// For reference, see: [Squirrel Command Line Options](https://github.com/Squirrel/Squirrel.Windows/blob/master/docs/advanced-releasify.md) [] -[] +[] type SquirrelParams = { /// The output directory for the generated installer @@ -62,7 +62,7 @@ type SquirrelParams = /// - `SignExecutable` - `None` /// - `SigningKeyFile` - `None` /// - `SigningSecret` - `None` -[] +[] let SquirrelDefaults = let toolname = "Squirrel.exe" { @@ -125,7 +125,7 @@ module internal ResultHandling = /// Target "CreatePackage" (fun _ -> /// SquirrelPack (fun p -> { p with WorkingDir = Some "./tmp" }) "./my.nupkg" /// ) -[] +[] let SquirrelPack setParams nugetPackage = use __ = traceStartTaskUsing "Squirrel" "" let parameters = SquirrelDefaults |> setParams