Skip to content

A Maven plugin that helps generically provide lightweight, convention-driven migration capabilities to make it easier to evolve your projects over time.

License

Notifications You must be signed in to change notification settings

TechnologyBrewery/baton

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Baton

Maven Central License Build (github)

Baton pays homage to Burton Baton, a fabulous beer that is a blend of a traditional English-style Old Ale and a modern imperial IPA. This "two thread" beer brings both source beers together into something arguably better than either beer was individually. Like the beer, this project blends your existing codebase with new migrations that helps modernize and keep your old code relevant. The result is more substantive and satisfying than either old or new code alone could accomplish.

Why Do You Need Baton?

Baton is a Maven plugin that helps generically provide lightweight, convention-driven migration capabilities to make it easier to evolve your projects over time. It makes it easy to embed automated migrations into your Maven build. Additionally, by using classpath-driven configurations, version upgrades of your migration jars control what gets applied when. This makes it easy to release a set of artifacts that correspond to a general release of a framework and get just the migration you need at the appropriate time.

Requirements

In order to use Baton, the following prerequisites must be installed:

  • Maven 3.9+
  • Java 11+

Usage

Creating and applying migrations is easy. Follow these steps and you'll be migrating your code in no time.

Create migration class

To start, implement your migration by extending AbstractMigration, adding your specific logic to:

  1. Determine if the migration applies to a given file
  2. If applicable, perform the migration

These steps are outlined below is a simple class that migrates files ending in .foo to now end in .bar:

public class FooToBarMigration extends AbstractMigration {

    @Override
    protected boolean shouldExecuteOnFile(File file) {
        return file.getName().contains(".foo");
    }

    @Override
    protected boolean performMigration(File file) {
        FileUtils.moveFile(file, new File(file.getParent(),  file.getName().replace(".foo", ".bar")));
        return true;
    }
}

Leveraging utilities classes

The following Java classes in org.technologybrewery.baton.util can be leveraged to easily implement common migration logic into your extension:

  • CommonUtils
  • FileUtils
  • pom.PomHelper
  • pom.PomModifications
  • pom.LocationAwareMavenReader

Configure Baton to use the migration

With a migration to apply, we both configure and tailor that use through a simple json file. This file can live anywhere in Baton's classpath and is named migrations.json by default.

The following example configures the migration to only look in the ./src/main/resources/legacy folder while leaving one file, original-specification-example.foo alone.

[
    {
        "group": "group-1",
        "migrations": [
            "name": "upgrade-foo-extension-files-migration",
            "implementation": "org.technologybrewery.baton.example.FooToBarMigration",
            "fileSets": [
                {
                    "includes": ["**/legacy/*.foo"],
                    "excludes": ["original-specification-example.foo"]
                }
            ]
        ]
    }
]

Group and Migration Ordering

Groups

Migration files contain groups of migrations. Each group is executed in its entirety before moving on to the next. Groups are executed in the order in which they appear in the migration file.

Example of ordered groups

[ 
    {
        "group": "foo",
        "type": "ordered",
        "migrations": [
            {
                "name": "migration_1"        
            }
        ]
    }, 
    {
        "group": "bar",
        "type": "ordered",
        "migrations": [
            {
                "name": "migration_2"        
            }
        ]
    }
 ]

If Baton is executed with this migrations.json, groups would be processed in the order in which they were specified (foo, bar)

Migrations

Migrations within a group can be ordered manually or by version.

Example of manually ordered migrations

[ 
      {
         "group": "foo",
         "type": "ordered",
         "migrations": [
            {
                  "name": "migration_1"        
            },
            {
                  "name": "migration_2" 
           },
           {
                 "name": "migration_3" 
           },
           {
                 "name": "migration_4"
           }
        ]
    }
 ]

If Baton is executed with this migration.json, migrations would be processed in the order in which they were specified (migration_1, migration_2, migration_3, migration_4)

Example of version ordered migrations

[ 
      {
         "group": "bar",
         "type": "versioned",
         "migrations": [
            {
                  "name": "migration_1", 
                  "version": "3.0.0"     
            },
            {
                  "name": "migration_2", 
                  "version": "2.0.0" 
           },
           {
                 "name": "migration_3",
                 "version": "1.0.0" 
           },
           {
                 "name": "migration_4", 
                 "version": "5.0.0" 
           }
        ]
    }
 ]

If Baton is executed with this migration.json, migrations would be processed by the version number in ascending order (migration_3, migration_2, migration_1, migration_4)

Add baton-maven-plugin to your Maven build

The last step is to add baton-maven-plugin to your Maven build process just like any other plugin.

The following example highlight the default plugin configuration as well as a notional dependency containing both the Migration class and configuration json file above.

                <plugin>
                    <groupId>org.technologybrewery.baton</groupId>
                    <artifactId>baton-maven-plugin</artifactId>
                    <version>0.1.0</version>
                    <extensions>true</extensions>
                    <executions>
                        <execution>
                            <id>default</id>
                            <goals>
                                <goal>baton-migrate</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>org.technologybrewery.baton.example</groupId>
                            <artifactId>example-migration-configuration</artifactId>
                            <version>1.0.0</version>
                        </dependency>
                    </dependencies>
                </plugin>

Example result output

When executing your next Maven build, Baton will execute as part of the initialize build lifecycle and output similar to the following will result:

[INFO] --- baton:0.1.0:baton-migrate (default) @ toml-file-update ---
[INFO] Loading migrations from: jar:file:/Users/dfh/.m2/repository/org/technologybrewery/baton/example/example-migration-configuration/1.0.0/example-migration-configuration-1.0.0-SNAPSHOT.jar!/migrations.json
[INFO] Found 1 migrations
[INFO] Migrations Processed: 1, Successfully Migrated Files: 1, Unsuccessfully Migrated Files: 0

Plugin Configuration

All Baton configurations may be set either via the baton-maven-plugin's <configuration> definition, Maven POM properties, or -D on the command line and follow a consistent naming pattern for the different configuration approaches. For setting configurations via POM properties or -D on the command line, all configuration keys may be prepended with baton.. For example, migrationsConfigurationFile controls the file name that Baton will use to find migrations and may be configured using the following approaches:

  1. Plugin <configuration>
	<plugin>
		<groupId>org.technologybrewery.baton</groupId>
		<artifactId>baton-maven-plugin</artifactId>
		<extensions>true</extensions>
		<configuration>
			<migrationsConfigurationFile>alternative-migrations.json</migrationsConfigurationFile>
		</configuration>
	</plugin>
  1. -D via command line
mvn clean install -DmigrationsConfigurationFile=alternative-migrations.json
  1. POM properties
	<properties>
		<migrationsConfigurationFile>alternative-migrations.json</migrationsConfigurationFile>
	</properties>

NOTE: The above list's order reflects the precedence in which configurations will be applied. For example, configuration values that are specified in the plugin's <configuration> definition will always take precedence, while system properties via the command line (-D) will take precedence over <properties> definitions.

baseDirectory

The desired base directory to use when Baton looks for files on which to perform migrations. By default, only pom.xml and *.toml from the base directory will be included.

Default: ${project.basedir}

sourceDirectory

The desired source directory to include when Baton looks for files on which to perform migrations.

Default: ${project.basedir}/src

testDirectory

The desired test directory to include, if desired, when Baton looks for files on which to perform migrations.

Default: None

fileSets

A standard Maven fileSets block that can be used to include any sets of file you desire. If used, no additional defaulting of fileSet values will be performed by Baton and only these filesets will be used for the plugin's execution.

NOTE: File set configurations specified in your migrations.json will override those specified to your plugin at large.

Default: None

migrationsConfigurationFile

The configurations file name to look for in the classpath (all matches will be used).

Default: migrations.json

backupOriginalMigratedFiles

Controls if backups of original files prior to migration are saved.

Default: true

backupCustomLocation

Customizes the location where backups of original files prior to migration are stored By default, the system temp directory is used. This will result in the files in that temp location being subjected to your local computer's policy for cleaning (often either on reboot or never). Changing this property to the location of your choosing will allow greater control.

Default: system temp directory

numberOfBackupsToKeep

The number of backups for original files prior to migration to keep. Once this number is hit, the backup files will rotate, with the oldest being purged.

It is worth noting that this controls number of old files kept, not the total number files kept. For instance, if set to 5, you will have 5 old backup files as well as one active backup. This is based on how the file rotation library works.

Default: 10

minimumVersion

Used to filter out version ordered migrations. All migrations with versions less than the minimum version will be skipped.

Default: 0.0.0

Format: [Number].[Number].[Number]

Migrations JSON File Configuration

When specifying your configurations in migrations.json or your custom migrationsConfigurationFile file name, the following options are available.

Group Configurations

group

The name of the group. Simply used to give a distinctive identifier to a group.

Required? true

Default: None

type

The migration ordering type.

Required? true

Default: None

Values: ordered | versioned

migrations

List of all the migrations in a group. This must be a non-zero list of migrations.

Required? true

Default: None

Migration Configurations

name

The name of the migration to perform. As of 0.1.0, name is not particularly impactful. However, in subsequent releases it will gain importance as a means to order migration execution (convention-driven in the style of Flyway) as well as inactivate specific migrations.

Required? true

Default: None

version

The version of the migration

Required? Only if type is set to versioned in the parent group configuration

Default: None

Format: [Number].[Number].[Number]

description

The description of the migration. This is intended to provide context on why the migration is needed.

Required? false

Default: None

implementation

The fully qualified Java class name that will perform the migration. This MUST implement the org.technologybrewery.baton.Migration interface, however it is recommended that it extend org.technologybrewery.baton.AbstractMigration to allow implementations to be more consistent and focus on migration logic rather than Baton plumbing. The implementation MUST have a default constructor.

Required? true

Default: None

fileSet

A Maven-inspired object that allows specification of common file sets. MUST be added as a list item.

Required? false

Default: None

fileSet/directory

The directory on which this file set should operate.

Required? false

Default: ./ (project base directory)

fileSet/includes

A list of specific inclusions following standard Maven conventions.

Required? false

Default: None

fileSet/excludes

A list of specific exclusions following standard Maven conventions.

Required? false

Default: None

fileSet/followSymlinks

Whether to follow symlinks following standard Maven conventions.

Required? false

Default: None

Building Baton

If you are working on Baton, please be aware of some nuances in working with a plugin that defines a custom Maven build lifecycle and packaging. examples are utilized to immediately test the baton-maven-plugin. If baton-maven-plugin has not been previously built, developers must manually build the baton-maven-plugin and then execute another, separate build of examples (and any other baton module) to use the updated baton-maven-plugin. That said, once an initial build has been completed, a single build may be used to build baton-maven-plugin and apply the updates to examples. To assist, there are two profiles available in the build:

  • mvn clean install -Pbootstrap: Builds the baton-maven-plugin such that it may be utilized within subsequent builds.
  • mvn clean install -Pdefault: (ACTIVE BY DEFAULT - -Pdefault does not need to be specified) builds all modules. Developers may use this profile to build and apply changes to existing baton-maven-plugin Mojo classes.

About

A Maven plugin that helps generically provide lightweight, convention-driven migration capabilities to make it easier to evolve your projects over time.

Resources

License

Stars

Watchers

Forks

Packages

No packages published