kubiy-paas-cli 0.1.19 → 0.1.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubiy-paas-cli",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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
  }
@@ -1,9 +1,9 @@
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
9
 
@@ -95,12 +95,58 @@ function loadKubiyIgnore(projectRoot) {
95
95
  }
96
96
  }
97
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
+
98
143
  /**
99
144
  * Empaquetar código en .tar.gz
100
145
  * - Se genera en el directorio temporal del sistema (NO dentro del repo)
101
146
  * - Usa .kubiyignore para excluir archivos y carpetas
147
+ * - No depende de ningún binario del SO: usa el paquete `tar` de npm
102
148
  */
103
- function createTarball(deployType) {
149
+ async function createTarball(deployType) {
104
150
  const projectRoot = process.cwd();
105
151
  // Si deployType es "server", usar nombre fijo deploy.tar.gz, sino usar timestamp
106
152
  const archiveName = deployType === "server" ? "deploy.tar.gz" : `deploy-${Date.now()}.tar.gz`;
@@ -108,43 +154,22 @@ function createTarball(deployType) {
108
154
 
109
155
  logInfo("Empaquetando código en " + archiveName + " ...");
110
156
 
111
- // Cargar patrones de exclusión desde .kubiyignore
112
157
  const excludes = loadKubiyIgnore(projectRoot);
113
-
114
- // Construir argumentos de exclusión para tar
115
- // Normalizar patrones: remover ./ inicial, agregar ./ si no tiene
116
- const excludeArgs = excludes.map((pattern) => {
117
- let normalized = pattern.trim();
118
- // Remover ./ inicial si existe
119
- if (normalized.startsWith("./")) {
120
- normalized = normalized.substring(2);
121
- }
122
- // Remover / final si existe (carpetas)
123
- if (normalized.endsWith("/")) {
124
- normalized = normalized.slice(0, -1);
125
- }
126
- return `--exclude=./${normalized}`;
127
- });
128
-
129
- const tarArgs = [
130
- "-czf",
131
- `"${archivePath}"`,
132
- ...excludeArgs,
133
- ".",
134
- ];
158
+ const filter = buildExcludeFilter(excludes);
135
159
 
136
160
  try {
137
- execSync(`tar ${tarArgs.join(" ")}`, {
138
- cwd: projectRoot,
139
- stdio: ["ignore", "pipe", "pipe"],
140
- encoding: "utf8",
141
- });
142
- } catch (err) {
143
- const stderr = err.stderr ? String(err.stderr) : "";
144
- throw new Error(
145
- "Error al crear el .tar.gz (¿tienes 'tar' instalado?).\n" +
146
- (stderr || err.message)
161
+ await tar.c(
162
+ {
163
+ gzip: true,
164
+ file: archivePath,
165
+ cwd: projectRoot,
166
+ portable: true,
167
+ filter,
168
+ },
169
+ ["."]
147
170
  );
171
+ } catch (err) {
172
+ throw new Error("Error al crear el .tar.gz: " + (err.message || String(err)));
148
173
  }
149
174
 
150
175
  logSuccess("Artefacto generado en temp: " + archivePath);
@@ -155,32 +180,27 @@ function createTarball(deployType) {
155
180
  * Empaquetar directorio static en .tar.gz
156
181
  * - Se genera en el directorio temporal del sistema
157
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
158
184
  */
159
- function createStaticTarball(staticDir) {
185
+ async function createStaticTarball(staticDir) {
160
186
  const archiveName = `static-deploy-${Date.now()}.tar.gz`;
161
187
  const archivePath = path.join(os.tmpdir(), archiveName);
162
188
 
163
189
  logInfo(`Empaquetando directorio static: ${staticDir} en ${archiveName}...`);
164
190
 
165
- // Cambiar al directorio static y empaquetar su contenido directamente
166
- const tarArgs = [
167
- "-czf",
168
- `"${archivePath}"`,
169
- "-C",
170
- `"${staticDir}"`,
171
- ".",
172
- ];
173
-
174
191
  try {
175
- execSync(`tar ${tarArgs.join(" ")}`, {
176
- stdio: ["ignore", "pipe", "pipe"],
177
- encoding: "utf8",
178
- });
192
+ await tar.c(
193
+ {
194
+ gzip: true,
195
+ file: archivePath,
196
+ cwd: staticDir,
197
+ portable: true,
198
+ },
199
+ ["."]
200
+ );
179
201
  } catch (err) {
180
- const stderr = err.stderr ? String(err.stderr) : "";
181
202
  throw new Error(
182
- "Error al crear el .tar.gz del directorio static (¿tienes 'tar' instalado?).\n" +
183
- (stderr || err.message)
203
+ "Error al crear el .tar.gz del directorio static: " + (err.message || String(err))
184
204
  );
185
205
  }
186
206
 
@@ -424,9 +444,9 @@ async function run(args = []) {
424
444
 
425
445
  logInfo(`Usando directorio static: ${outDir}`);
426
446
  assertStaticBuild(outDir);
427
- archivePath = createStaticTarball(outDir);
447
+ archivePath = await createStaticTarball(outDir);
428
448
  } else {
429
- archivePath = createTarball(deployType);
449
+ archivePath = await createTarball(deployType);
430
450
  }
431
451
 
432
452
  // 5) Pedir presigned URL a la API Kubiy
@@ -451,7 +471,7 @@ async function run(args = []) {
451
471
  }
452
472
  });
453
473
 
454
- logInfo(JSON.stringify(presignResp, null, 2));
474
+ //logInfo(JSON.stringify(presignResp, null, 2));
455
475
  const { deployId, releaseId } = presignResp || {};
456
476
  if (!deployId || !releaseId) {
457
477
  throw new Error(
@@ -477,23 +497,30 @@ async function run(args = []) {
477
497
  logSuccess("Artefacto subido correctamente a S3.");
478
498
 
479
499
  // 7) Avisar a Kubiy que inicie el deploy (POST /deploys)
480
- logInfo("Notificando a Kubiy que inicie el deploy...");
481
-
482
- const deployResp = await apiPost("/deploys", {
483
- deployId,
484
- appId,
485
- serviceId,
486
- artifactKey: key,
487
- commit,
488
- env: environment?.name
489
- }, {
490
- headers: {
491
- "x-kubiy-token": apikey
492
- }
493
- });
494
-
495
- logSuccess("🚀 Deploy iniciado correctamente en Kubiy.");
496
- console.log(JSON.stringify(deployResp, null, 2));
500
+ // En deploys static no se llama a /deploys: la subida del artefacto al
501
+ // bucket presignado es suficiente para que el backend publique el sitio.
502
+ if (opts.static) {
503
+ logSuccess("🚀 Deploy static publicado correctamente en Kubiy.");
504
+ logInfo(`deployId=${deployId} releaseId=${releaseId}`);
505
+ } else {
506
+ logInfo("Notificando a Kubiy que inicie el deploy...");
507
+
508
+ const deployResp = await apiPost("/deploys", {
509
+ deployId,
510
+ appId,
511
+ serviceId,
512
+ artifactKey: key,
513
+ commit,
514
+ env: environment?.name
515
+ }, {
516
+ headers: {
517
+ "x-kubiy-token": apikey
518
+ }
519
+ });
520
+
521
+ logSuccess("🚀 Deploy iniciado correctamente en Kubiy.");
522
+ console.log(JSON.stringify(deployResp, null, 2));
523
+ }
497
524
 
498
525
  logInfo(`Puedes consultar el servicio con: kubiy service:get ${serviceId}`);
499
526
  logInfo(`Y los logs con: kubiy logs ${serviceId} --kind=deploy --lines=200`);