From 68caf799ee651f765da512686d46d9c2b1503a0d Mon Sep 17 00:00:00 2001 From: Alimedhat000 Date: Mon, 18 May 2026 05:21:02 +0300 Subject: [PATCH 1/5] feat: add subdirectory support for static build pack --- packages/api/src/services/apps.ts | 1 + packages/shared/src/schema.ts | 1 + packages/shared/src/validators/app.ts | 6 +++ .../web/src/components/CreateAppModal.tsx | 17 ++++++++ .../src/deployments/steps/build-step.ts | 6 ++- .../src/deployments/steps/install-step.ts | 15 +++++-- .../src/deployments/steps/verify-step.ts | 5 ++- .../src/deployments/strategies/static.ts | 43 +++++++++++++------ 8 files changed, 75 insertions(+), 19 deletions(-) diff --git a/packages/api/src/services/apps.ts b/packages/api/src/services/apps.ts index 436d948..a71cbd8 100644 --- a/packages/api/src/services/apps.ts +++ b/packages/api/src/services/apps.ts @@ -17,6 +17,7 @@ const safeColumns = { githubRepo: apps.githubRepo, buildCommand: apps.buildCommand, outputDir: apps.outputDir, + subdirectory: apps.subdirectory, branch: apps.branch, buildTimeout: apps.buildTimeout, activeDeploymentId: apps.activeDeploymentId, diff --git a/packages/shared/src/schema.ts b/packages/shared/src/schema.ts index 2389a3f..e399b86 100644 --- a/packages/shared/src/schema.ts +++ b/packages/shared/src/schema.ts @@ -78,6 +78,7 @@ export const apps = pgTable( githubRepo: varchar("github_repo", { length: 500 }).notNull().default(""), buildCommand: varchar("build_command", { length: 500 }), outputDir: varchar("output_dir", { length: 255 }), + subdirectory: varchar("subdirectory", { length: 255 }), branch: varchar("branch", { length: 100 }).default("main"), buildTimeout: integer("build_timeout").default(900), activeDeploymentId: uuid("active_deployment_id"), diff --git a/packages/shared/src/validators/app.ts b/packages/shared/src/validators/app.ts index dd410c0..1396815 100644 --- a/packages/shared/src/validators/app.ts +++ b/packages/shared/src/validators/app.ts @@ -22,6 +22,12 @@ const appFieldDefs = { buildPack: z.enum(BUILD_PACKS), buildCommand: z.string().optional(), outputDir: z.string().optional(), + subdirectory: z + .string() + .optional() + .transform((val) => + val?.trim() ? val.replace(/^\/+|\/+$/g, "") : undefined, + ), port: z.number().int().positive(), runCommand: safeCommandSchema.optional(), installCommand: installCommandSchema.optional(), diff --git a/packages/web/src/components/CreateAppModal.tsx b/packages/web/src/components/CreateAppModal.tsx index d9a3179..012f377 100644 --- a/packages/web/src/components/CreateAppModal.tsx +++ b/packages/web/src/components/CreateAppModal.tsx @@ -12,6 +12,7 @@ function validate(input: { image: string; port: number; outputDir: string; + subdirectory: string; dockerfilePath: string; }): Record { const errs: Record = {}; @@ -83,6 +84,7 @@ export function CreateAppModal({ open, onClose }: Props) { const [branch, setBranch] = useState("main"); const [buildCommand, setBuildCommand] = useState(""); const [outputDir, setOutputDir] = useState("dist"); + const [subdirectory, setSubdirectory] = useState(""); const [port, setPort] = useState(80); const [runCommand, setRunCommand] = useState(""); const [dockerfilePath, setDockerfilePath] = useState("./Dockerfile"); @@ -101,6 +103,7 @@ export function CreateAppModal({ open, onClose }: Props) { setBuildPack("static"); setBuildCommand(""); setOutputDir("dist"); + setSubdirectory(""); setPort(80); setRunCommand(""); setDockerfilePath("./Dockerfile"); @@ -123,6 +126,7 @@ export function CreateAppModal({ open, onClose }: Props) { image, port, outputDir, + subdirectory, dockerfilePath, }); setFieldErrors(errs); @@ -144,6 +148,7 @@ export function CreateAppModal({ open, onClose }: Props) { if (buildPack === "static") { payload.outputDir = outputDir; payload.isSpa = isSpa; + if (subdirectory) payload.subdirectory = subdirectory; } if (buildPack === "dockerfile") { @@ -153,6 +158,7 @@ export function CreateAppModal({ open, onClose }: Props) { if (buildPack === "nixpacks") { if (runCommand) payload.runCommand = runCommand; if (outputDir) payload.outputDir = outputDir; + if (subdirectory) payload.subdirectory = subdirectory; } if (buildPack === "dockerimage") { @@ -298,6 +304,17 @@ export function CreateAppModal({ open, onClose }: Props) { /> +
+ + setSubdirectory(e.target.value)} + placeholder="e.g. frontend, packages/web" + className="w-full bg-ship-deep border border-ship-deck/50 px-3 py-2 font-mono text-sm text-white placeholder:text-ship-deck focus:outline-none focus:border-ship-buoy/50 transition-colors" + /> +