shilo-cli 0.8.0 → 0.9.0
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 +19 -0
- package/bin/shilo.js +15 -1
- package/package.json +1 -1
- package/src/ShiloClient.js +45 -6
- package/src/checks.js +57 -0
- package/src/commands/deploys.js +105 -0
- package/src/commands/diff.js +1 -1
- package/src/commands/init.js +8 -0
- package/src/commands/push.js +436 -62
- package/src/commands/status.js +112 -14
- package/src/projectconfig.js +76 -0
- package/src/safelist.js +101 -0
- package/src/secrets.js +75 -9
- package/src/sync.js +52 -4
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ cd mi-carpeta
|
|
|
36
36
|
shilo init --project <uuid> # ata la carpeta al repo (escribe .shilo.json)
|
|
37
37
|
shilo push # sube: crea/actualiza + commit
|
|
38
38
|
shilo push -m "mi mensaje" # con mensaje de commit
|
|
39
|
+
shilo push --deploy prod # este push despliega a producción ("[deploy: prod]")
|
|
39
40
|
shilo push --dry-run # muestra qué haría sin subir
|
|
40
41
|
shilo push --card ABC-123 # agrega "[card: ABC-123]" al mensaje del commit
|
|
41
42
|
shilo push --agent claude-code --model claude-opus-5 # lo escribió un agente, y queda anotado
|
|
@@ -169,6 +170,22 @@ shilo log --json
|
|
|
169
170
|
|
|
170
171
|
La paginación es **por commit**, no por archivo: si un archivo no aparece, subí `-n`.
|
|
171
172
|
|
|
173
|
+
### Deploys a Vercel (deploys)
|
|
174
|
+
|
|
175
|
+
Si el owner conectó el proyecto con Vercel (pestaña **Deploys** en la web), cada
|
|
176
|
+
push que cambia algo dispara un deploy: por defecto a **preview**. Producción sólo
|
|
177
|
+
si se pide explícitamente.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
shilo push --deploy prod # este push va a producción
|
|
181
|
+
shilo push --deploy off # este push no despliega
|
|
182
|
+
shilo deploys # destino y últimos deploys, con estado y URL
|
|
183
|
+
shilo deploys -n 20 --json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`--deploy` sólo agrega el trailer `[deploy: …]` al mensaje; escribirlo a mano en
|
|
187
|
+
`-m` da lo mismo.
|
|
188
|
+
|
|
172
189
|
### Diferencias (diff)
|
|
173
190
|
|
|
174
191
|
```bash
|
|
@@ -307,6 +324,8 @@ SHILO_TOKEN=<pat> shilo init --project <uuid> # variable de entorno (lo natura
|
|
|
307
324
|
manda una propuesta con la base exacta; el servidor la combina con la versión actual
|
|
308
325
|
y nunca pisa un archivo que otra persona cambió. `base` en `.shilo.json`,
|
|
309
326
|
`registerBlob` para binarios, camino viejo sólo como reserva.
|
|
327
|
+
- **Fase 8 — deploys** *(hecho)*: `push --deploy <prod|preview|off>` y
|
|
328
|
+
`shilo deploys` para el deploy automático a Vercel (PLAN_DEPLOY_VERCEL).
|
|
310
329
|
|
|
311
330
|
## Exit codes
|
|
312
331
|
|
package/bin/shilo.js
CHANGED
|
@@ -24,6 +24,7 @@ ${ui.c.bold('Comandos:')}
|
|
|
24
24
|
diff [archivo] Diferencias entre tu copia local y la remota.
|
|
25
25
|
review [versión] Quién revisó una versión, o dejar tu revisión.
|
|
26
26
|
export <carpeta> Escribe la historia del proyecto como un repositorio Git.
|
|
27
|
+
deploys Destino e historial de deploys a Vercel del proyecto.
|
|
27
28
|
help Muestra esta ayuda.
|
|
28
29
|
|
|
29
30
|
${ui.c.bold('Opciones de login:')}
|
|
@@ -55,6 +56,7 @@ ${ui.c.bold('Opciones de push:')}
|
|
|
55
56
|
-m, --message <m> Mensaje del commit.
|
|
56
57
|
--only <ruta> Sube sólo esa ruta (archivo o carpeta). Repetible.
|
|
57
58
|
--card <id> Id de tarjeta/tablero; se agrega al mensaje como [card: id].
|
|
59
|
+
--deploy <d> prod | preview | off: pisa el deploy automático de este push.
|
|
58
60
|
--agent <nombre> Declara que lo escribió un agente (ej: claude-code).
|
|
59
61
|
--model <modelo> Qué modelo, junto con --agent.
|
|
60
62
|
--session <id> Id de la conversación, para agrupar guardados.
|
|
@@ -62,12 +64,17 @@ ${ui.c.bold('Opciones de push:')}
|
|
|
62
64
|
--prompt-file <f> Igual, leyendo el pedido de un archivo (máx. 64 KB).
|
|
63
65
|
--dry-run Muestra qué haría (y quién tocó cada archivo), sin subir nada.
|
|
64
66
|
--yes No pide confirmación.
|
|
65
|
-
--allow-secrets Sube igual
|
|
67
|
+
--allow-secrets Sube igual TODO lo que parezca un secreto (avanzado). Mejor:
|
|
68
|
+
responder en la terminal; la decisión queda en .shilo/.
|
|
66
69
|
--concurrency <n> Requests en paralelo (1-32, default 8; o SHILO_CONCURRENCY).
|
|
67
70
|
--no-index Ignora el índice local (.shilo/index.json) y recompara todo.
|
|
71
|
+
--allow-failing-checks
|
|
72
|
+
Si falla un chequeo de shilo.config.json, propone igual
|
|
73
|
+
(queda marcada como fallida y aplicarla exige forzar).
|
|
68
74
|
--verbose Lista archivo por archivo en vez de resumir.
|
|
69
75
|
--base-url <url> Servidor Shilo.
|
|
70
76
|
${ui.c.dim('Respeta .gitignore y .shiloignore, incluidos los de subcarpetas.')}
|
|
77
|
+
${ui.c.dim('Exit code 3 = todo guardado, pero espera aprobación (no es un error).')}
|
|
71
78
|
|
|
72
79
|
${ui.c.bold('Opciones de pull:')}
|
|
73
80
|
--project <uuid> Proyecto origen (pisa el .shilo.json de la carpeta).
|
|
@@ -105,6 +112,11 @@ ${ui.c.bold('Opciones de export:')}
|
|
|
105
112
|
--base-url <url> Servidor Shilo.
|
|
106
113
|
${ui.c.dim('La primera vez, a una carpeta vacía; después sólo trae lo nuevo. Público sin sesión.')}
|
|
107
114
|
|
|
115
|
+
${ui.c.bold('Opciones de deploys:')}
|
|
116
|
+
-n <N> Cuántos deploys mostrar (1-20, default 10).
|
|
117
|
+
--json Salida JSON.
|
|
118
|
+
--project <uuid> Proyecto (pisa el .shilo.json de la carpeta).
|
|
119
|
+
|
|
108
120
|
${ui.c.bold('Globales:')}
|
|
109
121
|
-v, --version Versión del CLI.
|
|
110
122
|
-h, --help Ayuda.
|
|
@@ -122,6 +134,7 @@ const COMMANDS = {
|
|
|
122
134
|
diff: () => require('../src/commands/diff'),
|
|
123
135
|
review: () => require('../src/commands/review'),
|
|
124
136
|
export: () => require('../src/commands/export'),
|
|
137
|
+
deploys: () => require('../src/commands/deploys'),
|
|
125
138
|
};
|
|
126
139
|
|
|
127
140
|
async function main() {
|
|
@@ -133,6 +146,7 @@ async function main() {
|
|
|
133
146
|
booleans: [
|
|
134
147
|
'help', 'version', 'h', 'v', 'dry-run', 'yes', 'force', 'push', 'pull',
|
|
135
148
|
'json', 'short', 'stat', 'files', 'allow-secrets', 'no-index', 'verbose',
|
|
149
|
+
'allow-failing-checks',
|
|
136
150
|
'show-ignored', 'include-ignored', 'aprobado', 'reparos', 'rechazado',
|
|
137
151
|
],
|
|
138
152
|
aliases: { h: 'help', v: 'version', m: 'message' },
|
package/package.json
CHANGED
package/src/ShiloClient.js
CHANGED
|
@@ -791,6 +791,29 @@ class ShiloClient {
|
|
|
791
791
|
};
|
|
792
792
|
}
|
|
793
793
|
|
|
794
|
+
/**
|
|
795
|
+
* Destino e historial de deploys a Vercel (PLAN_DEPLOY_VERCEL). Devuelve
|
|
796
|
+
* `{ connected, can_manage, target, deployments, credentials }`. Un servidor
|
|
797
|
+
* sin deploys responde 404: se marca `notSupported`.
|
|
798
|
+
*/
|
|
799
|
+
async getDeploys(projectId, credentials) {
|
|
800
|
+
const url = `${this.baseURL}/api/deploys/${encodeURIComponent(projectId)}`;
|
|
801
|
+
const { res, data, credentials: creds } = await this._authFetch(url, credentials);
|
|
802
|
+
if (!res.ok) {
|
|
803
|
+
throw new ShiloError(
|
|
804
|
+
`No se pudieron leer los deploys: ${this._errMessage(data, `HTTP ${res.status}`)}`,
|
|
805
|
+
{ status: res.status, body: data, notSupported: res.status === 404 || res.status === 405 }
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
return {
|
|
809
|
+
connected: !!(data && data.connected),
|
|
810
|
+
can_manage: !!(data && data.can_manage),
|
|
811
|
+
target: (data && data.target) || null,
|
|
812
|
+
deployments: data && Array.isArray(data.deployments) ? data.deployments : [],
|
|
813
|
+
credentials: creds,
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
794
817
|
/**
|
|
795
818
|
* Propuestas (Etapa 3). Guardar es proponer: el cliente dice de qué versión
|
|
796
819
|
* partió (`base`, el oid de la versión actual que vio) y qué cambió, y el
|
|
@@ -818,6 +841,9 @@ class ShiloClient {
|
|
|
818
841
|
return {
|
|
819
842
|
main: (data && data.main) || null,
|
|
820
843
|
proposals: data && Array.isArray(data.proposals) ? data.proposals : [],
|
|
844
|
+
// Servidores anteriores no los mandan: quien llama tiene que tolerar null.
|
|
845
|
+
viewer: (data && data.viewer) || null,
|
|
846
|
+
project: (data && data.project) || null,
|
|
821
847
|
credentials: creds,
|
|
822
848
|
};
|
|
823
849
|
}
|
|
@@ -836,11 +862,16 @@ class ShiloClient {
|
|
|
836
862
|
* `[card: …]` del mensaje, que se queda porque es el formato de todo lo ya
|
|
837
863
|
* guardado; la clave es la que se puede consultar.
|
|
838
864
|
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
865
|
+
* `chequeos` es el resultado de los chequeos del proyecto (MU-93,
|
|
866
|
+
* `checks.js`): la propuesta lo lleva y una fallida no se aplica sola.
|
|
867
|
+
*
|
|
868
|
+
* Devuelve `{ applied, empty, proposal, conflicts, blockedBy, lineas,
|
|
869
|
+
* chequeos, piezas, main, message, warnings, credentials }`; `lineas` son las rutas
|
|
870
|
+
* que el servidor combinó por líneas (MU-92) y `piezas`, las piezas USEE
|
|
871
|
+
* que dejarían de pasar el Nivel 1 (MU-94). Los 200 y 202 son respuestas normales (aplicada /
|
|
841
872
|
* quedó abierta); cualquier otro estado es error.
|
|
842
873
|
*/
|
|
843
|
-
async propose(projectId, { base, title, summary, changes, agent, prompt, card }, credentials) {
|
|
874
|
+
async propose(projectId, { base, title, summary, changes, agent, prompt, card, chequeos }, credentials) {
|
|
844
875
|
const url = `${this.baseURL}/api/projects/proposals`;
|
|
845
876
|
const { res, data, credentials: creds } = await this._authFetch(url, credentials, {
|
|
846
877
|
method: 'POST',
|
|
@@ -853,6 +884,7 @@ class ShiloClient {
|
|
|
853
884
|
...(agent ? { agent } : {}),
|
|
854
885
|
...(prompt ? { prompt } : {}),
|
|
855
886
|
...(card ? { card } : {}),
|
|
887
|
+
...(chequeos ? { chequeos } : {}),
|
|
856
888
|
},
|
|
857
889
|
});
|
|
858
890
|
if (!res.ok && res.status !== 202) {
|
|
@@ -867,6 +899,9 @@ class ShiloClient {
|
|
|
867
899
|
proposal: (data && data.proposal) || null,
|
|
868
900
|
conflicts: data && Array.isArray(data.conflicts) ? data.conflicts : [],
|
|
869
901
|
blockedBy: data && Array.isArray(data.blocked_by) ? data.blocked_by : [],
|
|
902
|
+
lineas: data && Array.isArray(data.lineas) ? data.lineas : [],
|
|
903
|
+
chequeos: (data && data.chequeos) || null,
|
|
904
|
+
piezas: data && Array.isArray(data.piezas) ? data.piezas : [],
|
|
870
905
|
main: (data && data.main) || null,
|
|
871
906
|
message: (data && data.message) || '',
|
|
872
907
|
warnings: data && Array.isArray(data.warnings) ? data.warnings : [],
|
|
@@ -876,14 +911,16 @@ class ShiloClient {
|
|
|
876
911
|
|
|
877
912
|
/**
|
|
878
913
|
* Resuelve una propuesta abierta: `apply` (dueño) o `discard` (dueño o
|
|
879
|
-
* autor).
|
|
914
|
+
* autor). `resolutions` ({ ruta: 'actual' | 'propuesta' | 'ambas' }) decide
|
|
915
|
+
* los archivos en conflicto al aplicar; `force` aplica aunque sus chequeos
|
|
916
|
+
* o sus piezas USEE hayan fallado (MU-93, MU-94). Devuelve `{ applied, proposal, conflicts, main, credentials }`; con
|
|
880
917
|
* `apply` un 409 significa que sigue en conflicto y viene con los datos.
|
|
881
918
|
*/
|
|
882
|
-
async resolveProposal(projectId, id, action, credentials) {
|
|
919
|
+
async resolveProposal(projectId, id, action, credentials, { resolutions = null, force = false } = {}) {
|
|
883
920
|
const url = `${this.baseURL}/api/projects/proposals`;
|
|
884
921
|
const { res, data, credentials: creds } = await this._authFetch(url, credentials, {
|
|
885
922
|
method: 'POST',
|
|
886
|
-
body: { projectId, id, action },
|
|
923
|
+
body: { projectId, id, action, ...(resolutions ? { resolutions } : {}), ...(force ? { force: true } : {}) },
|
|
887
924
|
});
|
|
888
925
|
const stillOpen = action === 'apply' && res.status === 409 && data && data.proposal;
|
|
889
926
|
if (!res.ok && !stillOpen) {
|
|
@@ -896,6 +933,8 @@ class ShiloClient {
|
|
|
896
933
|
applied: !!(data && data.applied),
|
|
897
934
|
proposal: (data && data.proposal) || null,
|
|
898
935
|
conflicts: data && Array.isArray(data.conflicts) ? data.conflicts : [],
|
|
936
|
+
lineas: data && Array.isArray(data.lineas) ? data.lineas : [],
|
|
937
|
+
piezas: data && Array.isArray(data.piezas) ? data.piezas : [],
|
|
899
938
|
main: (data && data.main) || null,
|
|
900
939
|
message: (data && data.message) || '',
|
|
901
940
|
credentials: creds,
|
package/src/checks.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chequeos de propuesta (EP-S2, MU-93).
|
|
3
|
+
*
|
|
4
|
+
* Un proyecto declara en `shilo.config.json` los comandos que tienen que pasar
|
|
5
|
+
* antes de proponer (`"chequeos": ["npm test"]`). `shilo push` los corre en la
|
|
6
|
+
* carpeta, uno por uno y en orden, con la salida a la vista, y el resultado
|
|
7
|
+
* viaja con la propuesta: la web la muestra verificada o fallida, y aplicar una
|
|
8
|
+
* fallida exige forzar.
|
|
9
|
+
*
|
|
10
|
+
* Se corta en el primero que falla, como un hook de git: lo que viene después
|
|
11
|
+
* suele depender de lo anterior (instalar, compilar, probar) y correrlo sólo
|
|
12
|
+
* hace más largo el error. El resultado registra lo que se corrió.
|
|
13
|
+
*
|
|
14
|
+
* `run` se inyecta para probarlo sin lanzar procesos.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const childProcess = require('child_process');
|
|
20
|
+
|
|
21
|
+
// Un chequeo colgado no puede dejar colgado el push: a los diez minutos se corta
|
|
22
|
+
// y cuenta como fallido.
|
|
23
|
+
const CHECK_TIMEOUT_MS = 10 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
function spawnCheck(comando, dir) {
|
|
26
|
+
const r = childProcess.spawnSync(comando, {
|
|
27
|
+
cwd: dir,
|
|
28
|
+
shell: true,
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
timeout: CHECK_TIMEOUT_MS,
|
|
31
|
+
windowsHide: true,
|
|
32
|
+
});
|
|
33
|
+
// Sin `status` = no llegó a terminar (no se pudo lanzar, o se cortó por tiempo).
|
|
34
|
+
return { codigo: typeof r.status === 'number' ? r.status : null };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} dir - la carpeta donde se corren
|
|
39
|
+
* @param {string[]} comandos
|
|
40
|
+
* @param {{ run?: Function, now?: Function, onStart?: Function }} [opts]
|
|
41
|
+
* @returns {{ resultado: 'ok'|'fallido', comandos: Array<{ comando, ok, codigo?, ms }> }}
|
|
42
|
+
*/
|
|
43
|
+
function runChecks(dir, comandos, { run = spawnCheck, now = Date.now, onStart = () => {} } = {}) {
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const comando of comandos) {
|
|
46
|
+
onStart(comando, out.length + 1, comandos.length);
|
|
47
|
+
const t0 = now();
|
|
48
|
+
const { codigo } = run(comando, dir);
|
|
49
|
+
const entry = { comando, ok: codigo === 0, ms: Math.max(0, Math.round(now() - t0)) };
|
|
50
|
+
if (codigo !== null) entry.codigo = codigo;
|
|
51
|
+
out.push(entry);
|
|
52
|
+
if (!entry.ok) break;
|
|
53
|
+
}
|
|
54
|
+
return { resultado: out.every((c) => c.ok) ? 'ok' : 'fallido', comandos: out };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { runChecks, CHECK_TIMEOUT_MS };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const config = require('../config');
|
|
4
|
+
const ui = require('../ui');
|
|
5
|
+
const sync = require('../sync');
|
|
6
|
+
const { resolveContext } = require('../context');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_LIMIT = 10;
|
|
9
|
+
|
|
10
|
+
const STATUS = {
|
|
11
|
+
queued: (s) => ui.c.dim(s),
|
|
12
|
+
running: (s) => ui.c.cyan(s),
|
|
13
|
+
uploading: (s) => ui.c.cyan(s),
|
|
14
|
+
building: (s) => ui.c.cyan(s),
|
|
15
|
+
ready: (s) => ui.c.green(s),
|
|
16
|
+
error: (s) => ui.c.red(s),
|
|
17
|
+
canceled: (s) => ui.c.dim(s),
|
|
18
|
+
superseded: (s) => ui.c.dim(s),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const AUTO = {
|
|
22
|
+
off: 'sólo manual',
|
|
23
|
+
preview: 'preview en cada guardado',
|
|
24
|
+
production: 'producción en cada guardado',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* shilo deploys [-n <N>] [--project <uuid>] [--json]
|
|
29
|
+
*
|
|
30
|
+
* Destino e historial de deploys a Vercel del proyecto (PLAN_DEPLOY_VERCEL,
|
|
31
|
+
* Etapa 4.2). Sólo lectura. El servidor devuelve como mucho los últimos 20.
|
|
32
|
+
*/
|
|
33
|
+
async function deploys(args) {
|
|
34
|
+
const ctx = resolveContext({ ...args, _: [] });
|
|
35
|
+
if (!ctx.ok) return ctx.code;
|
|
36
|
+
|
|
37
|
+
const { projectId, baseURL, cfg, client, mgr } = ctx;
|
|
38
|
+
|
|
39
|
+
const limit = parseLimit(args.n || args.number);
|
|
40
|
+
if (limit === null) {
|
|
41
|
+
ui.error('El valor de -n debe ser un entero entre 1 y 20.');
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let info;
|
|
46
|
+
try {
|
|
47
|
+
info = await client.getDeploys(projectId, await mgr.valid());
|
|
48
|
+
} catch (e) {
|
|
49
|
+
ui.error(e.notSupported ? 'Este servidor de Shilo todavía no tiene deploys.' : e.message);
|
|
50
|
+
return 1;
|
|
51
|
+
} finally {
|
|
52
|
+
config.persistCredsIfChanged(cfg, mgr.current);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const list = info.deployments.slice(0, limit);
|
|
56
|
+
|
|
57
|
+
if (args.json) {
|
|
58
|
+
console.log(JSON.stringify({ projectId, connected: info.connected, target: info.target, deployments: list }, null, 2));
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ui.info(`Deploys del proyecto — ${ui.c.cyan(baseURL)}`);
|
|
63
|
+
if (!info.target) {
|
|
64
|
+
ui.info('');
|
|
65
|
+
ui.info('Este proyecto no despliega a ningún lado. El owner lo configura en la');
|
|
66
|
+
ui.info('pestaña "Deploys" del proyecto en la web.');
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const t = info.target;
|
|
71
|
+
ui.info(`Vercel: ${ui.c.bold(t.vercel_project_name || t.vercel_project)} · ${AUTO[t.auto_target] || t.auto_target}`);
|
|
72
|
+
if (!info.connected) {
|
|
73
|
+
ui.warn('La conexión con Vercel no está activa: el owner tiene que volver a conectar.');
|
|
74
|
+
}
|
|
75
|
+
ui.info('');
|
|
76
|
+
|
|
77
|
+
if (list.length === 0) {
|
|
78
|
+
ui.info('Todavía no hubo deploys.');
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const d of list) {
|
|
83
|
+
const paint = STATUS[d.status] || ((s) => s);
|
|
84
|
+
const commit = d.commit_oid ? String(d.commit_oid).slice(0, 8) : '--------';
|
|
85
|
+
const target = d.target === 'production' ? ui.c.bold('prod ') : 'preview';
|
|
86
|
+
const card = d.card ? ui.c.dim(` [card: ${d.card}]`) : '';
|
|
87
|
+
ui.info(`${paint(pad(d.status, 10))} ${target} ${ui.c.yellow(commit)} ${ui.c.dim(sync.formatDate(d.created_at))}${card}`);
|
|
88
|
+
if (d.url) ui.info(` ${d.url}`);
|
|
89
|
+
if (d.error) ui.info(` ${ui.c.red(d.error)}`);
|
|
90
|
+
}
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function pad(s, n) {
|
|
95
|
+
return String(s).padEnd(n);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseLimit(raw) {
|
|
99
|
+
if (raw === undefined || raw === null || raw === '') return DEFAULT_LIMIT;
|
|
100
|
+
const n = Number(raw);
|
|
101
|
+
if (!Number.isInteger(n) || n < 1 || n > 20) return null;
|
|
102
|
+
return n;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = deploys;
|
package/src/commands/diff.js
CHANGED
|
@@ -126,7 +126,7 @@ async function diffAll({ dir, projectId, baseURL, cfg, client, mgr }) {
|
|
|
126
126
|
config.persistCredsIfChanged(cfg, mgr.current);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
const cmp = sync.compareTrees(local.files, tree.files);
|
|
129
|
+
const cmp = sync.refineByHash(sync.compareTrees(local.files, tree.files), { dir, projectId, baseURL });
|
|
130
130
|
|
|
131
131
|
// Un archivo ignorado localmente no es una diferencia que haya que resolver:
|
|
132
132
|
// el push no lo sube y el pull no lo baja. Se nombra aparte, no como "sólo
|
package/src/commands/init.js
CHANGED
|
@@ -106,6 +106,14 @@ async function init(args) {
|
|
|
106
106
|
const written = remote.write(dir, { projectId, baseURL });
|
|
107
107
|
ui.success(`Carpeta atada al proyecto ${ui.c.bold(projectId)}.`);
|
|
108
108
|
ui.info(ui.c.dim(`Remote guardado en ${written}`));
|
|
109
|
+
// Se lee siempre (ver walk.js), pero no decirlo hacía que la gente copiara
|
|
110
|
+
// a mano cada línea del .gitignore al .shiloignore.
|
|
111
|
+
if (fs.existsSync(path.join(dir, '.gitignore'))) {
|
|
112
|
+
ui.info(
|
|
113
|
+
ui.c.dim('Se respeta tu .gitignore: lo que git ignora tampoco se sube. ') +
|
|
114
|
+
ui.c.dim('.shiloignore es sólo para exclusiones extra.')
|
|
115
|
+
);
|
|
116
|
+
}
|
|
109
117
|
|
|
110
118
|
// --push/--pull no implican confirmación: el usuario pidió *encadenar* el
|
|
111
119
|
// comando, no saltearse su preflight. Sin --yes, push muestra el plan (y el
|