Skip to content

Commit 4ca10dd

Browse files
committed
feat(maven-plugin): meta:migrate Flyway-naming option for codegen-kotlin consumers
1 parent 7f6d64d commit 4ca10dd

2 files changed

Lines changed: 112 additions & 4 deletions

File tree

server/java/maven-plugin/src/main/java/com/metaobjects/mojo/MetaDataMigrateMojo.java

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.maven.project.MavenProject;
2222

2323
import java.io.File;
24+
import java.io.IOException;
2425
import java.net.URL;
2526
import java.net.URLClassLoader;
2627
import java.nio.file.Files;
@@ -112,6 +113,23 @@ public class MetaDataMigrateMojo extends AbstractMojo {
112113
@Parameter(property = "allowTypeChange", defaultValue = "false")
113114
private boolean allowTypeChange;
114115

116+
/**
117+
* When true, emit migrations into a Flyway-conventional directory using
118+
* {@code <prefix><N>__<slug>.sql} naming (e.g., {@code V003__add_author_phone_field.sql}).
119+
* The next version number is the highest existing {@code <prefix><N>__*.sql} file + 1.
120+
*/
121+
@Parameter(property = "meta.migrate.flyway", defaultValue = "false")
122+
private boolean flyway;
123+
124+
/** Flyway migrations directory (relative to project basedir). Only used when {@link #flyway} is true. */
125+
@Parameter(property = "meta.migrate.flywayDir",
126+
defaultValue = "src/main/resources/db/migration")
127+
private String flywayDir;
128+
129+
/** Flyway version prefix (e.g., "V" or "U"). Only used when {@link #flyway} is true. */
130+
@Parameter(property = "meta.migrate.flywayPrefix", defaultValue = "V")
131+
private String flywayPrefix;
132+
115133
/**
116134
* Metadata loader configuration (mirrors {@code AbstractMetaDataMojo}'s {@code <loader>}
117135
* element).
@@ -213,11 +231,44 @@ private void runEmit(SchemaMigrationEngine engine, Connection c, AllowOptions al
213231
getLog().info("Nothing to emit — schema is already in sync.");
214232
return;
215233
}
216-
Path dir = Path.of(outputDir, timestamp() + "-" + sanitize(slug));
217-
Files.createDirectories(dir);
218234
String content = up.endsWith("\n") ? up : up + "\n";
219-
Files.writeString(dir.resolve("up.sql"), content);
220-
getLog().info("Wrote migration script: " + dir.resolve("up.sql"));
235+
236+
Path target;
237+
if (flyway) {
238+
Path dir = Path.of(flywayDir);
239+
Files.createDirectories(dir);
240+
int nextVersion = nextFlywayVersion(dir, flywayPrefix);
241+
String filename = String.format("%s%03d__%s.sql",
242+
flywayPrefix, nextVersion, sanitize(slug));
243+
target = dir.resolve(filename);
244+
} else {
245+
Path dir = Path.of(outputDir, timestamp() + "-" + sanitize(slug));
246+
Files.createDirectories(dir);
247+
target = dir.resolve("up.sql");
248+
}
249+
Files.writeString(target, content);
250+
getLog().info("Wrote migration script: " + target);
251+
}
252+
253+
/**
254+
* Inspect {@code dir} for existing {@code <prefix><N>__*.sql} files and return the next
255+
* unused version number. Returns 1 when the directory does not exist or contains no matches.
256+
*/
257+
static int nextFlywayVersion(Path dir, String prefix) throws IOException {
258+
if (!Files.isDirectory(dir)) return 1;
259+
java.util.regex.Pattern p = java.util.regex.Pattern.compile(
260+
"^" + java.util.regex.Pattern.quote(prefix) + "(\\d+)__.*\\.sql$");
261+
int max = 0;
262+
try (var stream = Files.list(dir)) {
263+
for (Path f : stream.toList()) {
264+
var m = p.matcher(f.getFileName().toString());
265+
if (m.matches()) {
266+
int v = Integer.parseInt(m.group(1));
267+
if (v > max) max = v;
268+
}
269+
}
270+
}
271+
return max + 1;
221272
}
222273

223274
private void runApply(SchemaMigrationEngine engine, Connection c, AllowOptions allow)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.metaobjects.mojo;
2+
3+
import org.junit.Test;
4+
5+
import java.nio.file.Files;
6+
import java.nio.file.Path;
7+
8+
import static org.junit.Assert.assertEquals;
9+
10+
/**
11+
* Unit tests for the Flyway version-numbering helper on {@link MetaDataMigrateMojo}.
12+
* Exercises {@code nextFlywayVersion(Path, String)} in isolation — the surrounding mojo
13+
* execution path requires a live JDBC connection and is covered by integration tests.
14+
*/
15+
public class MetaDataMigrateMojoFlywayTest {
16+
17+
@Test public void nextVersionStartsAtOneOnEmptyDir() throws Exception {
18+
Path dir = Files.createTempDirectory("fw-");
19+
try {
20+
int v = MetaDataMigrateMojo.nextFlywayVersion(dir, "V");
21+
assertEquals(1, v);
22+
} finally {
23+
Files.deleteIfExists(dir);
24+
}
25+
}
26+
27+
@Test public void nextVersionIncrementsHighest() throws Exception {
28+
Path dir = Files.createTempDirectory("fw-");
29+
try {
30+
Files.writeString(dir.resolve("V001__init.sql"), "");
31+
Files.writeString(dir.resolve("V003__skip.sql"), "");
32+
Files.writeString(dir.resolve("V005__latest.sql"), "");
33+
int v = MetaDataMigrateMojo.nextFlywayVersion(dir, "V");
34+
assertEquals(6, v);
35+
} finally {
36+
try (var s = Files.list(dir)) {
37+
s.forEach(p -> p.toFile().delete());
38+
}
39+
Files.deleteIfExists(dir);
40+
}
41+
}
42+
43+
@Test public void nextVersionIgnoresNonMatchingFiles() throws Exception {
44+
Path dir = Files.createTempDirectory("fw-");
45+
try {
46+
Files.writeString(dir.resolve("README.md"), "");
47+
Files.writeString(dir.resolve("V002__only.sql"), "");
48+
int v = MetaDataMigrateMojo.nextFlywayVersion(dir, "V");
49+
assertEquals(3, v);
50+
} finally {
51+
try (var s = Files.list(dir)) {
52+
s.forEach(p -> p.toFile().delete());
53+
}
54+
Files.deleteIfExists(dir);
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)