@kithinji/pod 1.0.19 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,6 +36,9 @@ export async function dockerize(env: "dev" | "prod" = "prod") {
36
36
  await setupDevelopment(cwd, projectName, selectedServices);
37
37
  }
38
38
 
39
+ // Write environment variables to .env if they don't exist
40
+ await writeEnvVars(cwd, selectedServices, env);
41
+
39
42
  printNextSteps(projectName, env, selectedServices);
40
43
  }
41
44
 
@@ -67,7 +70,6 @@ async function selectServices(
67
70
  })),
68
71
  });
69
72
 
70
- // Fixed: Handle empty response when user cancels
71
73
  if (!response.services || response.services.length === 0) return [];
72
74
  return detected.filter((s) => response.services.includes(s.name));
73
75
  }
@@ -99,6 +101,91 @@ async function restructureProject(cwd: string, projectName: string) {
99
101
  }
100
102
  }
101
103
 
104
+ async function writeEnvVars(
105
+ cwd: string,
106
+ services: DockerService[],
107
+ env: string
108
+ ) {
109
+ const envPath = path.join(cwd, ".env");
110
+ let existingEnv: Record<string, string> = {};
111
+ let existingContent = "";
112
+
113
+ // Read existing .env file if it exists
114
+ if (fs.existsSync(envPath)) {
115
+ existingContent = await fs.readFile(envPath, "utf8");
116
+ existingEnv = parseEnvFile(existingContent);
117
+ }
118
+
119
+ const newVars: string[] = [];
120
+
121
+ // Add HOST variable for production
122
+ if (env === "prod" && !existingEnv.HOST) {
123
+ newVars.push("HOST=example.com");
124
+ }
125
+
126
+ // Add service-specific variables
127
+ for (const service of services) {
128
+ const serviceVars = getEnvVars(service.name);
129
+ for (const varLine of serviceVars) {
130
+ const [key] = varLine.split("=");
131
+ if (!existingEnv[key]) {
132
+ newVars.push(varLine);
133
+ }
134
+ }
135
+
136
+ // Add tunnel-specific variables for dev mode
137
+ if (env === "dev" && service.needsTunnel) {
138
+ const remoteHostKey = `${service.name.toUpperCase()}_REMOTE_HOST`;
139
+ const remotePortKey = `${service.name.toUpperCase()}_REMOTE_PORT`;
140
+
141
+ if (!existingEnv[remoteHostKey]) {
142
+ newVars.push(`${remoteHostKey}=user@remote-server.com`);
143
+ }
144
+ if (!existingEnv[remotePortKey]) {
145
+ newVars.push(`${remotePortKey}=${getDefaultPort(service.name)}`);
146
+ }
147
+ }
148
+ }
149
+
150
+ // Write new variables to .env file
151
+ if (newVars.length > 0) {
152
+ const separator =
153
+ existingContent && !existingContent.endsWith("\n") ? "\n" : "";
154
+ const newContent =
155
+ existingContent +
156
+ separator +
157
+ (existingContent ? "\n" : "") +
158
+ newVars.join("\n") +
159
+ "\n";
160
+ await fs.writeFile(envPath, newContent);
161
+ console.log(
162
+ `✅ Added ${newVars.length} new environment variable(s) to .env`
163
+ );
164
+ } else {
165
+ console.log("✅ All required environment variables already exist in .env");
166
+ }
167
+ }
168
+
169
+ function parseEnvFile(content: string): Record<string, string> {
170
+ const env: Record<string, string> = {};
171
+ const lines = content.split("\n");
172
+
173
+ for (const line of lines) {
174
+ const trimmed = line.trim();
175
+ // Skip empty lines and comments
176
+ if (!trimmed || trimmed.startsWith("#")) continue;
177
+
178
+ const equalIndex = trimmed.indexOf("=");
179
+ if (equalIndex > 0) {
180
+ const key = trimmed.substring(0, equalIndex).trim();
181
+ const value = trimmed.substring(equalIndex + 1).trim();
182
+ env[key] = value;
183
+ }
184
+ }
185
+
186
+ return env;
187
+ }
188
+
102
189
  async function createDockerfile(cwd: string, projectName: string) {
103
190
  const dockerfilePath = path.join(cwd, projectName, "Dockerfile");
104
191
  const dockerignorePath = path.join(cwd, projectName, ".dockerignore");
@@ -153,9 +240,7 @@ __tests__
153
240
 
154
241
  # Build files
155
242
  dist
156
- build
157
- .next
158
- .nuxt
243
+ public
159
244
 
160
245
  # Logs
161
246
  logs
@@ -213,7 +298,7 @@ async function setupProduction(
213
298
  "./letsencrypt:/letsencrypt",
214
299
  ],
215
300
  networks: ["web"],
216
- env_file: [".env"], // Fixed: Added env_file to read HOST variable
301
+ env_file: [".env"],
217
302
  },
218
303
  [projectName]: {
219
304
  build: {
@@ -222,16 +307,16 @@ async function setupProduction(
222
307
  },
223
308
  labels: [
224
309
  "traefik.enable=true",
225
- "traefik.http.routers.app.rule=Host(`${HOST}`)", // Fixed: Changed from app.${HOST} to ${HOST}
310
+ "traefik.http.routers.app.rule=Host(`app.${HOST}`)",
226
311
  "traefik.http.routers.app.entrypoints=websecure",
227
312
  "traefik.http.routers.app.tls.certresolver=myresolver",
228
313
  "traefik.http.services.app.loadbalancer.server.port=8080",
229
314
  ],
230
315
  env_file: [".env"],
231
316
  networks: ["web"],
232
- volumes: [`./${projectName}:/app`], // Fixed: Changed from ./web to ./${projectName}
317
+ volumes: [`./${projectName}:/app`, `/app/node_modules`],
233
318
  command: "npm run dev",
234
- depends_on: [], // Fixed: Added missing depends_on array
319
+ depends_on: [],
235
320
  },
236
321
  },
237
322
  networks: {
@@ -239,7 +324,7 @@ async function setupProduction(
239
324
  driver: "bridge",
240
325
  },
241
326
  },
242
- volumes: {}, // Fixed: Added missing volumes object
327
+ volumes: {},
243
328
  };
244
329
 
245
330
  for (const service of services) {
@@ -321,7 +406,7 @@ async function setupDevelopment(
321
406
  },
322
407
  ports: ["8080:8080"],
323
408
  env_file: [".env"],
324
- volumes: [`./${projectName}:/app`, `/app/node_modules`], // Fixed: Changed from . to ./${projectName}
409
+ volumes: [`./${projectName}:/app`, `/app/node_modules`],
325
410
  command: "npm run dev",
326
411
  depends_on: [],
327
412
  },
@@ -494,11 +579,11 @@ function printNextSteps(
494
579
  console.log(`\n✅ Done! Next steps:\n`);
495
580
 
496
581
  if (env === "prod") {
497
- console.log(` # Edit .env with your settings`);
582
+ console.log(` # Review and edit .env with your settings`);
498
583
  console.log(` docker-compose up -d`);
499
- console.log(` # Access at https://\${HOST}\n`);
584
+ console.log(` # Access at https://app.\${HOST}\n`);
500
585
  } else {
501
- console.log(` # Edit .env with your settings`);
586
+ console.log(` # Review and edit .env with your settings`);
502
587
  if (services.some((s) => s.needsTunnel)) {
503
588
  console.log(` # Add SSH keys: {service}.pem`);
504
589
  }
package/src/main.ts CHANGED
@@ -25,10 +25,12 @@ import { addFeature } from "./add/module";
25
25
  import path from "path";
26
26
  import { execSync } from "child_process";
27
27
  import { dockerize } from "./docker";
28
+ import { deploy } from "./deploy";
29
+ import chalk from "chalk";
28
30
 
29
31
  const program = new Command();
30
32
 
31
- program.name("pod").description("Pod cli tool").version("1.0.19");
33
+ program.name("pod").description("Pod cli tool").version("1.0.20");
32
34
 
33
35
  program
34
36
  .command("new <name>")
@@ -88,12 +90,16 @@ program
88
90
  });
89
91
 
90
92
  program
91
- .command("deploy <type> <options>")
92
- .description("Deploy a Pod Project")
93
- .action(async (type, name) => {
93
+ .command("deploy")
94
+ .description("Deploy to a target environment")
95
+ .argument("<target>", "Target environment (e.g., ec2)")
96
+ .option("--force-install", "Force reinstallation even if already installed")
97
+ .action(async (target: string, options: { forceEnsure?: boolean }) => {
94
98
  try {
95
- } catch (err: any) {
96
- console.error("❌ Error:", err.message);
99
+ await deploy(target, options);
100
+ } catch (error: any) {
101
+ console.error(chalk.red(error.message));
102
+ process.exit(1);
97
103
  }
98
104
  });
99
105