@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.
- package/dist/main.js +664 -76
- 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 +156 -10
- 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,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"],
|
|
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(
|
|
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`],
|
|
317
|
+
volumes: [`./${projectName}:/app`, `/app/node_modules`],
|
|
172
318
|
command: "npm run dev",
|
|
173
|
-
depends_on: [],
|
|
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: {},
|
|
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`],
|
|
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(` #
|
|
582
|
+
console.log(` # Review and edit .env with your settings`);
|
|
437
583
|
console.log(` docker-compose up -d`);
|
|
438
|
-
console.log(` # Access at https
|
|
584
|
+
console.log(` # Access at https://app.\${HOST}\n`);
|
|
439
585
|
} else {
|
|
440
|
-
console.log(` #
|
|
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.
|
|
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
|
|