flyway-database-nc-mongodb hard-codes "mongosh" on the host PATH
Hi.
I'm building flyway-mongodb extension for quarkus. The extension works end-to-end against a host-installed mongosh, but breaks the moment MongoDB is run inside a container — which is the standard Quarkus workflow.
In Quarkus, the canonical way to spin MongoDB up for tests and dev mode is Dev Services: Quarkus launches a mongo:latest container automatically, no developer action required. This is how Quarkus contributors run MongoDB tests locally and how the project's CI runs them (Quarkus PR #54308 review thread). There is no mongosh on the host, only inside the container.
flyway-database-nc-mongodb 12.0.0 hard-codes "mongosh" as the executable invoked from the JVM for .js migrations, so Flyway fails fast with mongosh: command not found. Meanwhile, a fully-working mongosh is sitting inside the container Flyway is already talking to.
// MongoDBDatabase.checkMongoshInstalled — line 438
final List<String> commands = Arrays.asList("mongosh", "--version");
// MongoDBDatabase.getMongoshConnectCommands — line 483
List<String> commands = new ArrayList<>(List.of("mongosh", mongoshCredential.url()));The two unhappy workarounds today are:
- Install
mongoshon every contributor's machine and on CI. The Quarkus maintainers pushed back on this, reasonably: adding a host-level tool dependency to every contributor's setup just to test one extension is a non-starter. - Stick to
.json-only migrations. Works (the API path bypassesmongoshentirely) but loses.jsexpressivity — control flow, helpers, the fullmongoshAPI. I strongly believe that.jsoption has bigger potential as every mongoDB developer I know writes and debugs queries in js, once they are done they can just store them in VCS and move on. At the same time.jsonpath forces them to have a step on conversion their.jsto.json.
What unblocks every containerized scenario is making the executable configurable, so the embedder can point Flyway at docker exec -i <container_id> mongosh (or kubectl exec, or a wrapper script). The host PATH is left untouched, every existing user is unaffected because the default stays "mongosh".
Root cause analysis
MongoDBDatabase.checkMongoshInstalled and MongoDBDatabase.getMongoshConnectCommands both build their command list with a hard-coded "mongosh" literal. There is no config property, system property, or environment variable that overrides it. The only supported deployment shape is "mongosh installed on PATH on the same host as the JVM running Flyway."
This forces any embedder that runs MongoDB inside a container — Quarkus Dev Services, Testcontainers, Docker Compose, GitHub Actions service containers, Kubernetes — into the workarounds above. None of these scenarios are exotic, and the EXECUTABLE path is the only way to run .js migrations.
Proposed solution
The fix has two parts.
Part 1 — Configurable shell command (list)
Add a Flyway config property, e.g. flyway.mongodb.shellCommand, defaulting to ["mongosh"]. Anywhere MongoDBDatabase currently builds its command list, prepend this configured prefix instead of the bare literal:
// MongoDBDatabase
private List<String> shellCommand() {
final List<String> configured = configuration.getMongodb().getShellCommand();
return configured != null && !configured.isEmpty() ? configured : List.of("mongosh");
}
private void checkMongoshInstalled() {
final List<String> commands = new ArrayList<>(shellCommand());
commands.add("--version");
final NativeConnectorsProcessRunner runner = new NativeConnectorsProcessRunner(commands, "Mongosh");
runner.checkToolInstalled(false, "Mongosh ... " + FlywayDbWebsiteLinks.MONGOSH);
}
private List<String> getMongoshConnectCommands() {
final List<String> commands = new ArrayList<>(shellCommand());
commands.add(mongoshCredential.url());
if (mongoshCredential.username() != null) {
commands.addAll(List.of("--username", mongoshCredential.username()));
}
if (mongoshCredential.password() != null) {
commands.addAll(List.of("--password", mongoshCredential.password()));
}
return commands;
}Example usages:
# default — unchanged behaviour, mongosh on host PATH
flyway.mongodb.shellCommand=mongosh
# Quarkus Dev Services / Testcontainers — mongosh inside the container
flyway.mongodb.shellCommand=docker,exec,-i,my-mongo-container,mongosh
# Kubernetes pod
flyway.mongodb.shellCommand=kubectl,exec,-i,mongo-0,--,mongoshStrictly backwards-compatible: the default is ["mongosh"], and NativeConnectorsProcessRunner already accepts an arbitrary List<String> as its command.
Part 2 — Configurable shell URL
In a containerized setup the URL the JVM sees and the URL mongosh should connect to are usually different:
- The Java MongoDB driver runs on the host and connects via the container's mapped port, e.g.
mongodb://localhost:54321. mongoshrunning inside the container needsmongodb://localhost:27017(the container's view of itself).
Part 1 routes mongosh through docker exec, but Flyway still passes the JVM-side URL straight through to MongoshCredential, so mongosh ends up trying to connect to the host's localhost:54321 from inside the container.
A second property — flyway.mongodb.shellUrl, defaulting to flyway.url — overrides just the URL used by mongosh:
flyway.url=mongodb://localhost:54321/test # Java driver, host view
flyway.mongodb.shellUrl=mongodb://localhost:27017/test # mongosh, container viewOnly getMongoshConnectCommands reads shellUrl; everything else keeps using connectionString derived from flyway.url.
Help offered
Happy to open a PR. Let me know if you'd prefer the property names or namespace adjusted to match an existing convention in the codebase.
Source: flyway/flyway