diff --git a/wasp-app-runner/README.md b/wasp-app-runner/README.md index 8b98d90793..1c86747daf 100644 --- a/wasp-app-runner/README.md +++ b/wasp-app-runner/README.md @@ -48,17 +48,18 @@ npx @wasp.sh/wasp-app-runner dev ### Options ``` -npx run-wasp-app [--path-to-app ] [--wasp-cli-cmd ] +npx run-wasp-app [--path-to-app ] [--wasp-cli-cmd ] [--db-image ] ``` You must pass the `` as an argument, which can be either `dev` or `build`. -| Option | Description | Example | -| ---------------- | ------------------------------------------------------ | --------------- | -| `--path-to-app` | Path to your Wasp application directory (default: ".") | `./my-wasp-app` | -| `--wasp-cli-cmd` | Wasp CLI command (default: `wasp`) | `wasp-cli` | +| Option | Description | Example | +| ---------------- | ------------------------------------------------------ | ----------------- | +| `--path-to-app` | Path to your Wasp application directory (default: ".") | `./my-wasp-app` | +| `--wasp-cli-cmd` | Wasp CLI command (default: `wasp`) | `wasp-cli` | +| `--db-image` | Custom PostgreSQL Docker image (default: `postgres`) | `postgis/postgis` | -## Postgres Configuration +### Postgres Configuration Check the `./db/postgres.ts` file to see the Postgres configuration used. @@ -68,6 +69,15 @@ If Postgres is used, the script automatically sets the `DATABASE_URL` env variab DATABASE_URL=postgresql://postgres:devpass@localhost:5432/postgres ``` +#### Custom database image + +You can override the Docker image used for Postgres via `--db-image`. If not provided, the default `postgres` is used. + +Examples: `--db-image postgres:15`, `--db-image pgvector/pgvector:pg16`, `--db-image postgis/postgis:14-3.2`. + +> [!NOTE] +> The same requirements as in `wasp start db` apply for the Postgres image used. See [Wasp docs](../web/docs/data-model/databases.md#custom-database) for more details. + ### Env variables When using the `dev` mode: @@ -85,7 +95,7 @@ When developing, you can run the script directly from the local directory withou ``` npm install -npm run start -- [--path-to-app ] [--wasp-cli-cmd ] +npm run start -- [--path-to-app ] [--wasp-cli-cmd ] [--db-image ] ``` `npm run start` runs `npm run build` to build the TypeScript code and then runs the `./bin/index.js` script. diff --git a/wasp-app-runner/src/args.ts b/wasp-app-runner/src/args.ts index 09aa20a792..3ff983ff14 100644 --- a/wasp-app-runner/src/args.ts +++ b/wasp-app-runner/src/args.ts @@ -1,15 +1,18 @@ import { Argument, program } from "@commander-js/extra-typings"; import packageJson from "../package.json" with { type: "json" }; +import { defaultPostgresDbImage } from "./db/postgres.js"; import { Branded } from "./types.js"; export type Mode = "dev" | "build"; export type PathToApp = Branded; export type WaspCliCmd = Branded; +export type DockerImageName = Branded; export function parseArgs(): { mode: Mode; pathToApp: PathToApp; waspCliCmd: WaspCliCmd; + dbImage?: DockerImageName; } { const parsedProgram = program .name("run-wasp-app") @@ -22,6 +25,10 @@ export function parseArgs(): { ) .option("--path-to-app ", "Path to the Wasp application", ".") .option("--wasp-cli-cmd ", "Wasp CLI command to use", "wasp") + .option( + "--db-image ", + `Custom PostgreSQL Docker image to use (default: "${defaultPostgresDbImage}")`, + ) .parse(); const options = parsedProgram.opts(); @@ -31,5 +38,6 @@ export function parseArgs(): { mode, pathToApp: options.pathToApp as PathToApp, waspCliCmd: options.waspCliCmd as WaspCliCmd, + dbImage: options.dbImage as DockerImageName, }; } diff --git a/wasp-app-runner/src/build/index.ts b/wasp-app-runner/src/build/index.ts index c1de8d3eb8..35800134a4 100644 --- a/wasp-app-runner/src/build/index.ts +++ b/wasp-app-runner/src/build/index.ts @@ -1,4 +1,4 @@ -import type { PathToApp, WaspCliCmd } from "../args.js"; +import type { DockerImageName, PathToApp, WaspCliCmd } from "../args.js"; import { DbType, setupDb } from "../db/index.js"; import { startLocalSmtpServer } from "../smtp.js"; import { type AppName, waspBuild } from "../waspCli.js"; @@ -11,11 +11,13 @@ export async function startAppInBuildMode({ pathToApp, appName, dbType, + dbImage, }: { waspCliCmd: WaspCliCmd; pathToApp: PathToApp; appName: AppName; dbType: DbType; + dbImage: DockerImageName; }) { await waspBuild({ waspCliCmd, @@ -26,6 +28,7 @@ export async function startAppInBuildMode({ appName, dbType, pathToApp, + dbImage, }); await startLocalSmtpServer(); diff --git a/wasp-app-runner/src/db/index.ts b/wasp-app-runner/src/db/index.ts index 55283d1d97..bbd1acd40b 100644 --- a/wasp-app-runner/src/db/index.ts +++ b/wasp-app-runner/src/db/index.ts @@ -1,8 +1,8 @@ -import type { PathToApp } from "../args.js"; +import type { DockerImageName, PathToApp } from "../args.js"; import type { AppName } from "../waspCli.js"; import { setupPostgres } from "./postgres.js"; import { setupSqlite } from "./sqlite.js"; -import type { SetupDbFn } from "./types.js"; +import type { SetupDbResult } from "./types.js"; export enum DbType { Sqlite = "sqlite", @@ -13,16 +13,18 @@ export function setupDb({ appName, dbType, pathToApp, + dbImage, }: { dbType: DbType; appName: AppName; pathToApp: PathToApp; -}): ReturnType { + dbImage: DockerImageName; +}): Promise { switch (dbType) { case DbType.Sqlite: - return setupSqlite({ appName, pathToApp }); + return setupSqlite(); case DbType.Postgres: - return setupPostgres({ appName, pathToApp }); + return setupPostgres({ appName, pathToApp, dbImage }); default: dbType satisfies never; throw new Error(`Unknown database type: ${dbType}`); diff --git a/wasp-app-runner/src/db/postgres.ts b/wasp-app-runner/src/db/postgres.ts index ba9c623da8..b18ca50719 100644 --- a/wasp-app-runner/src/db/postgres.ts +++ b/wasp-app-runner/src/db/postgres.ts @@ -1,4 +1,4 @@ -import type { PathToApp } from "../args.js"; +import type { DockerImageName, PathToApp } from "../args.js"; import { DbContainerName, createAppSpecificDbContainerName, @@ -7,18 +7,29 @@ import { createLogger } from "../logging.js"; import { spawnAndCollectOutput } from "../process.js"; import { Branded } from "../types.js"; import type { AppName } from "../waspCli.js"; -import type { SetupDbFn } from "./types.js"; +import type { SetupDbResult } from "./types.js"; + +export const defaultPostgresDbImage = "postgres:16" as DockerImageName; type DatabaseConnectionUrl = Branded; const logger = createLogger("postgres"); -export const setupPostgres: SetupDbFn = async ({ appName, pathToApp }) => { +export const setupPostgres = async ({ + appName, + pathToApp, + dbImage, +}: { + appName: AppName; + pathToApp: PathToApp; + dbImage: DockerImageName; +}): Promise => { await ensureDockerIsRunning(); const databaseUrl = await startPostgresContainerForApp({ appName, pathToApp, + dbImage, }); logger.info(`Using DATABASE_URL: ${databaseUrl}`); @@ -31,9 +42,11 @@ export const setupPostgres: SetupDbFn = async ({ appName, pathToApp }) => { async function startPostgresContainerForApp({ appName, pathToApp, + dbImage, }: { appName: AppName; pathToApp: PathToApp; + dbImage: DockerImageName; }): Promise { const containerName = createAppSpecificDbContainerName({ appName, @@ -42,20 +55,22 @@ async function startPostgresContainerForApp({ logger.info(`Using container name: ${containerName}`); - const databaseUrl = - await startPostgresContainerAndWaitUntilReady(containerName); + const databaseUrl = await startPostgresContainerAndWaitUntilReady( + containerName, + dbImage, + ); return databaseUrl; } async function startPostgresContainerAndWaitUntilReady( containerName: DbContainerName, + dbImage: DockerImageName, ): Promise { const port = 5432; const password = "devpass"; - const image = "postgres:16"; - logger.info("Starting the PostgreSQL container..."); + logger.info(`Starting the PostgreSQL container with image: ${dbImage}...`); spawnAndCollectOutput({ name: "create-postgres-container", @@ -69,7 +84,7 @@ async function startPostgresContainerAndWaitUntilReady( "-e", `POSTGRES_PASSWORD=${password}`, `--rm`, - image, + dbImage, ], }) // If we awaited here, we would block the main thread indefinitely. diff --git a/wasp-app-runner/src/db/sqlite.ts b/wasp-app-runner/src/db/sqlite.ts index b8d81bd3ad..fe28cbcd55 100644 --- a/wasp-app-runner/src/db/sqlite.ts +++ b/wasp-app-runner/src/db/sqlite.ts @@ -1,6 +1,6 @@ -import type { SetupDbFn } from "./types.js"; +import type { SetupDbResult } from "./types.js"; -export const setupSqlite: SetupDbFn = async (_options) => { +export const setupSqlite = async (): Promise => { // No need to do anything special for SQLite, just return // an empty object for the env vars. return { diff --git a/wasp-app-runner/src/db/types.ts b/wasp-app-runner/src/db/types.ts index 34106c25e7..51516af34c 100644 --- a/wasp-app-runner/src/db/types.ts +++ b/wasp-app-runner/src/db/types.ts @@ -1,9 +1,5 @@ -import type { PathToApp } from "../args.js"; -import type { AppName } from "../waspCli.js"; - -export type SetupDbFn = (options: { - appName: AppName; - pathToApp: PathToApp; -}) => Promise<{ - dbEnvVars: { [envVarName: string]: string }; -}>; +export type SetupDbResult = { + dbEnvVars: { + [envVarName: string]: string; + }; +}; diff --git a/wasp-app-runner/src/dev/index.ts b/wasp-app-runner/src/dev/index.ts index b22673da16..48554ac893 100644 --- a/wasp-app-runner/src/dev/index.ts +++ b/wasp-app-runner/src/dev/index.ts @@ -1,4 +1,4 @@ -import type { PathToApp, WaspCliCmd } from "../args.js"; +import type { DockerImageName, PathToApp, WaspCliCmd } from "../args.js"; import { DbType, setupDb } from "../db/index.js"; import { type AppName, waspMigrateDb, waspStart } from "../waspCli.js"; @@ -7,16 +7,19 @@ export async function startAppInDevMode({ pathToApp, appName, dbType, + dbImage, }: { waspCliCmd: WaspCliCmd; pathToApp: PathToApp; appName: AppName; dbType: DbType; + dbImage: DockerImageName; }): Promise { const { dbEnvVars } = await setupDb({ appName, dbType, pathToApp, + dbImage, }); await waspMigrateDb({ diff --git a/wasp-app-runner/src/index.ts b/wasp-app-runner/src/index.ts index 7f3e13123b..aaf4e27cbe 100755 --- a/wasp-app-runner/src/index.ts +++ b/wasp-app-runner/src/index.ts @@ -1,6 +1,14 @@ import { readdir } from "fs/promises"; -import { type Mode, parseArgs, PathToApp, WaspCliCmd } from "./args.js"; +import { + parseArgs, + type DockerImageName, + type Mode, + type PathToApp, + type WaspCliCmd, +} from "./args.js"; import { startAppInBuildMode } from "./build/index.js"; +import { DbType } from "./db/index.js"; +import { defaultPostgresDbImage } from "./db/postgres.js"; import { checkDependencies } from "./dependencies.js"; import { startAppInDevMode } from "./dev/index.js"; import { createLogger } from "./logging.js"; @@ -9,13 +17,14 @@ import { waspInfo, waspTsSetup } from "./waspCli.js"; const logger = createLogger("main"); export async function main(): Promise { - const { mode, waspCliCmd, pathToApp } = parseArgs(); + const { mode, waspCliCmd, pathToApp, dbImage } = parseArgs(); try { await runWaspApp({ mode, waspCliCmd, pathToApp, + dbImage, }); } catch (error: unknown) { if (error instanceof Error) { @@ -31,10 +40,12 @@ async function runWaspApp({ mode, waspCliCmd, pathToApp, + dbImage: dbImageArg, }: { mode: Mode; waspCliCmd: WaspCliCmd; pathToApp: PathToApp; + dbImage?: DockerImageName; }): Promise { await checkDependencies(); @@ -43,6 +54,14 @@ async function runWaspApp({ pathToApp, }); + 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; + if (await isWaspTypescriptConfigProject(pathToApp)) { await waspTsSetup({ waspCliCmd, @@ -61,6 +80,7 @@ async function runWaspApp({ pathToApp, appName, dbType, + dbImage, }); break; @@ -70,6 +90,7 @@ async function runWaspApp({ pathToApp, appName, dbType, + dbImage, }); break;