diff --git a/Build.ps1 b/Build.ps1 index 2825a97..3c3779b 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -40,7 +40,7 @@ function Publish-Gzips($version) { $rids = @("ubuntu.14.04-x64", "ubuntu.16.04-x64", "rhel.7-x64", "osx.10.12-x64") foreach ($rid in $rids) { - & dotnet publish src/Datalust.Piggy/Datalust.Piggy.csproj -c Release -f netcoreapp2.0 -r $rid /p:VersionPrefix=$version /p:ShowLinkerSizeComparison=true + & dotnet publish src/Datalust.Piggy/Datalust.Piggy.csproj -c Release -f netcoreapp2.0 -r $rid /p:VersionPrefix=$version if($LASTEXITCODE -ne 0) { exit 4 } # Make sure the archive contains a reasonable root filename @@ -61,7 +61,7 @@ function Publish-Gzips($version) function Publish-Msi($version) { - & dotnet publish src/Datalust.Piggy/Datalust.Piggy.csproj -c Release -f netcoreapp2.0 -r win10-x64 /p:VersionPrefix=$version /p:ShowLinkerSizeComparison=true + & dotnet publish src/Datalust.Piggy/Datalust.Piggy.csproj -c Release -f netcoreapp2.0 -r win10-x64 /p:VersionPrefix=$version if($LASTEXITCODE -ne 0) { exit 7 } & msbuild ./setup/Datalust.Piggy.Setup/Datalust.Piggy.Setup.wixproj /t:Build /p:Configuration=Release /p:Platform=x64 /p:Version=$version /p:BuildProjectReferences=false @@ -70,6 +70,12 @@ function Publish-Msi($version) mv ./setup/Datalust.Piggy.Setup/bin/Release/piggy.msi ./artifacts/piggy-$version.msi } +function Publish-Nupkgs($version) +{ + & dotnet pack src/Datalust.Piggy/Datalust.Piggy.csproj -c Release -o $PSScriptRoot/artifacts /p:VersionPrefix=$version /p:OutputType=Library + if($LASTEXITCODE -ne 0) { exit 9 } +} + Push-Location $PSScriptRoot $version = @{ $true = $env:APPVEYOR_BUILD_VERSION; $false = "99.99.99" }[$env:APPVEYOR_BUILD_VERSION -ne $NULL]; @@ -83,5 +89,6 @@ Execute-Tests Create-ArtifactDir Publish-Gzips($version) Publish-Msi($version) +Publish-Nupkgs($version) Pop-Location diff --git a/README.md b/README.md index fbb25f4..5e0da14 100644 --- a/README.md +++ b/README.md @@ -101,3 +101,15 @@ Available commands are: Type `piggy help ` for detailed help ``` +### C♯ API + +For development and test automation purposes, the core script runner is also packaged as a C♯ API and published to NuGet as _Datalust.Piggy_. + +```csharp +// dotnet add package Datalust.Piggy +var connectionString = // Npgsql connection string +using (var connection = DatabaseConnector.Connect(connectionString, createIfMissing: true) +{ + UpdateSession.ApplyChangeScripts(connection, "./db", new Dictionary()); +} +``` diff --git a/appveyor.yml b/appveyor.yml index c08eba7..2a52689 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,13 +1,20 @@ version: 1.0.{build} skip_tags: true -image: Visual Studio 2017 +image: Visual Studio 2019 build_script: - ps: ./Build.ps1 test: off artifacts: - path: artifacts/piggy-*.msi - path: artifacts/piggy-*.tar.gz +- path: artifacts/Datalust.Piggy.*.nupkg deploy: +- provider: NuGet + api_key: + secure: Xi+qouQ6+cOJ85PMOQKEGy+MC1EKyYJCoF2YfuE9Rcj6lvNtm08TIZllTJCqId+M + skip_symbols: true + on: + branch: master - provider: GitHub auth_token: secure: Bo3ypKpKFxinjR9ShkNekNvkob2iklHJU+UlYyfHtcFFIAa58SV2TkEd0xWxz633 diff --git a/piggy.sln b/piggy.sln index 9cf99de..281c33f 100644 --- a/piggy.sln +++ b/piggy.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29020.237 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FC0A256C-CC1F-4ECE-AF09-707248D10DC1}" EndProject @@ -11,7 +11,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sln", "sln", "{2EA56595-519 appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 LICENSE = LICENSE - nuget.config = nuget.config README.md = README.md EndProjectSection EndProject diff --git a/piggy.sln.DotSettings b/piggy.sln.DotSettings new file mode 100644 index 0000000..5df1a0a --- /dev/null +++ b/piggy.sln.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/setup/Datalust.Piggy.Setup/Piggy.wxs b/setup/Datalust.Piggy.Setup/Piggy.wxs index f2dc701..4f81fd8 100644 --- a/setup/Datalust.Piggy.Setup/Piggy.wxs +++ b/setup/Datalust.Piggy.Setup/Piggy.wxs @@ -2,494 +2,707 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + + + + - + - + - + - + + + + - + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + - + + + + + + + - + - + - + + + + - + - + + + + + + + + + + - + + + + - + - + + + + - + + + + - + + + + - + - + - + - + + + + - + - + + + + - + - + - + + + + + + + - + - + + + + - + + + + - + + + + + + + - + + + + - + + + + - + + + + + + + + + + + + + + + + - + - + + + + + + + + + + - + + + + + + + - + - + + + + - + - + - + - + - + + + + - + + + + + + + + + + + + + + + + - + - + + + + - + - + - + + + + - + - + - + - + - + + + + - + - + + + + - + + + + + + + - + - + - + - + - + + + + + + + - + + + + - + + + + - + + + + + + + - + - + - + - + - + + + + - + - + + + + - + - + + + + + + + + + + - + - + - + - + - + - + + + + - + - + + + + - + - + - + + + + - + + + + + + + + + + + + + + + + + + + - + + + + - + - + - + - + + + + + + + - + - + - + - + @@ -546,12 +759,16 @@ + + + + @@ -570,92 +787,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Datalust.Piggy/Cli/Commands/UpdateCommand.cs b/src/Datalust.Piggy/Cli/Commands/UpdateCommand.cs index c40324f..d7587d6 100644 --- a/src/Datalust.Piggy/Cli/Commands/UpdateCommand.cs +++ b/src/Datalust.Piggy/Cli/Commands/UpdateCommand.cs @@ -1,5 +1,6 @@ using System; using Datalust.Piggy.Cli.Features; +using Datalust.Piggy.Database; using Datalust.Piggy.Update; using Npgsql; using Serilog; @@ -35,9 +36,10 @@ protected override int Run() try { - UpdateSession.ApplyChangeScripts( - _databaseFeature.Host, _databaseFeature.Database, _usernamePasswordFeature.Username, _usernamePasswordFeature.Password, - _createIfMissing, _scriptRootFeature.ScriptRoot, _defineVariablesFeature.Variables); + using (var connection = DatabaseConnector.Connect(_databaseFeature.Host, _databaseFeature.Database, _usernamePasswordFeature.Username, _usernamePasswordFeature.Password, _createIfMissing)) + { + UpdateSession.ApplyChangeScripts(connection, _scriptRootFeature.ScriptRoot, _defineVariablesFeature.Variables); + } return 0; } diff --git a/src/Datalust.Piggy/Database/DatabaseConnector.cs b/src/Datalust.Piggy/Database/DatabaseConnector.cs index 41b2e18..3353c97 100644 --- a/src/Datalust.Piggy/Database/DatabaseConnector.cs +++ b/src/Datalust.Piggy/Database/DatabaseConnector.cs @@ -1,35 +1,38 @@ +using System; using Npgsql; using Npgsql.Logging; using Serilog; namespace Datalust.Piggy.Database { - static class DatabaseConnector + /// + /// Assists with making a PostgreSQL database connection. + /// + public static class DatabaseConnector { static DatabaseConnector() { NpgsqlLogManager.Provider = new SerilogLoggingProvider(); } + /// + /// Connect to the specified database. + /// + /// The PostgreSQL host to connect to. + /// The database to update. + /// The username to authenticate with. + /// The password to authenticate with. + /// If true, Piggy will attempt to create the database if it doesn't exist. The + /// database must already exist, otherwise. + /// An open database connection. public static NpgsqlConnection Connect(string host, string database, string username, string password, bool createIfMissing) { - Log.Information("Connecting to database {Database} on {Host}", database, host); - - var cstr = $"Host={host};Username={username};Password={password};Database={database}"; - NpgsqlConnection conn = null; try { - conn = new NpgsqlConnection(cstr); - conn.Open(); - - Log.Information("Connected"); - - return conn; + return Connect($"Host={host};Username={username};Password={password};Database={database}"); } catch (PostgresException px) when (px.SqlState == "3D000") { - conn?.Dispose(); - if (createIfMissing && TryCreate(host, database, username, password)) return Connect(host, database, username, password, false); @@ -37,6 +40,34 @@ public static NpgsqlConnection Connect(string host, string database, string user } } + /// + /// Connect to the specified database. + /// + /// A connection string identifying the database. + /// An open database connection. + public static NpgsqlConnection Connect(string connectionString) + { + if (connectionString == null) throw new ArgumentNullException(nameof(connectionString)); + + var conn = new NpgsqlConnection(connectionString); + + Log.Information("Connecting to database {Database} on {Host}", conn.Database, conn.Host); + + try + { + conn.Open(); + } + catch + { + conn.Dispose(); + throw; + } + + Log.Information("Connected"); + + return conn; + } + static bool TryCreate(string host, string database, string username, string password) { Log.Information("Database does not exist; attempting to create it"); diff --git a/src/Datalust.Piggy/Datalust.Piggy.csproj b/src/Datalust.Piggy/Datalust.Piggy.csproj index 9a2b28e..91b6e45 100644 --- a/src/Datalust.Piggy/Datalust.Piggy.csproj +++ b/src/Datalust.Piggy/Datalust.Piggy.csproj @@ -1,14 +1,20 @@  + A friendly PostgreSQL script runner in the spirit of DbUp. Exe netcoreapp2.0 piggy ..\..\asset\Piggy-Icon-128px.ico win10-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;rhel.7-x64;osx.10.12-x64 True + Datalust and Contributors + Datalust.Piggy + postgresql + https://raw.githubusercontent.com/datalust/piggy/dev/asset/Piggy-Icon-128px.png + https://github.com/datalust/piggy + Apache-2.0 True - @@ -28,15 +34,14 @@ - - - - - - + + + + + + - \ No newline at end of file diff --git a/src/Datalust.Piggy/Update/UpdateSession.cs b/src/Datalust.Piggy/Update/UpdateSession.cs index 51a662e..4693c2b 100644 --- a/src/Datalust.Piggy/Update/UpdateSession.cs +++ b/src/Datalust.Piggy/Update/UpdateSession.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.IO; using System.Text; -using Datalust.Piggy.Database; using Datalust.Piggy.Filesystem; using Datalust.Piggy.History; using Datalust.Piggy.Status; @@ -11,32 +10,37 @@ namespace Datalust.Piggy.Update { - static class UpdateSession + /// + /// Applies updates to a target database. + /// + public static class UpdateSession { - public static void ApplyChangeScripts(string host, string database, string username, string password, - bool createIfMissing, string scriptRoot, IReadOnlyDictionary variables) + /// + /// Apply change scripts from a folder hierarchy. + /// + /// The database connection to use. + /// A root filesystem folder under which change scripts are stored. + /// A set of variables to replace within change scripts. + public static void ApplyChangeScripts(NpgsqlConnection connection, string scriptRoot, IReadOnlyDictionary variables) { - using (var connection = DatabaseConnector.Connect(host, database, username, password, createIfMissing)) - { - var scripts = DatabaseStatus.GetPendingScripts(connection, scriptRoot); - - Log.Information("Found {Count} new script files to apply", scripts.Length); + var scripts = DatabaseStatus.GetPendingScripts(connection, scriptRoot); - if (scripts.Length != 0) - { - Log.Information("Ensuring the change log table exists"); - using (var command = new NpgsqlCommand(AppliedChangeScriptLog.ChangesTableCreateScript, connection)) - command.ExecuteNonQuery(); - } + Log.Information("Found {Count} new script files to apply", scripts.Length); - foreach (var script in scripts) - { - Log.Information("Applying {FullPath} as {ScriptFile}", script.FullPath, script.RelativeName); - ApplyChangeScript(connection, script, variables); - } + if (scripts.Length != 0) + { + Log.Information("Ensuring the change log table exists"); + using (var command = new NpgsqlCommand(AppliedChangeScriptLog.ChangesTableCreateScript, connection)) + command.ExecuteNonQuery(); + } - Log.Information("Done"); + foreach (var script in scripts) + { + Log.Information("Applying {FullPath} as {ScriptFile}", script.FullPath, script.RelativeName); + ApplyChangeScript(connection, script, variables); } + + Log.Information("Done"); } static void ApplyChangeScript(NpgsqlConnection connection, ChangeScriptFile script, IReadOnlyDictionary variables)