@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.
- package/dist/main.js +607 -79
- package/dist/main.js.map +4 -4
- package/dist/types/deploy/deploy.d.ts +4 -0
- package/dist/types/deploy/deploy.d.ts.map +1 -0
- package/dist/types/deploy/index.d.ts +2 -0
- package/dist/types/deploy/index.d.ts.map +1 -0
- package/dist/types/docker/docker.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/deploy/deploy.ts +592 -0
- package/src/deploy/index.ts +1 -0
- package/src/dev/server.ts +1 -1
- package/src/docker/docker.ts +98 -13
- package/src/main.ts +12 -6
package/src/docker/docker.ts
CHANGED
|
@@ -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
|
-
|
|
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"],
|
|
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(
|
|
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`],
|
|
317
|
+
volumes: [`./${projectName}:/app`, `/app/node_modules`],
|
|
233
318
|
command: "npm run dev",
|
|
234
|
-
depends_on: [],
|
|
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: {},
|
|
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`],
|
|
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(` #
|
|
582
|
+
console.log(` # Review and edit .env with your settings`);
|
|
498
583
|
console.log(` docker-compose up -d`);
|
|
499
|
-
console.log(` # Access at https
|
|
584
|
+
console.log(` # Access at https://app.\${HOST}\n`);
|
|
500
585
|
} else {
|
|
501
|
-
console.log(` #
|
|
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.
|
|
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
|
|
92
|
-
.description("Deploy a
|
|
93
|
-
.
|
|
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
|
-
|
|
96
|
-
|
|
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
|
|