Skip to content

Commit

Permalink
Merge pull request #1878 from josiahdj/PortDacPacToFake5
Browse files Browse the repository at this point in the history
Ported Fake.Sql.DacPac to FAKE 5
  • Loading branch information
matthid authored Apr 22, 2018
2 parents b194ef2 + 34fcb4d commit 6c47ca5
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 8 deletions.
15 changes: 15 additions & 0 deletions Fake.sln
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Api.HockeyApp", "src/a
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.JavaScript.Yarn", "src/app/Fake.JavaScript.Yarn/Fake.JavaScript.Yarn.fsproj", "{DE7579F2-C20F-4C35-BC04-C10362912243}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Fake.Sql.DacPac", "src/app/Fake.Sql.DacPac/Fake.Sql.DacPac.fsproj", "{3BC4A91C-3381-4BF9-BF11-8E06706CF878}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -722,6 +724,18 @@ Global
{DE7579F2-C20F-4C35-BC04-C10362912243}.Release|x64.Build.0 = Release|Any CPU
{DE7579F2-C20F-4C35-BC04-C10362912243}.Release|x86.ActiveCfg = Release|Any CPU
{DE7579F2-C20F-4C35-BC04-C10362912243}.Release|x86.Build.0 = Release|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Debug|x64.ActiveCfg = Debug|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Debug|x64.Build.0 = Debug|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Debug|x86.ActiveCfg = Debug|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Debug|x86.Build.0 = Debug|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Release|Any CPU.Build.0 = Release|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Release|x64.ActiveCfg = Release|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Release|x64.Build.0 = Release|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Release|x86.ActiveCfg = Release|Any CPU
{3BC4A91C-3381-4BF9-BF11-8E06706CF878}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -779,6 +793,7 @@ Global
{819E2756-7BEE-4FCD-BA08-7BF323405CBE} = {7BFFAE76-DEE9-417A-A79B-6A6644C4553A}
{B636A082-4DB4-439D-8A37-E5214BDC00A3} = {901F162F-8925-4390-89C5-9EE2C343F744}
{DE7579F2-C20F-4C35-BC04-C10362912243} = {901F162F-8925-4390-89C5-9EE2C343F744}
{3BC4A91C-3381-4BF9-BF11-8E06706CF878} = {7BFFAE76-DEE9-417A-A79B-6A6644C4553A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {058A0C5E-2216-4306-8AFB-0AE28320C26A}
Expand Down
1 change: 1 addition & 0 deletions build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ let dotnetAssemblyInfos =
"Fake.Windows.Chocolatey", "Running and packaging with Chocolatey"
"Fake.Testing.SonarQube", "Analyzing your project with SonarQube"
"Fake.DotNet.Testing.OpenCover", "Code coverage with OpenCover"
"Fake.Sql.DacPac", "Sql Server Data Tools DacPac operations"
"Fake.Documentation.DocFx", "Documentation with DocFx" ]

let assemblyInfos =
Expand Down
29 changes: 29 additions & 0 deletions help/markdown/dacpac.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Packaging and Deploying SQL Databases

FAKE can be used to create a SQL DACPAC and also deploy it to a SQL Server using the MSDeploy executable. This is installed by default with Visual Studio and with the SQL Server Data Tools (SSDT) package.

DACPACs automatically diff from the source to the destination and generate the SQL script dynamically.

You can read up more on DACPac and MSDeploy arguments [here](https://msdn.microsoft.com/en-us/library/hh550081%28v=vs.103%29.aspx).

## Sample usage

Ensure that you have already built your database project (you can do this with standard MSBuild). Then, use the ``deployDb`` command to deploy the ``dbProject.dacpac`` to the ``myDatabase``.

open Fake.Sql

/// the database for local development + compile
Target.create "DeployLocalDb" (fun _ ->
let connectionString = "Data Source=(localdb)\MSSQLLocalDB;Integrated Security=True;Database=MyDatabase;Pooling=False"
let dacPacPath = "path/to/dbProject.dacpac"
DacPac.deployDb (fun args -> { args with Source = dacPacPath; Destination = localDbConnectionString }) |> ignore)

## Deployment Options

You can optionally specify the type of command to use (again, refer to the documentation above for more detail): -

* Deploy - full deploy to destination
* Script - SQL script
* Report - XML report of diff

In addition, you can also elect to deploy to Dacpac files rather than SQL databases - simply pass in the pass to the dacpac that you wish to generate.
14 changes: 7 additions & 7 deletions help/markdown/help-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ For example topics about "Targets" like "specifictargets" has been added to the
You can find a link to the old documentation at the top of the new documentation.

So to find the old documentation you usually:

* Find the module in the navigation
* Navigate to the FAKE 4 docs (linked at the top in bold)

> NOTE: This page might be removed without further notice once FAKE 5 has been released!
Here is the old sidebar:

## FAKE - F# Make

* [Old Startpage](legacy-index.html)

## Get FAKE via NuGet

* [Source Code on GitHub](https://github.com/fsharp/FAKE)
Expand All @@ -29,7 +30,7 @@ Here is the old sidebar:

* [Getting started](gettingstarted.html)
* [Build script caching](cache.html)

* [NuGet package restore](nuget.html)
* [Using FxCop in a build](fxcop.html)
* [Generating AssemblyInfo](assemblyinfo.html)
Expand All @@ -55,9 +56,8 @@ Here is the old sidebar:
* [Using Slack](slacknotification.html)
* [Using Microsoft Teams](msteamsnotification.html)
* [Using SonarQube](sonarcube.html)

* [Fake.Deploy](deploy.html)
* [Fake.IIS](iis.html)

* [API Reference](apidocs/index.html)

* [API Reference](apidocs/index.html)
2 changes: 1 addition & 1 deletion help/redirects/dacpac.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

This page moved to:

- Not yet migrated to FAKE 5
- [here for FAKE 5](dac-pack.html)
- [here for FAKE 4](todo-dacpac.html) (Final location not decided yet)
17 changes: 17 additions & 0 deletions src/app/Fake.Sql.DacPac/AssemblyInfo.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Auto-Generated by FAKE; do not edit
namespace System
open System.Reflection

[<assembly: AssemblyTitleAttribute("FAKE - F# Make Sql Server Data Tools DacPac operations")>]
[<assembly: AssemblyProductAttribute("FAKE - F# Make")>]
[<assembly: AssemblyVersionAttribute("5.0.0")>]
[<assembly: AssemblyInformationalVersionAttribute("5.0.0-rc005")>]
[<assembly: AssemblyFileVersionAttribute("5.0.0")>]
do ()

module internal AssemblyVersionInformation =
let [<Literal>] AssemblyTitle = "FAKE - F# Make Sql Server Data Tools DacPac operations"
let [<Literal>] AssemblyProduct = "FAKE - F# Make"
let [<Literal>] AssemblyVersion = "5.0.0"
let [<Literal>] AssemblyInformationalVersion = "5.0.0-rc005"
let [<Literal>] AssemblyFileVersion = "5.0.0"
25 changes: 25 additions & 0 deletions src/app/Fake.Sql.DacPac/Fake.Sql.DacPac.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworks>net46;netstandard1.6;netstandard2.0</TargetFrameworks>
<DefineConstants>$(DefineConstants);DOTNETCORE</DefineConstants>
<AssemblyName>Fake.Sql.DacPac</AssemblyName>
<OutputType>Library</OutputType>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="Sql.DacPac.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Fake.Core.Environment\Fake.Core.Environment.fsproj" />
<ProjectReference Include="..\Fake.Core.Process\Fake.Core.Process.fsproj" />
<ProjectReference Include="..\Fake.IO.FileSystem\Fake.IO.FileSystem.fsproj" />
</ItemGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<Import Project="..\..\..\.paket\Paket.Restore.targets" />
</Project>
118 changes: 118 additions & 0 deletions src/app/Fake.Sql.DacPac/Sql.DacPac.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
namespace Fake.Sql

/// Contains helpers around deploying databases.
[<RequireQualifiedAccess>]
module DacPac =

open Fake.Core
open Fake.IO.FileSystemOperators
open Fake.IO.Globbing.Operators
open System.IO
open System

/// The type of action to execute.
type DeployAction =
/// Generate and apply a synchronisation script between two databases.
| Deploy
/// Generate a SQL script to sync two databases.
| Script of OutputPath:string
/// Generate an XML report for the differences between two databases.
| Report of OutputPath:string

/// Configuration arguments for DacPac deploy
type DeployDbArgs = {
/// The path to SqlPackage.exe.
SqlPackageToolPath : string
/// Type of action to execute. Defaults to Deploy.
Action : DeployAction
/// Path to source (path to DACPAC or Connection String).
Source : string
/// Path to destination (path to DACPAC or Connection String).
Destination : string
/// Timeout for deploy (defaults to 120 seconds).
Timeout : int
/// Block deployment if data loss can occur. Defaults to true.
BlockOnPossibleDataLoss : bool
/// Drops objects in the destination that do not exist in the source. Defaults to false.
DropObjectsNotInSource : bool
/// Recreates the database from scratch on publish (rather than an in-place update). Defaults to false.
RecreateDb : bool
/// Additional configuration parameters required by sqlpackage.exe
AdditionalSqlPackageProperties : (string * string) list
/// SQLCMD variables
Variables : (string * string) list }

let validPaths =
let getSqlVersion (path:string) = path.Split '\\' |> Array.item 3 |> int
let getVsVersion path = (Path.GetDirectoryName path |> DirectoryInfo).Name |> int
let sql = !!(Environment.ProgramFilesX86 </> @"Microsoft SQL Server\**\DAC\bin\SqlPackage.exe") |> Seq.map(fun path -> path, getSqlVersion path)
let vs = !!(Environment.ProgramFilesX86 </> @"Microsoft Visual Studio*\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\*\SqlPackage.exe") |> Seq.map(fun path -> path, getVsVersion path)

[ sql; vs ]
|> List.collect Seq.toList
|> List.sortByDescending snd
|> List.map fst

/// The default DacPac deployment arguments.
let DefaultDeploymentArgs =
{ SqlPackageToolPath =
validPaths
|> List.tryHead
|> defaultArg <| ""
Action = Deploy
Source = ""
Destination = ""
Timeout = 120
BlockOnPossibleDataLoss = true
DropObjectsNotInSource = false
RecreateDb = false
AdditionalSqlPackageProperties = []
Variables = [] }

module PropertyKeys =
/// When creating a new SQL Azure database, specifies the database service tier to use e.g. S2, P1
let sqlAzureDbSize = "DatabaseServiceObjective"

let private generateCommandLine args =
let action, outputPath =
match args with
| Deploy -> "Publish", None
| Script outputPath -> "Script", Some outputPath
| Report outputPath -> "DeployReport", Some outputPath
let outputPath = defaultArg(outputPath |> Option.map(sprintf """/OutputPath:"%s" """)) ""
action, outputPath

/// Deploys a SQL DacPac or database to another database or DacPac.
let deployDb setParams =
let args = setParams DefaultDeploymentArgs
let action, outputPath = generateCommandLine args.Action

let concat parameter =
List.map (fun (key, value) -> sprintf "/%s:%s=%s" parameter key value)
>> String.concat " "

let additionalParameters = args.AdditionalSqlPackageProperties |> concat "p"

let variables = args.Variables |> concat "v"

if System.String.IsNullOrWhiteSpace args.SqlPackageToolPath then
failwith "No SqlPackage.exe filename was given."

if not (File.Exists args.SqlPackageToolPath) then
let paths =
if validPaths |> List.contains args.SqlPackageToolPath then validPaths
else [ args.SqlPackageToolPath ]
failwithf "Unable to find a valid instance of SqlPackage.exe. Paths checked were: %A." paths

let result =
Process.execRaw
(fun psi -> { psi with Arguments = sprintf """/Action:%s /SourceFile:"%s" /TargetConnectionString:"%s" %s /p:BlockOnPossibleDataLoss=%b /p:DropObjectsNotInSource=%b /p:CommandTimeout=%d /p:CreateNewDatabase=%b %s %s""" action args.Source args.Destination outputPath args.BlockOnPossibleDataLoss args.DropObjectsNotInSource args.Timeout args.RecreateDb additionalParameters variables; FileName = args.SqlPackageToolPath })
TimeSpan.MaxValue
true
(printfn "SqlPackage error: %s")
(printfn "%s")

match result with
| 0 -> ()
| _ -> failwith "Error executing DACPAC deployment. Please see output for error details."

4 changes: 4 additions & 0 deletions src/app/Fake.Sql.DacPac/paket.references
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
group netcore

FSharp.Core
NETStandard.Library
6 changes: 6 additions & 0 deletions src/legacy/FakeLib/Sql.DacPac.fs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// Contains helpers around deploying databases.
[<System.Obsolete("Please open Fake.Sql instead and use the Fake.Sql.DacPac module (FAKE0001 - package: Fake.Sql.DacPac, module: DacPac)")>]
module Fake.Sql.DacPac

open Fake.EnvironmentHelper
Expand All @@ -9,6 +10,7 @@ open System.Diagnostics
open System

/// The type of action to execute.
[<System.Obsolete("FAKE0001 Use `open Fake.Sql` and `DacPac.DeployAction`")>]
type DeployAction =
/// Generate and apply a synchronisation script between two databases.
| Deploy
Expand All @@ -18,6 +20,7 @@ type DeployAction =
| Report of OutputPath:string

/// Configuration arguments for DacPac deploy
[<System.Obsolete("FAKE0001 Use `open Fake.Sql` and `DacPac.DeployDbArgs`")>]
type DeployDbArgs = {
/// The path to SqlPackage.exe.
SqlPackageToolPath : string
Expand All @@ -40,6 +43,7 @@ type DeployDbArgs = {
/// SQLCMD variables
Variables : (string * string) list }

[<System.Obsolete("FAKE0001 Use `open Fake.Sql` and `DacPac.validPaths`")>]
let validPaths =
let getSqlVersion (path:string) = path.Split '\\' |> Array.item 3 |> int
let getVsVersion path = (Path.GetDirectoryName path |> DirectoryInfo).Name |> int
Expand All @@ -52,6 +56,7 @@ let validPaths =
|> List.map fst

/// The default DacPac deployment arguments.
[<System.Obsolete("FAKE0001 Use `open Fake.Sql` and `DacPac.DefaultDeploymentArgs`")>]
let defaultDeploymentArgs =
{ SqlPackageToolPath =
validPaths
Expand Down Expand Up @@ -81,6 +86,7 @@ let private generateCommandLine args =
action, outputPath

/// Deploys a SQL DacPac or database to another database or DacPac.
[<System.Obsolete("FAKE0001 Use `open Fake.Sql` and `DacPac.deployDb`")>]
let deployDb setParams =
let args = setParams defaultDeploymentArgs
let action, outputPath = generateCommandLine args.Action
Expand Down

0 comments on commit 6c47ca5

Please sign in to comment.