@kithinji/pod 1.0.18 → 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,8 +101,94 @@ 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");
191
+ const dockerignorePath = path.join(cwd, projectName, ".dockerignore");
104
192
 
105
193
  const dockerfile = `FROM node:18-alpine
106
194
 
@@ -115,9 +203,67 @@ COPY . .
115
203
  EXPOSE 8080
116
204
 
117
205
  CMD ["npm", "run", "dev"]
206
+ `;
207
+
208
+ const dockerignore = `# Dependencies
209
+ node_modules
210
+ npm-debug.log
211
+ yarn-error.log
212
+ package-lock.json
213
+ yarn.lock
214
+
215
+ # Environment files
216
+ .env
217
+ .env.*
218
+
219
+ # Git
220
+ .git
221
+ .gitignore
222
+
223
+ # IDE
224
+ .vscode
225
+ .idea
226
+ *.swp
227
+ *.swo
228
+ *~
229
+
230
+ # OS
231
+ .DS_Store
232
+ Thumbs.db
233
+
234
+ # Testing
235
+ coverage
236
+ .nyc_output
237
+ *.test.js
238
+ *.spec.js
239
+ __tests__
240
+
241
+ # Build files
242
+ dist
243
+ public
244
+
245
+ # Logs
246
+ logs
247
+ *.log
248
+
249
+ # Documentation
250
+ README.md
251
+ docs
252
+ *.md
253
+
254
+ # Docker
255
+ Dockerfile
256
+ .dockerignore
257
+ docker-compose*.yml
258
+
259
+ # Misc
260
+ .cache
261
+ tmp
262
+ temp
118
263
  `;
119
264
 
120
265
  await fs.writeFile(dockerfilePath, dockerfile);
266
+ await fs.writeFile(dockerignorePath, dockerignore);
121
267
  }
122
268
 
123
269
  async function setupProduction(
@@ -152,7 +298,7 @@ async function setupProduction(
152
298
  "./letsencrypt:/letsencrypt",
153
299
  ],
154
300
  networks: ["web"],
155
- env_file: [".env"], // Fixed: Added env_file to read HOST variable
301
+ env_file: [".env"],
156
302
  },
157
303
  [projectName]: {
158
304
  build: {
@@ -161,16 +307,16 @@ async function setupProduction(
161
307
  },
162
308
  labels: [
163
309
  "traefik.enable=true",
164
- "traefik.http.routers.app.rule=Host(`${HOST}`)", // Fixed: Changed from app.${HOST} to ${HOST}
310
+ "traefik.http.routers.app.rule=Host(`app.${HOST}`)",
165
311
  "traefik.http.routers.app.entrypoints=websecure",
166
312
  "traefik.http.routers.app.tls.certresolver=myresolver",
167
313
  "traefik.http.services.app.loadbalancer.server.port=8080",
168
314
  ],
169
315
  env_file: [".env"],
170
316
  networks: ["web"],
171
- volumes: [`./${projectName}:/app`], // Fixed: Changed from ./web to ./${projectName}
317
+ volumes: [`./${projectName}:/app`, `/app/node_modules`],
172
318
  command: "npm run dev",
173
- depends_on: [], // Fixed: Added missing depends_on array
319
+ depends_on: [],
174
320
  },
175
321
  },
176
322
  networks: {
@@ -178,7 +324,7 @@ async function setupProduction(
178
324
  driver: "bridge",
179
325
  },
180
326
  },
181
- volumes: {}, // Fixed: Added missing volumes object
327
+ volumes: {},
182
328
  };
183
329
 
184
330
  for (const service of services) {
@@ -260,7 +406,7 @@ async function setupDevelopment(
260
406
  },
261
407
  ports: ["8080:8080"],
262
408
  env_file: [".env"],
263
- volumes: [`./${projectName}:/app`, `/app/node_modules`], // Fixed: Changed from . to ./${projectName}
409
+ volumes: [`./${projectName}:/app`, `/app/node_modules`],
264
410
  command: "npm run dev",
265
411
  depends_on: [],
266
412
  },
@@ -433,11 +579,11 @@ function printNextSteps(
433
579
  console.log(`\n✅ Done! Next steps:\n`);
434
580
 
435
581
  if (env === "prod") {
436
- console.log(` # Edit .env with your settings`);
582
+ console.log(` # Review and edit .env with your settings`);
437
583
  console.log(` docker-compose up -d`);
438
- console.log(` # Access at https://\${HOST}\n`);
584
+ console.log(` # Access at https://app.\${HOST}\n`);
439
585
  } else {
440
- console.log(` # Edit .env with your settings`);
586
+ console.log(` # Review and edit .env with your settings`);
441
587
  if (services.some((s) => s.needsTunnel)) {
442
588
  console.log(` # Add SSH keys: {service}.pem`);
443
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.18");
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