├── .gitignore
├── LICENSE
├── README.md
├── build.cake
├── build.ps1
├── build.sh
├── buildconfig.cake
├── docker-compose.yml
├── init-local-config.sh
├── resources
├── .env.sample
├── integrationtests.local.ini.sample
└── nuget-usage.md
├── src
├── Directory.Build.props
├── DotNet-SqlDb.sln
├── projects
│ └── SqlDb
│ │ ├── Commands
│ │ ├── Command.fs
│ │ ├── DropDbCommand.fs
│ │ ├── EnsureDbCommand.fs
│ │ └── UpgradeDbCommand.fs
│ │ ├── Common.fs
│ │ ├── DbUp.fs
│ │ ├── DbUpScript.fs
│ │ ├── Errors.fs
│ │ ├── Logging.fs
│ │ ├── MsSql.fs
│ │ ├── Program.fs
│ │ └── SqlDb.fsproj
└── tests
│ ├── IntegrationTests
│ ├── Config.fs
│ ├── DropDbCommandTests.fs
│ ├── EnsureDbCommandTests.fs
│ ├── IntegrationTests.fsproj
│ ├── Should.fs
│ ├── SqlScript
│ │ └── 001_CreateTables.sql
│ ├── TestEnv.fs
│ └── UpgradeDbCommandTests.fs
│ └── UnitTests
│ ├── CommonTests.fs
│ ├── DbUpScriptTests.fs
│ ├── ErrorsTests.fs
│ ├── LoggingTests.fs
│ ├── Should.fs
│ ├── SqlDbTests.fs
│ └── UnitTests.fsproj
└── tools
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | # vs
2 | .vs/
3 | [Aa]pp_[Dd]ata/
4 | [Bb]in/
5 | [D]ebug/
6 | [Aa]rtifacts/
7 | [Oo]bj/
8 | [Tt]est[Rr]esult/
9 | [Tt]est[Rr]esults/
10 | [Tt]emp/
11 | *.user
12 | *.suo
13 | *.cache
14 |
15 | .env
16 | *.local
17 | *.local.json
18 | *.local.ini
19 |
20 | # deploy
21 | build/
22 | tools/**
23 | !tools/packages.config
24 |
25 | # misc
26 | *~
27 | *.swp
28 | *.sdf
29 | *.orig
30 | *.pfx
31 |
32 | # resharper
33 | _ReSharper.*
34 | *.resharper*
35 | *.[Rr]e[Ss]harper.user
36 | *.DotSettings.user
37 |
38 | #windows stuff
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Daniel Wertheim
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dotnet-sqldb
2 | Uses [DbUp](https://github.com/dbup/dbup) and [Command Line Parser](https://github.com/commandlineparser/commandline) to offer a simple [DotNet Global Tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools) for applying migration scripts etc. against a SQL-Server database.
3 |
4 | [](https://dev.azure.com/danielwertheim/dotnet-sqldb/_build/latest)
5 | [](http://nuget.org/packages/dotnet-sqldb)
6 |
7 | **Note:** It's your data. Use at your own risk. As [the license](https://github.com/danielwertheim/dotnet-sqldb/blob/master/LICENSE) (MIT) says: *"THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND..."*
8 |
9 | ## Installation
10 | It's a DotNet Global Tool, distributed via NuGet.
11 |
12 | ### Global installation
13 | To install it globally (machine), you use the [dotnet tool install command](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install) with the `-g` switch:
14 |
15 | ```
16 | dotnet tool install -g dotnet-sqldb
17 | ```
18 |
19 | After that you can start using it (you might need to restart your prompt of choice):
20 |
21 | ```
22 | dotnet sqldb --help
23 | ```
24 |
25 | or
26 |
27 | ```
28 | dotnet-sqldb --help
29 | ```
30 |
31 | ### Local installation
32 | To install it locally, you use the [dotnet tool install command](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install) with the `--tool-path` switch:
33 |
34 | ```
35 | dotnet tool install dotnet-sqldb --tool-path /path/for/tool
36 | ```
37 |
38 | To use it you will need to include it in the current environment path.
39 |
40 | ## Commands
41 | The following commands are supported:
42 |
43 | - **Ensure:** Ensures that the specified DB exists. If it does not exist, it gets created.
44 | ```
45 | dotnet sqldb ensure [--connectionstring|-c]=mycnstring
46 | ```
47 |
48 | or
49 |
50 | ```
51 | dotnet-sqldb ensure [--connectionstring|-c]=mycnstring
52 | ```
53 |
54 | - **Drop:** Drops the specified database if it exists
55 | ```
56 | dotnet sqldb drop [--connectionstring|-c]=mycnstring
57 | ```
58 |
59 | or
60 |
61 | ```
62 | dotnet-sqldb drop [--connectionstring|-c]=mycnstring
63 | ```
64 |
65 | - **Up:** Upgrades the database by applying SQL-scripts using [DbUp](https://github.com/dbup/dbup)
66 | ```
67 | dotnet sqldb up [--connectionstring|-c]=mycnstring [--assembly|-a]=myassembly_with_embedded_scripts
68 | ```
69 |
70 | or
71 |
72 | ```
73 | dotnet-sqldb up [--connectionstring|-c]=mycnstring [--assembly|-a]=myassembly_with_embedded_scripts
74 | ```
75 |
76 | ## Integration tests
77 | The `./.env` file and `./src/tests/IntegrationTests/integrationtests.local.ini` files are `.gitignored`. In order to create sample files of these, you can run:
78 |
79 | ```
80 | . init-local-config.sh
81 | ```
82 |
83 | ### Docker-Compose
84 | There's a `docker-compose.yml` file, that defines usage of an SQL Server instance over port `1401`. The `SA_PASSWORD` is configured via environment key `DNSQLDB_SA_PWD`, which can either be specified via:
85 |
86 | - Environment variable: `DNSQLDB_SA_PWD`, e.g.:
87 | ```
88 | DNSQLDB_SA_PWD=MyFooPassword
89 | ```
90 |
91 | - Docker Environment file `./.env` (`.gitignored`), e.g.:
92 | ```
93 | DNSQLDB_SA_PWD=MyFooPassword
94 | ```
95 |
96 | ### Test configuration
97 | A connection string needs to be provided, either via:
98 |
99 | - Local-INI-file (`.gitignored`): `./src/tests/IntegrationTests/integrationtests.local.ini` containing a connection string section with a `TestDb` node, e.g.:
100 | ```
101 | [ConnectionStrings]
102 | TestDb="Server=.,1401;Database=foodb;User ID=sa;Password=MyFooPassword"
103 | ```
104 |
105 | - Environment variable: `DNSQLDB_ConnectionStrings__TestDb`, e.g.:
106 |
107 | ```
108 | DNSQLDB_ConnectionStrings__TestDb=Server=.,1401;Database=foodb;User ID=sa;Password=MyFooPassword
109 | ```
110 |
--------------------------------------------------------------------------------
/build.cake:
--------------------------------------------------------------------------------
1 | #load "./buildconfig.cake"
2 |
3 | var config = BuildConfig.Create(Context, BuildSystem);
4 | var verbosity = DotNetCoreVerbosity.Minimal;
5 |
6 | Information($"SrcDir: '{config.SrcDir}'");
7 | Information($"ArtifactsDir: '{config.ArtifactsDir}'");
8 | Information($"SemVer: '{config.SemVer}'");
9 | Information($"BuildVersion: '{config.BuildVersion}'");
10 | Information($"BuildProfile: '{config.BuildProfile}'");
11 |
12 | Task("Default")
13 | .IsDependentOn("Clean")
14 | .IsDependentOn("Build")
15 | .IsDependentOn("UnitTests")
16 | .IsDependentOn("IntegrationTests");
17 |
18 | Task("CI")
19 | .IsDependentOn("Docker-Compose-Up")
20 | .IsDependentOn("Default")
21 | .IsDependentOn("Pack")
22 | .Finally(() => {
23 | Information("Running 'docker-compose down'...");
24 | StartProcess("docker-compose", "down");
25 | });
26 | /********************************************/
27 | Task("Clean").Does(() => {
28 | EnsureDirectoryExists(config.ArtifactsDir);
29 | CleanDirectory(config.ArtifactsDir);
30 | });
31 |
32 | Task("Build").Does(() => {
33 | var settings = new DotNetCoreBuildSettings {
34 | Configuration = config.BuildProfile,
35 | NoIncremental = true,
36 | NoRestore = false,
37 | Verbosity = verbosity,
38 | MSBuildSettings = new DotNetCoreMSBuildSettings()
39 | .WithProperty("TreatWarningsAsErrors", "true")
40 | .WithProperty("Version", config.SemVer)
41 | .WithProperty("AssemblyVersion", config.BuildVersion)
42 | .WithProperty("FileVersion", config.BuildVersion)
43 | .WithProperty("InformationalVersion", config.BuildVersion)
44 | };
45 |
46 | foreach(var sln in GetFiles($"{config.SrcDir}*.sln")) {
47 | DotNetCoreBuild(sln.FullPath, settings);
48 | }
49 | });
50 |
51 | Task("UnitTests").Does(() => {
52 | var settings = new DotNetCoreTestSettings {
53 | Configuration = config.BuildProfile,
54 | NoBuild = true,
55 | NoRestore = true,
56 | Logger = "trx",
57 | ResultsDirectory = config.TestResultsDir,
58 | Verbosity = verbosity
59 | };
60 | foreach(var testProj in GetFiles($"{config.SrcDir}tests/**/UnitTests.fsproj")) {
61 | DotNetCoreTest(testProj.FullPath, settings);
62 | }
63 | });
64 |
65 | Task("Docker-Compose-Up").Does(() => {
66 | StartProcess("docker-compose", "up -d");
67 | });
68 |
69 | Task("IntegrationTests").Does(() => {
70 | var settings = new DotNetCoreTestSettings {
71 | Configuration = config.BuildProfile,
72 | NoBuild = true,
73 | NoRestore = true,
74 | Logger = "trx",
75 | ResultsDirectory = config.TestResultsDir,
76 | Verbosity = verbosity
77 | };
78 | foreach(var testProj in GetFiles($"{config.SrcDir}tests/**/IntegrationTests.fsproj")) {
79 | DotNetCoreTest(testProj.FullPath, settings);
80 | }
81 | });
82 |
83 | Task("Pack").Does(() => {
84 | var settings = new DotNetCorePackSettings
85 | {
86 | Configuration = config.BuildProfile,
87 | OutputDirectory = config.ArtifactsDir,
88 | NoRestore = true,
89 | NoBuild = true,
90 | Verbosity = verbosity,
91 | MSBuildSettings = new DotNetCoreMSBuildSettings()
92 | .WithProperty("Version", config.SemVer)
93 | .WithProperty("AssemblyVersion", config.BuildVersion)
94 | .WithProperty("FileVersion", config.BuildVersion)
95 | .WithProperty("InformationalVersion", config.BuildVersion)
96 | };
97 |
98 | foreach(var proj in GetFiles($"{config.SrcDir}projects/**/*.fsproj")) {
99 | DotNetCorePack(proj.FullPath, settings);
100 | }
101 | });
102 |
103 | RunTarget(config.Target);
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | ##########################################################################
2 | # This is the Cake bootstrapper script for PowerShell.
3 | # This file was downloaded from https://github.com/cake-build/resources
4 | # Feel free to change this file to fit your needs.
5 | ##########################################################################
6 |
7 | <#
8 |
9 | .SYNOPSIS
10 | This is a Powershell script to bootstrap a Cake build.
11 |
12 | .DESCRIPTION
13 | This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
14 | and execute your Cake build script with the parameters you provide.
15 |
16 | .PARAMETER Script
17 | The build script to execute.
18 | .PARAMETER Target
19 | The build script target to run.
20 | .PARAMETER Configuration
21 | The build configuration to use.
22 | .PARAMETER Verbosity
23 | Specifies the amount of information to be displayed.
24 | .PARAMETER ShowDescription
25 | Shows description about tasks.
26 | .PARAMETER DryRun
27 | Performs a dry run.
28 | .PARAMETER Experimental
29 | Uses the nightly builds of the Roslyn script engine.
30 | .PARAMETER Mono
31 | Uses the Mono Compiler rather than the Roslyn script engine.
32 | .PARAMETER SkipToolPackageRestore
33 | Skips restoring of packages.
34 | .PARAMETER ScriptArgs
35 | Remaining arguments are added here.
36 |
37 | .LINK
38 | https://cakebuild.net
39 |
40 | #>
41 |
42 | [CmdletBinding()]
43 | Param(
44 | [string]$Script = "build.cake",
45 | [string]$Target,
46 | [string]$Configuration,
47 | [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
48 | [string]$Verbosity,
49 | [switch]$ShowDescription,
50 | [Alias("WhatIf", "Noop")]
51 | [switch]$DryRun,
52 | [switch]$Experimental,
53 | [switch]$Mono,
54 | [switch]$SkipToolPackageRestore,
55 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
56 | [string[]]$ScriptArgs
57 | )
58 |
59 | [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
60 | function MD5HashFile([string] $filePath)
61 | {
62 | if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
63 | {
64 | return $null
65 | }
66 |
67 | [System.IO.Stream] $file = $null;
68 | [System.Security.Cryptography.MD5] $md5 = $null;
69 | try
70 | {
71 | $md5 = [System.Security.Cryptography.MD5]::Create()
72 | $file = [System.IO.File]::OpenRead($filePath)
73 | return [System.BitConverter]::ToString($md5.ComputeHash($file))
74 | }
75 | finally
76 | {
77 | if ($file -ne $null)
78 | {
79 | $file.Dispose()
80 | }
81 | }
82 | }
83 |
84 | function GetProxyEnabledWebClient
85 | {
86 | $wc = New-Object System.Net.WebClient
87 | $proxy = [System.Net.WebRequest]::GetSystemWebProxy()
88 | $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
89 | $wc.Proxy = $proxy
90 | return $wc
91 | }
92 |
93 | Write-Host "Preparing to run build script..."
94 |
95 | if(!$PSScriptRoot){
96 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
97 | }
98 |
99 | $TOOLS_DIR = Join-Path $PSScriptRoot "tools"
100 | $ADDINS_DIR = Join-Path $TOOLS_DIR "Addins"
101 | $MODULES_DIR = Join-Path $TOOLS_DIR "Modules"
102 | $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
103 | $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
104 | $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
105 | $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
106 | $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
107 | $ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config"
108 | $MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config"
109 |
110 | # Make sure tools folder exists
111 | if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
112 | Write-Verbose -Message "Creating tools directory..."
113 | New-Item -Path $TOOLS_DIR -Type directory | out-null
114 | }
115 |
116 | # Make sure that packages.config exist.
117 | if (!(Test-Path $PACKAGES_CONFIG)) {
118 | Write-Verbose -Message "Downloading packages.config..."
119 | try {
120 | $wc = GetProxyEnabledWebClient
121 | $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
122 | Throw "Could not download packages.config."
123 | }
124 | }
125 |
126 | # Try find NuGet.exe in path if not exists
127 | if (!(Test-Path $NUGET_EXE)) {
128 | Write-Verbose -Message "Trying to find nuget.exe in PATH..."
129 | $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) }
130 | $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
131 | if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
132 | Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
133 | $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
134 | }
135 | }
136 |
137 | # Try download NuGet.exe if not exists
138 | if (!(Test-Path $NUGET_EXE)) {
139 | Write-Verbose -Message "Downloading NuGet.exe..."
140 | try {
141 | $wc = GetProxyEnabledWebClient
142 | $wc.DownloadFile($NUGET_URL, $NUGET_EXE)
143 | } catch {
144 | Throw "Could not download NuGet.exe."
145 | }
146 | }
147 |
148 | # Save nuget.exe path to environment to be available to child processed
149 | $ENV:NUGET_EXE = $NUGET_EXE
150 |
151 | # Restore tools from NuGet?
152 | if(-Not $SkipToolPackageRestore.IsPresent) {
153 | Push-Location
154 | Set-Location $TOOLS_DIR
155 |
156 | # Check for changes in packages.config and remove installed tools if true.
157 | [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
158 | if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
159 | ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
160 | Write-Verbose -Message "Missing or changed package.config hash..."
161 | Remove-Item * -Recurse -Exclude packages.config,nuget.exe
162 | }
163 |
164 | Write-Verbose -Message "Restoring tools from NuGet..."
165 | $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
166 |
167 | if ($LASTEXITCODE -ne 0) {
168 | Throw "An error occurred while restoring NuGet tools."
169 | }
170 | else
171 | {
172 | $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
173 | }
174 | Write-Verbose -Message ($NuGetOutput | out-string)
175 |
176 | Pop-Location
177 | }
178 |
179 | # Restore addins from NuGet
180 | if (Test-Path $ADDINS_PACKAGES_CONFIG) {
181 | Push-Location
182 | Set-Location $ADDINS_DIR
183 |
184 | Write-Verbose -Message "Restoring addins from NuGet..."
185 | $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`""
186 |
187 | if ($LASTEXITCODE -ne 0) {
188 | Throw "An error occurred while restoring NuGet addins."
189 | }
190 |
191 | Write-Verbose -Message ($NuGetOutput | out-string)
192 |
193 | Pop-Location
194 | }
195 |
196 | # Restore modules from NuGet
197 | if (Test-Path $MODULES_PACKAGES_CONFIG) {
198 | Push-Location
199 | Set-Location $MODULES_DIR
200 |
201 | Write-Verbose -Message "Restoring modules from NuGet..."
202 | $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`""
203 |
204 | if ($LASTEXITCODE -ne 0) {
205 | Throw "An error occurred while restoring NuGet modules."
206 | }
207 |
208 | Write-Verbose -Message ($NuGetOutput | out-string)
209 |
210 | Pop-Location
211 | }
212 |
213 | # Make sure that Cake has been installed.
214 | if (!(Test-Path $CAKE_EXE)) {
215 | Throw "Could not find Cake.exe at $CAKE_EXE"
216 | }
217 |
218 |
219 |
220 | # Build Cake arguments
221 | $cakeArguments = @("$Script");
222 | if ($Target) { $cakeArguments += "-target=$Target" }
223 | if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
224 | if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
225 | if ($ShowDescription) { $cakeArguments += "-showdescription" }
226 | if ($DryRun) { $cakeArguments += "-dryrun" }
227 | if ($Experimental) { $cakeArguments += "-experimental" }
228 | if ($Mono) { $cakeArguments += "-mono" }
229 | $cakeArguments += $ScriptArgs
230 |
231 | # Start Cake
232 | Write-Host "Running build script..."
233 | &$CAKE_EXE $cakeArguments
234 | exit $LASTEXITCODE
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##########################################################################
4 | # This is the Cake bootstrapper script for Linux and OS X.
5 | # This file was downloaded from https://github.com/cake-build/resources
6 | # Feel free to change this file to fit your needs.
7 | ##########################################################################
8 |
9 | # Define directories.
10 | SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
11 | TOOLS_DIR=$SCRIPT_DIR/tools
12 | ADDINS_DIR=$TOOLS_DIR/Addins
13 | MODULES_DIR=$TOOLS_DIR/Modules
14 | NUGET_EXE=$TOOLS_DIR/nuget.exe
15 | CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
16 | PACKAGES_CONFIG=$TOOLS_DIR/packages.config
17 | PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum
18 | ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config
19 | MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config
20 |
21 | # Define md5sum or md5 depending on Linux/OSX
22 | MD5_EXE=
23 | if [[ "$(uname -s)" == "Darwin" ]]; then
24 | MD5_EXE="md5 -r"
25 | else
26 | MD5_EXE="md5sum"
27 | fi
28 |
29 | # Define default arguments.
30 | SCRIPT="build.cake"
31 | CAKE_ARGUMENTS=()
32 |
33 | # Parse arguments.
34 | for i in "$@"; do
35 | case $1 in
36 | -s|--script) SCRIPT="$2"; shift ;;
37 | --) shift; CAKE_ARGUMENTS+=("$@"); break ;;
38 | *) CAKE_ARGUMENTS+=("$1") ;;
39 | esac
40 | shift
41 | done
42 |
43 | # Make sure the tools folder exist.
44 | if [ ! -d "$TOOLS_DIR" ]; then
45 | mkdir "$TOOLS_DIR"
46 | fi
47 |
48 | # Make sure that packages.config exist.
49 | if [ ! -f "$TOOLS_DIR/packages.config" ]; then
50 | echo "Downloading packages.config..."
51 | curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages
52 | if [ $? -ne 0 ]; then
53 | echo "An error occurred while downloading packages.config."
54 | exit 1
55 | fi
56 | fi
57 |
58 | # Download NuGet if it does not exist.
59 | if [ ! -f "$NUGET_EXE" ]; then
60 | echo "Downloading NuGet..."
61 | curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
62 | if [ $? -ne 0 ]; then
63 | echo "An error occurred while downloading nuget.exe."
64 | exit 1
65 | fi
66 | fi
67 |
68 | # Restore tools from NuGet.
69 | pushd "$TOOLS_DIR" >/dev/null
70 | if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then
71 | find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf
72 | fi
73 |
74 | mono "$NUGET_EXE" install -ExcludeVersion
75 | if [ $? -ne 0 ]; then
76 | echo "Could not restore NuGet tools."
77 | exit 1
78 | fi
79 |
80 | $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5"
81 |
82 | popd >/dev/null
83 |
84 | # Restore addins from NuGet.
85 | if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then
86 | pushd "$ADDINS_DIR" >/dev/null
87 |
88 | mono "$NUGET_EXE" install -ExcludeVersion
89 | if [ $? -ne 0 ]; then
90 | echo "Could not restore NuGet addins."
91 | exit 1
92 | fi
93 |
94 | popd >/dev/null
95 | fi
96 |
97 | # Restore modules from NuGet.
98 | if [ -f "$MODULES_PACKAGES_CONFIG" ]; then
99 | pushd "$MODULES_DIR" >/dev/null
100 |
101 | mono "$NUGET_EXE" install -ExcludeVersion
102 | if [ $? -ne 0 ]; then
103 | echo "Could not restore NuGet modules."
104 | exit 1
105 | fi
106 |
107 | popd >/dev/null
108 | fi
109 |
110 | # Make sure that Cake has been installed.
111 | if [ ! -f "$CAKE_EXE" ]; then
112 | echo "Could not find Cake.exe at '$CAKE_EXE'."
113 | exit 1
114 | fi
115 |
116 | # Start Cake
117 | exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}"
--------------------------------------------------------------------------------
/buildconfig.cake:
--------------------------------------------------------------------------------
1 | public class BuildConfig
2 | {
3 | private const string Version = "0.1.4";
4 | private const bool IsPreRelease = false;
5 |
6 | public readonly string SrcDir = "./src/";
7 | public readonly string ArtifactsDir = "./artifacts/";
8 | public readonly string TestResultsDir = "./testresults/";
9 |
10 | public string Target { get; private set; }
11 | public string SemVer { get; private set; }
12 | public string BuildVersion { get; private set; }
13 | public string BuildProfile { get; private set; }
14 |
15 | public static BuildConfig Create(
16 | ICakeContext context,
17 | BuildSystem buildSystem)
18 | {
19 | if (context == null)
20 | throw new ArgumentNullException("context");
21 |
22 | var buildRevision = context.Argument("buildrevision", "0");
23 |
24 | return new BuildConfig
25 | {
26 | Target = context.Argument("target", "Default"),
27 | SemVer = Version + (IsPreRelease ? $"-pre{buildRevision}" : string.Empty),
28 | BuildVersion = Version + "." + buildRevision,
29 | BuildProfile = context.Argument("configuration", "Release")
30 | };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | sqldb:
4 | image: "mcr.microsoft.com/mssql/server:2017-latest"
5 | environment:
6 | ACCEPT_EULA: "Y"
7 | MSSQL_PID: "Express"
8 | SA_PASSWORD: ${DNSQLDB_SA_PWD}
9 | ports:
10 | - "1401:1433"
--------------------------------------------------------------------------------
/init-local-config.sh:
--------------------------------------------------------------------------------
1 | cp ./resources/.env.sample .env
2 | cp ./resources/integrationtests.local.ini.sample ./src/tests/integrationtests/integrationtests.local.ini
--------------------------------------------------------------------------------
/resources/.env.sample:
--------------------------------------------------------------------------------
1 | DNSQLDB_SA_PWD=MyFooPassword
2 |
--------------------------------------------------------------------------------
/resources/integrationtests.local.ini.sample:
--------------------------------------------------------------------------------
1 | [ConnectionStrings]
2 | TestDb="Server=.,1401;Database=foodb;User ID=sa;Password=MyFooPassword"
--------------------------------------------------------------------------------
/resources/nuget-usage.md:
--------------------------------------------------------------------------------
1 | ## Global installation
2 | ```
3 | dotnet tool install -g dotnet-sqldb
4 | ```
5 |
6 | Usage either: `dotnet-sqldb` or `dotnet sqldb`
7 |
8 | ## Help
9 |
10 | ```
11 | dotnet-sqldb --help
12 | ```
13 |
14 | ## EnsureDb
15 | Ensures that the specified DB exists. If it does not exist, it gets created.
16 |
17 | ```
18 | dotnet-sqldb ensure [--connectionstring|-c] mycnstring
19 | ```
20 |
21 | ## DropDb
22 | Drops the specified database if it exists.
23 |
24 | ```
25 | dotnet-sqldb drop [--connectionstring|-c] mycnstring
26 | ```
27 |
28 | ## UpgradeDb
29 | Upgrades the database by applying SQL-scripts using [DbUp](https://github.com/dbup/dbup)
30 |
31 | ```
32 | dotnet-sqldb up [--connectionstring|-c] mycnstring [--assembly|-a] myassembly_with_embedded_scripts
33 | ```
34 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0.0.0
4 | .NET Global Tool using e.g. DbUp for SqlServer to manage migration scripts.
5 | danielwertheim
6 | danielwertheim
7 | Copyright danielwertheim 2019
8 | https://github.com/danielwertheim/dotnet-sqldb
9 | Git
10 | latest
11 |
12 |
--------------------------------------------------------------------------------
/src/DotNet-SqlDb.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.329
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C4A4BD2-46A9-4C30-9D07-ADC15B0F35DA}"
7 | ProjectSection(SolutionItems) = preProject
8 | Directory.Build.props = Directory.Build.props
9 | EndProjectSection
10 | EndProject
11 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "SqlDb", "projects\SqlDb\SqlDb.fsproj", "{40C771FE-915C-474A-B39C-557748C8EA52}"
12 | EndProject
13 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "UnitTests", "tests\UnitTests\UnitTests.fsproj", "{3AD116DB-C951-495E-8F57-9D7CBD30CD66}"
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{8C81C048-225C-4647-B173-3BEF807242D2}"
16 | EndProject
17 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.fsproj", "{CF41BFF6-FEC0-4CFD-8588-C6FE99D549C8}"
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Release|Any CPU = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {40C771FE-915C-474A-B39C-557748C8EA52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {40C771FE-915C-474A-B39C-557748C8EA52}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {40C771FE-915C-474A-B39C-557748C8EA52}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {40C771FE-915C-474A-B39C-557748C8EA52}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {3AD116DB-C951-495E-8F57-9D7CBD30CD66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {3AD116DB-C951-495E-8F57-9D7CBD30CD66}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {3AD116DB-C951-495E-8F57-9D7CBD30CD66}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {3AD116DB-C951-495E-8F57-9D7CBD30CD66}.Release|Any CPU.Build.0 = Release|Any CPU
33 | {CF41BFF6-FEC0-4CFD-8588-C6FE99D549C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {CF41BFF6-FEC0-4CFD-8588-C6FE99D549C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {CF41BFF6-FEC0-4CFD-8588-C6FE99D549C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {CF41BFF6-FEC0-4CFD-8588-C6FE99D549C8}.Release|Any CPU.Build.0 = Release|Any CPU
37 | EndGlobalSection
38 | GlobalSection(SolutionProperties) = preSolution
39 | HideSolutionNode = FALSE
40 | EndGlobalSection
41 | GlobalSection(NestedProjects) = preSolution
42 | {3AD116DB-C951-495E-8F57-9D7CBD30CD66} = {8C81C048-225C-4647-B173-3BEF807242D2}
43 | {CF41BFF6-FEC0-4CFD-8588-C6FE99D549C8} = {8C81C048-225C-4647-B173-3BEF807242D2}
44 | EndGlobalSection
45 | GlobalSection(ExtensibilityGlobals) = postSolution
46 | SolutionGuid = {E383C75D-C7F0-4753-B3C4-6D61FD1F7F96}
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/src/projects/SqlDb/Commands/Command.fs:
--------------------------------------------------------------------------------
1 | module SqlDb.Commands.Command
2 |
3 | open SqlDb
4 | open Serilog.Events
5 |
6 | let inline tryRun<'opts when 'opts : (member Verbose : bool)> commandName (f: 'opts -> Result) (opts: 'opts) =
7 | try
8 | let verbose = ((^opts) : (member Verbose : bool) (opts))
9 |
10 | if verbose = true then Logging.overrideLogLevelWith LogEventLevel.Verbose
11 |
12 | f opts
13 | with ex ->
14 | Errors.CommandError (commandName, ex) |> Error
--------------------------------------------------------------------------------
/src/projects/SqlDb/Commands/DropDbCommand.fs:
--------------------------------------------------------------------------------
1 | module SqlDb.Commands.DbDropCommand
2 |
3 | open CommandLine
4 | open DbUp
5 | open SqlDb
6 | open SqlDb.Logging
7 |
8 | let name = CommandName "DropDbCommand"
9 |
10 | let private logger = Logging.loggerFor LoggingContexts.DbDropCommand
11 |
12 | []
13 | type Options = {
14 | [