From 7e606335521a51b244fcfddf1d190c8e59373d12 Mon Sep 17 00:00:00 2001 From: Matt Hall Date: Sun, 8 Mar 2026 13:18:16 -0400 Subject: [PATCH 1/2] feat: add .dokploy.json local project config and init command Add a per-directory .dokploy.json config file that stores projectId, environmentId, and applicationId defaults. Commands read this file (walking up parent dirs) before falling into interactive prompts, allowing non-interactive usage from project folders. Includes `dokploy init` command to create the config interactively. --- src/commands/app/create.ts | 5 ++ src/commands/app/delete.ts | 6 ++ src/commands/app/deploy.ts | 6 ++ src/commands/app/stop.ts | 6 ++ src/commands/database/mariadb/create.ts | 5 ++ src/commands/database/mariadb/delete.ts | 5 ++ src/commands/database/mariadb/deploy.ts | 5 ++ src/commands/database/mariadb/stop.ts | 5 ++ src/commands/database/mongo/create.ts | 5 ++ src/commands/database/mongo/delete.ts | 5 ++ src/commands/database/mongo/deploy.ts | 5 ++ src/commands/database/mongo/stop.ts | 5 ++ src/commands/database/mysql/create.ts | 7 +- src/commands/database/mysql/delete.ts | 5 ++ src/commands/database/mysql/deploy.ts | 5 ++ src/commands/database/mysql/stop.ts | 5 ++ src/commands/database/postgres/create.ts | 5 ++ src/commands/database/postgres/delete.ts | 5 ++ src/commands/database/postgres/deploy.ts | 5 ++ src/commands/database/postgres/stop.ts | 5 ++ src/commands/database/redis/create.ts | 5 ++ src/commands/database/redis/delete.ts | 5 ++ src/commands/database/redis/deploy.ts | 5 ++ src/commands/database/redis/stop.ts | 5 ++ src/commands/env/pull.ts | 67 +++++++++++------- src/commands/env/push.ts | 67 +++++++++++------- src/commands/environment/create.ts | 4 ++ src/commands/environment/delete.ts | 5 ++ src/commands/init.ts | 88 ++++++++++++++++++++++++ src/utils/local-config.ts | 45 ++++++++++++ 30 files changed, 350 insertions(+), 51 deletions(-) create mode 100644 src/commands/init.ts create mode 100644 src/utils/local-config.ts diff --git a/src/commands/app/create.ts b/src/commands/app/create.ts index 40b77c7..e058d36 100644 --- a/src/commands/app/create.ts +++ b/src/commands/app/create.ts @@ -6,6 +6,7 @@ import inquirer from "inquirer"; import { type Project, getProjects } from "../../utils/shared.js"; import { slugify } from "../../utils/slug.js"; import { readAuthConfig } from "../../utils/utils.js"; +import { readLocalConfig } from "../../utils/local-config.js"; export interface Answers { project: Project; @@ -53,6 +54,10 @@ export default class AppCreate extends Command { const { flags } = await this.parse(AppCreate); let { projectId, environmentId, name, description, appName } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !name || !appName) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/app/delete.ts b/src/commands/app/delete.ts index af2a87b..a7e392e 100644 --- a/src/commands/app/delete.ts +++ b/src/commands/app/delete.ts @@ -5,6 +5,7 @@ import inquirer from "inquirer"; import { getProject, getProjects, type Application } from "../../utils/shared.js"; import { readAuthConfig } from "../../utils/utils.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import type { Answers } from "./create.js"; export default class AppDelete extends Command { @@ -43,6 +44,11 @@ export default class AppDelete extends Command { const { flags } = await this.parse(AppDelete); let { projectId, environmentId, applicationId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + if (!applicationId && localConfig.applicationId) applicationId = localConfig.applicationId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !applicationId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/app/deploy.ts b/src/commands/app/deploy.ts index 479f7f0..3fbbd88 100644 --- a/src/commands/app/deploy.ts +++ b/src/commands/app/deploy.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import { getProject, getProjects, type Application } from "../../utils/shared.js"; import inquirer from "inquirer"; import type { Answers } from "./create.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import axios from "axios"; export default class AppDeploy extends Command { @@ -43,6 +44,11 @@ export default class AppDeploy extends Command { const { flags } = await this.parse(AppDeploy); let { projectId, applicationId, environmentId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + if (!applicationId && localConfig.applicationId) applicationId = localConfig.applicationId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !applicationId || !environmentId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/app/stop.ts b/src/commands/app/stop.ts index b642e24..cc1ce77 100644 --- a/src/commands/app/stop.ts +++ b/src/commands/app/stop.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { getProject, getProjects, type Application } from "../../utils/shared.js"; import type { Answers } from "./create.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import axios from "axios"; export default class AppStop extends Command { @@ -39,6 +40,11 @@ export default class AppStop extends Command { const { flags } = await this.parse(AppStop); let { projectId, environmentId, applicationId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + if (!applicationId && localConfig.applicationId) applicationId = localConfig.applicationId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !applicationId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mariadb/create.ts b/src/commands/database/mariadb/create.ts index d2c4811..32fa261 100644 --- a/src/commands/database/mariadb/create.ts +++ b/src/commands/database/mariadb/create.ts @@ -3,6 +3,7 @@ import axios from "axios"; import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProjects, type Database } from "../../../utils/shared.js"; import { slugify } from "../../../utils/slug.js"; import type { Answers } from "../../app/create.js"; @@ -80,6 +81,10 @@ export default class DatabaseMariadbCreate extends Command { appName } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !name || !databaseName || !appName) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mariadb/delete.ts b/src/commands/database/mariadb/delete.ts index 87c9ba6..f215f8c 100644 --- a/src/commands/database/mariadb/delete.ts +++ b/src/commands/database/mariadb/delete.ts @@ -5,6 +5,7 @@ import inquirer from "inquirer"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; export default class DatabaseMariadbDelete extends Command { static description = "Delete a MariaDB database from a project."; @@ -40,6 +41,10 @@ export default class DatabaseMariadbDelete extends Command { const { flags } = await this.parse(DatabaseMariadbDelete); let { projectId, environmentId, mariadbId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + if (!projectId || !environmentId || !mariadbId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); diff --git a/src/commands/database/mariadb/deploy.ts b/src/commands/database/mariadb/deploy.ts index bc54751..e6be33e 100644 --- a/src/commands/database/mariadb/deploy.ts +++ b/src/commands/database/mariadb/deploy.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabaseMariadbDeploy extends Command { const { flags } = await this.parse(DatabaseMariadbDeploy); let { projectId, environmentId, mariadbId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mariadbId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mariadb/stop.ts b/src/commands/database/mariadb/stop.ts index f5c9d7e..74cb493 100644 --- a/src/commands/database/mariadb/stop.ts +++ b/src/commands/database/mariadb/stop.ts @@ -4,6 +4,7 @@ import inquirer from "inquirer"; import axios from "axios"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import type { Answers } from "../../app/create.js"; export default class DatabaseMariadbStop extends Command { @@ -39,6 +40,10 @@ export default class DatabaseMariadbStop extends Command { const { flags } = await this.parse(DatabaseMariadbStop); let { projectId, environmentId, mariadbId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mariadbId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mongo/create.ts b/src/commands/database/mongo/create.ts index 30ebd1b..939afc1 100644 --- a/src/commands/database/mongo/create.ts +++ b/src/commands/database/mongo/create.ts @@ -3,6 +3,7 @@ import axios from "axios"; import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProjects, type Database } from "../../../utils/shared.js"; import { slugify } from "../../../utils/slug.js"; import type { Answers } from "../../app/create.js"; @@ -75,6 +76,10 @@ export default class DatabaseMongoCreate extends Command { appName } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !name || !databaseName || !appName || !databasePassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mongo/delete.ts b/src/commands/database/mongo/delete.ts index 1fe34eb..966d0c1 100644 --- a/src/commands/database/mongo/delete.ts +++ b/src/commands/database/mongo/delete.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; export default class DatabaseMongoDelete extends Command { @@ -42,6 +43,10 @@ export default class DatabaseMongoDelete extends Command { const { flags } = await this.parse(DatabaseMongoDelete); let { projectId, environmentId, mongoId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mongoId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mongo/deploy.ts b/src/commands/database/mongo/deploy.ts index 7353d7a..0317bb8 100644 --- a/src/commands/database/mongo/deploy.ts +++ b/src/commands/database/mongo/deploy.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabaseMongoDeploy extends Command { const { flags } = await this.parse(DatabaseMongoDeploy); let { projectId, environmentId, mongoId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mongoId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mongo/stop.ts b/src/commands/database/mongo/stop.ts index e26f9ce..fdcff41 100644 --- a/src/commands/database/mongo/stop.ts +++ b/src/commands/database/mongo/stop.ts @@ -4,6 +4,7 @@ import inquirer from "inquirer"; import axios from "axios"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import type { Answers } from "../../app/create.js"; export default class DatabaseMongoStop extends Command { @@ -39,6 +40,10 @@ export default class DatabaseMongoStop extends Command { const { flags } = await this.parse(DatabaseMongoStop); let { projectId, environmentId, mongoId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mongoId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mysql/create.ts b/src/commands/database/mysql/create.ts index 402e8cb..1fc61d2 100644 --- a/src/commands/database/mysql/create.ts +++ b/src/commands/database/mysql/create.ts @@ -5,6 +5,7 @@ import inquirer from "inquirer"; import { slugify } from "../../../utils/slug.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProjects, type Database } from "../../../utils/shared.js"; import type { Answers } from "../../app/create.js"; @@ -81,7 +82,11 @@ export default class DatabaseMysqlCreate extends Command { appName } = flags; - // Modo interactivo si no se proporcionan los flags necesarios + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !name || !databaseName || !appName || !databasePassword || !databaseRootPassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); const projects = await getProjects(auth, this); diff --git a/src/commands/database/mysql/delete.ts b/src/commands/database/mysql/delete.ts index c05c442..fb3acd7 100644 --- a/src/commands/database/mysql/delete.ts +++ b/src/commands/database/mysql/delete.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; export default class DatabaseMysqlDelete extends Command { @@ -42,6 +43,10 @@ export default class DatabaseMysqlDelete extends Command { const { flags } = await this.parse(DatabaseMysqlDelete); let { projectId, environmentId, mysqlId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mysqlId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mysql/deploy.ts b/src/commands/database/mysql/deploy.ts index 747027b..a1c53ce 100644 --- a/src/commands/database/mysql/deploy.ts +++ b/src/commands/database/mysql/deploy.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabaseMysqlDeploy extends Command { const { flags } = await this.parse(DatabaseMysqlDeploy); let { projectId, environmentId, mysqlId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mysqlId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/mysql/stop.ts b/src/commands/database/mysql/stop.ts index 50ca2f6..8a7deac 100644 --- a/src/commands/database/mysql/stop.ts +++ b/src/commands/database/mysql/stop.ts @@ -4,6 +4,7 @@ import inquirer from "inquirer"; import axios from "axios"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import type { Answers } from "../../app/create.js"; export default class DatabaseMysqlStop extends Command { @@ -39,6 +40,10 @@ export default class DatabaseMysqlStop extends Command { const { flags } = await this.parse(DatabaseMysqlStop); let { projectId, environmentId, mysqlId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !mysqlId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/postgres/create.ts b/src/commands/database/postgres/create.ts index 55db1b7..38035c5 100644 --- a/src/commands/database/postgres/create.ts +++ b/src/commands/database/postgres/create.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { slugify } from "../../../utils/slug.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProjects, type Database } from "../../../utils/shared.js"; import type { Answers } from "../../app/create.js"; export default class DatabasePostgresCreate extends Command { @@ -74,6 +75,10 @@ export default class DatabasePostgresCreate extends Command { appName } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !name || !databaseName || !appName || !databasePassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/postgres/delete.ts b/src/commands/database/postgres/delete.ts index 339d66c..d200413 100644 --- a/src/commands/database/postgres/delete.ts +++ b/src/commands/database/postgres/delete.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; export default class DatabasePostgresDelete extends Command { @@ -42,6 +43,10 @@ export default class DatabasePostgresDelete extends Command { const { flags } = await this.parse(DatabasePostgresDelete); let { projectId, environmentId, postgresId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !postgresId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/postgres/deploy.ts b/src/commands/database/postgres/deploy.ts index 490994d..3677b1b 100644 --- a/src/commands/database/postgres/deploy.ts +++ b/src/commands/database/postgres/deploy.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabasePostgresDeploy extends Command { const { flags } = await this.parse(DatabasePostgresDeploy); let { projectId, environmentId, postgresId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !postgresId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/postgres/stop.ts b/src/commands/database/postgres/stop.ts index 920c56b..606d315 100644 --- a/src/commands/database/postgres/stop.ts +++ b/src/commands/database/postgres/stop.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabasePostgresStop extends Command { const { flags } = await this.parse(DatabasePostgresStop); let { projectId, environmentId, postgresId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !postgresId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/redis/create.ts b/src/commands/database/redis/create.ts index 4376b10..dad15f5 100644 --- a/src/commands/database/redis/create.ts +++ b/src/commands/database/redis/create.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import inquirer from "inquirer"; import { slugify } from "../../../utils/slug.js"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import { getProjects, type Database } from "../../../utils/shared.js"; import type { Answers } from "../../app/create.js"; @@ -64,6 +65,10 @@ export default class DatabaseRedisCreate extends Command { appName } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !name || !appName || !databasePassword) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/redis/delete.ts b/src/commands/database/redis/delete.ts index c1bba51..b17cc9a 100644 --- a/src/commands/database/redis/delete.ts +++ b/src/commands/database/redis/delete.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -42,6 +43,10 @@ export default class DatabaseRedisDelete extends Command { const { flags } = await this.parse(DatabaseRedisDelete); let { projectId, environmentId, redisId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !redisId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/redis/deploy.ts b/src/commands/database/redis/deploy.ts index 5cd7d7b..2d7fda6 100644 --- a/src/commands/database/redis/deploy.ts +++ b/src/commands/database/redis/deploy.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabaseRedisDeploy extends Command { const { flags } = await this.parse(DatabaseRedisDeploy); let { projectId, environmentId, redisId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !redisId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/database/redis/stop.ts b/src/commands/database/redis/stop.ts index a71a2b5..f081600 100644 --- a/src/commands/database/redis/stop.ts +++ b/src/commands/database/redis/stop.ts @@ -1,5 +1,6 @@ import { Command, Flags } from "@oclif/core"; import { readAuthConfig } from "../../../utils/utils.js"; +import { readLocalConfig } from "../../../utils/local-config.js"; import chalk from "chalk"; import { getProject, getProjects, type Database } from "../../../utils/shared.js"; import inquirer from "inquirer"; @@ -39,6 +40,10 @@ export default class DatabaseRedisStop extends Command { const { flags } = await this.parse(DatabaseRedisStop); let { projectId, environmentId, redisId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId || !redisId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/env/pull.ts b/src/commands/env/pull.ts index c89793e..ee96942 100644 --- a/src/commands/env/pull.ts +++ b/src/commands/env/pull.ts @@ -4,6 +4,7 @@ import chalk from "chalk"; import {getProject, getProjects} from "../../utils/shared.js"; import inquirer from "inquirer"; import {Answers} from "../app/create.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import fs from 'fs'; export default class EnvPull extends Command { @@ -36,34 +37,50 @@ export default class EnvPull extends Command { } } const auth = await readAuthConfig(this); - console.log(chalk.blue.bold("\n Listing all Projects \n")); + const localConfig = readLocalConfig(); + + let projectId = localConfig.projectId; + let environment: any; + + if (!projectId) { + console.log(chalk.blue.bold("\n Listing all Projects \n")); + const projects = await getProjects(auth, this); + const {project} = await inquirer.prompt([ + { + choices: projects.map((project) => ({ + name: project.name, + value: project, + })), + message: "Select the project:", + name: "project", + type: "list", + }, + ]); + projectId = project.projectId; + } - const projects = await getProjects(auth, this); - const {project} = await inquirer.prompt([ - { - choices: projects.map((project) => ({ - name: project.name, - value: project, - })), - message: "Select the project:", - name: "project", - type: "list", - }, - ]); - const projectId = project.projectId; const projectSelected = await getProject(projectId, auth, this); - const {environment} = await inquirer.prompt([ - { - choices: projectSelected.environments.map((environment: any) => ({ - name: environment.name, - value: environment, - })), - message: "Select the environment:", - name: "environment", - type: "list", - }, - ]); + if (localConfig.environmentId) { + environment = projectSelected.environments.find( + (e: any) => e.environmentId === localConfig.environmentId + ); + } + + if (!environment) { + const envAnswer = await inquirer.prompt([ + { + choices: projectSelected.environments.map((environment: any) => ({ + name: environment.name, + value: environment, + })), + message: "Select the environment:", + name: "environment", + type: "list", + }, + ]); + environment = envAnswer.environment; + } const choices = [ ...environment.applications.map((app: any) => ({ diff --git a/src/commands/env/push.ts b/src/commands/env/push.ts index c3f2809..1569cef 100644 --- a/src/commands/env/push.ts +++ b/src/commands/env/push.ts @@ -5,6 +5,7 @@ import inquirer from "inquirer"; import {readAuthConfig} from "../../utils/utils.js"; import {getProject, getProjects} from "../../utils/shared.js"; import {Answers} from "../app/create.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import axios from "axios"; export default class EnvPush extends Command { @@ -42,34 +43,50 @@ export default class EnvPush extends Command { const fileContent = fs.readFileSync(args.file, 'utf-8'); const auth = await readAuthConfig(this); - console.log(chalk.blue.bold("\n Listing all Projects \n")); + const localConfig = readLocalConfig(); + + let projectId = localConfig.projectId; + let environment: any; + + if (!projectId) { + console.log(chalk.blue.bold("\n Listing all Projects \n")); + const projects = await getProjects(auth, this); + const {project} = await inquirer.prompt([ + { + choices: projects.map((project) => ({ + name: project.name, + value: project, + })), + message: "Select the project:", + name: "project", + type: "list", + }, + ]); + projectId = project.projectId; + } - const projects = await getProjects(auth, this); - const {project} = await inquirer.prompt([ - { - choices: projects.map((project) => ({ - name: project.name, - value: project, - })), - message: "Select the project:", - name: "project", - type: "list", - }, - ]); - const projectId = project.projectId; const projectSelected = await getProject(projectId, auth, this); - const {environment} = await inquirer.prompt([ - { - choices: projectSelected.environments.map((environment: any) => ({ - name: environment.name, - value: environment, - })), - message: "Select the environment:", - name: "environment", - type: "list", - }, - ]); + if (localConfig.environmentId) { + environment = projectSelected.environments.find( + (e: any) => e.environmentId === localConfig.environmentId + ); + } + + if (!environment) { + const envAnswer = await inquirer.prompt([ + { + choices: projectSelected.environments.map((environment: any) => ({ + name: environment.name, + value: environment, + })), + message: "Select the environment:", + name: "environment", + type: "list", + }, + ]); + environment = envAnswer.environment; + } const choices = [ ...environment.applications.map((app: any) => ({ diff --git a/src/commands/environment/create.ts b/src/commands/environment/create.ts index 761008d..94c135b 100644 --- a/src/commands/environment/create.ts +++ b/src/commands/environment/create.ts @@ -5,6 +5,7 @@ import inquirer from "inquirer"; import { getProjects } from "../../utils/shared.js"; import { readAuthConfig } from "../../utils/utils.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import type { Answers } from "../app/create.js"; export default class EnvironmentCreate extends Command { @@ -40,6 +41,9 @@ export default class EnvironmentCreate extends Command { const { flags } = await this.parse(EnvironmentCreate); let { projectId, name, description } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !name) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/environment/delete.ts b/src/commands/environment/delete.ts index 8417ac1..cffccfa 100644 --- a/src/commands/environment/delete.ts +++ b/src/commands/environment/delete.ts @@ -5,6 +5,7 @@ import inquirer from "inquirer"; import { getProjects } from "../../utils/shared.js"; import { readAuthConfig } from "../../utils/utils.js"; +import { readLocalConfig } from "../../utils/local-config.js"; import type { Answers } from "../app/create.js"; export default class EnvironmentDelete extends Command { @@ -38,6 +39,10 @@ export default class EnvironmentDelete extends Command { const { flags } = await this.parse(EnvironmentDelete); let { projectId, environmentId } = flags; + const localConfig = readLocalConfig(); + if (!projectId && localConfig.projectId) projectId = localConfig.projectId; + if (!environmentId && localConfig.environmentId) environmentId = localConfig.environmentId; + // Modo interactivo si no se proporcionan los flags necesarios if (!projectId || !environmentId) { console.log(chalk.blue.bold("\n Listing all Projects \n")); diff --git a/src/commands/init.ts b/src/commands/init.ts new file mode 100644 index 0000000..f565d23 --- /dev/null +++ b/src/commands/init.ts @@ -0,0 +1,88 @@ +import { Command } from "@oclif/core"; +import chalk from "chalk"; +import inquirer from "inquirer"; + +import { getProjects, type Application } from "../utils/shared.js"; +import { readAuthConfig } from "../utils/utils.js"; +import { writeLocalConfig, LOCAL_CONFIG_FILE } from "../utils/local-config.js"; +import type { Answers } from "./app/create.js"; + +export default class Init extends Command { + static description = `Create a ${LOCAL_CONFIG_FILE} file in the current directory to set default project, environment, and application for CLI commands.`; + + static examples = [ + "$ <%= config.bin %> init", + ]; + + public async run(): Promise { + const auth = await readAuthConfig(this); + + console.log(chalk.blue.bold("\n Listing all Projects \n")); + const projects = await getProjects(auth, this); + + // 1. Select project + const { project } = await inquirer.prompt([ + { + choices: projects.map((project) => ({ + name: project.name, + value: project, + })), + message: "Select a project:", + name: "project", + type: "list", + }, + ]); + + const config: Record = {}; + config.projectId = project.projectId!; + + // 2. Select environment + if (project.environments && project.environments.length > 0) { + const { environment } = await inquirer.prompt([ + { + choices: project.environments.map((env) => ({ + name: `${env.name} (${env.description})`, + value: env, + })), + message: "Select an environment:", + name: "environment", + type: "list", + }, + ]); + + config.environmentId = environment.environmentId; + + // 3. Optionally select application + if (environment.applications && environment.applications.length > 0) { + const { selectApp } = await inquirer.prompt([ + { + type: "confirm", + name: "selectApp", + message: "Do you want to set a default application?", + default: true, + }, + ]); + + if (selectApp) { + const { selectedApp } = await inquirer.prompt([ + { + choices: environment.applications.map((app: Application) => ({ + name: app.name, + value: app.applicationId, + })), + message: "Select an application:", + name: "selectedApp", + type: "list", + }, + ]); + + config.applicationId = selectedApp; + } + } + } + + const filePath = writeLocalConfig(config); + this.log(chalk.green(`\nCreated ${filePath}`)); + this.log(chalk.gray("CLI commands will now use these defaults instead of prompting interactively.")); + } +} diff --git a/src/utils/local-config.ts b/src/utils/local-config.ts new file mode 100644 index 0000000..bb18cd1 --- /dev/null +++ b/src/utils/local-config.ts @@ -0,0 +1,45 @@ +import * as fs from "node:fs"; +import * as path from "node:path"; + +export type LocalConfig = { + projectId?: string; + environmentId?: string; + applicationId?: string; +}; + +export const LOCAL_CONFIG_FILE = ".dokploy.json"; + +/** + * Walk up from the given directory looking for a .dokploy.json file. + * Returns the parsed config if found, or an empty object if not. + */ +export const readLocalConfig = (startDir: string = process.cwd()): LocalConfig => { + let dir = path.resolve(startDir); + + while (true) { + const candidate = path.join(dir, LOCAL_CONFIG_FILE); + if (fs.existsSync(candidate)) { + try { + const content = fs.readFileSync(candidate, "utf8"); + return JSON.parse(content) as LocalConfig; + } catch { + return {}; + } + } + + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + + return {}; +}; + +/** + * Write a .dokploy.json file to the given directory (defaults to cwd). + */ +export const writeLocalConfig = (config: LocalConfig, dir: string = process.cwd()): string => { + const filePath = path.join(dir, LOCAL_CONFIG_FILE); + fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf8"); + return filePath; +}; From af23bd1f341245086242094c5a055aa86a9fe29a Mon Sep 17 00:00:00 2001 From: Matt Hall Date: Sun, 8 Mar 2026 13:21:59 -0400 Subject: [PATCH 2/2] docs: add project configuration section to README for .dokploy.json --- readme.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/readme.md b/readme.md index d4cc848..dcb072b 100644 --- a/readme.md +++ b/readme.md @@ -13,6 +13,7 @@ Dokploy CLI is a powerful and versatile command-line tool designed to remotely m - [Installation](#installation) - [Usage](#usage) +- [Project Configuration](#project-configuration) - [Commands](#commands) - [Authentication](#authentication) - [Project Management](#project-management) @@ -44,6 +45,34 @@ USAGE ... ``` +## Project Configuration + +You can create a `.dokploy.json` file in any project directory to set default values for project, environment, and application. This lets you skip interactive prompts when running commands. + +```sh-session +$ cd ~/my-app +$ dokploy init +``` + +This walks you through selecting a project, environment, and (optionally) an application, then writes a `.dokploy.json` file: + +```json +{ + "projectId": "abc123", + "environmentId": "def456", + "applicationId": "ghi789" +} +``` + +Now commands run from that directory (or any subdirectory) will use these defaults automatically: + +```sh-session +$ dokploy app deploy -y # deploys the configured application +$ dokploy env pull .env.local # pulls env vars without prompts +``` + +Flags passed on the command line always take priority over `.dokploy.json` values. + ## Commands ### Authentication