@kithinji/pod 1.0.22 → 1.0.23
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/README.md +84 -70
- package/dist/main.js +407 -217
- package/dist/main.js.map +2 -2
- package/dist/types/deploy/deploy.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -4266,7 +4266,7 @@ build
|
|
|
4266
4266
|
`;
|
|
4267
4267
|
}
|
|
4268
4268
|
function genEnv() {
|
|
4269
|
-
return `
|
|
4269
|
+
return `HOST=localhost
|
|
4270
4270
|
`;
|
|
4271
4271
|
}
|
|
4272
4272
|
function genIndexHtml(name) {
|
|
@@ -4546,100 +4546,123 @@ async function createDeployfile(cwd, projectName) {
|
|
|
4546
4546
|
const deployFile = `name: ${projectName}
|
|
4547
4547
|
version: 1.0.0
|
|
4548
4548
|
|
|
4549
|
+
vars:
|
|
4550
|
+
deploy_path: &deploy_path "/home/ubuntu/${projectName}"
|
|
4551
|
+
backup_path: &backup_path "/home/ubuntu/backups"
|
|
4552
|
+
user: &user "ubuntu"
|
|
4553
|
+
|
|
4554
|
+
shared_operations:
|
|
4555
|
+
install_docker: &install_docker
|
|
4556
|
+
type: ensure
|
|
4557
|
+
ensure:
|
|
4558
|
+
docker:
|
|
4559
|
+
version: "28.5.2"
|
|
4560
|
+
addUserToGroup: true
|
|
4561
|
+
|
|
4562
|
+
stop_containers: &stop_containers
|
|
4563
|
+
type: action
|
|
4564
|
+
action:
|
|
4565
|
+
command: docker compose down --remove-orphans 2>/dev/null || true
|
|
4566
|
+
|
|
4567
|
+
pull_images: &pull_images
|
|
4568
|
+
type: action
|
|
4569
|
+
action:
|
|
4570
|
+
command: docker compose pull --quiet
|
|
4571
|
+
|
|
4572
|
+
build_and_start: &build_and_start
|
|
4573
|
+
type: action
|
|
4574
|
+
action:
|
|
4575
|
+
command: docker compose up -d --build --remove-orphans --wait
|
|
4576
|
+
|
|
4577
|
+
cleanup_docker: &cleanup_docker
|
|
4578
|
+
type: action
|
|
4579
|
+
action:
|
|
4580
|
+
command: docker system prune -f --volumes --filter "until=168h"
|
|
4581
|
+
|
|
4549
4582
|
targets:
|
|
4583
|
+
localhost:
|
|
4584
|
+
type: local
|
|
4585
|
+
operations:
|
|
4586
|
+
#- name: "Environment Setup"
|
|
4587
|
+
# <<: *install_docker
|
|
4588
|
+
- name: "Refresh Stack"
|
|
4589
|
+
<<: *build_and_start
|
|
4590
|
+
|
|
4550
4591
|
ec2:
|
|
4592
|
+
type: ssh
|
|
4551
4593
|
host: ec2-xx-xx-xxx-xxx.xx-xxxx-x.compute.amazonaws.com
|
|
4552
|
-
user:
|
|
4594
|
+
user: *user
|
|
4553
4595
|
keyPath: ~/xxxx.pem
|
|
4554
4596
|
port: 22
|
|
4555
|
-
deployPath:
|
|
4597
|
+
deployPath: *deploy_path
|
|
4556
4598
|
|
|
4557
4599
|
operations:
|
|
4558
|
-
- name: "
|
|
4600
|
+
- name: "Provision Directories and Swap"
|
|
4559
4601
|
type: ensure
|
|
4560
4602
|
ensure:
|
|
4561
4603
|
swap:
|
|
4562
4604
|
size: 4G
|
|
4563
4605
|
|
|
4564
4606
|
- name: "Install Docker"
|
|
4565
|
-
|
|
4566
|
-
ensure:
|
|
4567
|
-
docker:
|
|
4568
|
-
version: "28.5.2"
|
|
4569
|
-
addUserToGroup: true
|
|
4607
|
+
<<: *install_docker
|
|
4570
4608
|
|
|
4571
4609
|
- name: "Create application directories"
|
|
4572
4610
|
type: ensure
|
|
4573
4611
|
ensure:
|
|
4574
4612
|
directory:
|
|
4575
|
-
path:
|
|
4576
|
-
owner:
|
|
4613
|
+
path: *deploy_path
|
|
4614
|
+
owner: *user
|
|
4577
4615
|
|
|
4578
4616
|
- name: "Create backup directory"
|
|
4579
4617
|
type: ensure
|
|
4580
4618
|
ensure:
|
|
4581
4619
|
directory:
|
|
4582
|
-
path:
|
|
4583
|
-
owner:
|
|
4584
|
-
|
|
4585
|
-
- name: "Stop running containers"
|
|
4586
|
-
type: action
|
|
4587
|
-
action:
|
|
4588
|
-
command: cd \${deployPath} && docker compose down 2>/dev/null || true
|
|
4620
|
+
path: *backup_path
|
|
4621
|
+
owner: *user
|
|
4589
4622
|
|
|
4590
|
-
- name: "Sync
|
|
4623
|
+
- name: "Sync Source Files"
|
|
4591
4624
|
type: action
|
|
4592
4625
|
action:
|
|
4593
4626
|
rsync:
|
|
4594
4627
|
source: ./
|
|
4595
|
-
destination:
|
|
4628
|
+
destination: *deploy_path
|
|
4629
|
+
delete: true
|
|
4596
4630
|
exclude:
|
|
4597
|
-
- node_modules/
|
|
4598
4631
|
- .git/
|
|
4599
|
-
-
|
|
4632
|
+
- node_modules/
|
|
4600
4633
|
- .env.local
|
|
4601
|
-
-
|
|
4602
|
-
- public/
|
|
4634
|
+
- "*.log"
|
|
4603
4635
|
|
|
4604
|
-
- name: "
|
|
4636
|
+
- name: "Navigate to Deploy Path"
|
|
4605
4637
|
type: action
|
|
4606
4638
|
action:
|
|
4607
|
-
command: cd
|
|
4639
|
+
command: cd *deploy_path
|
|
4608
4640
|
|
|
4609
|
-
- name: "
|
|
4641
|
+
- name: "Create Pre-deployment Backup"
|
|
4610
4642
|
type: action
|
|
4611
4643
|
action:
|
|
4612
|
-
command:
|
|
4644
|
+
command: tar -czf *backup_path/backup-$(date +%Y%m%d-%H%M%S).tar.gz .
|
|
4613
4645
|
|
|
4614
|
-
- name: "
|
|
4615
|
-
|
|
4616
|
-
action:
|
|
4617
|
-
command: sleep 10
|
|
4646
|
+
- name: "Pull Latest Images"
|
|
4647
|
+
<<: *pull_images
|
|
4618
4648
|
|
|
4619
|
-
- name: "
|
|
4620
|
-
|
|
4621
|
-
action:
|
|
4622
|
-
command: cd \${deployPath} && docker compose ps
|
|
4649
|
+
- name: "Stop Existing Stack"
|
|
4650
|
+
<<: *stop_containers
|
|
4623
4651
|
|
|
4624
|
-
- name: "
|
|
4625
|
-
|
|
4626
|
-
action:
|
|
4627
|
-
command: cd \${deployPath} && docker compose logs --tail=30
|
|
4652
|
+
- name: "Build and Launch"
|
|
4653
|
+
<<: *build_and_start
|
|
4628
4654
|
|
|
4629
|
-
- name: "
|
|
4630
|
-
type:
|
|
4631
|
-
|
|
4632
|
-
command:
|
|
4655
|
+
- name: "Verify Health Status"
|
|
4656
|
+
type: verify
|
|
4657
|
+
verify:
|
|
4658
|
+
command: ! "[ $(docker compose ps --format json | grep -qv 'running\\|healthy') ]"
|
|
4633
4659
|
|
|
4634
|
-
- name: "Cleanup
|
|
4660
|
+
- name: "Maintenance: Cleanup"
|
|
4635
4661
|
type: action
|
|
4636
4662
|
action:
|
|
4637
|
-
command:
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
type: verify
|
|
4641
|
-
verify:
|
|
4642
|
-
command: cd \${deployPath} && docker compose ps | grep -q "Up"
|
|
4663
|
+
command: |
|
|
4664
|
+
find *backup_path -name "backup-*.tar.gz" -mtime +7 -delete
|
|
4665
|
+
docker image prune -af --filter "until=24h"
|
|
4643
4666
|
`;
|
|
4644
4667
|
const deployFilePath = path12.join(cwd, "pod.deploy.yml");
|
|
4645
4668
|
await fs9.writeFile(deployFilePath, deployFile);
|
|
@@ -4944,6 +4967,9 @@ import path13 from "path";
|
|
|
4944
4967
|
import os from "os";
|
|
4945
4968
|
import { NodeSSH } from "node-ssh";
|
|
4946
4969
|
import chalk from "chalk";
|
|
4970
|
+
import { exec } from "child_process";
|
|
4971
|
+
import { promisify } from "util";
|
|
4972
|
+
var execAsync = promisify(exec);
|
|
4947
4973
|
function interpolate(str, context2) {
|
|
4948
4974
|
if (!str) return "";
|
|
4949
4975
|
return str.replace(/\${([^}]+)}/g, (match, key) => {
|
|
@@ -5142,19 +5168,42 @@ if [ "${addToGroup}" = "true" ]; then
|
|
|
5142
5168
|
fi
|
|
5143
5169
|
`
|
|
5144
5170
|
};
|
|
5145
|
-
var
|
|
5146
|
-
constructor(
|
|
5147
|
-
this.ssh =
|
|
5171
|
+
var SSHStrategy = class {
|
|
5172
|
+
constructor(target) {
|
|
5173
|
+
this.ssh = new NodeSSH();
|
|
5174
|
+
this.target = target;
|
|
5175
|
+
this.currentDir = target.deployPath || ".";
|
|
5176
|
+
}
|
|
5177
|
+
async connect() {
|
|
5178
|
+
await this.ssh.connect({
|
|
5179
|
+
host: this.target.host,
|
|
5180
|
+
username: this.target.user,
|
|
5181
|
+
privateKeyPath: this.target.keyPath,
|
|
5182
|
+
port: this.target.port || 22
|
|
5183
|
+
});
|
|
5148
5184
|
}
|
|
5149
|
-
async
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5185
|
+
async disconnect() {
|
|
5186
|
+
this.ssh.dispose();
|
|
5187
|
+
}
|
|
5188
|
+
async runCommand(cmd, silent = false) {
|
|
5189
|
+
const trimmed = cmd.trim();
|
|
5190
|
+
if (trimmed.startsWith("cd ")) {
|
|
5191
|
+
const newPath = trimmed.replace("cd ", "").trim();
|
|
5192
|
+
this.currentDir = path13.posix.resolve(this.currentDir, newPath);
|
|
5193
|
+
if (!silent) console.log(chalk.gray(` [SSH Path: ${this.currentDir}]`));
|
|
5194
|
+
return { stdout: "", stderr: "", code: 0 };
|
|
5157
5195
|
}
|
|
5196
|
+
const result = await this.ssh.execCommand(trimmed, {
|
|
5197
|
+
cwd: this.currentDir
|
|
5198
|
+
});
|
|
5199
|
+
if (result.code !== 0 && result.code !== null) {
|
|
5200
|
+
throw new Error(`Execution failed: ${trimmed}
|
|
5201
|
+
STDERR: ${result.stderr}`);
|
|
5202
|
+
}
|
|
5203
|
+
if (!silent && result.stdout) {
|
|
5204
|
+
result.stdout.split("\n").filter((l) => l.startsWith("LOG:")).forEach((l) => console.log(chalk.gray(` ${l.replace("LOG: ", "")}`)));
|
|
5205
|
+
}
|
|
5206
|
+
return result;
|
|
5158
5207
|
}
|
|
5159
5208
|
async runScript(name, content, context2) {
|
|
5160
5209
|
const interpolated = interpolate(content, context2);
|
|
@@ -5162,22 +5211,20 @@ var RemoteShell = class {
|
|
|
5162
5211
|
await this.uploadContent(remotePath, interpolated);
|
|
5163
5212
|
try {
|
|
5164
5213
|
await this.ssh.execCommand(`chmod +x ${remotePath}`);
|
|
5165
|
-
|
|
5214
|
+
await this.runCommand(remotePath);
|
|
5166
5215
|
} finally {
|
|
5167
5216
|
await this.ssh.execCommand(`rm -f ${remotePath}`);
|
|
5168
5217
|
}
|
|
5169
5218
|
}
|
|
5170
|
-
async
|
|
5171
|
-
const
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
}
|
|
5177
|
-
|
|
5178
|
-
result.stdout.split("\n").filter((l) => l.startsWith("LOG:")).forEach((l) => console.log(chalk.gray(` ${l.replace("LOG: ", "")}`)));
|
|
5219
|
+
async uploadContent(remotePath, content) {
|
|
5220
|
+
const localTmp = path13.join(os.tmpdir(), `pod_tmp_${Date.now()}`);
|
|
5221
|
+
fs10.writeFileSync(localTmp, content);
|
|
5222
|
+
try {
|
|
5223
|
+
await this.ssh.execCommand(`mkdir -p $(dirname ${remotePath})`);
|
|
5224
|
+
await this.ssh.putFile(localTmp, remotePath);
|
|
5225
|
+
} finally {
|
|
5226
|
+
if (fs10.existsSync(localTmp)) fs10.unlinkSync(localTmp);
|
|
5179
5227
|
}
|
|
5180
|
-
return result;
|
|
5181
5228
|
}
|
|
5182
5229
|
async readJson(remotePath) {
|
|
5183
5230
|
const res = await this.ssh.execCommand(`cat ${remotePath}`);
|
|
@@ -5187,211 +5234,354 @@ STDERR: ${result.stderr}`);
|
|
|
5187
5234
|
return null;
|
|
5188
5235
|
}
|
|
5189
5236
|
}
|
|
5237
|
+
async syncDirectory(source, destination, exclude) {
|
|
5238
|
+
const putOptions = { recursive: true, concurrency: 10 };
|
|
5239
|
+
if (exclude?.length) {
|
|
5240
|
+
putOptions.validate = (filePath) => {
|
|
5241
|
+
const relative = path13.relative(source, filePath);
|
|
5242
|
+
if (relative === "") return true;
|
|
5243
|
+
return !exclude.some((pattern) => {
|
|
5244
|
+
if (pattern.endsWith("/")) {
|
|
5245
|
+
const dir = pattern.slice(0, -1);
|
|
5246
|
+
const segment = "/" + dir + "/";
|
|
5247
|
+
return relative === dir || relative.startsWith(dir + "/") || relative.includes(segment);
|
|
5248
|
+
}
|
|
5249
|
+
if (pattern.startsWith("*.")) {
|
|
5250
|
+
return relative.endsWith(pattern.slice(1));
|
|
5251
|
+
}
|
|
5252
|
+
return relative === pattern;
|
|
5253
|
+
});
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
console.log(chalk.gray(` Syncing ${source} \u2192 ${destination}`));
|
|
5257
|
+
await this.ssh.putDirectory(source, destination, putOptions);
|
|
5258
|
+
}
|
|
5190
5259
|
};
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
...rawTarget
|
|
5208
|
-
});
|
|
5209
|
-
target = resolveLocalPaths(target, cwd);
|
|
5210
|
-
const ssh = new NodeSSH();
|
|
5211
|
-
const shell = new RemoteShell(ssh);
|
|
5212
|
-
try {
|
|
5213
|
-
await ssh.connect({
|
|
5214
|
-
host: target.host,
|
|
5215
|
-
username: target.user,
|
|
5216
|
-
privateKeyPath: target.keyPath,
|
|
5217
|
-
port: target.port || 22
|
|
5218
|
-
});
|
|
5219
|
-
const lockPath = path13.posix.join(target.deployPath, "pod-lock.json");
|
|
5220
|
-
let lock = await shell.readJson(lockPath) || {
|
|
5221
|
-
ensures: {},
|
|
5222
|
-
once_actions: []
|
|
5223
|
-
};
|
|
5224
|
-
if (lock.deployment_version !== rawConfig.version) {
|
|
5225
|
-
console.log(chalk.magenta(`\u2192 Version change: ${rawConfig.version}`));
|
|
5226
|
-
lock.deployment_version = rawConfig.version;
|
|
5227
|
-
lock.once_actions = [];
|
|
5228
|
-
await shell.uploadContent(lockPath, JSON.stringify(lock, null, 2));
|
|
5260
|
+
var LocalStrategy = class {
|
|
5261
|
+
constructor(target, cwd) {
|
|
5262
|
+
this.target = target;
|
|
5263
|
+
this.currentDir = cwd;
|
|
5264
|
+
}
|
|
5265
|
+
async connect() {
|
|
5266
|
+
}
|
|
5267
|
+
async disconnect() {
|
|
5268
|
+
}
|
|
5269
|
+
async runCommand(cmd, silent = false) {
|
|
5270
|
+
const trimmed = cmd.trim();
|
|
5271
|
+
if (trimmed.startsWith("cd ")) {
|
|
5272
|
+
const newPath = trimmed.replace("cd ", "").trim();
|
|
5273
|
+
this.currentDir = path13.resolve(this.currentDir, newPath);
|
|
5274
|
+
if (!silent) console.log(chalk.gray(` [Local Path: ${this.currentDir}]`));
|
|
5275
|
+
return { stdout: "", stderr: "", code: 0 };
|
|
5229
5276
|
}
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5277
|
+
try {
|
|
5278
|
+
const { stdout, stderr } = await execAsync(trimmed, {
|
|
5279
|
+
cwd: this.currentDir
|
|
5280
|
+
});
|
|
5281
|
+
if (!silent && stdout) {
|
|
5282
|
+
stdout.split("\n").filter((l) => l.startsWith("LOG:")).forEach(
|
|
5283
|
+
(l) => console.log(chalk.gray(` ${l.replace("LOG: ", "")}`))
|
|
5284
|
+
);
|
|
5285
|
+
}
|
|
5286
|
+
return { stdout, stderr, code: 0 };
|
|
5287
|
+
} catch (err) {
|
|
5288
|
+
throw new Error(
|
|
5289
|
+
`Execution failed: ${trimmed}
|
|
5290
|
+
STDERR: ${err.stderr || err.message}`
|
|
5291
|
+
);
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
async runScript(name, content, context2) {
|
|
5295
|
+
const interpolated = interpolate(content, context2);
|
|
5296
|
+
const scriptPath = path13.join(
|
|
5297
|
+
os.tmpdir(),
|
|
5298
|
+
`pod_script_${name}_${Date.now()}.sh`
|
|
5299
|
+
);
|
|
5300
|
+
fs10.writeFileSync(scriptPath, interpolated);
|
|
5301
|
+
fs10.chmodSync(scriptPath, "755");
|
|
5302
|
+
try {
|
|
5303
|
+
await this.runCommand(scriptPath);
|
|
5304
|
+
} finally {
|
|
5305
|
+
if (fs10.existsSync(scriptPath)) fs10.unlinkSync(scriptPath);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
async uploadContent(localPath, content) {
|
|
5309
|
+
const dir = path13.dirname(localPath);
|
|
5310
|
+
if (!fs10.existsSync(dir)) {
|
|
5311
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
5312
|
+
}
|
|
5313
|
+
fs10.writeFileSync(localPath, content);
|
|
5314
|
+
}
|
|
5315
|
+
async readJson(localPath) {
|
|
5316
|
+
try {
|
|
5317
|
+
if (!fs10.existsSync(localPath)) return null;
|
|
5318
|
+
const content = fs10.readFileSync(localPath, "utf8");
|
|
5319
|
+
return JSON.parse(content);
|
|
5320
|
+
} catch {
|
|
5321
|
+
return null;
|
|
5322
|
+
}
|
|
5323
|
+
}
|
|
5324
|
+
async syncDirectory(source, destination, exclude) {
|
|
5325
|
+
console.log(chalk.gray(` Copying ${source} \u2192 ${destination}`));
|
|
5326
|
+
if (!fs10.existsSync(destination)) {
|
|
5327
|
+
fs10.mkdirSync(destination, { recursive: true });
|
|
5328
|
+
}
|
|
5329
|
+
const shouldExclude = (relativePath) => {
|
|
5330
|
+
if (!exclude?.length) return false;
|
|
5331
|
+
return exclude.some((pattern) => {
|
|
5332
|
+
if (pattern.endsWith("/")) {
|
|
5333
|
+
const dir = pattern.slice(0, -1);
|
|
5334
|
+
const segment = "/" + dir + "/";
|
|
5335
|
+
return relativePath === dir || relativePath.startsWith(dir + "/") || relativePath.includes(segment);
|
|
5336
|
+
}
|
|
5337
|
+
if (pattern.startsWith("*.")) {
|
|
5338
|
+
return relativePath.endsWith(pattern.slice(1));
|
|
5339
|
+
}
|
|
5340
|
+
return relativePath === pattern;
|
|
5341
|
+
});
|
|
5342
|
+
};
|
|
5343
|
+
const copyRecursive = (src, dest) => {
|
|
5344
|
+
const entries = fs10.readdirSync(src, { withFileTypes: true });
|
|
5345
|
+
for (const entry of entries) {
|
|
5346
|
+
const srcPath = path13.join(src, entry.name);
|
|
5347
|
+
const destPath = path13.join(dest, entry.name);
|
|
5348
|
+
const relativePath = path13.relative(source, srcPath);
|
|
5349
|
+
if (shouldExclude(relativePath)) continue;
|
|
5350
|
+
if (entry.isDirectory()) {
|
|
5351
|
+
if (!fs10.existsSync(destPath)) {
|
|
5352
|
+
fs10.mkdirSync(destPath, { recursive: true });
|
|
5353
|
+
}
|
|
5354
|
+
copyRecursive(srcPath, destPath);
|
|
5238
5355
|
} else {
|
|
5239
|
-
|
|
5356
|
+
fs10.copyFileSync(srcPath, destPath);
|
|
5240
5357
|
}
|
|
5241
|
-
} catch (err) {
|
|
5242
|
-
throw new Error(`Failed at operation "${op.name}": ${err.message}`);
|
|
5243
5358
|
}
|
|
5359
|
+
};
|
|
5360
|
+
copyRecursive(source, destination);
|
|
5361
|
+
}
|
|
5362
|
+
};
|
|
5363
|
+
var StrategyFactory = class {
|
|
5364
|
+
static create(target, cwd) {
|
|
5365
|
+
const targetType = target.type || (target.host ? "ssh" : "local");
|
|
5366
|
+
switch (targetType) {
|
|
5367
|
+
case "ssh":
|
|
5368
|
+
return new SSHStrategy(target);
|
|
5369
|
+
case "local":
|
|
5370
|
+
return new LocalStrategy(target, cwd);
|
|
5371
|
+
default:
|
|
5372
|
+
throw new Error(`Unknown target type: ${targetType}`);
|
|
5244
5373
|
}
|
|
5245
|
-
console.log(chalk.green.bold(`
|
|
5246
|
-
\u2705 Deployment successful!
|
|
5247
|
-
`));
|
|
5248
|
-
} catch (err) {
|
|
5249
|
-
console.error(chalk.red.bold(`
|
|
5250
|
-
\u274C Deployment Failed: ${err.message}`));
|
|
5251
|
-
throw err;
|
|
5252
|
-
} finally {
|
|
5253
|
-
ssh.dispose();
|
|
5254
5374
|
}
|
|
5255
|
-
}
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5375
|
+
};
|
|
5376
|
+
var OperationHandler = class {
|
|
5377
|
+
constructor(strategy, target, lock, lockPath) {
|
|
5378
|
+
this.strategy = strategy;
|
|
5379
|
+
this.target = target;
|
|
5380
|
+
this.lock = lock;
|
|
5381
|
+
this.lockPath = lockPath;
|
|
5382
|
+
}
|
|
5383
|
+
async handleEnsure(op, options) {
|
|
5384
|
+
if (!op.ensure) {
|
|
5385
|
+
throw new Error(`Ensure operation "${op.name}" missing ensure config`);
|
|
5386
|
+
}
|
|
5387
|
+
if (op.ensure.swap) {
|
|
5388
|
+
await this.ensureSwap(op, options);
|
|
5389
|
+
}
|
|
5390
|
+
if (op.ensure.docker) {
|
|
5391
|
+
await this.ensureDocker(op, options);
|
|
5392
|
+
}
|
|
5393
|
+
if (op.ensure.directory) {
|
|
5394
|
+
await this.ensureDirectory(op, options);
|
|
5395
|
+
}
|
|
5259
5396
|
}
|
|
5260
|
-
|
|
5397
|
+
async ensureSwap(op, options) {
|
|
5261
5398
|
const key = "swap";
|
|
5262
|
-
const locked = lock.ensures[key];
|
|
5399
|
+
const locked = this.lock.ensures[key];
|
|
5263
5400
|
const currentConfig = op.ensure.swap;
|
|
5264
5401
|
const configChanged = JSON.stringify(locked?.config) !== JSON.stringify(currentConfig);
|
|
5265
5402
|
if (options?.forceEnsure || !locked || locked.version !== currentConfig.size || configChanged) {
|
|
5266
5403
|
console.log(chalk.yellow(`\u2192 Ensuring: ${op.name}`));
|
|
5267
5404
|
const script = SCRIPTS.SWAP(currentConfig.size);
|
|
5268
|
-
await
|
|
5269
|
-
lock.ensures[key] = {
|
|
5405
|
+
await this.strategy.runScript(key, script, this.target);
|
|
5406
|
+
this.lock.ensures[key] = {
|
|
5270
5407
|
version: currentConfig.size,
|
|
5271
5408
|
config: currentConfig
|
|
5272
5409
|
};
|
|
5273
|
-
await
|
|
5410
|
+
await this.saveLock();
|
|
5274
5411
|
} else {
|
|
5275
5412
|
console.log(chalk.gray(`\u2713 ${op.name} (already satisfied)`));
|
|
5276
5413
|
}
|
|
5277
5414
|
}
|
|
5278
|
-
|
|
5415
|
+
async ensureDocker(op, options) {
|
|
5279
5416
|
const key = "docker";
|
|
5280
|
-
const locked = lock.ensures[key];
|
|
5417
|
+
const locked = this.lock.ensures[key];
|
|
5281
5418
|
const currentConfig = op.ensure.docker;
|
|
5282
5419
|
const configChanged = JSON.stringify(locked?.config) !== JSON.stringify(currentConfig);
|
|
5283
5420
|
if (options?.forceEnsure || !locked || locked.version !== currentConfig.version || configChanged) {
|
|
5284
5421
|
console.log(chalk.yellow(`\u2192 Ensuring: ${op.name}`));
|
|
5285
5422
|
const script = SCRIPTS.DOCKER(
|
|
5286
5423
|
currentConfig.version,
|
|
5287
|
-
target.user,
|
|
5424
|
+
this.target.user || os.userInfo().username,
|
|
5288
5425
|
!!currentConfig.addUserToGroup
|
|
5289
5426
|
);
|
|
5290
|
-
await
|
|
5291
|
-
lock.ensures[key] = {
|
|
5427
|
+
await this.strategy.runScript(key, script, this.target);
|
|
5428
|
+
this.lock.ensures[key] = {
|
|
5292
5429
|
version: currentConfig.version,
|
|
5293
5430
|
config: currentConfig
|
|
5294
5431
|
};
|
|
5295
|
-
await
|
|
5432
|
+
await this.saveLock();
|
|
5296
5433
|
} else {
|
|
5297
5434
|
console.log(chalk.gray(`\u2713 ${op.name} (already satisfied)`));
|
|
5298
5435
|
}
|
|
5299
5436
|
}
|
|
5300
|
-
|
|
5437
|
+
async ensureDirectory(op, options) {
|
|
5301
5438
|
const key = `directory_${op.ensure.directory.path}`;
|
|
5302
|
-
const locked = lock.ensures[key];
|
|
5439
|
+
const locked = this.lock.ensures[key];
|
|
5303
5440
|
const currentConfig = op.ensure.directory;
|
|
5304
5441
|
const configChanged = JSON.stringify(locked?.config) !== JSON.stringify(currentConfig);
|
|
5305
5442
|
if (options?.forceEnsure || !locked || configChanged) {
|
|
5306
5443
|
console.log(chalk.yellow(`\u2192 Ensuring: ${op.name}`));
|
|
5307
|
-
const dirPath = interpolate(currentConfig.path, target);
|
|
5308
|
-
const owner = currentConfig.owner ? interpolate(currentConfig.owner, target) : target.user;
|
|
5309
|
-
await
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5444
|
+
const dirPath = interpolate(currentConfig.path, this.target);
|
|
5445
|
+
const owner = currentConfig.owner ? interpolate(currentConfig.owner, this.target) : this.target.user || os.userInfo().username;
|
|
5446
|
+
await this.strategy.runCommand(`mkdir -p ${dirPath}`, true);
|
|
5447
|
+
if (this.target.user) {
|
|
5448
|
+
await this.strategy.runCommand(
|
|
5449
|
+
`sudo chown -R ${owner}:${owner} ${dirPath}`,
|
|
5450
|
+
true
|
|
5451
|
+
);
|
|
5452
|
+
}
|
|
5453
|
+
this.lock.ensures[key] = {
|
|
5316
5454
|
version: dirPath,
|
|
5317
5455
|
config: currentConfig
|
|
5318
5456
|
};
|
|
5319
|
-
await
|
|
5457
|
+
await this.saveLock();
|
|
5320
5458
|
} else {
|
|
5321
5459
|
console.log(chalk.gray(`\u2713 ${op.name} (already satisfied)`));
|
|
5322
5460
|
}
|
|
5323
5461
|
}
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
if (op.action.
|
|
5345
|
-
const
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
const dir = pattern.slice(0, -1);
|
|
5352
|
-
const segment = "/" + dir + "/";
|
|
5353
|
-
return relative === dir || relative.startsWith(dir + "/") || relative.includes(segment);
|
|
5354
|
-
}
|
|
5355
|
-
if (pattern.startsWith("*.")) {
|
|
5356
|
-
return relative.endsWith(pattern.slice(1));
|
|
5357
|
-
}
|
|
5358
|
-
return relative === pattern;
|
|
5359
|
-
});
|
|
5360
|
-
};
|
|
5462
|
+
async handleAction(op) {
|
|
5463
|
+
if (!op.action) {
|
|
5464
|
+
throw new Error(`Action operation "${op.name}" missing action config`);
|
|
5465
|
+
}
|
|
5466
|
+
const when = op.when || "always";
|
|
5467
|
+
if (when === "never") {
|
|
5468
|
+
console.log(chalk.gray(`\u2298 ${op.name} (disabled)`));
|
|
5469
|
+
return;
|
|
5470
|
+
}
|
|
5471
|
+
const actionId = `action_${op.name}`;
|
|
5472
|
+
if (when === "once" && this.lock.once_actions.includes(actionId)) {
|
|
5473
|
+
console.log(chalk.gray(`\u2713 ${op.name} (already completed)`));
|
|
5474
|
+
return;
|
|
5475
|
+
}
|
|
5476
|
+
console.log(chalk.cyan(`\u2192 Running: ${op.name}`));
|
|
5477
|
+
if (op.action.rsync) {
|
|
5478
|
+
const src = op.action.rsync.source;
|
|
5479
|
+
const dest = interpolate(op.action.rsync.destination || ".", this.target);
|
|
5480
|
+
await this.strategy.syncDirectory(src, dest, op.action.rsync.exclude);
|
|
5481
|
+
}
|
|
5482
|
+
if (op.action.command) {
|
|
5483
|
+
const cmd = interpolate(op.action.command, this.target);
|
|
5484
|
+
await this.strategy.runCommand(cmd);
|
|
5485
|
+
}
|
|
5486
|
+
if (when === "once") {
|
|
5487
|
+
this.lock.once_actions.push(actionId);
|
|
5488
|
+
await this.saveLock();
|
|
5361
5489
|
}
|
|
5362
|
-
console.log(chalk.gray(` Syncing ${src} \u2192 ${dest}`));
|
|
5363
|
-
await shell.ssh.putDirectory(src, dest, putOptions);
|
|
5364
|
-
}
|
|
5365
|
-
if (op.action.command) {
|
|
5366
|
-
await shell.run(op.action.command, target);
|
|
5367
|
-
}
|
|
5368
|
-
if (when === "once") {
|
|
5369
|
-
lock.once_actions.push(actionId);
|
|
5370
|
-
await shell.uploadContent(lockPath, JSON.stringify(lock, null, 2));
|
|
5371
5490
|
}
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5491
|
+
async handleVerify(op) {
|
|
5492
|
+
if (!op.verify) {
|
|
5493
|
+
throw new Error(`Verify operation "${op.name}" missing verify config`);
|
|
5494
|
+
}
|
|
5495
|
+
console.log(chalk.cyan(`\u2192 Verifying: ${op.name}`));
|
|
5496
|
+
if (op.verify.http) {
|
|
5497
|
+
const url = interpolate(op.verify.http.url, this.target);
|
|
5498
|
+
const timeout = op.verify.http.timeout || "30s";
|
|
5499
|
+
await this.strategy.runCommand(
|
|
5500
|
+
`curl -f --max-time ${timeout} ${url}`,
|
|
5501
|
+
true
|
|
5502
|
+
);
|
|
5503
|
+
}
|
|
5504
|
+
if (op.verify.command) {
|
|
5505
|
+
const cmd = interpolate(op.verify.command, this.target);
|
|
5506
|
+
await this.strategy.runCommand(cmd, true);
|
|
5507
|
+
}
|
|
5376
5508
|
}
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5509
|
+
async saveLock() {
|
|
5510
|
+
await this.strategy.uploadContent(
|
|
5511
|
+
this.lockPath,
|
|
5512
|
+
JSON.stringify(this.lock, null, 2)
|
|
5513
|
+
);
|
|
5382
5514
|
}
|
|
5383
|
-
|
|
5384
|
-
|
|
5515
|
+
};
|
|
5516
|
+
async function deploy(targetName, options) {
|
|
5517
|
+
const cwd = process.cwd();
|
|
5518
|
+
const rawConfig = yaml2.load(
|
|
5519
|
+
fs10.readFileSync(path13.join(cwd, "pod.deploy.yml"), "utf8"),
|
|
5520
|
+
{ schema: yaml2.DEFAULT_SCHEMA }
|
|
5521
|
+
);
|
|
5522
|
+
const rawTarget = rawConfig.targets?.[targetName];
|
|
5523
|
+
if (!rawTarget) throw new Error(`Target ${targetName} not found.`);
|
|
5524
|
+
console.log(
|
|
5525
|
+
chalk.blue.bold(
|
|
5526
|
+
`
|
|
5527
|
+
Pod Deploy: ${rawConfig.name} v${rawConfig.version} \u2192 ${targetName}
|
|
5528
|
+
`
|
|
5529
|
+
)
|
|
5530
|
+
);
|
|
5531
|
+
let target = deepInterpolate(rawTarget, {
|
|
5532
|
+
...rawConfig,
|
|
5533
|
+
...rawTarget
|
|
5534
|
+
});
|
|
5535
|
+
target = resolveLocalPaths(target, cwd);
|
|
5536
|
+
const strategy = StrategyFactory.create(target, cwd);
|
|
5537
|
+
try {
|
|
5538
|
+
await strategy.connect();
|
|
5539
|
+
const lockPath = target.deployPath ? path13.posix.join(target.deployPath, "pod-lock.json") : path13.join(cwd, "pod-lock.json");
|
|
5540
|
+
let lock = await strategy.readJson(lockPath) || {
|
|
5541
|
+
ensures: {},
|
|
5542
|
+
once_actions: []
|
|
5543
|
+
};
|
|
5544
|
+
if (lock.deployment_version !== rawConfig.version) {
|
|
5545
|
+
console.log(chalk.magenta(`\u2192 Version change: ${rawConfig.version}`));
|
|
5546
|
+
lock.deployment_version = rawConfig.version;
|
|
5547
|
+
lock.once_actions = [];
|
|
5548
|
+
await strategy.uploadContent(lockPath, JSON.stringify(lock, null, 2));
|
|
5549
|
+
}
|
|
5550
|
+
const handler = new OperationHandler(strategy, target, lock, lockPath);
|
|
5551
|
+
for (const op of target.operations) {
|
|
5552
|
+
try {
|
|
5553
|
+
if (op.type === "ensure") {
|
|
5554
|
+
await handler.handleEnsure(op, options);
|
|
5555
|
+
} else if (op.type === "action") {
|
|
5556
|
+
await handler.handleAction(op);
|
|
5557
|
+
} else if (op.type === "verify") {
|
|
5558
|
+
await handler.handleVerify(op);
|
|
5559
|
+
} else {
|
|
5560
|
+
throw new Error(`Unknown operation type: ${op.type}`);
|
|
5561
|
+
}
|
|
5562
|
+
} catch (err) {
|
|
5563
|
+
throw new Error(`Failed at operation "${op.name}": ${err.message}`);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
console.log(chalk.green.bold(`
|
|
5567
|
+
Deployment successful!
|
|
5568
|
+
`));
|
|
5569
|
+
} catch (err) {
|
|
5570
|
+
console.error(chalk.red.bold(`
|
|
5571
|
+
Deployment Failed: ${err.message}`));
|
|
5572
|
+
throw err;
|
|
5573
|
+
} finally {
|
|
5574
|
+
await strategy.disconnect();
|
|
5385
5575
|
}
|
|
5386
5576
|
}
|
|
5387
5577
|
|
|
5388
5578
|
// src/main.ts
|
|
5389
5579
|
import chalk2 from "chalk";
|
|
5390
5580
|
var program = new Command();
|
|
5391
|
-
program.name("pod").description("Pod cli tool").version("1.0.
|
|
5581
|
+
program.name("pod").description("Pod cli tool").version("1.0.23");
|
|
5392
5582
|
program.command("new <name>").description("Start a new Orca Project").action(async (name) => {
|
|
5393
5583
|
await addNew(name);
|
|
5394
|
-
const appDir = path14.resolve(process.cwd()
|
|
5584
|
+
const appDir = path14.resolve(process.cwd());
|
|
5395
5585
|
const shell = process.platform === "win32" ? process.env.ComSpec || "cmd.exe" : "/bin/sh";
|
|
5396
5586
|
console.log("Installing dependencies...");
|
|
5397
5587
|
execSync("npm install", { stdio: "inherit", cwd: appDir, shell });
|