Skip to content

Commit

Permalink
Feat #54: Add --baseline support (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
wokket authored Oct 7, 2021
1 parent 228d5d4 commit 1551475
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 14 deletions.
3 changes: 1 addition & 2 deletions docs/MigratingFromRoundhousE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ grate is built using the new [`System.CommandLine`](https://github.com/dotnet/co
- grate has a single mandatory `-cs`/`--connstring` argument for simplicity. RH's `--database`, `--server`, `--accesstoken` etc arguments are now longer allowed.

- Not all previously supported tokens are available yet. For more information see the [Token Replacement docs](TokenReplacement.md).
- UserTokens have had two small changes. In keeping with the `System.CommandLine` standards the `--ut` option is now passed multiple times for multiple tokens, rather than parsing a single ';' delimited string. As a result of this change, the longer form of the option is now `--usertoken` (singular).
- UserTokens have had two small changes. In keeping with the `System.CommandLine` standards the `--ut` option is now passed multiple times for multiple tokens, rather than parsing a single ';' delimited string. Support for `;` delimited lists of tokens may be re-added in the future.

- The `DropCreate` 'mode' has been merged with the `--drop` option. RH used a single-run workflow for `Normal` and `RestoreRun` modes, but needed two executions for the `DropCreate` mode. grate uses the `--drop` option like RH but **it continues with the creation and migration afterwards**! If you have a scenario for dropping a database but _not_ then running a migration, please open an issue!

Expand All @@ -32,7 +32,6 @@ Rebuilding a decade old product from scratch takes time, and features have to be

Expect this list to shrink over time.

- `--baseline`
- `--defaultencoding`
- `--isuptodate`
- MSBuild Task.
Expand Down
16 changes: 13 additions & 3 deletions grate.unittests/CommandLineParsing.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.CommandLine.Invocation;
using System.CommandLine.Parsing;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using grate.Commands;
Expand Down Expand Up @@ -217,6 +218,14 @@ public async Task WarnAndIgnoreOnOneTimeScriptChanges(string args, bool expected
cfg?.WarnAndIgnoreOnOneTimeScriptChanges.Should().Be(expected);
}

[TestCase("", false)]
[TestCase("--baseline", true)]
public async Task Baseline(string args, bool expected)
{
var cfg = await ParseGrateConfiguration(args);
cfg?.Baseline.Should().Be(expected);
}

[Test]
public async Task WithoutTransaction_Default()
{
Expand All @@ -226,12 +235,13 @@ public async Task WithoutTransaction_Default()

[TestCase("--silent", 0)]
[TestCase("--ut=token=value", 1)]
[TestCase("--ut=token=value;abe=123", 2)]
[TestCase("--ut=token=value --usertoken=abc=123", 2)]
[TestCase("--ut=token=value --usertokens=abc=123", 2)]
//[TestCase("--usertokens=token=value;abe=123", 2)] This is a back-compat scenario we may want to add support for.
public async Task UserTokens(string args, int expectedCount)
{
var cfg = await ParseGrateConfiguration(args);
cfg?.UserTokens?.Should().HaveCount(expectedCount);
var t = cfg?.UserTokens.Safe().ToList();
t.Should().HaveCount(expectedCount);
}

[TestCase("", DatabaseType.sqlserver)] // default
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using FluentAssertions;
Expand Down Expand Up @@ -103,6 +104,48 @@ public async Task Are_recognized_by_script_name()
scripts.Should().HaveCount(7); // one time script ran once, the two everytime scripts ran every time.
}

[Test]
public async Task Are_not_run_in_baseline()
{
var db = TestConfig.RandomDatabase();

var knownFolders = KnownFolders.In(CreateRandomTempDirectory());
var config = new GrateConfiguration
{
Baseline = true, // this is important!
CreateDatabase = true,
ConnectionString = Context.ConnectionString(db),
AdminConnectionString = Context.AdminConnectionString,
Version = "a.b.c.e",
KnownFolders = knownFolders,
AlterDatabase = true,
NonInteractive = true,
Transaction = true,
DatabaseType = Context.DatabaseType
};

var path = knownFolders?.Views?.Path ?? throw new Exception("Config Fail");

WriteSql(path, "view.sql", "create view grate as select '1' as col;");

await using (var migrator = Context.GetMigrator(config))
{
await migrator.Migrate();
}

string sql = $"SELECT script_name FROM {Context.Syntax.TableWithSchema("grate", "ScriptsRun")}";

await using var conn = Context.CreateDbConnection(Context.ConnectionString(db));
var scripts = (await conn.QueryAsync<string>(sql)).ToArray();
scripts.Should().HaveCount(1); //marked as run

// but doesn't exist
Assert.ThrowsAsync(Context.DbExceptionType, async () => await conn.QueryAsync<string>("select * from grate"));



}

private void CreateEveryTimeScriptFile(MigrationsFolder? folder)
{
var dummySql = Context.Sql.SelectCurrentDatabase;
Expand Down
16 changes: 11 additions & 5 deletions grate/Commands/MigrateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public MigrateCommand(GrateMigrator mi) : base("Migrates the database")
Add(WarnAndIgnoreOnScriptChange());
Add(UserTokens());
Add(DoNotStoreScriptText());
Add(Baseline());
Add(RunAllAnyTimeScripts());
Add(DryRun());

Expand Down Expand Up @@ -158,18 +159,18 @@ private static Option<bool> Tokens() =>
private static Option<bool> WarnAndRunOnScriptChange() =>
new(
new[] { "-w", "--warnononetimescriptchanges" },
"WarnOnOneTimeScriptChanges - Instructs grate to execute changed one time scripts (DDL/DML in Upfolder) that have previously been run against the database instead of failing. A warning is logged for each one time script that is rerun. Defaults to false."
);
"WarnOnOneTimeScriptChanges - Instructs grate to execute changed one time scripts(DDL / DML in Upfolder) that have previously been run against the database instead of failing. A warning is logged for each one time script that is rerun. Defaults to false."
);

private static Option<bool> WarnAndIgnoreOnScriptChange() =>
new(
new[] { "--warnandignoreononetimescriptchanges" },
"WarnAndIgnoreOnOneTimeScriptChanges - Instructs grate to ignore and update the hash of changed one time scripts (DDL/DML in Up folder) that have previously been run against the database instead of failing. A warning is logged for each one time scripts that is rerun. Defaults to false."
);
);

private static Option<IEnumerable<string>> UserTokens() =>
new(
new[] { "--ut", "--usertoken" },
new[] { "--ut", "--usertokens" },
"User Tokens - Allows grate to perform token replacement on custom tokens ({{my_token}}). Set as a key=value pair, eg '--ut=my_token=myvalue'. Can be specified multiple times."
);

Expand All @@ -184,8 +185,13 @@ private static Option<bool> RunAllAnyTimeScripts() =>
new[] { "--runallanytimescripts", "--forceanytimescripts" },
"RunAllAnyTimeScripts - This instructs grate to run any time scripts every time it is run even if they haven't changed. Defaults to false."
);


private static Option<bool> Baseline() =>
new(
new[] { "--baseline" },
"Baseline - This instructs grate to mark the scripts as run, but not to actually run anything against the database. Use this option if you already have scripts that have been run through other means (and BEFORE you start the new ones)."
);

private static Option<bool> DryRun() =>
new(
new[] { "--dryrun" },
Expand Down
9 changes: 7 additions & 2 deletions grate/Configuration/GrateConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public string? AdminConnectionString
public bool WarnAndIgnoreOnOneTimeScriptChanges { get; init; }

//private static KnownFolders InCurrentDirectory() => KnownFolders.In(CurrentDirectory);

/// <summary>
/// The set of user-provided "key=value" pairs for use in token replacement.
/// </summary>
Expand All @@ -104,7 +104,12 @@ public string? AdminConnectionString
/// <summary>
/// If true grate will not store script text in the database to save space in small/embedded databases.
/// </summary>
public bool DoNotStoreScriptsRunText { get; init; }
public bool DoNotStoreScriptsRunText { get; init; }

/// <summary>
/// If true we mark scripts as run but don't actually run them.
/// </summary>
public bool Baseline { get; init; }

/// <summary>
/// If true we need to log what we would have done, but NOT run any SQL
Expand Down
2 changes: 1 addition & 1 deletion grate/Infrastructure/TokenProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public TokenProvider(GrateConfiguration config, IDatabase db)

["AfterMigrationFolderName"] = _config.KnownFolders?.AfterMigration.ToToken(),
["AlterDatabaseFolderName"] = _config.KnownFolders?.AlterDatabase.ToToken(),
//["Baseline"] = _config.Baseline.ToString(),
["Baseline"] = _config.Baseline.ToString(),
["BeforeMigrationFolderName"] = _config.KnownFolders?.BeforeMigration.ToToken(),
["CommandTimeout"] = _config.CommandTimeout.ToString(),
["CommandTimeoutAdmin"] = _config.AdminCommandTimeout.ToString(),
Expand Down
7 changes: 7 additions & 0 deletions grate/Migration/DbMigrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ async Task<bool> LogAndRunSql()
sql = ReplaceTokensIn(sql);
}

if (Configuration.Baseline)
{
await RecordScriptInScriptsRunTable(scriptName, sql, migrationType, versionId);
return false;
}

if (await ThisScriptIsAlreadyRun(scriptName) && !IsEverytimeScript(scriptName, migrationType))
{

Expand Down Expand Up @@ -233,6 +239,7 @@ private async Task RunTheActualSql(
throw;
}
}

await RecordScriptInScriptsRunTable(scriptName, sql, migrationType, versionId);
}

Expand Down

0 comments on commit 1551475

Please sign in to comment.