kubiy-paas-cli 0.1.18 → 0.1.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/package.json +5 -2
- package/src/commands/deploy.js +82 -60
- package/src/commands/logs.js +11 -8
- package/src/commands/rollback.js +8 -6
- package/src/commands/service-get.js +20 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kubiy-paas-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"description": "CLI para la plataforma PaaS de Kubiy",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -23,5 +23,8 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"index.js",
|
|
25
25
|
"src"
|
|
26
|
-
]
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"tar": "^7.4.3"
|
|
29
|
+
}
|
|
27
30
|
}
|
package/src/commands/deploy.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// src/commands/deploy.js
|
|
2
|
-
const { execSync } = require("child_process");
|
|
3
2
|
const fs = require("fs");
|
|
4
3
|
const path = require("path");
|
|
5
4
|
const os = require("os");
|
|
6
5
|
const https = require("https");
|
|
6
|
+
const tar = require("tar");
|
|
7
7
|
const { apiPost } = require("../api");
|
|
8
8
|
const { logInfo, logSuccess, logError } = require("../logger");
|
|
9
|
-
const { getCliToken } = require("../config");
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Helpers para .kubiy.json
|
|
@@ -96,12 +95,58 @@ function loadKubiyIgnore(projectRoot) {
|
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Convertir un patrón estilo glob simple (con `*` y `?`) a RegExp.
|
|
100
|
+
* Solo soporta `*` (cualquier secuencia sin `/`) y `?` (un caracter sin `/`).
|
|
101
|
+
*/
|
|
102
|
+
function globToRegex(pattern) {
|
|
103
|
+
const escaped = pattern
|
|
104
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
105
|
+
.replace(/\*/g, "[^/]*")
|
|
106
|
+
.replace(/\?/g, "[^/]");
|
|
107
|
+
return new RegExp(`^${escaped}$`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Normalizar patrones de exclusión: quitar ./ inicial y / final.
|
|
112
|
+
*/
|
|
113
|
+
function normalizeExcludePattern(pattern) {
|
|
114
|
+
let normalized = pattern.trim();
|
|
115
|
+
if (normalized.startsWith("./")) normalized = normalized.substring(2);
|
|
116
|
+
if (normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
117
|
+
return normalized;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Construir filtro para la API de `tar` a partir de la lista de patrones.
|
|
122
|
+
* Aplica la regla "si cualquier segmento del path coincide, se excluye",
|
|
123
|
+
* equivalente al comportamiento de `tar --exclude=` con nombres de carpetas
|
|
124
|
+
* o archivos a cualquier profundidad.
|
|
125
|
+
*/
|
|
126
|
+
function buildExcludeFilter(patterns) {
|
|
127
|
+
const regexes = patterns
|
|
128
|
+
.map(normalizeExcludePattern)
|
|
129
|
+
.filter(Boolean)
|
|
130
|
+
.map(globToRegex);
|
|
131
|
+
|
|
132
|
+
return function shouldInclude(filePath /* , stat */) {
|
|
133
|
+
const normalized = filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
134
|
+
if (!normalized) return true;
|
|
135
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
136
|
+
for (const re of regexes) {
|
|
137
|
+
if (segments.some((seg) => re.test(seg))) return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
99
143
|
/**
|
|
100
144
|
* Empaquetar código en .tar.gz
|
|
101
145
|
* - Se genera en el directorio temporal del sistema (NO dentro del repo)
|
|
102
146
|
* - Usa .kubiyignore para excluir archivos y carpetas
|
|
147
|
+
* - No depende de ningún binario del SO: usa el paquete `tar` de npm
|
|
103
148
|
*/
|
|
104
|
-
function createTarball(deployType) {
|
|
149
|
+
async function createTarball(deployType) {
|
|
105
150
|
const projectRoot = process.cwd();
|
|
106
151
|
// Si deployType es "server", usar nombre fijo deploy.tar.gz, sino usar timestamp
|
|
107
152
|
const archiveName = deployType === "server" ? "deploy.tar.gz" : `deploy-${Date.now()}.tar.gz`;
|
|
@@ -109,43 +154,22 @@ function createTarball(deployType) {
|
|
|
109
154
|
|
|
110
155
|
logInfo("Empaquetando código en " + archiveName + " ...");
|
|
111
156
|
|
|
112
|
-
// Cargar patrones de exclusión desde .kubiyignore
|
|
113
157
|
const excludes = loadKubiyIgnore(projectRoot);
|
|
114
|
-
|
|
115
|
-
// Construir argumentos de exclusión para tar
|
|
116
|
-
// Normalizar patrones: remover ./ inicial, agregar ./ si no tiene
|
|
117
|
-
const excludeArgs = excludes.map((pattern) => {
|
|
118
|
-
let normalized = pattern.trim();
|
|
119
|
-
// Remover ./ inicial si existe
|
|
120
|
-
if (normalized.startsWith("./")) {
|
|
121
|
-
normalized = normalized.substring(2);
|
|
122
|
-
}
|
|
123
|
-
// Remover / final si existe (carpetas)
|
|
124
|
-
if (normalized.endsWith("/")) {
|
|
125
|
-
normalized = normalized.slice(0, -1);
|
|
126
|
-
}
|
|
127
|
-
return `--exclude=./${normalized}`;
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const tarArgs = [
|
|
131
|
-
"-czf",
|
|
132
|
-
`"${archivePath}"`,
|
|
133
|
-
...excludeArgs,
|
|
134
|
-
".",
|
|
135
|
-
];
|
|
158
|
+
const filter = buildExcludeFilter(excludes);
|
|
136
159
|
|
|
137
160
|
try {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
(stderr || err.message)
|
|
161
|
+
await tar.c(
|
|
162
|
+
{
|
|
163
|
+
gzip: true,
|
|
164
|
+
file: archivePath,
|
|
165
|
+
cwd: projectRoot,
|
|
166
|
+
portable: true,
|
|
167
|
+
filter,
|
|
168
|
+
},
|
|
169
|
+
["."]
|
|
148
170
|
);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
throw new Error("Error al crear el .tar.gz: " + (err.message || String(err)));
|
|
149
173
|
}
|
|
150
174
|
|
|
151
175
|
logSuccess("Artefacto generado en temp: " + archivePath);
|
|
@@ -156,32 +180,27 @@ function createTarball(deployType) {
|
|
|
156
180
|
* Empaquetar directorio static en .tar.gz
|
|
157
181
|
* - Se genera en el directorio temporal del sistema
|
|
158
182
|
* - Empaqueta el contenido del directorio directamente (sin la carpeta contenedora)
|
|
183
|
+
* - No depende de ningún binario del SO: usa el paquete `tar` de npm
|
|
159
184
|
*/
|
|
160
|
-
function createStaticTarball(staticDir) {
|
|
185
|
+
async function createStaticTarball(staticDir) {
|
|
161
186
|
const archiveName = `static-deploy-${Date.now()}.tar.gz`;
|
|
162
187
|
const archivePath = path.join(os.tmpdir(), archiveName);
|
|
163
188
|
|
|
164
189
|
logInfo(`Empaquetando directorio static: ${staticDir} en ${archiveName}...`);
|
|
165
190
|
|
|
166
|
-
// Cambiar al directorio static y empaquetar su contenido directamente
|
|
167
|
-
const tarArgs = [
|
|
168
|
-
"-czf",
|
|
169
|
-
`"${archivePath}"`,
|
|
170
|
-
"-C",
|
|
171
|
-
`"${staticDir}"`,
|
|
172
|
-
".",
|
|
173
|
-
];
|
|
174
|
-
|
|
175
191
|
try {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
192
|
+
await tar.c(
|
|
193
|
+
{
|
|
194
|
+
gzip: true,
|
|
195
|
+
file: archivePath,
|
|
196
|
+
cwd: staticDir,
|
|
197
|
+
portable: true,
|
|
198
|
+
},
|
|
199
|
+
["."]
|
|
200
|
+
);
|
|
180
201
|
} catch (err) {
|
|
181
|
-
const stderr = err.stderr ? String(err.stderr) : "";
|
|
182
202
|
throw new Error(
|
|
183
|
-
"Error al crear el .tar.gz del directorio static (
|
|
184
|
-
(stderr || err.message)
|
|
203
|
+
"Error al crear el .tar.gz del directorio static: " + (err.message || String(err))
|
|
185
204
|
);
|
|
186
205
|
}
|
|
187
206
|
|
|
@@ -368,16 +387,19 @@ async function run(args = []) {
|
|
|
368
387
|
|
|
369
388
|
try {
|
|
370
389
|
const opts = parseArgs(args);
|
|
371
|
-
const token = getCliToken();
|
|
372
|
-
if (!token) {
|
|
373
|
-
logError("No hay CLI token. Ejecuta: kubiy login <cliToken>");
|
|
374
|
-
process.exit(1);
|
|
375
|
-
}
|
|
376
390
|
|
|
377
391
|
// 3) Cargar config del proyecto (.kubiy.json)
|
|
378
392
|
const kubiyConfig = loadProjectKubiyConfig();
|
|
379
393
|
const { appId, serviceId, environment, healthPath, apikey, configPath, config } = kubiyConfig;
|
|
380
394
|
|
|
395
|
+
// La autenticación se hace con el apikey de .kubiy.json (generado por `kubiy link`),
|
|
396
|
+
// no con el CLI token. Si falta el apikey, hay que re-linkear el proyecto.
|
|
397
|
+
if (!apikey) {
|
|
398
|
+
logError("No se encontró 'apikey' en .kubiy.json. Vuelve a linkear el proyecto:");
|
|
399
|
+
logError(" kubiy link <appId> --apikey=<tu-api-key>");
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
|
|
381
403
|
// Validar que deployType coincida con el modo de deploy
|
|
382
404
|
const deployType = config.deployType;
|
|
383
405
|
if (!opts.static && deployType === "static") {
|
|
@@ -422,9 +444,9 @@ async function run(args = []) {
|
|
|
422
444
|
|
|
423
445
|
logInfo(`Usando directorio static: ${outDir}`);
|
|
424
446
|
assertStaticBuild(outDir);
|
|
425
|
-
archivePath = createStaticTarball(outDir);
|
|
447
|
+
archivePath = await createStaticTarball(outDir);
|
|
426
448
|
} else {
|
|
427
|
-
archivePath = createTarball(deployType);
|
|
449
|
+
archivePath = await createTarball(deployType);
|
|
428
450
|
}
|
|
429
451
|
|
|
430
452
|
// 5) Pedir presigned URL a la API Kubiy
|
package/src/commands/logs.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const { apiGet } = require("../api");
|
|
5
|
-
const { getCliToken } = require("../config");
|
|
6
5
|
const { logInfo, logError } = require("../logger");
|
|
7
6
|
|
|
8
7
|
function parseArgs(args) {
|
|
@@ -27,7 +26,7 @@ function parseArgs(args) {
|
|
|
27
26
|
return { serviceId: clean[0] || null, opts };
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
function
|
|
29
|
+
function loadKubiyLink() {
|
|
31
30
|
const filepath = path.join(process.cwd(), ".kubiy.json");
|
|
32
31
|
|
|
33
32
|
if (!fs.existsSync(filepath)) {
|
|
@@ -38,7 +37,7 @@ function resolveServiceIdFromLink() {
|
|
|
38
37
|
const raw = fs.readFileSync(filepath, "utf8");
|
|
39
38
|
const cfg = JSON.parse(raw);
|
|
40
39
|
|
|
41
|
-
return cfg.serviceId || null;
|
|
40
|
+
return { serviceId: cfg.serviceId || null, apikey: cfg.apikey || null };
|
|
42
41
|
} catch (err) {
|
|
43
42
|
// Si el json está roto, que el usuario se entere
|
|
44
43
|
logError("No se pudo leer .kubiy.json:", err.message || String(err));
|
|
@@ -49,11 +48,13 @@ function resolveServiceIdFromLink() {
|
|
|
49
48
|
async function run(args) {
|
|
50
49
|
const { serviceId: cliServiceId, opts } = parseArgs(args);
|
|
51
50
|
|
|
51
|
+
const linked = loadKubiyLink();
|
|
52
52
|
let serviceId = cliServiceId;
|
|
53
|
+
const apikey = linked && linked.apikey;
|
|
53
54
|
|
|
54
55
|
// 🍀 Si no viene serviceId por CLI, intentamos usar el del link
|
|
55
56
|
if (!serviceId) {
|
|
56
|
-
const linkedServiceId =
|
|
57
|
+
const linkedServiceId = linked && linked.serviceId;
|
|
57
58
|
if (!linkedServiceId) {
|
|
58
59
|
logError(
|
|
59
60
|
"Uso: kubiy logs <serviceId> --kind=deploy|app --lines=200\n" +
|
|
@@ -65,9 +66,9 @@ async function run(args) {
|
|
|
65
66
|
logInfo(`Usando serviceId del link (.kubiy.json): ${serviceId}`);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
logError("
|
|
69
|
+
if (!apikey) {
|
|
70
|
+
logError("No se encontró 'apikey' en .kubiy.json. Vuelve a linkear el proyecto:");
|
|
71
|
+
logError(" kubiy link <appId> --apikey=<tu-api-key>");
|
|
71
72
|
process.exit(1);
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -86,7 +87,9 @@ async function run(args) {
|
|
|
86
87
|
|
|
87
88
|
const pathUrl = `/services/${encodeURIComponent(serviceId)}/logs?${qs}`;
|
|
88
89
|
|
|
89
|
-
const resp = await apiGet(pathUrl
|
|
90
|
+
const resp = await apiGet(pathUrl, {
|
|
91
|
+
headers: { "x-kubiy-token": apikey },
|
|
92
|
+
}); // { stdout, stderr, ... }
|
|
90
93
|
const stdout = resp.stdout || "";
|
|
91
94
|
const stderr = resp.stderr || "";
|
|
92
95
|
|
package/src/commands/rollback.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const { apiPost } = require("../api");
|
|
5
|
-
const { getCliToken } = require("../config");
|
|
6
5
|
const { logInfo, logError, logSuccess } = require("../logger");
|
|
7
6
|
|
|
8
7
|
function loadProjectKubiyConfig() {
|
|
@@ -10,7 +9,9 @@ function loadProjectKubiyConfig() {
|
|
|
10
9
|
if (!fs.existsSync(configPath)) return null;
|
|
11
10
|
try {
|
|
12
11
|
const json = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
13
|
-
return json.appId && json.serviceId
|
|
12
|
+
return json.appId && json.serviceId
|
|
13
|
+
? { appId: json.appId, serviceId: json.serviceId, apikey: json.apikey }
|
|
14
|
+
: null;
|
|
14
15
|
} catch {
|
|
15
16
|
return null;
|
|
16
17
|
}
|
|
@@ -22,6 +23,7 @@ async function run(args) {
|
|
|
22
23
|
const appId = args.length === 3 ? args[0] : (linked && linked.appId);
|
|
23
24
|
const serviceId = args.length === 3 ? args[1] : (linked && linked.serviceId);
|
|
24
25
|
const deployId = args.length === 3 ? args[2] : args[0];
|
|
26
|
+
const apikey = linked && linked.apikey;
|
|
25
27
|
|
|
26
28
|
if (!deployId) {
|
|
27
29
|
logError("Uso: kubiy rollback <deployId>\n kubiy rollback <appId> <serviceId> <deployId>\nO ejecuta desde un proyecto con kubiy link y pasa <deployId>.");
|
|
@@ -38,9 +40,9 @@ async function run(args) {
|
|
|
38
40
|
process.exit(1);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
logError("
|
|
43
|
+
if (!apikey) {
|
|
44
|
+
logError("No se encontró 'apikey' en .kubiy.json. Vuelve a linkear el proyecto:");
|
|
45
|
+
logError(" kubiy link <appId> --apikey=<tu-api-key>");
|
|
44
46
|
process.exit(1);
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -50,7 +52,7 @@ async function run(args) {
|
|
|
50
52
|
const resp = await apiPost(
|
|
51
53
|
`/rollback`,
|
|
52
54
|
{ appId, serviceId, deployId },
|
|
53
|
-
{ headers: { "
|
|
55
|
+
{ headers: { "x-kubiy-token": apikey } }
|
|
54
56
|
);
|
|
55
57
|
logSuccess("Rollback enviado a Kubiy.");
|
|
56
58
|
|
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
1
3
|
const { apiGet } = require("../api");
|
|
2
4
|
const { logError, logSuccess } = require("../logger");
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
function loadApiKeyFromLink() {
|
|
7
|
+
const filepath = path.join(process.cwd(), ".kubiy.json");
|
|
8
|
+
if (!fs.existsSync(filepath)) return null;
|
|
9
|
+
try {
|
|
10
|
+
const cfg = JSON.parse(fs.readFileSync(filepath, "utf8"));
|
|
11
|
+
return cfg.apikey || null;
|
|
12
|
+
} catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
4
16
|
|
|
5
17
|
async function run(args) {
|
|
6
18
|
const serviceId = args[0];
|
|
@@ -10,13 +22,16 @@ async function run(args) {
|
|
|
10
22
|
process.exit(1);
|
|
11
23
|
}
|
|
12
24
|
|
|
13
|
-
const
|
|
14
|
-
if (!
|
|
15
|
-
logError("No
|
|
25
|
+
const apikey = loadApiKeyFromLink();
|
|
26
|
+
if (!apikey) {
|
|
27
|
+
logError("No se encontró 'apikey' en .kubiy.json. Vuelve a linkear el proyecto:");
|
|
28
|
+
logError(" kubiy link <appId> --apikey=<tu-api-key>");
|
|
16
29
|
process.exit(1);
|
|
17
30
|
}
|
|
18
31
|
|
|
19
|
-
const service = await apiGet(`/services/${serviceId}
|
|
32
|
+
const service = await apiGet(`/services/${serviceId}`, {
|
|
33
|
+
headers: { "x-kubiy-token": apikey },
|
|
34
|
+
});
|
|
20
35
|
logSuccess("Servicio obtenido:");
|
|
21
36
|
console.log(JSON.stringify(service, null, 2));
|
|
22
37
|
}
|