Skip to content

Commit 252fbd0

Browse files
Merge pull request #13 from TechnologyBrewery/migration_ordering_feature
Added migration manual versioned ordering
2 parents f0290f4 + a5b4b5b commit 252fbd0

File tree

16 files changed

+548
-163
lines changed

16 files changed

+548
-163
lines changed

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,94 @@ one file, `original-specification-example.foo` alone.
7171
}
7272
]
7373
```
74+
75+
### Group and Migration Ordering
76+
77+
#### Groups
7478
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.
7579

80+
Example of ordered groups
81+
```json
82+
[
83+
{
84+
"group": "foo",
85+
"type": "ordered",
86+
"migrations": [
87+
{
88+
"name": "migration_1"
89+
}
90+
]
91+
},
92+
{
93+
"group": "bar",
94+
"type": "ordered",
95+
"migrations": [
96+
{
97+
"name": "migration_2"
98+
}
99+
]
100+
}
101+
]
102+
```
103+
If Baton is executed with this migrations.json, groups would be processed in the order in which they were specified (foo, bar)
104+
105+
#### Migrations
106+
Migrations within a group can be ordered manually or by version.
107+
108+
Example of manually ordered migrations
109+
```json
110+
[
111+
{
112+
"group": "foo",
113+
"type": "ordered",
114+
"migrations": [
115+
{
116+
"name": "migration_1"
117+
},
118+
{
119+
"name": "migration_2"
120+
},
121+
{
122+
"name": "migration_3"
123+
},
124+
{
125+
"name": "migration_4"
126+
}
127+
]
128+
}
129+
]
130+
```
131+
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)
132+
133+
Example of version ordered migrations
134+
```json
135+
[
136+
{
137+
"group": "bar",
138+
"type": "versioned",
139+
"migrations": [
140+
{
141+
"name": "migration_1",
142+
"version": "3.0.0"
143+
},
144+
{
145+
"name": "migration_2",
146+
"version": "2.0.0"
147+
},
148+
{
149+
"name": "migration_3",
150+
"version": "1.0.0"
151+
},
152+
{
153+
"name": "migration_4",
154+
"version": "5.0.0"
155+
}
156+
]
157+
}
158+
]
159+
```
160+
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)
161+
76162
### Add `baton-maven-plugin` to your Maven build
77163
The last step is to add `baton-maven-plugin` to your Maven build process just like any other plugin.
78164

@@ -206,6 +292,14 @@ works.
206292

207293
Default: `10`
208294

295+
### minimumVersion
296+
Used to filter out version ordered migrations. All migrations with versions less
297+
than the minimum version will be skipped.
298+
299+
Default: `0.0.0`
300+
301+
Format: `[Number].[Number].[Number]`
302+
209303
## Migrations JSON File Configuration
210304
When specifying your configurations in `migrations.json` or your custom `migrationsConfigurationFile` file name, the
211305
following options are available.
@@ -219,6 +313,15 @@ Required? `true`
219313

220314
Default: None
221315

316+
#### type
317+
The migration ordering type.
318+
319+
Required? `true`
320+
321+
Default: None
322+
323+
Values: `ordered | versioned`
324+
222325
#### migrations
223326
List of all the migrations in a group. This must be a non-zero list of migrations.
224327

@@ -237,6 +340,15 @@ Required? `true`
237340

238341
Default: None
239342

343+
#### version
344+
The version of the migration
345+
346+
Required? Only if type is set to `versioned` in the parent group configuration
347+
348+
Default: None
349+
350+
Format: `[Number].[Number].[Number]`
351+
240352
#### description
241353
The description of the migration. This is intended to provide context on why the migration is needed.
242354

baton-maven-plugin/src/main/java/org/technologybrewery/baton/AbstractMigration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public abstract class AbstractMigration implements Migration {
5656
public MigrationSummary execute(FileSet[] fileSets) {
5757
if (!active) {
5858
logger.info("Migration {}' is marked inactive - skipping", getName());
59-
return new MigrationSummary(0, 0);
59+
return new MigrationSummary(this.name, 0, 0);
6060
}
6161

6262
FileSetManager fileSetManager = new FileSetManager();
@@ -90,7 +90,7 @@ public MigrationSummary execute(FileSet[] fileSets) {
9090
}
9191
}
9292

93-
return new MigrationSummary(successfulMigrations, unsuccessfulMigrations);
93+
return new MigrationSummary(this.name, successfulMigrations, unsuccessfulMigrations);
9494
}
9595

9696
/**

baton-maven-plugin/src/main/java/org/technologybrewery/baton/BatonMojo.java

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@
2626
import java.nio.file.StandardCopyOption;
2727
import java.util.ArrayList;
2828
import java.util.Collection;
29+
import java.util.Collections;
30+
import java.util.Comparator;
2931
import java.util.Enumeration;
3032
import java.util.List;
3133
import java.util.Set;
34+
import java.util.regex.Matcher;
35+
import java.util.regex.Pattern;
3236

3337
/**
3438
* A Maven plugin that allows migration logic to be executed against a Maven module based on a classpath-provided
@@ -64,6 +68,12 @@ public class BatonMojo extends AbstractMojo {
6468
@Parameter(property = "baton.testDirectory", required = false)
6569
protected File testDirectory;
6670

71+
/**
72+
* Used to filter out migrations that are less than the value
73+
*/
74+
@Parameter(property = "baton.minimumVersion", required = false, defaultValue = "0.0.0")
75+
protected String minimumVersion;
76+
6777
/**
6878
* A list of fileSet rules to select files and directories. Will be defaulted based on project information
6979
* if not specified.
@@ -110,6 +120,12 @@ public class BatonMojo extends AbstractMojo {
110120

111121
private List<GroupTarget> groups = new ArrayList<>();
112122

123+
protected static final String VERSIONED = "versioned";
124+
125+
protected static final String ORDERED = "ordered";
126+
127+
private static final String minVersionPattern = "\\d+\\.\\d+\\.\\d+";
128+
113129
protected ObjectMapper initializeObjectMapper() {
114130
ObjectMapper localObjectMapper = new ObjectMapper();
115131

@@ -156,21 +172,45 @@ protected void defaultFileSets() {
156172

157173
@Override
158174
public void execute() throws MojoExecutionException, MojoFailureException {
175+
validateMinimumVersion();
159176
loadMigrations();
160-
177+
sortMigrationsInGroups(groups);
161178
defaultFileSets();
162179

163180
BatonExecutionSummary summary = performMigration(groups);
164181
getLog().info(summary.getSummary());
165182

166183
}
167184

185+
private void validateMinimumVersion() {
186+
Pattern pattern = Pattern.compile(minVersionPattern);
187+
Matcher matcher = pattern.matcher(this.minimumVersion);
188+
if(!matcher.find()) {
189+
this.minimumVersion = "0.0.0";
190+
getLog().warn(String.format("Invalid format for minimum version %s, " +
191+
"setting to 0.0.0. Must be of the format [Number].[Number].[Number]", this.minimumVersion));
192+
}
193+
}
194+
195+
protected void sortMigrationsInGroups(List<GroupTarget> groupTargets) {
196+
for(GroupTarget groupTarget : groupTargets) {
197+
if(groupTarget.getType().equals(VERSIONED)) {
198+
Collections.sort(groupTarget.getMigrations(), new Comparator<MigrationTarget>() {
199+
@Override
200+
public int compare(MigrationTarget o1, MigrationTarget o2) {
201+
return o1.getVersion().compareTo(o2.getVersion());
202+
}
203+
});
204+
}
205+
}
206+
}
207+
168208
BatonExecutionSummary performMigration(Collection<GroupTarget> groupTargets) {
169209
BatonExecutionSummary executionSummary = new BatonExecutionSummary();
170210
for(GroupTarget groupTarget : groupTargets) {
171211
GroupSummary groupSummary = new GroupSummary(groupTarget.getGroup());
172212
for (MigrationTarget migrationTarget : groupTarget.getMigrations()) {
173-
if (isActive(migrationTarget)) {
213+
if (isActive(migrationTarget) && isMinimumVersion(migrationTarget, groupTarget.getType())) {
174214
try {
175215
getLog().debug(String.format("Executing Migration: %s (%s)", migrationTarget.getName(), migrationTarget.getImplementation()));
176216
Class<Migration> implementationClass = (Class<Migration>) Class.forName(migrationTarget.getImplementation());
@@ -209,24 +249,36 @@ protected boolean isActive(MigrationTarget migrationTarget) {
209249
return isActive;
210250
}
211251

252+
protected boolean isMinimumVersion(MigrationTarget migrationTarget, String groupType) {
253+
boolean isMinimumVersion = true;
254+
if(groupType.equals(VERSIONED)) {
255+
isMinimumVersion = migrationTarget.getVersion().compareTo(this.minimumVersion) >= 0;
256+
}
257+
if(!isMinimumVersion) {
258+
getLog().info(String.format("Skipping, migration %s with version %s does not meet minimum version of %s",
259+
migrationTarget.getName(), migrationTarget.getVersion(), this.minimumVersion));
260+
}
261+
return isMinimumVersion;
262+
}
263+
212264
/**
213265
* Scans the classpath for any migrations.json files and loads all defined {@link MigrationTarget} configurations.
214266
*/
215267
protected void loadMigrations() {
216-
Enumeration<URL> migrationsEnumeration = null;
268+
Enumeration<URL> groupsEnumeration = null;
217269
try {
218-
migrationsEnumeration = getClass().getClassLoader().getResources(migrationsFileName);
270+
groupsEnumeration = getClass().getClassLoader().getResources(migrationsFileName);
219271

220272
} catch (IOException ioe) {
221273
throw new BatonException("Unable to find migrations!", ioe);
222274
}
223275

224-
URL migrationsResource;
225-
while (migrationsEnumeration.hasMoreElements()) {
226-
migrationsResource = migrationsEnumeration.nextElement();
227-
getLog().info(String.format("Loading migrations from: %s", migrationsResource.toString()));
276+
URL groupsResource;
277+
while (groupsEnumeration.hasMoreElements()) {
278+
groupsResource = groupsEnumeration.nextElement();
279+
getLog().info(String.format("Loading migrations from: %s", groupsResource.toString()));
228280

229-
try (InputStream migrationsStream = migrationsResource.openStream()) {
281+
try (InputStream migrationsStream = groupsResource.openStream()) {
230282
File tempMigrationsFile = File.createTempFile("migrations", ".json");
231283
Files.copy(
232284
migrationsStream,

baton-maven-plugin/src/main/java/org/technologybrewery/baton/MigrationSummary.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
*/
66
public class MigrationSummary {
77

8-
private int filesSuccessfullyMigrated;
8+
private final String name;
99

10-
private int filesUnsuccessfullyMigrated;
10+
private final int filesSuccessfullyMigrated;
1111

12-
public MigrationSummary(int filesSuccessfullyMigrated, int fileUnsuccessfullyMigrated) {
12+
private final int filesUnsuccessfullyMigrated;
13+
14+
public MigrationSummary(String name, int filesSuccessfullyMigrated, int fileUnsuccessfullyMigrated) {
15+
this.name = name;
1316
this.filesSuccessfullyMigrated = filesSuccessfullyMigrated;
1417
this.filesUnsuccessfullyMigrated = fileUnsuccessfullyMigrated;
1518
}
@@ -21,4 +24,8 @@ public int getFilesSuccessfullyMigrated() {
2124
public int getFilesUnsuccessfullyMigrated() {
2225
return filesUnsuccessfullyMigrated;
2326
}
27+
28+
public String getName() {
29+
return this.name;
30+
}
2431
}

baton-maven-plugin/src/main/java/org/technologybrewery/baton/config/GroupTarget.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ public class GroupTarget extends AbstractValidatedElement {
1717
@JsonProperty(required = true)
1818
private String group;
1919

20+
@JsonProperty(required = true)
21+
private String type;
22+
2023
@JsonProperty(required = true)
2124
@JsonInclude(JsonInclude.Include.NON_EMPTY)
2225
private List<MigrationTarget> migrations = new ArrayList<>();
@@ -31,6 +34,14 @@ public String getGroup() {
3134
return this.group;
3235
}
3336

37+
public void setType(String type) {
38+
this.type = type;
39+
}
40+
41+
public String getType() {
42+
return this.type;
43+
}
44+
3445
@JsonIgnore
3546
public void addMigration(MigrationTarget migration) {
3647
this.migrations.add(migration);

baton-maven-plugin/src/main/java/org/technologybrewery/baton/config/MigrationTarget.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public class MigrationTarget {
2727
@JsonProperty(required = true)
2828
private String implementation;
2929

30+
@JsonProperty
31+
private String version;
32+
3033
@JsonProperty
3134
@JsonInclude(JsonInclude.Include.NON_EMPTY)
3235
private List<FileSet> fileSets = new ArrayList<>();
@@ -58,6 +61,14 @@ public void setImplementation(String implementation) {
5861
this.implementation = implementation;
5962
}
6063

64+
public void setVersion(String version) {
65+
this.version = version;
66+
}
67+
68+
public String getVersion() {
69+
return this.version;
70+
}
71+
6172
public List<FileSet> getFileSets() {
6273
return fileSets;
6374
}

0 commit comments

Comments
 (0)