Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Refactor of the /searchallinsteadoftraverse option and added ablility to specific an abritrary file ordering. #80

Open
wants to merge 5 commits into from

2 participants

@icedtoast

Refactor of the /searchallinsteadoftraverse logic into a FileLocator interface.

New /scriptorder option which chooses between traverse, recurse or the specified ordering locators.

Specific Ordering allows you to place a file inside the migration directory which specifies the order of the files containing in that directory and it's subdirectories. This can be used to:

  • order scripts in terms of their dependencies
  • help handle the merging of database branches.
icedtoast added some commits
@icedtoast icedtoast Refactor searchallinsteadoftraverse handling
Add the FileLocator interface to abstract the selection of files from
the migration folders. This replaces the need for the Migration Runner
to explicitly decide what method to call on the FileSystemAccess
interface.

 - the Traverse class replaces the get_all_file_name_strings_in method.

 - the Recurse class replace the get_all_file_name_strings_recurevly_in
   method.

The dependency injection container picks the appropiate implementation
dependening on the prescene or lack of the /searchallinsteadoftraverse
switch.
dd9afa2
@icedtoast icedtoast Add Specified Ordering File Locator
Add SpecifiedOrder File Locator and added a /scriptorder option which
allows you to select between the traverse, recurse or scriptorder
options. The /searchallinsteadoftraverse option also controls the
/scriptorder so RoundHouse stays backwards compatibile.

The SpecifiedOrder Locator looks at file (set by the /SpecifiedOrderFile
option, otherwise scriptorder.txt) in the migration folder and
orders each file in that migration folder and it's subdirectories based
on the position that file has in the specified order file.

Files that aren't inside the specified order file are returned after the
files that are and are sorted via the culture invariant case-insensitive
comparer on the filename relative to the migration directory.

Use Cases:

1. Using the scriptorder file allows to specify the script order in your
integration branch as you merge different feature branches into it. This
means that feature branch scripts can always rely on the existing
scripts in the integration branch being run first.

2. Stored Procedure A depends on Stored Procedure B.

Notes:

The script order file considers lines starting with # as comment lines
and are ignored.
8251f7a
@ferventcoder ferventcoder commented on the diff
product/roundhouse.console/Program.cs
@@ -291,7 +292,19 @@ private static void parse_arguments_and_set_up_configuration(ConfigurationProper
option => configuration.DryRun = option != null)
.Add("searchallinsteadoftraverse=|searchallsubdirectoriesinsteadoftraverse=",
"SearchAllSubdirectoriesInsteadOfTraverse - Each Migration folder's subdirectories are traversed by default. This option pulls back scripts from the main directory and all subdirectories at once. Defaults to 'false'",
- option => configuration.SearchAllSubdirectoriesInsteadOfTraverse = option != null)
+ option => configuration.ScriptOrder = option != null ? "recurse" : "traverse")
+ .Add("scriptorder=",
+ string.Format(
+ "ScriptOrder is used to tell RoundHouse which script ordering method to use, can be: traverse, recurse or specified, defaults to: {0}",
+ ApplicationParameters.default_script_order),
+ option => configuration.ScriptOrder = option)
+ .Add("specifiedorderfile=",
@ferventcoder Owner

ScriptOrder itself should accept a file, no need for this second argument

ScriptOrder is so you can select between all the different orderings, I left /searchallinsteadoftraverse in there for backwards compatibility. ScriptOrder only accepts a fixed set of strings: traverse, recurse or specified, so that if somebody adds another ordering later they can just add a possible string to this option. (maybe it should be an enum?).

I could change /specifiedorderfile to automatically select the specified order option if it is set?

@ferventcoder Owner

Thinking the file itself could be specified, and if it is, it automatically sets to specified.

so /scriptorder will accept "traverse", "recurse", "specified" (defaulting to "scriptorder.txt") or the filename for the specified ordering?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ferventcoder
Owner

I'm digging the naming convention here for traverse/recurse and the name change for the option.

@ferventcoder
Owner

I do like most of this commit. I like the refactoring back to one set of files.

I would like to see some automated tests coming with specified ordering. That has some complexity in it.

@icedtoast

For the tests, what test runner do you use? I'm using Gallio at the moment.

@ferventcoder
Owner

Developwithpassion.bdd but looking to move to tinyspec

icedtoast added some commits
@icedtoast icedtoast Merge remote-tracking branch 'origin/master' into specified-ordering
Conflicts:
	product/roundhouse.console/Program.cs
2388553
@icedtoast icedtoast Add tests for SpecifiedOrdering
Add Unit Tests to specify the behaviour of the specified ordering file
locator. To make it easier to test, the comparsion logic was extracted
into a Comparer implementation. The unit tests test the Comparer
implementation itself, not the SpecifiedOrdering FileLocator, which just
wraps the Comparer.
6d257c0
@ferventcoder

It's alright, most folks really don't get the idea of natural language in unit testing for awhile.

@icedtoast icedtoast Change specifiedordering to ignore file extensions
Change specified ordering filelocator to ignore file extensions when it
compares files not contained within the specified order file. This means
if you have files named like this:
  - 001.sql
  - 001.env.production.sql
It will return them in that order, rather than the opposite.
699bf5a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 6, 2012
  1. @icedtoast

    Refactor searchallinsteadoftraverse handling

    icedtoast authored
    Add the FileLocator interface to abstract the selection of files from
    the migration folders. This replaces the need for the Migration Runner
    to explicitly decide what method to call on the FileSystemAccess
    interface.
    
     - the Traverse class replaces the get_all_file_name_strings_in method.
    
     - the Recurse class replace the get_all_file_name_strings_recurevly_in
       method.
    
    The dependency injection container picks the appropiate implementation
    dependening on the prescene or lack of the /searchallinsteadoftraverse
    switch.
Commits on Sep 7, 2012
  1. @icedtoast

    Add Specified Ordering File Locator

    icedtoast authored
    Add SpecifiedOrder File Locator and added a /scriptorder option which
    allows you to select between the traverse, recurse or scriptorder
    options. The /searchallinsteadoftraverse option also controls the
    /scriptorder so RoundHouse stays backwards compatibile.
    
    The SpecifiedOrder Locator looks at file (set by the /SpecifiedOrderFile
    option, otherwise scriptorder.txt) in the migration folder and
    orders each file in that migration folder and it's subdirectories based
    on the position that file has in the specified order file.
    
    Files that aren't inside the specified order file are returned after the
    files that are and are sorted via the culture invariant case-insensitive
    comparer on the filename relative to the migration directory.
    
    Use Cases:
    
    1. Using the scriptorder file allows to specify the script order in your
    integration branch as you merge different feature branches into it. This
    means that feature branch scripts can always rely on the existing
    scripts in the integration branch being run first.
    
    2. Stored Procedure A depends on Stored Procedure B.
    
    Notes:
    
    The script order file considers lines starting with # as comment lines
    and are ignored.
Commits on Sep 11, 2012
  1. @icedtoast

    Merge remote-tracking branch 'origin/master' into specified-ordering

    icedtoast authored
    Conflicts:
    	product/roundhouse.console/Program.cs
  2. @icedtoast

    Add tests for SpecifiedOrdering

    icedtoast authored
    Add Unit Tests to specify the behaviour of the specified ordering file
    locator. To make it easier to test, the comparsion logic was extracted
    into a Comparer implementation. The unit tests test the Comparer
    implementation itself, not the SpecifiedOrdering FileLocator, which just
    wraps the Comparer.
Commits on Sep 27, 2012
  1. @icedtoast

    Change specifiedordering to ignore file extensions

    icedtoast authored
    Change specified ordering filelocator to ignore file extensions when it
    compares files not contained within the specified order file. This means
    if you have files named like this:
      - 001.sql
      - 001.env.production.sql
    It will return them in that order, rather than the opposite.
This page is out of date. Refresh to see the latest.
Showing with 537 additions and 96 deletions.
  1. +18 −3 product/roundhouse.console/Program.cs
  2. +12 −2 product/roundhouse.tasks/Roundhouse.cs
  3. +219 −0 product/roundhouse.tests/infrastructure/filesystem/filelocators/SpecifiedOrderingSpecs.cs
  4. +1 −0  product/roundhouse.tests/roundhouse.tests.csproj
  5. +3 −1 product/roundhouse/Migrate.cs
  6. +2 −1  product/roundhouse/consoles/DefaultConfiguration.cs
  7. +28 −0 product/roundhouse/infrastructure.app/ApplicationConfiguraton.cs
  8. +3 −1 product/roundhouse/infrastructure.app/ConfigurationPropertyHolder.cs
  9. +2 −0  product/roundhouse/infrastructure/ApplicationParameters.cs
  10. +0 −30 product/roundhouse/infrastructure/filesystem/FileSystemAccess.cs
  11. +0 −44 product/roundhouse/infrastructure/filesystem/WindowsFileSystemAccess.cs
  12. +18 −0 product/roundhouse/infrastructure/filesystem/filelocators/FileLocator.cs
  13. +35 −0 product/roundhouse/infrastructure/filesystem/filelocators/Recurse.cs
  14. +134 −0 product/roundhouse/infrastructure/filesystem/filelocators/SpecifiedOrdering.cs
  15. +45 −0 product/roundhouse/infrastructure/filesystem/filelocators/Traverse.cs
  16. +4 −0 product/roundhouse/roundhouse.csproj
  17. +7 −12 product/roundhouse/runners/RoundhouseMigrationRunner.cs
  18. +6 −2 product/roundhouse/runners/RoundhouseRedGateCompareRunner.cs
View
21 product/roundhouse.console/Program.cs
@@ -17,6 +17,7 @@
using roundhouse.migrators;
using roundhouse.resolvers;
using roundhouse.runners;
+using roundhouse.infrastructure.filesystem.filelocators;
namespace roundhouse.console
{
@@ -304,7 +305,19 @@ private static void parse_arguments_and_set_up_configuration(ConfigurationProper
option => configuration.DryRun = option != null)
.Add("searchallinsteadoftraverse=|searchallsubdirectoriesinsteadoftraverse=",
"SearchAllSubdirectoriesInsteadOfTraverse - Each Migration folder's subdirectories are traversed by default. This option pulls back scripts from the main directory and all subdirectories at once. Defaults to 'false'",
- option => configuration.SearchAllSubdirectoriesInsteadOfTraverse = option != null)
+ option => configuration.ScriptOrder = option != null ? "recurse" : "traverse")
+ .Add("scriptorder=",
+ string.Format(
+ "ScriptOrder is used to tell RoundHouse which script ordering method to use, can be: traverse, recurse or specified, defaults to: {0}",
+ ApplicationParameters.default_script_order),
+ option => configuration.ScriptOrder = option)
+ .Add("specifiedorderfile=",
@ferventcoder Owner

ScriptOrder itself should accept a file, no need for this second argument

ScriptOrder is so you can select between all the different orderings, I left /searchallinsteadoftraverse in there for backwards compatibility. ScriptOrder only accepts a fixed set of strings: traverse, recurse or specified, so that if somebody adds another ordering later they can just add a possible string to this option. (maybe it should be an enum?).

I could change /specifiedorderfile to automatically select the specified order option if it is set?

@ferventcoder Owner

Thinking the file itself could be specified, and if it is, it automatically sets to specified.

so /scriptorder will accept "traverse", "recurse", "specified" (defaulting to "scriptorder.txt") or the filename for the specified ordering?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ string.Format(
+ "SpecifiedOrderFile - The file name relative to the migration folder of the ordering file using by the specified scriptorder type, defaults to: {0}",
+ ApplicationParameters.default_specified_order_file),
+ option => configuration.SpecifiedOrderFile = option
+ );
+
;
try
@@ -431,7 +444,8 @@ private static RoundhouseMigrationRunner get_migration_runner(ConfigurationPrope
configuration.DoNotCreateDatabase,
configuration.WithTransaction,
configuration.RecoveryModeSimple,
- configuration);
+ configuration,
+ Container.get_an_instance_of<FileLocator>());
}
private static RoundhouseRedGateCompareRunner get_diff_runner(ConfigurationPropertyHolder configuration, RoundhouseMigrationRunner migration_runner)
@@ -439,7 +453,8 @@ private static RoundhouseRedGateCompareRunner get_diff_runner(ConfigurationPrope
return new RoundhouseRedGateCompareRunner(
Container.get_an_instance_of<KnownFolders>(),
Container.get_an_instance_of<FileSystemAccess>(),
- configuration, migration_runner);
+ configuration, migration_runner,
+ Container.get_an_instance_of<FileLocator>());
}
}
}
View
14 product/roundhouse.tasks/Roundhouse.cs
@@ -13,6 +13,7 @@
using resolvers;
using runners;
using Environment = environments.Environment;
+ using roundhouse.infrastructure.filesystem.filelocators;
public sealed class Roundhouse : ITask, ConfigurationPropertyHolder
{
@@ -130,10 +131,18 @@ bool ITask.Execute()
public bool DisableTokenReplacement { get; set; }
- public bool SearchAllSubdirectoriesInsteadOfTraverse { get; set; }
+ public bool SearchAllSubdirectoriesInsteadOfTraverse
+ {
+ get { return ScriptOrder == "recurse"; }
+ set { ScriptOrder = value ? "recurse" : "traverse"; }
+ }
public bool DisableOutput { get; set; }
+ public string ScriptOrder { get; set; }
+
+ public string SpecifiedOrderFile { get; set; }
+
#endregion
public void run_the_task()
@@ -162,7 +171,8 @@ public void run_the_task()
DoNotCreateDatabase,
WithTransaction,
RecoveryModeSimple,
- this);
+ this,
+ Container.get_an_instance_of<FileLocator>());
roundhouse_runner.run();
}
View
219 product/roundhouse.tests/infrastructure/filesystem/filelocators/SpecifiedOrderingSpecs.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using developwithpassion.bdd.mbunit.standard.observations;
+
+namespace roundhouse.tests.infrastructure.filesystem.filelocators
+{
+ using bdddoc.core;
+ using developwithpassion.bdd.mbunit;
+ using developwithpassion.bdd.mbunit.standard;
+ using developwithpassion.bdd.mbunit.standard.observations;
+ using roundhouse.infrastructure.extensions;
+ using roundhouse.infrastructure.filesystem.filelocators;
+ using MbUnit.Framework;
+
+ public class SpecifiedOrderingSpecs
+ {
+ public abstract class concern_for_specified_ordering : observations_for_a_static_sut
+ {
+ }
+
+ [Concern(typeof(SpecifiedOrdering.Comparer))]
+ public class specified_ordering : concern_for_specified_ordering
+ {
+ [Observation]
+ public void Should_put_items_in_order_of_specified()
+ {
+ var specified = new[] {
+ @"second\world.sql",
+ @"first\hello.sql"
+ };
+
+ var actual = new[] {
+ @"first\hello.sql",
+ @"second\world.sql"
+ };
+
+ var comparator = new SpecifiedOrdering.Comparer(specified, "");
+
+ Array.Sort(actual, comparator);
+
+ CollectionAssert.AreEqual(specified, actual);
+ }
+
+ [Observation]
+ public void Should_put_specified_items_before_unspecified_items()
+ {
+ var specified = new[] {
+ @"second\world.sql",
+ };
+
+ var expected = new[] {
+ @"second\world.sql",
+ @"first\hello.sql"
+ };
+
+ var actual = new[] {
+ @"first\hello.sql",
+ @"second\world.sql"
+ };
+
+ var comparator = new SpecifiedOrdering.Comparer(specified, "");
+
+ Array.Sort(actual, comparator);
+
+ CollectionAssert.AreEqual(expected, actual);
+ }
+
+ [Observation]
+ public void Should_put_unspecified_items_in_caseinsensitive_invariant_order()
+ {
+ var expected = new[] {
+ @"first\hello.sql",
+ @"second\world.sql"
+ };
+
+ var actual = new[] {
+ @"second\world.sql",
+ @"first\hello.sql"
+ };
+
+ var comparator = new SpecifiedOrdering.Comparer(Enumerable.Empty<string>(), "");
+
+ Array.Sort(actual, comparator);
+
+ CollectionAssert.AreEqual(expected, actual);
+ }
+
+ [Observation]
+ public void Should_put_shorter_filenames_before_longer_filenames()
+ {
+ var expected = new string[]
+ {
+ @"001\first.sql",
+ @"001\first.longer.sql"
+ };
+ var actual = new string[]
+ {
+ @"001\first.longer.sql",
+ @"001\first.sql",
+ };
+ var comparator = new SpecifiedOrdering.Comparer(Enumerable.Empty<string>(), "");
+
+ Array.Sort(actual, comparator);
+
+ CollectionAssert.AreEqual(expected, actual);
+ }
+
+ [Observation]
+ public void Should_throw_when_item_is_specified_twice()
+ {
+ bool threw = false;
+
+ try
+ {
+ new SpecifiedOrdering.Comparer(new[] { "twice", "twice" }, "");
+ }
+ catch (ArgumentException ae)
+ {
+ threw = true;
+ }
+
+ Assert.IsTrue(threw, "ArgumentException should have been thrown, but wasn't");
+ }
+
+ // Should return less than when lhs is specified and rhs isn't
+ [Observation]
+ public void Should_return_less_than_when_lhs_is_specified_and_rhs_is_not()
+ {
+ string lhs = "specified";
+
+ string rhs = "hello";
+
+ var comparer = new SpecifiedOrdering.Comparer(new [] { lhs }, "");
+
+ Assert.Less(comparer.Compare(lhs, rhs), 0);
+ }
+
+ // should return greater than when lhs is not specified and rhs is.
+ [Observation]
+ public void Should_return_less_than_when_lhs_is_not_specified_and_rhs_is()
+ {
+ string lhs = "good morning";
+
+ string rhs = "hello";
+
+ var comparer = new SpecifiedOrdering.Comparer(new [] { rhs }, "");
+
+ Assert.Greater(comparer.Compare(lhs, rhs), 0);
+ }
+
+ // should return less than when lhs is before rhs in specified list
+ [Observation]
+ public void Should_return_less_than_when_lhs_is_specified_before_rhs()
+ {
+ string lhs = "good morning";
+
+ string rhs = "hello";
+
+ var comparer = new SpecifiedOrdering.Comparer(new [] { lhs, rhs }, "");
+
+ Assert.Less(comparer.Compare(lhs, rhs), 0);
+ }
+
+ // should return greater than when lhs is after rhs in specified list
+ [Observation]
+ public void Should_return_greater_than_when_lhs_is_specified_after_rhs()
+ {
+ string lhs = "good morning";
+
+ string rhs = "hello";
+
+ var comparer = new SpecifiedOrdering.Comparer(new [] { rhs, lhs }, "");
+
+ Assert.Greater(comparer.Compare(lhs, rhs), 0);
+ }
+
+ // should return less than when both aren't specified and lhs is less than rhs
+ [Observation]
+ public void Should_return_less_than_when_neither_are_specified_and_is_lhs_less_than_rhs()
+ {
+ string lhs = "good morning";
+
+ string rhs = "hello";
+
+ var comparer = new SpecifiedOrdering.Comparer(Enumerable.Empty<string>(), "");
+
+ Assert.Less(comparer.Compare(lhs, rhs), 0);
+ }
+
+ // should return greater than when both aren't specified and lhs is greater than rhs
+
+ [Observation]
+ public void Should_return_greater_than_when_neither_are_specified_and_is_lhs_greater_than_rhs()
+ {
+ string lhs = "good morning";
+
+ string rhs = "are you there?";
+
+ var comparer = new SpecifiedOrdering.Comparer(Enumerable.Empty<string>(), "");
+
+ Assert.Greater(comparer.Compare(lhs, rhs), 0);
+ }
+
+ // should return 0 when strings are the same.
+ [Observation]
+ public void Should_return_equal_when_both_sides_are_equal()
+ {
+ string lhs = "Hello";
+ string rhs = "hello";
+
+ var comparer = new SpecifiedOrdering.Comparer(Enumerable.Empty<string>(), "");
+
+ Assert.AreEqual(0, comparer.Compare(lhs, rhs));
+ }
+ }
+ }
+}
View
1  product/roundhouse.tests/roundhouse.tests.csproj
@@ -115,6 +115,7 @@
<Compile Include="infrastructure\extensions\StringExtensionsSpecs.cs" />
<Compile Include="infrastructure\extensions\TypeCastingSpecs.cs" />
<Compile Include="infrastructure\extensions\TypeExtensionsSpecs.cs" />
+ <Compile Include="infrastructure\filesystem\filelocators\SpecifiedOrderingSpecs.cs" />
<Compile Include="infrastructure\logging\custom\Log4NetLogFactorySpecs.cs" />
<Compile Include="infrastructure\logging\custom\Log4NetLoggerSpecs.cs" />
<Compile Include="infrastructure\logging\LogSpecs.cs" />
View
4 product/roundhouse/Migrate.cs
@@ -13,6 +13,7 @@ namespace roundhouse
using resolvers;
using runners;
using Environment = roundhouse.environments.Environment;
+ using roundhouse.infrastructure.filesystem.filelocators;
public class Migrate
{
@@ -69,7 +70,8 @@ public void Run()
configuration.DoNotCreateDatabase,
configuration.WithTransaction,
configuration.RecoveryModeSimple,
- configuration);
+ configuration,
+ Container.get_an_instance_of<FileLocator>());
migrator.run();
}
View
3  product/roundhouse/consoles/DefaultConfiguration.cs
@@ -55,7 +55,8 @@ public sealed class DefaultConfiguration : ConfigurationPropertyHolder
public bool Baseline { get; set; }
public bool RunAllAnyTimeScripts { get; set; }
public bool DisableTokenReplacement { get; set; }
- public bool SearchAllSubdirectoriesInsteadOfTraverse { get; set; }
public bool DisableOutput { get; set; }
+ public string ScriptOrder { get; set; }
+ public string SpecifiedOrderFile { get; set; }
}
}
View
28 product/roundhouse/infrastructure.app/ApplicationConfiguraton.cs
@@ -23,6 +23,7 @@ namespace roundhouse.infrastructure.app
using StructureMap;
using Container = roundhouse.infrastructure.containers.Container;
using Environment = roundhouse.environments.Environment;
+ using roundhouse.infrastructure.filesystem.filelocators;
public static class ApplicationConfiguraton
{
@@ -140,6 +141,14 @@ public static void set_defaults_if_properties_are_not_set(ConfigurationPropertyH
{
configuration_property_holder.RecoveryMode = RecoveryMode.Simple;
}
+ if(string.IsNullOrEmpty(configuration_property_holder.ScriptOrder))
+ {
+ configuration_property_holder.ScriptOrder = ApplicationParameters.default_script_order;
+ }
+ if(string.IsNullOrEmpty(configuration_property_holder.SpecifiedOrderFile))
+ {
+ configuration_property_holder.SpecifiedOrderFile = ApplicationParameters.default_specified_order_file;
+ }
}
private static void set_up_current_mappings(ConfigurationPropertyHolder configuration_property_holder)
@@ -180,6 +189,25 @@ private static InversionContainer build_items_for_container(ConfigurationPropert
cfg.For<VersionResolver>().Singleton().Use(
context => VersionResolverBuilder.build(context.GetInstance<FileSystemAccess>(), configuration_property_holder));
cfg.For<Environment>().Singleton().Use(new DefaultEnvironment(configuration_property_holder));
+ cfg.For<FileLocator>().Singleton().Use(() =>
+ {
+ switch (configuration_property_holder.ScriptOrder)
+ {
+ case "traverse":
+ return new Traverse();
+ case "recurse":
+ return new Recurse();
+ case "specified":
+ return
+ new SpecifiedOrdering (
+ configuration_property_holder.SpecifiedOrderFile);
+ }
+ throw new ArgumentOutOfRangeException
+ ("ScriptOrder",
+ configuration_property_holder
+ .ScriptOrder,
+ "Unknown ScriptOrder");
+ });
});
// forcing a build of database to initialize connections so we can be sure server/database have values
View
4 product/roundhouse/infrastructure.app/ConfigurationPropertyHolder.cs
@@ -55,7 +55,9 @@ public interface ConfigurationPropertyHolder
bool Baseline { get; set; }
bool RunAllAnyTimeScripts { get; set; }
bool DisableTokenReplacement { get; set; }
- bool SearchAllSubdirectoriesInsteadOfTraverse { get; set; }
bool DisableOutput { get; set; }
+ string ScriptOrder { get; set; }
+
+ string SpecifiedOrderFile { get; set; }
}
}
View
2  product/roundhouse/infrastructure/ApplicationParameters.cs
@@ -42,6 +42,8 @@ public static class ApplicationParameters
public static readonly int default_admin_command_timeout = 300;
public static readonly int default_restore_timeout = 900;
public static readonly bool default_disable_output = false;
+ public static readonly string default_script_order = "traverse";
+ public static readonly string default_specified_order_file = "scriptorder.txt";
public static string get_merged_assembly_name()
{
View
30 product/roundhouse/infrastructure/filesystem/FileSystemAccess.cs
@@ -117,13 +117,6 @@ public interface FileSystemAccess
/// <returns>The extension of the file.</returns>
string get_file_extension_from(string file_path);
- /// <summary>
- /// Gets a list of files inside of an existing directory
- /// </summary>
- /// <param name="directory">Path to the directory</param>
- /// <returns>A list of files inside of an existing directory</returns>
- string[] get_all_file_name_strings_in(string directory);
-
#endregion
#region Directory
@@ -176,29 +169,6 @@ public interface FileSystemAccess
/// <param name="recursive">Would you like to delete the directories inside of this directory? Almost always true.</param>
void delete_directory(string directory, bool recursive);
- /// <summary>
- /// Gets a list of directories inside of an existing directory
- /// </summary>
- /// <param name="directory">Directory to look for subdirectories in</param>
- /// <returns>A list of subdirectories inside of the existing directory</returns>
- string[] get_all_directory_name_strings_in(string directory);
-
- /// <summary>
- /// Gets a list of files inside of an existing directory
- /// </summary>
- /// <param name="directory">Path to the directory</param>
- /// <param name="pattern">Pattern or extension</param>
- /// <returns>A list of files inside of an existing directory</returns>
- string[] get_all_file_name_strings_in(string directory, string pattern);
-
- /// <summary>
- /// Gets a list of all files inside of an existing directory, includes files in subdirectories also
- /// </summary>
- /// <param name="directory">Path to the directory</param>
- /// <param name="pattern">Pattern or extension</param>
- /// <returns>A list of files inside of an existing directory</returns>
- string[] get_all_file_name_strings_recurevly_in(string directory, string pattern);
-
#endregion
/// <summary>
View
44 product/roundhouse/infrastructure/filesystem/WindowsFileSystemAccess.cs
@@ -328,50 +328,6 @@ public void delete_directory(string directory, bool recursive)
Directory.Delete(directory, recursive);
}
- /// <summary>
- /// Gets a list of directories inside of an existing directory
- /// </summary>
- /// <param name="directory">Directory to look for subdirectories in</param>
- /// <returns>A list of subdirectories inside of the existing directory</returns>
- public string[] get_all_directory_name_strings_in(string directory)
- {
- return Directory.GetDirectories(directory);
- }
-
- /// <summary>
- /// Gets a list of files inside of an existing directory
- /// </summary>
- /// <param name="directory">Path to the directory</param>
- /// <returns>A list of files inside of an existing directory</returns>
- public string[] get_all_file_name_strings_in(string directory)
- {
- return get_all_file_name_strings_in(directory, "*.*");
- }
-
- /// <summary>
- /// Gets a list of files inside of an existing directory
- /// </summary>
- /// <param name="directory">Path to the directory</param>
- /// <param name="pattern">Pattern or extension</param>
- /// <returns>A list of files inside of an existing directory</returns>
- public string[] get_all_file_name_strings_in(string directory, string pattern)
- {
- string[] returnList = Directory.GetFiles(directory, pattern);
- return returnList.OrderBy(get_file_name_from).ToArray();
- }
-
- /// <summary>
- /// Gets a list of all files inside of an existing directory, includes files in subdirectories also
- /// </summary>
- /// <param name="directory">Path to the directory</param>
- /// <param name="pattern">Pattern or extension</param>
- /// <returns>A list of files inside of an existing directory</returns>
- public string[] get_all_file_name_strings_recurevly_in(string directory, string pattern)
- {
- string[] returnList = Directory.GetFiles(directory, pattern, SearchOption.AllDirectories);
- return returnList.OrderBy(get_file_name_from).ToArray();
- }
-
#endregion
/// <summary>
View
18 product/roundhouse/infrastructure/filesystem/filelocators/FileLocator.cs
@@ -0,0 +1,18 @@
+namespace roundhouse.infrastructure.filesystem.filelocators
+{
+ /// <summary>
+ /// Interface which abstracts the method for retrieving files
+ /// from a directory.
+ /// </summary>
+ public interface FileLocator
+ {
+ /// <summary>
+ /// Return all the files in the given directory.
+ /// The files are returned in an implementation dependent order.
+ /// </summary>
+ /// <param name="directory">The full path to the directory</param>
+ /// <param name="pattern">The file name pattern</param>
+ /// <returns>All the files in the directory and all subdirectories, in an implementation defined order.</returns>
+ string[] locate_all_files_in(string directory, string pattern);
+ }
+}
View
35 product/roundhouse/infrastructure/filesystem/filelocators/Recurse.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace roundhouse.infrastructure.filesystem.filelocators
+{
+ /// <summary>
+ /// The recurse files option.
+ /// </summary>
+ public class Recurse : FileLocator
+ {
+ /// <summary>
+ /// Returns all files in this directory and it's subdirectories, globally sorted using the
+ /// filename.
+ /// </summary>
+ /// <example>
+ /// It will return like this:
+ /// subdir\a.txt
+ /// b.txt
+ /// subdir\c.txt
+ /// d.txt
+ ///
+ /// </example>
+ /// <param name="directory">The directory to search in</param>
+ /// <param name="pattern">The filename pattern</param>
+ /// <returns>A globally sorted list of files.</returns>
+ public string[] locate_all_files_in(string directory, string pattern)
+ {
+ string[] returnList = Directory.GetFiles(directory, pattern, SearchOption.AllDirectories);
+ return returnList.OrderBy(Path.GetFileName).ToArray();
+ }
+ }
+}
View
134 product/roundhouse/infrastructure/filesystem/filelocators/SpecifiedOrdering.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace roundhouse.infrastructure.filesystem.filelocators
+{
+ /// <summary>
+ /// Supports the use of a specific ordering in a directory, via
+ /// the use of a file which specifies the order of each file, using
+ /// a relative path.
+ ///
+ /// Files in the directory or subdirectories that are not in the specified order file
+ /// are sorted according to their relative path.
+ ///
+ /// The Script Ordering File:
+ /// - Lines starting with # are ignored
+ /// - Each relative file path are on it's own line, without the leading directory seperator
+ /// </summary>
+ public class SpecifiedOrdering : FileLocator
+ {
+ /// <summary>
+ /// Construct a SpecifiedOrdering FileLocator.
+ /// </summary>
+ /// <param name="ordering_file_name">The path to the specific ordering file relative to the directory.</param>
+ public SpecifiedOrdering(string ordering_file_name)
+ {
+ this.ordering_file_name = ordering_file_name;
+ }
+
+ private readonly string ordering_file_name;
+
+ /// <summary>
+ /// Return the files in this directory and all subdirectories,
+ /// the order is as follows:
+ /// 1. Files specified in the ordering file, in the order of their entry in that file
+ /// 2. Files not specified in the ordering, in the culture invariant case-insensitive order
+ /// of the relative file name.
+ /// </summary>
+ /// <param name="directory">The directory to search</param>
+ /// <param name="pattern">The file pattern you are searching for</param>
+ /// <returns>The ordered list of files.</returns>
+ public string[] locate_all_files_in(string directory, string pattern)
+ {
+ var ordering_file = Path.Combine(directory, ordering_file_name);
+ // Load the specified order file and create a dictionary of the filenames and their positions.
+ var specifiedOrder = File.Exists(ordering_file)
+ ? File.ReadAllLines(ordering_file)
+ // skip comment lines.
+ .Where(line => !line.StartsWith("#"))
+ : Enumerable.Empty<string>();
+
+ var files = Directory.GetFiles(directory, pattern, SearchOption.AllDirectories);
+ // Sort the files according to their position in the ordering file
+ // or failing that their relative file paths.
+ Array.Sort(files, new Comparer(specifiedOrder, directory));
+
+ return files;
+ }
+
+ /// <summary>
+ /// Sorts string according to their position in the specified ordering,
+ /// or by the invariant culture ignore case comparer.
+ /// </summary>
+ public class Comparer : IComparer<string>
+ {
+ public Comparer(IEnumerable<string> ordering, string directory)
+ {
+ this.specifiedOrder = ordering
+ .Select((relativeFileName, order) => new KeyValuePair<string, int>(relativeFileName, order))
+ .ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.InvariantCultureIgnoreCase);
+ this.directory = directory;
+ }
+
+ private Dictionary<string, int> specifiedOrder;
+ private string directory;
+
+ public int Compare(string lhsRaw, string rhsRaw)
+ {
+ // obtain the file names relative to the search directory.
+ var lhs =
+ lhsRaw.Substring(directory.Length).TrimStart(
+ Path.DirectorySeparatorChar);
+ var rhs =
+ rhsRaw.Substring(directory.Length).TrimStart(
+ Path.DirectorySeparatorChar);
+ int lhsPosition;
+ int rhsPosition;
+
+ // Figure out whether the files are in the specified order file.
+ bool lhsIsSpecified = specifiedOrder.TryGetValue(lhs, out lhsPosition);
+ bool rhsIsSpecified = specifiedOrder.TryGetValue(rhs, out rhsPosition);
+
+ if(lhsIsSpecified && rhsIsSpecified)
+ {
+ // Both in specified ordering file,
+ // Order them by their location in that file.
+ return Comparer<int>.Default.Compare(lhsPosition, rhsPosition);
+ } else if(lhsIsSpecified)
+ {
+ // Left hand side is in the specified file,
+ // so it goes before the right hand side.
+ return -1;
+ } else if (rhsIsSpecified)
+ {
+ // Right hand side is in the specified file,
+ // so it goes before the left hand side.
+ return 1;
+ }
+
+ // we want to ignore the file extension when comparing
+ int lhsLengthExcludingFileExtension = lhs.LastIndexOf('.');
+ int rhsLengthExcludingFileExtension = rhs.LastIndexOf('.');
+
+ if(lhsLengthExcludingFileExtension != -1)
+ {
+ lhs = lhs.Substring(0, lhsLengthExcludingFileExtension);
+ }
+
+ if(rhsLengthExcludingFileExtension != -1)
+ {
+ rhs = rhs.Substring(0, rhsLengthExcludingFileExtension);
+ }
+
+ // neither side is in the ordering file,
+ // use the string comparer against the the relative path to
+ // each file.
+ return StringComparer.InvariantCultureIgnoreCase.Compare(lhs, rhs);
+ }
+ }
+
+ }
+}
View
45 product/roundhouse/infrastructure/filesystem/filelocators/Traverse.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace roundhouse.infrastructure.filesystem.filelocators
+{
+ /// <summary>
+ /// The traverse files option.
+ /// </summary>
+ public class Traverse : FileLocator
+ {
+ /// <summary>
+ /// Returns the files in the provided directory first, in sorted order based on their FileName.
+ /// Then each subdirectory, with the each subdirectory files, in sorted order based on their FileName.
+ /// </summary>
+ /// <example>
+ /// It will return like this (files are only sorted local to their directory):
+ /// b.txt
+ /// d.txt
+ /// subdir\a.txt
+ /// subdir\c.txt
+ /// </example>
+ /// <param name="directory">The directory to find files in</param>
+ /// <param name="pattern">The wildcard pattern for those files</param>
+ /// <returns>An ordered list of files in this directory and it's subdirectories</returns>
+ public string[] locate_all_files_in(string directory, string pattern)
+ {
+ var files = new List<string>();
+
+ // find all the files, sort them add to our list.
+ files.AddRange(Directory.GetFiles(directory, pattern).OrderBy(Path.GetFileName));
+
+ // find all the subdirectories.
+ foreach (var subDir in Directory.GetDirectories(directory))
+ {
+ // find all the files in the subdirectories.
+ files.AddRange(locate_all_files_in(subDir, pattern));
+ }
+
+ return files.ToArray();
+ }
+ }
+}
View
4 product/roundhouse/roundhouse.csproj
@@ -117,6 +117,9 @@
<Compile Include="infrastructure.app\DatabaseTypeSynonyms.cs" />
<Compile Include="infrastructure.app\persistence\NHibernateMigrationSessionFactory.cs" />
<Compile Include="infrastructure\extensions\ObjectExtensions.cs" />
+ <Compile Include="infrastructure\filesystem\filelocators\Recurse.cs" />
+ <Compile Include="infrastructure\filesystem\filelocators\SpecifiedOrdering.cs" />
+ <Compile Include="infrastructure\filesystem\filelocators\Traverse.cs" />
<Compile Include="infrastructure\logging\custom\ConsoleLogger.cs" />
<Compile Include="infrastructure\persistence\AuditEventListener.cs" />
<Compile Include="infrastructure\persistence\DifferencingNHibernateSessionFactory.cs" />
@@ -137,6 +140,7 @@
<Compile Include="RoundhouseMode.cs" />
<Compile Include="runners\RoundhouseNHibernateCompareRunner.cs" />
<Compile Include="runners\RoundhouseRedGateCompareRunner.cs" />
+ <Compile Include="infrastructure\filesystem\filelocators\FileLocator.cs" />
<Compile Include="sqlsplitters\StatementSplitter.cs" />
<Compile Include="consoles\DefaultConfiguration.cs" />
<Compile Include="cryptography\CryptographicService.cs" />
View
19 product/roundhouse/runners/RoundhouseMigrationRunner.cs
@@ -12,6 +12,7 @@ namespace roundhouse.runners
using migrators;
using resolvers;
using Environment = environments.Environment;
+using roundhouse.infrastructure.filesystem.filelocators;
public sealed class RoundhouseMigrationRunner : IRunner
{
@@ -27,6 +28,7 @@ public sealed class RoundhouseMigrationRunner : IRunner
private bool run_in_a_transaction;
private readonly bool use_simple_recovery;
private readonly ConfigurationPropertyHolder configuration;
+ private readonly FileLocator file_locator;
private const string SQL_EXTENSION = "*.sql";
public RoundhouseMigrationRunner(
@@ -41,7 +43,8 @@ public sealed class RoundhouseMigrationRunner : IRunner
bool dont_create_the_database,
bool run_in_a_transaction,
bool use_simple_recovery,
- ConfigurationPropertyHolder configuration)
+ ConfigurationPropertyHolder configuration,
+ FileLocator file_locator)
{
this.known_folders = known_folders;
this.repository_path = repository_path;
@@ -55,6 +58,7 @@ public sealed class RoundhouseMigrationRunner : IRunner
this.run_in_a_transaction = run_in_a_transaction;
this.use_simple_recovery = use_simple_recovery;
this.configuration = configuration;
+ this.file_locator = file_locator;
}
public void run()
@@ -87,7 +91,7 @@ public void run()
create_change_drop_folder();
Log.bound_to(this).log_a_debug_event_containing("The change_drop (output) folder is: {0}", known_folders.change_drop.folder_full_path);
- Log.bound_to(this).log_a_debug_event_containing("Using SearchAllSubdirectoriesInsteadOfTraverse execution: {0}", configuration.SearchAllSubdirectoriesInsteadOfTraverse);
+ Log.bound_to(this).log_a_debug_event_containing("Using ScriptOrder execution: {0}", configuration.ScriptOrder);
try
{
@@ -257,10 +261,7 @@ private void remove_share_from_change_drop_folder()
{
if (!file_system.directory_exists(directory)) return;
- var fileNames = configuration.SearchAllSubdirectoriesInsteadOfTraverse
- ? file_system.get_all_file_name_strings_recurevly_in(directory, SQL_EXTENSION)
- : file_system.get_all_file_name_strings_in(directory, SQL_EXTENSION);
- foreach (string sql_file in fileNames)
+ foreach (string sql_file in file_locator.locate_all_files_in(directory, SQL_EXTENSION))
{
string sql_file_text = replace_tokens(get_file_text(sql_file));
Log.bound_to(this).log_a_debug_event_containing(" Found and running {0}.", sql_file);
@@ -281,12 +282,6 @@ private void remove_share_from_change_drop_folder()
}
}
}
-
- if (configuration.SearchAllSubdirectoriesInsteadOfTraverse) return;
- foreach (string child_directory in file_system.get_all_directory_name_strings_in(directory))
- {
- traverse_files_and_run_sql(child_directory, version_id, migration_folder, migrating_environment, repository_version, connection_type);
- }
}
public string get_file_text(string file_location)
View
8 product/roundhouse/runners/RoundhouseRedGateCompareRunner.cs
@@ -1,3 +1,5 @@
+using roundhouse.infrastructure.filesystem.filelocators;
+
namespace roundhouse.runners
{
using System.Collections.Generic;
@@ -15,15 +17,17 @@ public class RoundhouseRedGateCompareRunner : IRunner
private readonly FileSystemAccess file_system;
private readonly ConfigurationPropertyHolder configuration;
private readonly RoundhouseMigrationRunner migration_runner;
+ private readonly FileLocator file_locator;
private readonly string redgate_install_location = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ProgramFiles) + @"\Red Gate\SQL Compare 8";
private const string redgate_compare_tool = @"sqlcompare.exe";
- public RoundhouseRedGateCompareRunner(KnownFolders known_folders, FileSystemAccess file_system, ConfigurationPropertyHolder configuration, RoundhouseMigrationRunner migration_runner)
+ public RoundhouseRedGateCompareRunner(KnownFolders known_folders, FileSystemAccess file_system, ConfigurationPropertyHolder configuration, RoundhouseMigrationRunner migration_runner, FileLocator file_locator)
{
this.known_folders = known_folders;
this.file_system = file_system;
this.configuration = configuration;
this.migration_runner = migration_runner;
+ this.file_locator = file_locator;
}
@@ -55,7 +59,7 @@ public void run()
public string get_new_change_number()
{
- string[] files = file_system.get_all_file_name_strings_in(known_folders.up.folder_full_path);
+ string[] files = file_locator.locate_all_files_in(known_folders.up.folder_full_path, "*.*");
IList<string> file_numbers = files.Select(file => file_system.get_file_name_from(file).Substring(0, file_system.get_file_name_from(file).IndexOf('_'))).ToList();
int length_of_number_format = 0;
Something went wrong with that request. Please try again.