-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add --db-image
argument to wasp-app-runner
#3183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Allows users to specify a custom Docker image when starting the development database. Usage: wasp start db --image postgres:15 This enables developers to test with different PostgreSQL versions or custom database images.
- Note default postgres image in help text - Add error handling for unrecognized arguments - Use fromMaybe to simplify image selection logic - Add comprehensive documentation in CLI and database docs
- Replace manual argument parsing with Options.Applicative parser - Move parser to top level as requested - Add note about PostgreSQL extensions use case in documentation - Remove redundant CLI documentation as requested
- Change StartDbArgs from newtype to data type - Move startDbArgsParser below the main start function - Parse arguments as the first operation in start function
d61b39e
to
4a541ed
Compare
4a541ed
to
cd24309
Compare
wasp-app-runner
wasp-app-runner
--db-image
argument to wasp-app-runner
e278763
to
dcdd083
Compare
Deploying wasp-docs-on-main with
|
Latest commit: |
9d09644
|
Status: | ✅ Deploy successful! |
Preview URL: | https://faf2c3f8.wasp-docs-on-main.pages.dev |
Branch Preview URL: | https://image-arg-for-wasp-app-runne.wasp-docs-on-main.pages.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make the db-image
configuration global rather than local.
Very straightforward and clean RP otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a concern similar to what I've voiced in #3182 (review).
Details here: #3183 (comment)
wasp-app-runner/src/args.ts
Outdated
mode: Mode; | ||
pathToApp: PathToApp; | ||
waspCliCmd: WaspCliCmd; | ||
dbImage?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this code has the same problem I talked about in my review of #3182 (review).
The "optionality" of dbImage
is stretching itself too deep into the code. The default value should be handled as soon as possible. Most of the code shouldn't even know whether it's working with a default value or a custom value.
Think of it like this:
If we want to change the default value, the change should be in a single, obvious place. And the place should be the same for all flags/default values. We shouldn't have to dig all the way down to specific functions to change specific values.
Btw, am I missing something here? Since all other values are handled this way already and only the new one isn't, it's possible I'm not seeing what you're seeing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it on purpose really, because of two reasons:
-
IMO semantically, it doesn't make sense to handle the default value in the CLI parsing level. Imagine we add support for a MySQL DB, then the default value of
--db-image
would actually be dynamically determined through the flow of the program, since we wouldn't want to setpostgres
image then, but amysql
one.You can see this in the current code where the
sqlite
andpostgres
drivers have the same input parameters, so if I'm marking thedbImage
as required and giving it a value at the top-level, I'm at some point passingdbImage: postgres
into the sqlite driver. Of course (1) that's a no-op, and (2) nothing a refactor can't fix. But it points to this issue I'm talking about. -
The fact that we are using a default value or not is sometimes relevant. For example, in the Railway PR, we need to set some variables if we're not using the default image. Of course, here's not an issue, but in my mind they're both working at the same level of abstraction.
But yeah this is me overvaluing a "correct" implementation ready to get extended, and undervaluing the straightforward-ness to handle this at the top level.
Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But yeah this is me overvaluing a "correct" implementation ready to get extended, and undervaluing the straightforward-ness to handle this at the top level.
This is not clear to me hm, why do you think you are overvaluing a correct implementation vs simpler implementation? I think @sodic was also pushing for the most correct implementation.
I am not sure if I am getting the whole thing right. I think what you are saying is, we can, in case when CLI arg is not provided, set it to the default value right at the top, or we can recognize it is not set (set it to Nothing
), and then when we want to do something with it, we can at that moment decide how to handle that and grab that default value from somewhere.
I think @sodic suggested the first approach because it means you have that logic of "what do I do when it is empty, aha I set it to default" at only one place in the code, avoiding duplication and desync in the future.
We could alternatively have one central function which does this ("give me the value of db docker image") and then leave the value of image to be Nothing
, and then expect other parts of code to call that function. This gives the rest of the code more knowledge, because there is extra state of Nothing
that they can react on (if relevant), but it does leave some extra space for a mistake, since they might not call that function as intended and do something else, by accident.
So first approach is simpler, and less code, but one "state" less, therefore less "knowledge" in the system.
Second approach is more complex, more code, more space for future mistakes, but one "state" more and more "knowledge" in the system.
I would say, unless it is clear that more knowledge is useful, or that it feels semantically correct (I know this sounds vague, but sometimes it just makes sense, I can't say right now because I am not immersed enough into this code), let's go for simpler option (approach one).
Btw I didn't get the point with mysql and postgres, so my bad if there was something there that would inform this discussion further. I do like the idea of tying up the default docker image with the default db system at the type (or some hm) level, if that is what you are suggesting.
Aaaaah I get what you mean now. If user set db.system
to mysql
, then we wouldn't set the value of --db-image
to postgres, but to mysql docker image. Ok, true, that makes sense to me. But we can just say in the CLI docs that we will set it to default image for their chosen db system. We could even dynamically determine which one is that, as you said :D.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you think you are overvaluing a correct implementation vs simpler implementation?
oof not calling the other thing "incorrect", sorry! just that IMO with the constraints i had in mind, that was the most semantically accurate.
or that it feels semantically correct (I know this sounds vague, but sometimes it just makes sense, I can't say right now because I am not immersed enough into this code),
yeah this, it felt correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I don't have anything else to say here so I will let the two of you figure it out, as I think @sodic has a better grasp of the code than me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO semantically, it doesn't make sense to handle the default value in the CLI parsing level. Imagine we add support for a MySQL DB, then the default value of --db-image would actually be dynamically determined through the flow of the program, since we wouldn't want to set postgres image then, but a mysql one.
Me personally, I would still do it at the start of the program.
If above happens we will most likely need to parse the AppSpec
to know what to do with the application (it also includes the prisma
schema).
So the CLI and the AppSpec
would be the two configuration inputs into the program.
As is usually with these programs, CLI would override the AppSpec
provided values.
If so I would declare default values as part of the AppSpec
parsing process.
That way we:
We have a single source of truth for configuration.
The information is known at the start of the program. If we ever need it at some other place we don't have to refactor the code to move the default value declaration to shared parent.
wasp-app-runner
core/driver has to knew less about how Wasp applications work.
My thinking is if this operation can be overriden at the top, why not just default value it at the top?
The program must work with values defined at the top anyway.
This way we don't have to care about undefined values in the core/driver itself.
You can see this in the current code where the sqlite and postgres drivers have the same input parameters, so if I'm marking the dbImage as required and giving it a value at the top-level, I'm at some point passing dbImage: postgres into the sqlite driver. Of course (1) that's a no-op, and (2) nothing a refactor can't fix. But it points to this issue I'm talking about.
We shouldn't enforce the same interface if the needs are not the same.
I would say this was a mistake on our part.
We will have some branching logic no matter the solution, but I'm sure we could abstract some docker behavior when we support multiple databases if we do it this way.
The fact that we are using a default value or not is sometimes relevant. For example, in the Railway PR, we need to set some variables if we're not using the default image. Of course, here's not an issue, but in my mind they're both working at the same level of abstraction.
Yes, if we don't mind if it's default or not, let's just combine all the input data at the top?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see this in the current code where the sqlite and postgres drivers have the same input parameters, so if I'm marking the dbImage as required and giving it a value at the top-level, I'm at some point passing dbImage: postgres into the sqlite driver.
This is a great observation, but my conclusion is different :)
The core problem is that our current implementation has a "type error." It's not a real type error, but our groupings and parameters are set up in a way that allows invalid states.
Effective TS explains this in Item 29: Prefer Types That Always Represent Valid States.
Specifically, the parameter combination of dbType: DbType, dbImage?: string
does not properly model our constraints. We lose information and are forced to think about a state which shouldn't exist, dbType: SQLite, dbImage: somethingOtherThanUndefined
.
That is the root issue we should address. I think we should address it as high up as possible and propagate this information through the type system.
This problem likely affects the CLI too - we shouldn't really allow users to set --db-image
if they're using dev
mode. We should instead tell them something like "You can only use --db-image
in build mode." If I'm not mistaken, the current implementation will just ignore the --db-image
argument when used in dev
mode.
Imagine we add support for a MySQL DB, then the default value of --db-image would actually be dynamically determined
@FranjoMindek did a great job explaining this point.
Wasp app runner currently asssumes that "build" means "Use a postgres database" and "dev" means "Use an SQLite database."
If we do add MySQL, we'll have to break this assumption and pass an extra input to the runner - either the full app specification like Franjo suggested, or at least an argument specifying the database. In any case, the app runner will get more info and still be able to decide which default to use at the top-level.
Finally, notice how "dev" doesn't have to mean "Use an SQLite database." Apps in dev mode can also use Postgres and, if that becomes necessary, we will again want to communicate this through argument codependencies and the type system.
For example, let's assume we add MySQL to Wasp. Let's also assume we want to work with real databases in development, so we add a --db-type
flag.
run-wasp-app dev
should then allow the --db-type
to be either SQLite
, MySql
, or PostgreSQL
.
- If
--db-type
isSQLite
, then we don't allow a--db-image
flag. - If
--db-type
isMySQL
orPostgreSQL
, then we allow a--db-image
flag and choose an appropriate default if one isn't provided. We could even check the image compatibility with the database if we decide it's useful (but again, near the top level).
These constraints and codependencies between arguments should then again be properly modelled by the type system.
The fact that we are using a default value or not is sometimes relevant. For example, in the Railway PR, we need to set some variables if we're not using the default image. Of course, here's not an issue, but in my mind they're both working at the same level of abstraction.
I don't think those are related. Wasp app runner needs the image to start a docker container. It's not doing anything fancy and I'd expect it to stay that way.
Finally, @FranjoMindek said:
We shouldn't enforce the same interface if the needs are not the same.
I would say this was a mistake on our part.
Yes, I agree. We should drop the SetupDbFn
type. It doesn't provide anything useful and just forces our drivers to have the same inputs. I think the correct way to handle this would be doing something like:
// This would be parsed somewhere up top
// I'm making up type names and don't have this fleshed out, but you get the idea
type DbConfig = { dbType: SQLite } | { dbType: PostgreSQL, appName: string, pathToApp: string, dbImage: string }
export function setupDb(dbConfig: DbConfig): DbSetupResult {
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep I see the point. So as far as I can see, due to the needs of the code right now, I'm just pushing the default value to as top as I can get it (argument parser). In the future if we add more DB systems, we can then try and model the types in the way of "Making illegal states invalid".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, that's the best thing to do right now, I agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can then try and model the types in the way of "Making illegal states invalid".
@cprecioso Do you mind creating an issue for this? 😬
Also, I still think we should warn the caller that this option can't be used in dev mode instead of silently ignoring it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Others covered main stuff well, I just poped a couple of extra comments. I will remain in the role of weak reviewer here and just comment, I won'd be doing approving.
76b79c0
to
3d8fd82
Compare
Ready for re-review @sodic @FranjoMindek @Martinsos |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my side it's okay.
I didn't check comments left by others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good stuff, I'd just like us to inform the user about the inability to use --db-image
in dev mode, both in the docs and when using the CLI.
DATABASE_URL=postgresql://postgres:devpass@localhost:5432/postgres | ||
``` | ||
|
||
#### Custom database image |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs should mention that this can't be used in dev mode too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where did you get the impression that it can't be used on dev mode? It can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to go!
if (dbImageArg && dbType !== DbType.Postgres) { | ||
logger.error( | ||
`The --db-image option is only valid when using PostgreSQL as the database.`, | ||
); | ||
process.exit(1); | ||
} | ||
const dbImage = dbImageArg ?? defaultPostgresDbImage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a shame we had to push this all the way down here but I see no other way :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep 🫠
--db-image
argument towasp start db
command #3182Adds an optional
--db-image
argument to therun-wasp-app
command, allowing us to specify a custom Docker image for the database.This is part of #3174. We're adding
ask-the-documents
to the repo, which needs a specific PostgreSQL extension. This way we can more easily test the app.Changes
run-wasp-app
to accept and use custom Docker imagesNote
🤖 Generated with Claude Code
and 👨🏻 reviewed and edited by a person