kubiy-paas-cli 0.1.0 → 0.1.1
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/fix-link.js +62 -40
- package/index.js +45 -67
- package/package.json +5 -6
- package/src/api.js +79 -0
- package/src/commands/deploy.js +167 -0
- package/src/commands/git-connect.js +113 -0
- package/src/commands/login.js +16 -0
- package/src/commands/service-get.js +24 -0
- package/src/commands/version.js +17 -0
- package/src/commands/whoami.js +15 -0
- package/src/config.js +47 -0
- package/src/git.js +24 -0
- package/src/logger.js +31 -0
- package/src/ssh-config.js +57 -0
- package/src/ssh.js +40 -0
package/fix-link.js
CHANGED
|
@@ -4,47 +4,69 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
7
|
+
const isWin = process.platform === 'win32';
|
|
8
|
+
|
|
9
|
+
if (isWin) {
|
|
10
|
+
// ==========================
|
|
11
|
+
// LADO WINDOWS (lo que ya tenías)
|
|
12
|
+
// ==========================
|
|
13
|
+
|
|
14
|
+
// Ruta donde npm instala los comandos globales (típico en Windows)
|
|
15
|
+
const npmBinPath = path.join(os.homedir(), 'AppData', 'Roaming', 'npm');
|
|
16
|
+
const kubiyCmdPath = path.join(npmBinPath, 'kubiy.cmd');
|
|
17
|
+
const kubiyPs1Path = path.join(npmBinPath, 'kubiy.ps1');
|
|
18
|
+
|
|
19
|
+
// Corregir kubiy.cmd
|
|
20
|
+
if (fs.existsSync(kubiyCmdPath)) {
|
|
21
|
+
let content = fs.readFileSync(kubiyCmdPath, 'utf8');
|
|
22
|
+
|
|
23
|
+
// Verificar si necesita corrección (no tiene 'node' explícito)
|
|
24
|
+
if (!content.includes('node "%dp0%')) {
|
|
25
|
+
// Reemplazar la línea que ejecuta el .js directamente
|
|
26
|
+
content = content.replace(
|
|
27
|
+
/"%dp0%\\node_modules\\kubiy-paas-cli\\index\.js"/g,
|
|
28
|
+
'node "%dp0%\\node_modules\\kubiy-paas-cli\\index.js"'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
fs.writeFileSync(kubiyCmdPath, content, 'utf8');
|
|
32
|
+
console.log('✅ Archivo kubiy.cmd corregido');
|
|
33
|
+
}
|
|
26
34
|
}
|
|
27
|
-
}
|
|
28
35
|
|
|
29
|
-
// Corregir kubiy.ps1
|
|
30
|
-
if (fs.existsSync(kubiyPs1Path)) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
// Corregir kubiy.ps1
|
|
37
|
+
if (fs.existsSync(kubiyPs1Path)) {
|
|
38
|
+
let content = fs.readFileSync(kubiyPs1Path, 'utf8');
|
|
39
|
+
|
|
40
|
+
// Verificar si necesita corrección (no tiene 'node' explícito)
|
|
41
|
+
if (!content.includes('node "$basedir')) {
|
|
42
|
+
// Reemplazar las líneas que ejecutan el .js directamente
|
|
43
|
+
content = content.replace(
|
|
44
|
+
/& "`\$basedir\/node_modules\/kubiy-paas-cli\/index\.js"/g,
|
|
45
|
+
'& node "$basedir/node_modules/kubiy-paas-cli/index.js"'
|
|
46
|
+
);
|
|
47
|
+
// También corregir la versión sin backticks (por si acaso)
|
|
48
|
+
content = content.replace(
|
|
49
|
+
/& "\$basedir\/node_modules\/kubiy-paas-cli\/index\.js"/g,
|
|
50
|
+
'& node "$basedir/node_modules/kubiy-paas-cli/index.js"'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
fs.writeFileSync(kubiyPs1Path, content, 'utf8');
|
|
54
|
+
console.log('✅ Archivo kubiy.ps1 corregido');
|
|
55
|
+
}
|
|
48
56
|
}
|
|
49
|
-
}
|
|
50
57
|
|
|
58
|
+
} else {
|
|
59
|
+
// ==========================
|
|
60
|
+
// LADO UNIX / MAC
|
|
61
|
+
// ==========================
|
|
62
|
+
try {
|
|
63
|
+
const indexPath = path.join(__dirname, 'index.js');
|
|
64
|
+
|
|
65
|
+
if (fs.existsSync(indexPath)) {
|
|
66
|
+
fs.chmodSync(indexPath, 0o755);
|
|
67
|
+
console.log('✅ Unix: permisos ejecutables aplicados a index.js (chmod +x)');
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.log('⚠ No se pudo aplicar chmod a index.js en Unix:', e.message);
|
|
71
|
+
}
|
|
72
|
+
}
|
package/index.js
CHANGED
|
@@ -1,77 +1,55 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// CLI minimalista
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const os = require("os");
|
|
6
3
|
const path = require("path");
|
|
4
|
+
const { logInfo, logError } = require("./src/logger");
|
|
7
5
|
|
|
8
|
-
const args = process.argv.slice(2);
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const command = args[0];
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
9
|
+
async function main() {
|
|
10
|
+
try {
|
|
11
|
+
switch (command) {
|
|
12
|
+
case "login":
|
|
13
|
+
await require("./src/commands/login").run(args.slice(1));
|
|
14
|
+
break;
|
|
15
|
+
case "whoami":
|
|
16
|
+
await require("./src/commands/whoami").run(args.slice(1));
|
|
17
|
+
break;
|
|
18
|
+
case "service:get":
|
|
19
|
+
await require("./src/commands/service-get").run(args.slice(1));
|
|
20
|
+
break;
|
|
21
|
+
case "git:connect":
|
|
22
|
+
await require("./src/commands/git-connect").run(args.slice(1));
|
|
23
|
+
break;
|
|
24
|
+
case "deploy":
|
|
25
|
+
await require("./src/commands/deploy").run();
|
|
26
|
+
break;
|
|
27
|
+
case "version":
|
|
28
|
+
case "--version":
|
|
29
|
+
case "-v":
|
|
30
|
+
await require("./src/commands/version").run(args.slice(1));
|
|
31
|
+
break;
|
|
32
|
+
default:
|
|
33
|
+
printHelp();
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
logError("Error en CLI:", err.message || err);
|
|
36
38
|
process.exit(1);
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
const cfg = readConfig();
|
|
40
|
-
cfg.cliToken = token.trim();
|
|
41
|
-
writeConfig(cfg);
|
|
42
|
-
|
|
43
|
-
console.log("✅ CLI token guardado en ~/.kubiy/config.json");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function cmdWhoami() {
|
|
47
|
-
const cfg = readConfig();
|
|
48
|
-
if (!cfg.cliToken) {
|
|
49
|
-
console.log("No hay token configurado. Ejecuta: kubiy login <cliToken>");
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
console.log("CLI configurado. Token (truncado):", cfg.cliToken.slice(0, 6) + "****");
|
|
53
40
|
}
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
console.log("Kubiy PaaS CLI");
|
|
67
|
-
console.log("Uso:");
|
|
68
|
-
console.log(" kubiy login <cliToken>");
|
|
69
|
-
console.log(" kubiy whoami");
|
|
70
|
-
process.exit(0);
|
|
71
|
-
}
|
|
42
|
+
function printHelp() {
|
|
43
|
+
logInfo("Kubiy PaaS CLI");
|
|
44
|
+
console.log("");
|
|
45
|
+
console.log("Uso:");
|
|
46
|
+
console.log(" kubiy login <cliToken>");
|
|
47
|
+
console.log(" kubiy whoami");
|
|
48
|
+
console.log(" kubiy service:get <serviceId>");
|
|
49
|
+
console.log(" kubiy git:connect <serviceId>");
|
|
50
|
+
console.log(" kubiy deploy");
|
|
51
|
+
console.log(" kubiy version");
|
|
52
|
+
console.log("");
|
|
72
53
|
}
|
|
73
54
|
|
|
74
|
-
main()
|
|
75
|
-
console.error("Error en CLI:", err);
|
|
76
|
-
process.exit(1);
|
|
77
|
-
});
|
|
55
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kubiy-paas-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI para
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI para la plataforma PaaS de Kubiy",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kubiy": "index.js"
|
|
8
8
|
},
|
|
9
|
-
"preferGlobal": true,
|
|
10
9
|
"scripts": {
|
|
11
|
-
"start": "node index.js"
|
|
12
|
-
"postinstall": "node fix-link.js"
|
|
10
|
+
"start": "node index.js"
|
|
13
11
|
},
|
|
14
12
|
"keywords": [
|
|
15
13
|
"kubiy",
|
|
@@ -17,9 +15,10 @@
|
|
|
17
15
|
"cli",
|
|
18
16
|
"deploy"
|
|
19
17
|
],
|
|
20
|
-
"author": "
|
|
18
|
+
"author": "Kubiy",
|
|
21
19
|
"license": "MIT",
|
|
22
20
|
"engines": {
|
|
23
21
|
"node": ">=12.0.0"
|
|
24
22
|
}
|
|
25
23
|
}
|
|
24
|
+
|
package/src/api.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const https = require("https");
|
|
2
|
+
const { getCliToken } = require("./config");
|
|
3
|
+
const { logError } = require("./logger");
|
|
4
|
+
|
|
5
|
+
// Ajusta esta URL a tu API real
|
|
6
|
+
const API_BASE = process.env.KUBIY_API_BASE || "https://i5c812hwh2.execute-api.us-east-1.amazonaws.com";
|
|
7
|
+
|
|
8
|
+
function request(method, path, body, opts = {}) {
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const token = opts.token || getCliToken();
|
|
11
|
+
const url = new URL(path, API_BASE);
|
|
12
|
+
|
|
13
|
+
const headers = {
|
|
14
|
+
"Accept": "application/json"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
let payload = null;
|
|
18
|
+
if (body) {
|
|
19
|
+
payload = JSON.stringify(body);
|
|
20
|
+
headers["Content-Type"] = "application/json";
|
|
21
|
+
headers["Content-Length"] = Buffer.byteLength(payload);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (token) {
|
|
25
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const options = {
|
|
29
|
+
method,
|
|
30
|
+
headers
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const req = https.request(url, options, (res) => {
|
|
34
|
+
let data = "";
|
|
35
|
+
|
|
36
|
+
res.on("data", (chunk) => (data += chunk));
|
|
37
|
+
res.on("end", () => {
|
|
38
|
+
if (!data) {
|
|
39
|
+
return resolve(null);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
43
|
+
try {
|
|
44
|
+
resolve(JSON.parse(data));
|
|
45
|
+
} catch (e) {
|
|
46
|
+
resolve(data);
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
reject(
|
|
50
|
+
new Error(
|
|
51
|
+
`API ${res.statusCode}: ${data || res.statusMessage || "Error"}`
|
|
52
|
+
)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
req.on("error", reject);
|
|
59
|
+
|
|
60
|
+
if (payload) {
|
|
61
|
+
req.write(payload);
|
|
62
|
+
}
|
|
63
|
+
req.end();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function apiGet(path, opts) {
|
|
68
|
+
return request("GET", path, null, opts);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function apiPost(path, body, opts) {
|
|
72
|
+
return request("POST", path, body, opts);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = {
|
|
76
|
+
apiGet,
|
|
77
|
+
apiPost,
|
|
78
|
+
API_BASE
|
|
79
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// src/commands/deploy.js
|
|
2
|
+
const { execSync } = require("child_process");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const { logInfo, logSuccess, logError } = require("../logger");
|
|
5
|
+
|
|
6
|
+
function runGit(command, options = {}) {
|
|
7
|
+
const full = `git ${command}`;
|
|
8
|
+
try {
|
|
9
|
+
return execSync(full, {
|
|
10
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
11
|
+
...options,
|
|
12
|
+
}).toString().trim();
|
|
13
|
+
} catch (err) {
|
|
14
|
+
throw new Error(`Error al ejecutar: ${full}\n${err.message}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isGitRepo() {
|
|
19
|
+
try {
|
|
20
|
+
runGit("rev-parse --is-inside-work-tree");
|
|
21
|
+
return true;
|
|
22
|
+
} catch (_) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function ensureGitRepo() {
|
|
28
|
+
if (isGitRepo()) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
logInfo("Este directorio no es un repositorio git. Inicializando...");
|
|
33
|
+
runGit("init");
|
|
34
|
+
|
|
35
|
+
// Intentar crear master como rama inicial
|
|
36
|
+
try {
|
|
37
|
+
runGit("checkout -b master");
|
|
38
|
+
} catch (_) {
|
|
39
|
+
// Si falla, probablemente ya haya HEAD en detached; no pasa nada por ahora.
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
logSuccess("Repositorio git inicializado y rama master creada (si fue posible).");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getCurrentBranch() {
|
|
46
|
+
try {
|
|
47
|
+
return runGit("rev-parse --abbrev-ref HEAD");
|
|
48
|
+
} catch (_) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function branchExists(branchName) {
|
|
54
|
+
try {
|
|
55
|
+
const out = runGit(`branch --list ${branchName}`);
|
|
56
|
+
return out !== "";
|
|
57
|
+
} catch (_) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Normaliza para que la rama activa sea master.
|
|
64
|
+
*
|
|
65
|
+
* Casos:
|
|
66
|
+
* - Si ya estamos en master → OK.
|
|
67
|
+
* - Si existe master → hacemos checkout master.
|
|
68
|
+
* - Si NO existe master → renombramos la rama actual a master.
|
|
69
|
+
*/
|
|
70
|
+
function ensureOnMaster() {
|
|
71
|
+
const current = getCurrentBranch();
|
|
72
|
+
|
|
73
|
+
if (!current) {
|
|
74
|
+
throw new Error("No se pudo determinar la rama actual.");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (current === "master") {
|
|
78
|
+
logInfo("Ya estás en la rama master.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
logInfo(`Rama actual: ${current}`);
|
|
83
|
+
|
|
84
|
+
if (branchExists("master")) {
|
|
85
|
+
logInfo("Encontrada rama master. Cambiando a master...");
|
|
86
|
+
runGit("checkout master", { stdio: "inherit" });
|
|
87
|
+
logSuccess("Ahora estás en master.");
|
|
88
|
+
} else {
|
|
89
|
+
logInfo(`No existe rama master. Renombrando rama actual '${current}' a 'master'...`);
|
|
90
|
+
runGit(`branch -m master`, { stdio: "inherit" });
|
|
91
|
+
logSuccess("Rama actual renombrada a master.");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function ensureKubiyRemote() {
|
|
96
|
+
try {
|
|
97
|
+
const url = runGit("remote get-url kubiy");
|
|
98
|
+
logInfo(`Remote 'kubiy' encontrado: ${url}`);
|
|
99
|
+
} catch (_) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
"No se encontró el remote 'kubiy'.\n" +
|
|
102
|
+
"Asegúrate de haber ejecutado antes: kubiy git:connect <serviceId>"
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function hasChangesToCommit() {
|
|
108
|
+
const status = runGit("status --porcelain");
|
|
109
|
+
return status !== "";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function createAutoCommit() {
|
|
113
|
+
const msg = `kubiy deploy - ${new Date().toISOString()}`;
|
|
114
|
+
logInfo("Creando commit automático...");
|
|
115
|
+
|
|
116
|
+
runGit("add .", { stdio: "inherit" });
|
|
117
|
+
try {
|
|
118
|
+
runGit(`commit -m "${msg}"`, { stdio: "inherit" });
|
|
119
|
+
logSuccess(`Commit creado: ${msg}`);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
// Si no hay nada que commitear, git commit truena. Lo detectamos.
|
|
122
|
+
if (String(err.message).includes("nothing to commit")) {
|
|
123
|
+
logInfo("No hay cambios nuevos para commitear.");
|
|
124
|
+
} else {
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function pushToKubiy() {
|
|
131
|
+
logInfo("Haciendo push a 'kubiy' (rama master)...");
|
|
132
|
+
runGit("push kubiy master", { stdio: "inherit" });
|
|
133
|
+
logSuccess("Push a kubiy master completado.");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function run() {
|
|
137
|
+
try {
|
|
138
|
+
// 1) Asegurar repo git
|
|
139
|
+
ensureGitRepo();
|
|
140
|
+
|
|
141
|
+
// 2) Asegurar que estamos en master
|
|
142
|
+
ensureOnMaster();
|
|
143
|
+
|
|
144
|
+
// 3) Asegurar remote kubiy
|
|
145
|
+
ensureKubiyRemote();
|
|
146
|
+
|
|
147
|
+
// 4) Crear commit automático si hay cambios
|
|
148
|
+
if (hasChangesToCommit()) {
|
|
149
|
+
createAutoCommit();
|
|
150
|
+
} else {
|
|
151
|
+
logInfo("No hay cambios locales. Solo se intentará hacer push.");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 5) Push a kubiy master
|
|
155
|
+
pushToKubiy();
|
|
156
|
+
|
|
157
|
+
logSuccess("🚀 Deploy completado. Revisa tu servicio Kubiy.");
|
|
158
|
+
} catch (err) {
|
|
159
|
+
logError("Error en kubiy deploy:");
|
|
160
|
+
logError(err.message);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = {
|
|
166
|
+
run,
|
|
167
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// src/commands/git-connect.js
|
|
2
|
+
const { getCliToken } = require("../config");
|
|
3
|
+
const { apiGet, apiPost } = require("../api");
|
|
4
|
+
const { ensureSshKey, SSH_PRIV } = require("../ssh");
|
|
5
|
+
const { ensureSshHostConfig } = require("../ssh-config");
|
|
6
|
+
const { addRemote } = require("../git");
|
|
7
|
+
const { logInfo, logSuccess, logError } = require("../logger");
|
|
8
|
+
|
|
9
|
+
const { execSync } = require("child_process");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
|
|
12
|
+
function ensureGitRepo() {
|
|
13
|
+
if (fs.existsSync(".git")) return;
|
|
14
|
+
|
|
15
|
+
logInfo("Este directorio no es un repositorio git. Inicializando...");
|
|
16
|
+
execSync("git init", { stdio: "inherit" });
|
|
17
|
+
try {
|
|
18
|
+
execSync("git checkout -b main", { stdio: "inherit" });
|
|
19
|
+
} catch (_) {
|
|
20
|
+
// Si ya existe main/master, no pasa nada
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseRepoUrl(gitRepoUrl) {
|
|
25
|
+
// Esperamos algo como ssh://deploy@1.2.3.4/home/deploy/app.git
|
|
26
|
+
try {
|
|
27
|
+
const url = new URL(gitRepoUrl);
|
|
28
|
+
return {
|
|
29
|
+
host: url.hostname,
|
|
30
|
+
path: url.pathname // /home/deploy/app.git
|
|
31
|
+
};
|
|
32
|
+
} catch (e) {
|
|
33
|
+
// fallback muy básico
|
|
34
|
+
// ssh://deploy@1.2.3.4/home/deploy/app.git
|
|
35
|
+
if (!gitRepoUrl.startsWith("ssh://")) {
|
|
36
|
+
throw new Error(`gitRepoUrl inválido: ${gitRepoUrl}`);
|
|
37
|
+
}
|
|
38
|
+
const withoutScheme = gitRepoUrl.slice("ssh://".length);
|
|
39
|
+
const [userHost, pathPart] = withoutScheme.split("/", 2);
|
|
40
|
+
const host = userHost.split("@").pop();
|
|
41
|
+
return {
|
|
42
|
+
host,
|
|
43
|
+
path: "/" + pathPart
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function run(argv) {
|
|
49
|
+
const serviceId = argv[0];
|
|
50
|
+
|
|
51
|
+
if (!serviceId) {
|
|
52
|
+
logError("Uso: kubiy git:connect <serviceId>");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const token = getCliToken();
|
|
57
|
+
if (!token) {
|
|
58
|
+
logError("No hay CLI token. Ejecuta: kubiy login <cliToken>");
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 1) Asegurar llave SSH local (~/.kubiy/id_ed25519)
|
|
63
|
+
const pubKey = ensureSshKey();
|
|
64
|
+
|
|
65
|
+
// 2) Registrar llave en tu API
|
|
66
|
+
logInfo("Registrando llave SSH en Kubiy...");
|
|
67
|
+
await apiPost("/cli/ssh-keys", { publicKey: pubKey });
|
|
68
|
+
logSuccess("Llave SSH registrada.");
|
|
69
|
+
|
|
70
|
+
// 3) Sincronizar llaves con la EC2 del servicio
|
|
71
|
+
logInfo("Sincronizando llaves SSH con la instancia...");
|
|
72
|
+
try {
|
|
73
|
+
const syncResp = await apiPost(`/cli/services/${serviceId}/sync-ssh`, {});
|
|
74
|
+
logSuccess(
|
|
75
|
+
`Llaves sincronizadas. keysApplied=${syncResp.keysApplied}, ssmStatus=${syncResp.ssmStatus}`
|
|
76
|
+
);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
logError(`No se pudo sincronizar llaves con la instancia: ${e.message}`);
|
|
79
|
+
// seguimos, porque igual podrías tener la llave ya metida de antes
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 4) Obtener info del servicio (para gitRepoUrl)
|
|
83
|
+
logInfo("Obteniendo información del servicio...");
|
|
84
|
+
const service = await apiGet(`/services/${serviceId}`);
|
|
85
|
+
|
|
86
|
+
const gitRepoUrl = service.gitRepoUrl;
|
|
87
|
+
if (!gitRepoUrl) {
|
|
88
|
+
logError("El servicio no tiene gitRepoUrl todavía. Asegúrate que esté en RUNNING.");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const { host: ec2Host, path: repoPath } = parseRepoUrl(gitRepoUrl);
|
|
93
|
+
|
|
94
|
+
// 5) Configurar ~/.ssh/config con un host lógico único para este servicio
|
|
95
|
+
const hostAlias = `kubiy-${serviceId}`.slice(0, 60); // por si se va muy largo
|
|
96
|
+
|
|
97
|
+
ensureSshHostConfig(hostAlias, ec2Host, SSH_PRIV);
|
|
98
|
+
|
|
99
|
+
// 6) Configurar remote con el alias, no con la IP directa
|
|
100
|
+
ensureGitRepo();
|
|
101
|
+
logInfo("Configurando remote 'kubiy'...");
|
|
102
|
+
const remoteUrl = `ssh://${hostAlias}${repoPath}`;
|
|
103
|
+
addRemote("kubiy", remoteUrl);
|
|
104
|
+
|
|
105
|
+
logSuccess("Remote 'kubiy' configurado.");
|
|
106
|
+
|
|
107
|
+
logSuccess("Listo. Puedes hacer ahora:");
|
|
108
|
+
console.log(" git add .");
|
|
109
|
+
console.log(" git commit -m \"tu mensaje\"");
|
|
110
|
+
console.log(" git push kubiy main # o master");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { run };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { setCliToken } = require("../config");
|
|
2
|
+
const { logSuccess, logError } = require("../logger");
|
|
3
|
+
|
|
4
|
+
async function run(args) {
|
|
5
|
+
const token = args[0];
|
|
6
|
+
|
|
7
|
+
if (!token) {
|
|
8
|
+
logError("Uso: kubiy login <cliToken>");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
setCliToken(token.trim());
|
|
13
|
+
logSuccess("CLI token guardado en ~/.kubiy/config.json");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { run };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { apiGet } = require("../api");
|
|
2
|
+
const { logError, logSuccess } = require("../logger");
|
|
3
|
+
const { getCliToken } = require("../config");
|
|
4
|
+
|
|
5
|
+
async function run(args) {
|
|
6
|
+
const serviceId = args[0];
|
|
7
|
+
|
|
8
|
+
if (!serviceId) {
|
|
9
|
+
logError("Uso: kubiy service:get <serviceId>");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const token = getCliToken();
|
|
14
|
+
if (!token) {
|
|
15
|
+
logError("No hay CLI token. Ejecuta: kubiy login <cliToken>");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const service = await apiGet(`/services/${serviceId}`);
|
|
20
|
+
logSuccess("Servicio obtenido:");
|
|
21
|
+
console.log(JSON.stringify(service, null, 2));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { run };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { logInfo } = require("../logger");
|
|
2
|
+
const { API_BASE, apiGet } = require("../api");
|
|
3
|
+
const pkg = require("../../package.json");
|
|
4
|
+
|
|
5
|
+
async function run() {
|
|
6
|
+
logInfo(`Kubiy CLI version: ${pkg.version}`);
|
|
7
|
+
logInfo(`API base: ${API_BASE}`);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const status = await apiGet("/status"); // ajusta si no tienes este endpoint
|
|
11
|
+
console.log("API status:", status);
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.log("No se pudo consultar /status de la API:", e.message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = { run };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { getCliToken } = require("../config");
|
|
2
|
+
const { logInfo, logWarn } = require("../logger");
|
|
3
|
+
|
|
4
|
+
async function run() {
|
|
5
|
+
const token = getCliToken();
|
|
6
|
+
if (!token) {
|
|
7
|
+
logWarn("No hay CLI token configurado. Ejecuta: kubiy login <cliToken>");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
logInfo("CLI configurado.");
|
|
12
|
+
console.log("Token (truncado):", token.slice(0, 6) + "****");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { run };
|
package/src/config.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), ".kubiy");
|
|
6
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
7
|
+
|
|
8
|
+
function ensureConfigDir() {
|
|
9
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
10
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readConfig() {
|
|
15
|
+
try {
|
|
16
|
+
if (!fs.existsSync(CONFIG_FILE)) return {};
|
|
17
|
+
const raw = fs.readFileSync(CONFIG_FILE, "utf8");
|
|
18
|
+
return JSON.parse(raw);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function writeConfig(obj) {
|
|
25
|
+
ensureConfigDir();
|
|
26
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(obj, null, 2), "utf8");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getCliToken() {
|
|
30
|
+
const cfg = readConfig();
|
|
31
|
+
return cfg.cliToken;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function setCliToken(token) {
|
|
35
|
+
const cfg = readConfig();
|
|
36
|
+
cfg.cliToken = token;
|
|
37
|
+
writeConfig(cfg);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = {
|
|
41
|
+
CONFIG_DIR,
|
|
42
|
+
CONFIG_FILE,
|
|
43
|
+
readConfig,
|
|
44
|
+
writeConfig,
|
|
45
|
+
getCliToken,
|
|
46
|
+
setCliToken
|
|
47
|
+
};
|
package/src/git.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const { logInfo, logSuccess, logWarn } = require("./logger");
|
|
3
|
+
|
|
4
|
+
function run(cmd) {
|
|
5
|
+
execSync(cmd, { stdio: "inherit" });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function addRemote(name, url) {
|
|
9
|
+
try {
|
|
10
|
+
// quitar si existe
|
|
11
|
+
execSync(`git remote remove ${name}`, { stdio: "ignore" });
|
|
12
|
+
} catch (_) {
|
|
13
|
+
// no pasa nada si no existe
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
logInfo(`Agregando remote '${name}' -> ${url}`);
|
|
17
|
+
execSync(`git remote add ${name} "${url}"`, { stdio: "inherit" });
|
|
18
|
+
logSuccess(`Remote '${name}' configurado.`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
run,
|
|
23
|
+
addRemote
|
|
24
|
+
};
|
package/src/logger.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const COLORS = {
|
|
2
|
+
reset: "\x1b[0m",
|
|
3
|
+
green: "\x1b[32m",
|
|
4
|
+
yellow: "\x1b[33m",
|
|
5
|
+
red: "\x1b[31m",
|
|
6
|
+
cyan: "\x1b[36m"
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function logInfo(...args) {
|
|
10
|
+
console.log(COLORS.cyan + "ℹ" + COLORS.reset, ...args);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function logSuccess(...args) {
|
|
14
|
+
console.log(COLORS.green + "✔" + COLORS.reset, ...args);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function logWarn(...args) {
|
|
18
|
+
console.log(COLORS.yellow + "⚠" + COLORS.reset, ...args);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function logError(...args) {
|
|
22
|
+
console.error(COLORS.red + "✖" + COLORS.reset, ...args);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = {
|
|
26
|
+
logInfo,
|
|
27
|
+
logSuccess,
|
|
28
|
+
logWarn,
|
|
29
|
+
logError
|
|
30
|
+
};
|
|
31
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/ssh-config.js
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { logInfo, logSuccess } = require("./logger");
|
|
6
|
+
|
|
7
|
+
const SSH_DIR = path.join(os.homedir(), ".ssh");
|
|
8
|
+
const SSH_CONFIG = path.join(SSH_DIR, "config");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Asegura que exista una entrada Host en ~/.ssh/config
|
|
12
|
+
* para usar la llave de Kubiy con ese host.
|
|
13
|
+
*
|
|
14
|
+
* hostAlias: nombre lógico (p. ej. "kubiy-srv-<serviceId>")
|
|
15
|
+
* hostName: IP pública o hostname real de la EC2
|
|
16
|
+
* identityFile: ruta absoluta al id_ed25519 de Kubiy
|
|
17
|
+
*/
|
|
18
|
+
function ensureSshHostConfig(hostAlias, hostName, identityFile) {
|
|
19
|
+
if (!fs.existsSync(SSH_DIR)) {
|
|
20
|
+
fs.mkdirSync(SSH_DIR, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let existing = "";
|
|
24
|
+
if (fs.existsSync(SSH_CONFIG)) {
|
|
25
|
+
existing = fs.readFileSync(SSH_CONFIG, "utf8");
|
|
26
|
+
// Si ya hay un bloque para este Host, no duplicamos
|
|
27
|
+
const already = new RegExp(`^Host\\s+${hostAlias}\\b`, "m").test(existing);
|
|
28
|
+
if (already) {
|
|
29
|
+
logInfo(`SSH config ya tiene entrada para Host '${hostAlias}'`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Convertir backslashes a slashes para que OpenSSH en Windows no se maree
|
|
35
|
+
const identityPath = identityFile.replace(/\\/g, "/");
|
|
36
|
+
|
|
37
|
+
const block = [
|
|
38
|
+
"",
|
|
39
|
+
`Host ${hostAlias}`,
|
|
40
|
+
` HostName ${hostName}`,
|
|
41
|
+
` User deploy`,
|
|
42
|
+
` IdentityFile ${identityPath}`,
|
|
43
|
+
` IdentitiesOnly yes`,
|
|
44
|
+
""
|
|
45
|
+
].join("\n");
|
|
46
|
+
|
|
47
|
+
const newContent = existing ? `${existing.trimEnd()}\n${block}` : block.trimStart();
|
|
48
|
+
|
|
49
|
+
fs.writeFileSync(SSH_CONFIG, newContent, "utf8");
|
|
50
|
+
logSuccess(`Configurado ~/.ssh/config para Host '${hostAlias}'`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
ensureSshHostConfig,
|
|
55
|
+
SSH_DIR,
|
|
56
|
+
SSH_CONFIG
|
|
57
|
+
};
|
package/src/ssh.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const { execSync } = require("child_process");
|
|
5
|
+
const { logInfo, logSuccess, logWarn } = require("./logger");
|
|
6
|
+
|
|
7
|
+
const SSH_DIR = path.join(os.homedir(), ".kubiy");
|
|
8
|
+
const SSH_PRIV = path.join(SSH_DIR, "id_ed25519");
|
|
9
|
+
const SSH_PUB = SSH_PRIV + ".pub";
|
|
10
|
+
|
|
11
|
+
function ensureSshKey() {
|
|
12
|
+
if (!fs.existsSync(SSH_DIR)) {
|
|
13
|
+
fs.mkdirSync(SSH_DIR, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(SSH_PRIV) || !fs.existsSync(SSH_PUB)) {
|
|
17
|
+
logInfo("No existe llave SSH de Kubiy, generando una nueva...");
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
execSync(
|
|
21
|
+
`ssh-keygen -t ed25519 -f "${SSH_PRIV}" -N "" -C "kubiy-cli"`,
|
|
22
|
+
{ stdio: "inherit" }
|
|
23
|
+
);
|
|
24
|
+
logSuccess("Llave SSH generada en ~/.kubiy/");
|
|
25
|
+
} catch (e) {
|
|
26
|
+
logWarn("Error generando llave SSH:", e.message);
|
|
27
|
+
throw e;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const pub = fs.readFileSync(SSH_PUB, "utf8").trim();
|
|
32
|
+
return pub;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
SSH_DIR,
|
|
37
|
+
SSH_PRIV,
|
|
38
|
+
SSH_PUB,
|
|
39
|
+
ensureSshKey
|
|
40
|
+
};
|