@orkastery/cli 0.5.3 → 0.5.6
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/adapters/claude-code/README.md +158 -0
- package/adapters/claude-code/mods/autoridade.json +56 -0
- package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
- package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
- package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
- package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
- package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
- package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
- package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
- package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
- package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
- package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
- package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
- package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
- package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
- package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
- package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
- package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
- package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
- package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
- package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
- package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
- package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
- package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
- package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
- package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
- package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
- package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
- package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
- package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
- package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
- package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
- package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
- package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
- package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
- package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
- package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
- package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
- package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
- package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
- package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
- package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
- package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +1 -1
- package/adapters/openclaw/README.md +2 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
- package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
- package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
- package/assets/docs-en/produto/README.md +14 -0
- package/assets/docs-en/produto/_modelo-feature.md +62 -0
- package/assets/docs-en/roadmap/README.md +14 -0
- package/assets/docs-en/roadmap/_modelo-item.md +75 -0
- package/dist/adapters/claude-bg.js +77 -13
- package/dist/adapters/codex.js +10 -1
- package/dist/agents-md.js +24 -15
- package/dist/auditrun.js +17 -2
- package/dist/board.js +4 -3
- package/dist/branch-de-estado.js +23 -1
- package/dist/catalogo.js +33 -0
- package/dist/ci.js +13 -7
- package/dist/claims-conferir.js +342 -0
- package/dist/claims.js +3 -1
- package/dist/company-brain-client.js +36 -1
- package/dist/company-brain-journal.js +2 -1
- package/dist/company-brain-migration.js +36 -1
- package/dist/conducao-sentinela.js +388 -0
- package/dist/conducao-texto.js +12 -18
- package/dist/conducao.js +122 -30
- package/dist/conhecimento.js +490 -0
- package/dist/docs.js +22 -8
- package/dist/doctor-pulse-cron.js +182 -27
- package/dist/doctor-rede.js +18 -17
- package/dist/doctor.js +115 -111
- package/dist/entrega-pr.js +22 -21
- package/dist/escopo-escrita.js +10 -1
- package/dist/estado-thread.js +23 -1
- package/dist/experiencia-instalacao.js +45 -25
- package/dist/fabrica-batida.js +310 -0
- package/dist/fabrica-estado.js +38 -8
- package/dist/fabrica-publicar.js +3 -1
- package/dist/fila-pesada.js +444 -0
- package/dist/fix.js +3 -2
- package/dist/forja.js +1 -1
- package/dist/gates.js +3 -28
- package/dist/handoff.js +10 -6
- package/dist/hitl-local-receipt.js +2 -0
- package/dist/hitl-resumo.js +29 -3
- package/dist/hitl-tempo-parado.js +4 -4
- package/dist/hitl.js +29 -33
- package/dist/horario.js +14 -7
- package/dist/hosts.js +52 -1
- package/dist/impedimento.js +108 -14
- package/dist/index.js +623 -521
- package/dist/indice.js +7 -1
- package/dist/init.js +43 -47
- package/dist/instabilidade.js +144 -0
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +92 -21
- package/dist/intelligence-graph-contexto.js +56 -17
- package/dist/intelligence-graph-contract.js +2 -1
- package/dist/intelligence-graph-extract-md.js +56 -4
- package/dist/intelligence-graph-extract-ts.js +71 -3
- package/dist/intelligence-graph-query.js +66 -19
- package/dist/ledger.js +15 -1
- package/dist/liveness.js +6 -3
- package/dist/locale-ajuda.js +761 -0
- package/dist/locale-gerados.js +160 -0
- package/dist/locale-master.js +99 -0
- package/dist/locale-onboarding.js +92 -0
- package/dist/locale-phase.js +151 -0
- package/dist/locale-pulse.js +211 -0
- package/dist/locale-retry.js +385 -0
- package/dist/locale-roadmap.js +102 -0
- package/dist/locale-ship.js +494 -0
- package/dist/locale-thread.js +277 -0
- package/dist/locale-verify.js +125 -0
- package/dist/locale.js +714 -0
- package/dist/manifest.js +129 -3
- package/dist/maquina.js +23 -4
- package/dist/master-digest.js +6 -1
- package/dist/master-nota.js +4 -6
- package/dist/master.js +21 -24
- package/dist/mcp-artifacts.js +11 -2
- package/dist/mcp-git.js +23 -6
- package/dist/mcp-grafo.js +7 -4
- package/dist/mcp-server.js +8 -2
- package/dist/mcp-ship.js +3 -1
- package/dist/mcp-verify.js +6 -1
- package/dist/modos.js +3 -6
- package/dist/network-roadmap.js +5 -2
- package/dist/noctum-arco.js +911 -0
- package/dist/noctum-capacidade.js +141 -0
- package/dist/noctum-envelope.js +810 -0
- package/dist/noctum-execucao.js +304 -0
- package/dist/noctum-fila.js +280 -0
- package/dist/ocupacao.js +29 -2
- package/dist/onboarding.js +40 -21
- package/dist/orkmind.js +9 -2
- package/dist/orquestracao.js +13 -12
- package/dist/parado-no-condutor.js +34 -9
- package/dist/phase.js +94 -39
- package/dist/policies.js +4 -4
- package/dist/portfolio.js +4 -2
- package/dist/postura-local.js +234 -0
- package/dist/preflight.js +11 -10
- package/dist/procedencia.js +225 -0
- package/dist/projeto-alvo.js +127 -4
- package/dist/prompts.js +6 -2
- package/dist/pulse-delivery.js +13 -2
- package/dist/pulse.js +52 -19
- package/dist/ratelimit.js +4 -2
- package/dist/redacao-saida.js +63 -0
- package/dist/rede-status.js +3 -2
- package/dist/retry.js +232 -148
- package/dist/roadmap-reservas.js +20 -3
- package/dist/roadmap-status.js +129 -33
- package/dist/runtime-context.js +181 -3
- package/dist/runtimes.js +2 -1
- package/dist/session-events.js +7 -3
- package/dist/session-watcher-claude.js +7 -1
- package/dist/session-watcher.js +185 -33
- package/dist/sessoes-contas.js +1 -1
- package/dist/ship-publicar.js +800 -0
- package/dist/ship.js +84 -81
- package/dist/steal.js +77 -0
- package/dist/thread-close.js +3 -2
- package/dist/thread.js +153 -82
- package/dist/util.js +8 -1
- package/dist/verify.js +141 -52
- package/dist/vida-da-thread.js +254 -0
- package/dist/worktree-local.js +165 -0
- package/dist/worktree.js +20 -0
- package/dist/yaml.js +27 -8
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +2 -1
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ETAPAS = exports.MODOS_QUE_PUBLICAM = exports.CONTRATO_DA_PISTA = exports.CONTRATO_DA_PUBLICACAO = void 0;
|
|
37
|
+
exports.tituloDoPr = tituloDoPr;
|
|
38
|
+
exports.entreAspas = entreAspas;
|
|
39
|
+
exports.comandoDoDono = comandoDoDono;
|
|
40
|
+
exports.publicarEntrega = publicarEntrega;
|
|
41
|
+
exports.linhasDaPublicacao = linhasDaPublicacao;
|
|
42
|
+
exports.pistaDeEntrega = pistaDeEntrega;
|
|
43
|
+
/**
|
|
44
|
+
* RM-059 (fatia F7, T7.1; DD4 do plano dos mods): a publicacao provada da branch e do PR, e a pista da entrega.
|
|
45
|
+
*
|
|
46
|
+
* Ate aqui, quem publicava a branch de uma thread #Auto e abria o PR era o script do condutor externo, fora do
|
|
47
|
+
* nucleo e sem prova pelo remoto. O `ork ship publicar` faz o mesmo pelo nucleo, e so no modo que ja autoriza o
|
|
48
|
+
* push pela #TAG (#Auto e #Maestro, a mesma lista do `ork ship`):
|
|
49
|
+
*
|
|
50
|
+
* 1. confere, antes de qualquer efeito, a thread, o modo, a conducao, a branch, o CHECK, o verify e o bundle no
|
|
51
|
+
* HEAD, o remoto, a ponta da branch nele e os PRs da branch; a recusa volta tipada e nada vai ao ledger (D2 da
|
|
52
|
+
* thread ork-rm059pista);
|
|
53
|
+
* 2. empurra sem forca o sha conferido, e nao a ref, com refspec explicito, e prova pelo `git ls-remote` (a prova do
|
|
54
|
+
* `ork ship`);
|
|
55
|
+
* 3. reaproveita o PR aberto da branch ou abre um com o texto do `pr-body.md` da thread, e prova pela releitura da
|
|
56
|
+
* lista;
|
|
57
|
+
* 4. grava `ship_published` com a prova, so quando houve efeito; o push sem prova tambem vai. Repetido, ou com o PR
|
|
58
|
+
* ja mesclado no HEAD, devolve `ja-publicada`.
|
|
59
|
+
*
|
|
60
|
+
* A pista (`ork ship pista`) e a leitura que a Pista de Entrega do Claude Code desenha: por thread aberta que ja
|
|
61
|
+
* esta na entrega, as etapas do CHECK ao `ship_done` e o proximo passo que o nucleo declara disponivel. Ela nao
|
|
62
|
+
* grava nada: uma leitura do remoto (`git ls-remote --heads`) e uma da forja (`lerPrsDaForja`) por chamada.
|
|
63
|
+
*
|
|
64
|
+
* Merge, tag e promocao de site continuam fora daqui: o merge e do dono ou do condutor, e o `ship_done` do PR e do
|
|
65
|
+
* `ork ship registrar-pr`.
|
|
66
|
+
*/
|
|
67
|
+
const fs = __importStar(require("node:fs"));
|
|
68
|
+
const path = __importStar(require("node:path"));
|
|
69
|
+
const branch_de_estado_1 = require("./branch-de-estado");
|
|
70
|
+
const ci_1 = require("./ci");
|
|
71
|
+
const hitl_1 = require("./hitl");
|
|
72
|
+
const claims_1 = require("./claims");
|
|
73
|
+
const ledger_1 = require("./ledger");
|
|
74
|
+
const manifest_1 = require("./manifest");
|
|
75
|
+
const modos_1 = require("./modos");
|
|
76
|
+
const parado_no_condutor_1 = require("./parado-no-condutor");
|
|
77
|
+
const entrega_pr_1 = require("./entrega-pr");
|
|
78
|
+
const ship_1 = require("./ship");
|
|
79
|
+
const session_watcher_claude_1 = require("./session-watcher-claude");
|
|
80
|
+
const thread_1 = require("./thread");
|
|
81
|
+
const util_1 = require("./util");
|
|
82
|
+
const verify_1 = require("./verify");
|
|
83
|
+
const worktree_1 = require("./worktree");
|
|
84
|
+
const locale_1 = require("./locale");
|
|
85
|
+
exports.CONTRATO_DA_PUBLICACAO = 'ork.ship-publicar/v1';
|
|
86
|
+
exports.CONTRATO_DA_PISTA = 'ork.ship-pista/v1';
|
|
87
|
+
/** Os modos em que a #TAG ja autoriza o push: a mesma lista do `ork ship` (`autorizacaoDePush`); o `publicar` so age neles. */
|
|
88
|
+
exports.MODOS_QUE_PUBLICAM = ship_1.MODOS_QUE_AUTORIZAM_PUSH;
|
|
89
|
+
const SHA = /^[0-9a-f]{40}$/;
|
|
90
|
+
/** O `pr-body.md` maior que isto nao e corpo de PR: o `publicar` usa o corpo padrao. */
|
|
91
|
+
const TETO_DO_CORPO = 64 * 1024;
|
|
92
|
+
/**
|
|
93
|
+
* Prazos de cada chamada de rede. No pior caso, ls-remote, lista de PRs, push, ls-remote, criacao e releitura somam
|
|
94
|
+
* 435 s, abaixo dos 10 min que o motor do Claude Code da a um processo de mod (a Pista espera 9 min pelo publicar).
|
|
95
|
+
*/
|
|
96
|
+
const PRAZO_DO_GH_MS = 45000;
|
|
97
|
+
const PRAZO_DO_PUSH_MS = 180000;
|
|
98
|
+
const PRAZO_DO_LS_REMOTE_MS = 60000;
|
|
99
|
+
/** Os campos do `gh pr list` de uma branch: os do `prDaForja`, com o estado de merge do PR aberto. */
|
|
100
|
+
const CAMPOS_DA_BRANCH = 'number,state,headRefName,headRefOid,baseRefName,isDraft,isCrossRepository,url,createdAt,mergedAt,mergeable';
|
|
101
|
+
/** Git local sem rede escondida nem pergunta de credencial no terminal. */
|
|
102
|
+
function git(raiz, args, prazoMs = 60000) {
|
|
103
|
+
return (0, util_1.exec)('git', args, raiz, prazoMs, { ...process.env, GIT_NO_LAZY_FETCH: '1', GIT_TERMINAL_PROMPT: '0' });
|
|
104
|
+
}
|
|
105
|
+
const curto = (sha) => (sha ? sha.slice(0, 7) : '-');
|
|
106
|
+
/** Texto curto: uma linha, sem controle, com teto. */
|
|
107
|
+
function umaLinha(texto, teto = 200) {
|
|
108
|
+
return Array.from(texto.replace(/\p{Cc}/gu, ' ').replace(/\s+/g, ' ').trim()).slice(0, teto).join('') || '-';
|
|
109
|
+
}
|
|
110
|
+
/** Texto de fora (stderr do git ou do gh): sem segredo, numa linha e com teto, antes de ir ao `--json` e ao ledger. */
|
|
111
|
+
function deFora(texto, teto = 200) {
|
|
112
|
+
return umaLinha((0, hitl_1.redigirSegredos)(texto), teto);
|
|
113
|
+
}
|
|
114
|
+
exports.ETAPAS = ['check', 'verify', 'bundle', 'branch', 'pr', 'ci', 'conflito', 'entrega'];
|
|
115
|
+
const etapa = (id, estado, texto) => ({ id, estado, texto });
|
|
116
|
+
/** Os caminhos mudados so cabem no bundle (`.ork-ci/`)? Lista vazia tambem nao e produto. */
|
|
117
|
+
function soOBundle(caminhos) {
|
|
118
|
+
return caminhos.split('\n').map((l) => l.trim()).filter(Boolean).every((l) => l.startsWith('.ork-ci/'));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* O ultimo commit de produto da branch, com a hora do commit. Merge nao conta, nem o commit que so mexe em `.ork-ci/`
|
|
122
|
+
* (o bundle): decide o conteudo do commit, e nao o assunto, porque `ci(` tambem e o tipo de commit de workflow.
|
|
123
|
+
*/
|
|
124
|
+
function ultimoCommitDeProduto(raiz, baseRef, head) {
|
|
125
|
+
const r = git(raiz, ['log', '--no-merges', '--format=%H%x09%ct', baseRef ? `${baseRef}..${head}` : head, '-n', '200']);
|
|
126
|
+
if (!r.ok)
|
|
127
|
+
return null;
|
|
128
|
+
for (const linha of r.stdout.split('\n')) {
|
|
129
|
+
const [sha, ct] = linha.split('\t');
|
|
130
|
+
if (!sha || !SHA.test(sha))
|
|
131
|
+
continue;
|
|
132
|
+
const mudou = git(raiz, ['diff-tree', '--no-commit-id', '--name-only', '-r', '--root', sha]);
|
|
133
|
+
if (mudou.ok && soOBundle(mudou.stdout))
|
|
134
|
+
continue;
|
|
135
|
+
const em = Number(ct) * 1000;
|
|
136
|
+
if (Number.isFinite(em))
|
|
137
|
+
return { sha, em };
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
/** O CHECK: exatamente um veredito, PASSOU, gravado depois do ultimo commit de produto. */
|
|
142
|
+
function conferirCheck(raiz, t, baseRef, head) {
|
|
143
|
+
if (!(t.fases ?? []).includes('CHECK')) {
|
|
144
|
+
return { etapa: etapa('check', 'nao-se-aplica', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.checkSemCiclo((0, modos_1.tagDoModo)(t.modo)))), passou: true };
|
|
145
|
+
}
|
|
146
|
+
const artefato = (0, session_watcher_claude_1.estadoDoArtefato)(raiz, t.id, 'docs/check.md');
|
|
147
|
+
if (artefato.texto === null || artefato.mtimeMs === null) {
|
|
148
|
+
return { etapa: etapa('check', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.checkAusente)), passou: false };
|
|
149
|
+
}
|
|
150
|
+
const veredito = (0, session_watcher_claude_1.vereditoDoParecer)(artefato.texto);
|
|
151
|
+
if (veredito === null)
|
|
152
|
+
return { etapa: etapa('check', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.checkSemVeredito)), passou: false };
|
|
153
|
+
if (veredito !== 'PASSOU') {
|
|
154
|
+
return { etapa: etapa('check', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.checkOutroVeredito(veredito))), passou: false };
|
|
155
|
+
}
|
|
156
|
+
const ultimo = head ? ultimoCommitDeProduto(raiz, baseRef, head) : null;
|
|
157
|
+
if (ultimo && artefato.mtimeMs < ultimo.em) {
|
|
158
|
+
return { etapa: etapa('check', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.checkVelho(curto(ultimo.sha)))), passou: true };
|
|
159
|
+
}
|
|
160
|
+
return { etapa: etapa('check', 'feita', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.checkFeito)), passou: true };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* O verify: o ultimo `verify_run` passou, com os comandos do manifesto (`esperados`, o build e o test), no HEAD ou num
|
|
164
|
+
* ancestral dele com so `.ork-ci/` mudando depois.
|
|
165
|
+
*/
|
|
166
|
+
function conferirVerify(raiz, eventos, head, esperados) {
|
|
167
|
+
const ultimo = [...eventos].reverse().find((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.verificacao);
|
|
168
|
+
if (!ultimo)
|
|
169
|
+
return etapa('verify', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifyAusente));
|
|
170
|
+
const commit = typeof ultimo.commit === 'string' && SHA.test(ultimo.commit) ? ultimo.commit : null;
|
|
171
|
+
if (ultimo.veredito !== 'verdade sustentada') {
|
|
172
|
+
const motivos = Array.isArray(ultimo.motivos) ? ultimo.motivos.map(String).join(', ') : '';
|
|
173
|
+
return etapa('verify', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifyReprovou(curto(commit), motivos || '-')));
|
|
174
|
+
}
|
|
175
|
+
// S2 da revisao: o `ork verify --so-claims` nao roda o build nem o test do manifesto; nao vale como verify do HEAD.
|
|
176
|
+
if (esperados > 0 && (!Array.isArray(ultimo.comandos) || ultimo.comandos.length === 0)) {
|
|
177
|
+
return etapa('verify', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifySoClaims(curto(commit))));
|
|
178
|
+
}
|
|
179
|
+
if (!commit || !head)
|
|
180
|
+
return etapa('verify', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifyAntigo(curto(commit))));
|
|
181
|
+
if (commit === head)
|
|
182
|
+
return etapa('verify', 'feita', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifyNoHead(curto(head))));
|
|
183
|
+
const ancestral = git(raiz, ['merge-base', '--is-ancestor', commit, head]).ok;
|
|
184
|
+
const diff = ancestral ? git(raiz, ['diff', '--name-only', '--no-renames', commit, head]) : null;
|
|
185
|
+
const soBundle = !!diff?.ok && soOBundle(diff.stdout);
|
|
186
|
+
return soBundle ? etapa('verify', 'feita', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifySoBundle(curto(commit))))
|
|
187
|
+
: etapa('verify', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.verifyAntigo(curto(commit))));
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* O bundle no HEAD: desta thread, desta branch, com exatamente as claims ativas (sem claim retirada nem faltando), o
|
|
191
|
+
* comando de cada uma como esta agora e os comandos do manifesto que o runner roda.
|
|
192
|
+
*/
|
|
193
|
+
function conferirBundle(raiz, t, branch, head, comandos) {
|
|
194
|
+
const arquivo = `.ork-ci/${t.id}.json`;
|
|
195
|
+
if (!head)
|
|
196
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleAusente(arquivo)));
|
|
197
|
+
const r = git(raiz, ['show', `${head}:${arquivo}`]);
|
|
198
|
+
if (!r.ok)
|
|
199
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleAusente(arquivo)));
|
|
200
|
+
let bundle;
|
|
201
|
+
try {
|
|
202
|
+
bundle = JSON.parse(r.stdout);
|
|
203
|
+
if (bundle.schema !== 'ork.ci-bundle/v1' || bundle.thread !== t.id || !Array.isArray(bundle.claims))
|
|
204
|
+
throw new Error('formato');
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleInvalido(arquivo)));
|
|
208
|
+
}
|
|
209
|
+
if (typeof bundle.branch === 'string' && bundle.branch !== branch) {
|
|
210
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleOutraBranch(arquivo, String(bundle.branch))));
|
|
211
|
+
}
|
|
212
|
+
const noBundle = new Set([...bundle.claims.map((c) => c.id), ...(bundle.deferredClaims ?? []).map((c) => c.id)]);
|
|
213
|
+
let claimsAtivas = [];
|
|
214
|
+
try {
|
|
215
|
+
claimsAtivas = (0, claims_1.lerClaims)(raiz, t.id).filter((c) => c.estado !== 'retirada');
|
|
216
|
+
}
|
|
217
|
+
catch { /* sem claims: lista vazia */ }
|
|
218
|
+
const ativas = claimsAtivas.map((c) => c.id);
|
|
219
|
+
const faltam = ativas.filter((id) => !noBundle.has(id));
|
|
220
|
+
const sobram = [...noBundle].filter((id) => !ativas.includes(id));
|
|
221
|
+
if (faltam.length || sobram.length) {
|
|
222
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleVelho(faltam.join(', '), sobram.join(', '))));
|
|
223
|
+
}
|
|
224
|
+
// A14 da revisao (rodada 2): a claim que mudou de comando sem mudar de id (o `ork claims anexar`) deixa o bundle velho,
|
|
225
|
+
// e o runner rodaria o comando antigo; o mesmo vale para os comandos do manifesto que o bundle leva.
|
|
226
|
+
const comandoDe = new Map(claimsAtivas.map((c) => [c.id, JSON.stringify(c.verificar)]));
|
|
227
|
+
const mudaram = bundle.claims.filter((c) => comandoDe.has(c.id) && comandoDe.get(c.id) !== JSON.stringify(c.verificar)).map((c) => c.id);
|
|
228
|
+
if (mudaram.length)
|
|
229
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleMudou(mudaram.join(', '))));
|
|
230
|
+
if (JSON.stringify(bundle.commands ?? []) !== JSON.stringify(comandos)) {
|
|
231
|
+
return etapa('bundle', 'pendente', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleManifestoMudou));
|
|
232
|
+
}
|
|
233
|
+
return etapa('bundle', 'feita', (0, locale_1.textoDoCatalogo)((m) => m.ship.publicar.bundleFeito(ativas.length)));
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* O manifesto que vale para o verify e o bundle de uma branch: o do HEAD dela (A15 da revisao, rodada 3). O `ork verify`
|
|
237
|
+
* e o `ork ci prepare` rodam na worktree, com o `orkastery.yaml` da branch, e o runner do CI roda o codigo dela; quem
|
|
238
|
+
* chama o publicar (a Pista, o condutor) pode estar noutro checkout, com outro manifesto. Sem manifesto versionado no
|
|
239
|
+
* HEAD, ou ilegivel, vale o de quem chama.
|
|
240
|
+
*/
|
|
241
|
+
function manifestoDaBranch(carregado, head) {
|
|
242
|
+
if (!head)
|
|
243
|
+
return carregado;
|
|
244
|
+
for (const nome of [manifest_1.NOME_MANIFESTO, manifest_1.NOME_MANIFESTO_LEGADO]) {
|
|
245
|
+
const r = git(carregado.raiz, ['show', `${head}:./${nome}`]);
|
|
246
|
+
if (!r.ok)
|
|
247
|
+
continue;
|
|
248
|
+
const doHead = (0, manifest_1.comandosDoTextoDoManifesto)(r.stdout);
|
|
249
|
+
if (!doHead)
|
|
250
|
+
return carregado;
|
|
251
|
+
return { ...carregado, manifesto: { ...carregado.manifesto,
|
|
252
|
+
verify: { ...carregado.manifesto.verify, typecheck: doHead.typecheck, build: doHead.build, test: doHead.test },
|
|
253
|
+
ci: { ...carregado.manifesto.ci, command: doHead.ciCommand } } };
|
|
254
|
+
}
|
|
255
|
+
return carregado;
|
|
256
|
+
}
|
|
257
|
+
function fatosDaThread(carregado, t, refs, baseRef, quando) {
|
|
258
|
+
const raiz = carregado.raiz;
|
|
259
|
+
let eventos = [];
|
|
260
|
+
try {
|
|
261
|
+
eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, t.id));
|
|
262
|
+
}
|
|
263
|
+
catch { /* ledger ilegivel: sem verify nem conducao provada */ }
|
|
264
|
+
const branch = (0, worktree_1.branchDaWorktree)(t);
|
|
265
|
+
const head = (0, branch_de_estado_1.branchValida)(branch) ? refs.get(`refs/heads/${branch}`) ?? null : null;
|
|
266
|
+
const daBranch = manifestoDaBranch(carregado, head);
|
|
267
|
+
const esperados = (0, verify_1.comandosDoManifesto)(daBranch.manifesto).length;
|
|
268
|
+
const contagem = head && baseRef ? git(raiz, ['rev-list', '--count', `${baseRef}..${head}`]) : null;
|
|
269
|
+
const comProduto = !!contagem?.ok && Number(contagem.stdout.trim()) > 0;
|
|
270
|
+
const check = conferirCheck(raiz, t, baseRef, head);
|
|
271
|
+
return {
|
|
272
|
+
t, eventos, branch, head, comProduto, conducao: (0, parado_no_condutor_1.conducaoAgora)(raiz, t, eventos, quando),
|
|
273
|
+
check: check.etapa, checkPassou: check.passou, verify: conferirVerify(raiz, eventos, head, esperados),
|
|
274
|
+
bundle: conferirBundle(raiz, t, branch, head, (0, ci_1.comandosDoBundle)(daBranch)),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/** A base de comparacao: a copia do remoto primeiro (a base local desta maquina pode estar parada). */
|
|
278
|
+
function refDaBase(refs, remoto, base) {
|
|
279
|
+
return [`refs/remotes/${remoto}/${base}`, `refs/heads/${base}`].find((r) => refs.has(r)) ?? null;
|
|
280
|
+
}
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// O remoto e a forja.
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
/**
|
|
285
|
+
* A ponta da branch no remoto pelo `ls-remote`: `null` quando ela nao existe la; `erro` quando o git nao respondeu. Vale
|
|
286
|
+
* so a linha da ref exata: o padrao do `ls-remote` casa por sufixo (`refs/heads/a/refs/heads/<branch>` tambem casaria).
|
|
287
|
+
*/
|
|
288
|
+
function pontaNoRemoto(raiz, remoto, branch) {
|
|
289
|
+
const r = git(raiz, ['ls-remote', '--', remoto, `refs/heads/${branch}`], PRAZO_DO_LS_REMOTE_MS);
|
|
290
|
+
if (!r.ok)
|
|
291
|
+
return { sha: null, erro: deFora(r.stderr || r.stdout || `codigo ${r.code}`) };
|
|
292
|
+
const alvo = `refs/heads/${branch}`;
|
|
293
|
+
const linha = r.stdout.split('\n').map((l) => l.trim().split(/\s+/)).find((partes) => partes[1] === alvo);
|
|
294
|
+
const sha = linha?.[0] ?? '';
|
|
295
|
+
return { sha: SHA.test(sha) ? sha : null, erro: null };
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* O sha do remoto esta contido no HEAD local? Sem o objeto nesta copia, a resposta tambem e nao: se ele fosse ancestral
|
|
299
|
+
* do HEAD, a copia o teria (A3 da revisao). `null` so quando o git nao respondeu.
|
|
300
|
+
*/
|
|
301
|
+
function remotoContido(raiz, remota, head) {
|
|
302
|
+
if (!git(raiz, ['cat-file', '-e', `${remota}^{commit}`]).ok)
|
|
303
|
+
return false;
|
|
304
|
+
const r = git(raiz, ['merge-base', '--is-ancestor', remota, head]);
|
|
305
|
+
return r.code === 0 ? true : r.code === 1 ? false : null;
|
|
306
|
+
}
|
|
307
|
+
/** As pontas de todas as branches do remoto numa chamada so. */
|
|
308
|
+
function pontasNoRemoto(raiz, remoto) {
|
|
309
|
+
const r = git(raiz, ['ls-remote', '--heads', '--', remoto], PRAZO_DO_LS_REMOTE_MS);
|
|
310
|
+
if (!r.ok)
|
|
311
|
+
return null;
|
|
312
|
+
const mapa = new Map();
|
|
313
|
+
for (const linha of r.stdout.split('\n')) {
|
|
314
|
+
const [sha, ref] = linha.trim().split(/\s+/);
|
|
315
|
+
if (sha && ref && SHA.test(sha) && ref.startsWith('refs/heads/'))
|
|
316
|
+
mapa.set(ref.slice('refs/heads/'.length), sha);
|
|
317
|
+
}
|
|
318
|
+
return mapa;
|
|
319
|
+
}
|
|
320
|
+
/** Os PRs de uma branch, da base do projeto, do mais novo para o mais velho. */
|
|
321
|
+
function prsDaBranch(executor, forja, base, branch) {
|
|
322
|
+
const r = executor(['pr', 'list', `--repo=${forja.host}/${forja.repositorio}`, `--head=${branch}`, '--state=all', '--limit=10',
|
|
323
|
+
`--json=${CAMPOS_DA_BRANCH}`], PRAZO_DO_GH_MS);
|
|
324
|
+
if (r.status !== 0)
|
|
325
|
+
return { prs: [], erro: deFora(r.stderr || r.stdout || `codigo ${r.status ?? '-'}`) };
|
|
326
|
+
try {
|
|
327
|
+
const lista = JSON.parse(r.stdout);
|
|
328
|
+
if (!Array.isArray(lista))
|
|
329
|
+
throw new Error('a resposta nao e uma lista');
|
|
330
|
+
const prs = lista.map((p) => (0, parado_no_condutor_1.prDaForja)(p, base)).filter((p) => !!p && p.branch === branch);
|
|
331
|
+
return { prs: prs.sort((a, b) => b.numero - a.numero), erro: null };
|
|
332
|
+
}
|
|
333
|
+
catch (e) {
|
|
334
|
+
return { prs: [], erro: deFora(e.message) };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// O corpo e o titulo do PR.
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
/** O `pr-body.md` da thread, quando e arquivo comum e cabe no teto. */
|
|
341
|
+
function corpoDaThread(raiz, threadId) {
|
|
342
|
+
const arquivo = path.join((0, thread_1.dirThread)(raiz, threadId), 'pr-body.md');
|
|
343
|
+
try {
|
|
344
|
+
const st = fs.lstatSync(arquivo);
|
|
345
|
+
if (!st.isFile() || st.size === 0 || st.size > TETO_DO_CORPO)
|
|
346
|
+
return null;
|
|
347
|
+
const texto = fs.readFileSync(arquivo, 'utf8');
|
|
348
|
+
// O corpo vai ao gh como argumento (A5 da revisao): com NUL ele nao passa, e vale o corpo padrao.
|
|
349
|
+
return texto.includes('\0') ? null : { arquivo, texto };
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/** O titulo do PR: `ship(<thread>): ` e o primeiro cabecalho do corpo, ou o nome da thread. */
|
|
356
|
+
function tituloDoPr(t, corpo) {
|
|
357
|
+
let titulo = null;
|
|
358
|
+
for (const linha of (corpo ?? '').split('\n')) {
|
|
359
|
+
const l = linha.trim();
|
|
360
|
+
if (!l)
|
|
361
|
+
continue;
|
|
362
|
+
const m = /^#{1,6}\s+(.+)$/.exec(l);
|
|
363
|
+
titulo = m ? umaLinha(m[1], 120) : null;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
const prefixo = `ship(${t.id})`;
|
|
367
|
+
if (titulo?.startsWith(prefixo))
|
|
368
|
+
return titulo;
|
|
369
|
+
return `${prefixo}: ${titulo ?? umaLinha(t.nome || t.id, 120)}`;
|
|
370
|
+
}
|
|
371
|
+
/** Uma palavra do shell entre aspas simples: nada dentro dela vira comando, nem `;`, `$`, crase ou barra. */
|
|
372
|
+
function entreAspas(s) {
|
|
373
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* O que o dono roda no proprio terminal quando o modo da thread nao autoriza o push. A branch e a base vem do estado e do
|
|
377
|
+
* manifesto, que podem ser do clone: cada parte vai entre aspas simples (A4 da revisao).
|
|
378
|
+
*/
|
|
379
|
+
function comandoDoDono(remoto, base, branch, titulo, comCorpo) {
|
|
380
|
+
return `git push -u ${entreAspas(remoto)} ${entreAspas(branch)} && gh pr create --base ${entreAspas(base)} --head ${entreAspas(branch)} ` +
|
|
381
|
+
`--title ${entreAspas(titulo)} ${comCorpo ? `--body-file ${entreAspas(comCorpo)}` : '--fill'}`;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* `ork ship publicar <thread>`: publica a branch da thread e abre (ou reaproveita) o PR, com prova, so no modo que
|
|
385
|
+
* ja autoriza push. Nunca lanca por reprovacao: a recusa antes de qualquer efeito volta tipada e nada vai ao ledger; o
|
|
386
|
+
* push que aconteceu vai sempre ao ledger, provado ou nao (B1 da revisao).
|
|
387
|
+
*/
|
|
388
|
+
function publicarEntrega(carregado, threadId, opcoes = {}) {
|
|
389
|
+
const raiz = carregado.raiz, base = carregado.manifesto.worktree.base_branch;
|
|
390
|
+
const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? 'origin', 'ship');
|
|
391
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
392
|
+
const dryRun = opcoes.dryRun === true;
|
|
393
|
+
const executor = opcoes.executorGh ?? parado_no_condutor_1.ghPadrao;
|
|
394
|
+
const p = (m) => m.ship.publicar;
|
|
395
|
+
const r = {
|
|
396
|
+
contrato: exports.CONTRATO_DA_PUBLICACAO, thread: threadId, modo: null, tag: null, acao: 'recusada', motivo: null,
|
|
397
|
+
detalhe: '', correcao: '', branch: null, base, remoto, head: null, remotaAntes: null, remota: null, pushVerificado: false,
|
|
398
|
+
pr: null, corpo: null, passos: [], dryRun,
|
|
399
|
+
};
|
|
400
|
+
const recusar = (motivo, detalhe, correcao) => ({ ...r, acao: 'recusada', motivo, detalhe, correcao });
|
|
401
|
+
// 1. A thread que existe e esta aberta. S3 da revisao: a que nao se le tambem volta como recusa tipada, no contrato.
|
|
402
|
+
let lida;
|
|
403
|
+
try {
|
|
404
|
+
lida = (0, thread_1.lerThread)(raiz, threadId);
|
|
405
|
+
}
|
|
406
|
+
catch (e) {
|
|
407
|
+
return recusar('artifact.missing', (0, locale_1.textoDoCatalogo)((m) => p(m).threadInexistente(umaLinha(threadId, 80), deFora(e.message))), (0, locale_1.textoDoCatalogo)((m) => p(m).threadInexistenteCorrecao));
|
|
408
|
+
}
|
|
409
|
+
const t = lida;
|
|
410
|
+
r.thread = t.id;
|
|
411
|
+
r.modo = t.modo;
|
|
412
|
+
r.tag = (0, modos_1.tagDoModo)(t.modo);
|
|
413
|
+
if (t.status === 'fechada') {
|
|
414
|
+
return recusar('artifact.missing', (0, locale_1.textoDoCatalogo)((m) => p(m).threadFechada(t.id)), (0, locale_1.textoDoCatalogo)((m) => p(m).threadFechadaCorrecao));
|
|
415
|
+
}
|
|
416
|
+
const refs = (0, parado_no_condutor_1.refsLocais)(raiz, remoto);
|
|
417
|
+
const baseRef = refDaBase(refs, remoto, base);
|
|
418
|
+
const f = fatosDaThread(carregado, t, refs, baseRef, quando);
|
|
419
|
+
r.branch = f.branch;
|
|
420
|
+
r.head = f.head;
|
|
421
|
+
const corpo = corpoDaThread(raiz, t.id);
|
|
422
|
+
// 2. O modo que ja autoriza push (D1): fora dele, o push e do dono, e a correcao diz o comando dele.
|
|
423
|
+
if (!exports.MODOS_QUE_PUBLICAM.includes(t.modo)) {
|
|
424
|
+
const comando = comandoDoDono(remoto, base, f.branch, tituloDoPr(t, corpo?.texto ?? null), corpo ? path.relative(raiz, corpo.arquivo) : null);
|
|
425
|
+
return recusar('human.pending', (0, locale_1.textoDoCatalogo)((m) => p(m).modoSemPush((0, modos_1.tagDoModo)(t.modo))), (0, locale_1.textoDoCatalogo)((m) => p(m).modoSemPushCorrecao(comando)));
|
|
426
|
+
}
|
|
427
|
+
// 3. Ninguem conduz a thread agora: a branch pode mudar no meio da publicacao.
|
|
428
|
+
if (f.conducao) {
|
|
429
|
+
const sessao = f.conducao.sessao ? f.conducao.sessao.slice(0, 8) : f.conducao.canal;
|
|
430
|
+
return recusar('conducao.em-andamento', (0, locale_1.textoDoCatalogo)((m) => p(m).conduzida(sessao, f.conducao?.fase ?? '-')), (0, locale_1.textoDoCatalogo)((m) => p(m).conduzidaCorrecao(t.id)));
|
|
431
|
+
}
|
|
432
|
+
// 4. A branch com produto, que nao e a base.
|
|
433
|
+
if (!(0, branch_de_estado_1.branchValida)(f.branch) || !f.head) {
|
|
434
|
+
return recusar('artifact.missing', (0, locale_1.textoDoCatalogo)((m) => p(m).semBranch(f.branch)), (0, locale_1.textoDoCatalogo)((m) => p(m).semBranchCorrecao(t.id)));
|
|
435
|
+
}
|
|
436
|
+
const head = f.head;
|
|
437
|
+
if (f.branch === base) {
|
|
438
|
+
return recusar('policy.violation', (0, locale_1.textoDoCatalogo)((m) => p(m).branchEBase(base)), (0, locale_1.textoDoCatalogo)((m) => p(m).branchEBaseCorrecao));
|
|
439
|
+
}
|
|
440
|
+
if (!f.comProduto) {
|
|
441
|
+
return recusar('artifact.missing', (0, locale_1.textoDoCatalogo)((m) => p(m).semProduto(f.branch, base)), (0, locale_1.textoDoCatalogo)((m) => p(m).semProdutoCorrecao));
|
|
442
|
+
}
|
|
443
|
+
// 5 a 7. O CHECK, o verify e o bundle no HEAD.
|
|
444
|
+
if (f.check.estado === 'pendente')
|
|
445
|
+
return recusar('artifact.missing', f.check.texto, (0, locale_1.textoDoCatalogo)((m) => p(m).checkCorrecao(t.id)));
|
|
446
|
+
if (f.verify.estado === 'pendente')
|
|
447
|
+
return recusar('verify.failed', f.verify.texto, (0, locale_1.textoDoCatalogo)((m) => p(m).verifyCorrecao(t.id)));
|
|
448
|
+
if (f.bundle.estado === 'pendente')
|
|
449
|
+
return recusar('artifact.missing', f.bundle.texto, (0, locale_1.textoDoCatalogo)((m) => p(m).bundleCorrecao(t.id)));
|
|
450
|
+
// 8. O remoto e a forja. S3 da revisao: sem o remoto, a publicacao nao tem para onde ir (runtime.unavailable).
|
|
451
|
+
if (!git(raiz, ['remote', 'get-url', '--', remoto]).ok) {
|
|
452
|
+
return recusar('runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).semRemoto(remoto)), (0, locale_1.textoDoCatalogo)((m) => p(m).semRemotoCorrecao));
|
|
453
|
+
}
|
|
454
|
+
const forja = opcoes.forja ?? (0, parado_no_condutor_1.githubDoRemoto)(raiz, remoto);
|
|
455
|
+
if (!forja)
|
|
456
|
+
return recusar('runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).semForja(remoto)), (0, locale_1.textoDoCatalogo)((m) => p(m).semForjaCorrecao));
|
|
457
|
+
// 9. A ponta no remoto e os PRs da branch, antes de qualquer efeito.
|
|
458
|
+
const provaDoPush = `git ls-remote ${remoto} refs/heads/${f.branch}`;
|
|
459
|
+
const antes = pontaNoRemoto(raiz, remoto, f.branch);
|
|
460
|
+
r.passos.push(provaDoPush);
|
|
461
|
+
if (antes.erro !== null) {
|
|
462
|
+
return recusar('runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).lsRemoteFalhou(remoto, antes.erro ?? '-')), (0, locale_1.textoDoCatalogo)((m) => p(m).redeCorrecao));
|
|
463
|
+
}
|
|
464
|
+
r.remotaAntes = antes.sha;
|
|
465
|
+
r.remota = antes.sha;
|
|
466
|
+
const lidos = prsDaBranch(executor, forja, base, f.branch);
|
|
467
|
+
r.passos.push(`gh pr list --head ${f.branch} --state all`);
|
|
468
|
+
if (lidos.erro !== null) {
|
|
469
|
+
return recusar('runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).prsFalharam(lidos.erro ?? '-')), (0, locale_1.textoDoCatalogo)((m) => p(m).redeCorrecao));
|
|
470
|
+
}
|
|
471
|
+
const aberto = lidos.prs.find((x) => x.estado === 'aberto') ?? null;
|
|
472
|
+
const mesclado = lidos.prs.find((x) => x.estado === 'mesclado' && x.head === head) ?? null;
|
|
473
|
+
const noRemoto = antes.sha === head;
|
|
474
|
+
r.pushVerificado = noRemoto;
|
|
475
|
+
// A2 da revisao: o PR mesclado no HEAD ja e a entrega, com ou sem a branch no remoto (o GitHub pode te-la apagado).
|
|
476
|
+
if (mesclado && !aberto) {
|
|
477
|
+
return { ...r, acao: 'ja-publicada', pr: { numero: mesclado.numero, url: mesclado.url, criado: false, head: mesclado.head },
|
|
478
|
+
detalhe: (0, locale_1.textoDoCatalogo)((m) => p(m).jaMesclado(mesclado.numero)), correcao: (0, locale_1.textoDoCatalogo)((m) => p(m).registreAEntrega(t.id)) };
|
|
479
|
+
}
|
|
480
|
+
if (noRemoto && aberto) {
|
|
481
|
+
return { ...r, acao: 'ja-publicada', pr: { numero: aberto.numero, url: aberto.url, criado: false, head: aberto.head },
|
|
482
|
+
detalhe: (0, locale_1.textoDoCatalogo)((m) => p(m).jaPublicada(aberto.numero)) };
|
|
483
|
+
}
|
|
484
|
+
// O remoto com commit que a branch local nao tem, ou que esta copia nem conhece (A3 da revisao): o push sem forca
|
|
485
|
+
// seria recusado. Nunca forcar.
|
|
486
|
+
if (antes.sha && !noRemoto && remotoContido(raiz, antes.sha, head) === false) {
|
|
487
|
+
return recusar('tree.blocked', (0, locale_1.textoDoCatalogo)((m) => p(m).remotoDivergiu(remoto, f.branch, curto(antes.sha))), (0, locale_1.textoDoCatalogo)((m) => p(m).remotoDivergiuCorrecao(remoto, f.branch)));
|
|
488
|
+
}
|
|
489
|
+
r.corpo = corpo ? 'pr-body.md' : 'padrao';
|
|
490
|
+
const titulo = tituloDoPr(t, corpo?.texto ?? null);
|
|
491
|
+
// A5 da revisao: o corpo vai ao gh como texto, o mesmo que foi lido agora; o gh nunca reabre o arquivo.
|
|
492
|
+
const textoDoCorpo = corpo?.texto ?? (0, locale_1.textoDoCatalogo)((m) => p(m).corpoPadrao((0, modos_1.tagDoModo)(t.modo), f.branch, curto(head), carregado.manifesto.ci.context || 'ork-verify'));
|
|
493
|
+
// B1 da revisao: o push leva o sha conferido, e nao a ref: o commit que chegue a branch depois das conferencias fica
|
|
494
|
+
// fora desta publicacao.
|
|
495
|
+
const refspec = `${head}:refs/heads/${f.branch}`;
|
|
496
|
+
if (dryRun) {
|
|
497
|
+
const passos = [...r.passos];
|
|
498
|
+
if (!noRemoto)
|
|
499
|
+
passos.push(`git push -- ${remoto} ${refspec}`, provaDoPush);
|
|
500
|
+
passos.push(aberto ? (0, locale_1.textoDoCatalogo)((m) => p(m).passoReaproveita(aberto.numero))
|
|
501
|
+
: `gh pr create --base ${base} --head ${f.branch} --title "${titulo}" --body <${corpo ? 'pr-body.md' : 'padrao'}>`);
|
|
502
|
+
return { ...r, acao: 'publicaria', passos, pr: aberto ? { numero: aberto.numero, url: aberto.url, criado: false, head: aberto.head } : null,
|
|
503
|
+
detalhe: (0, locale_1.textoDoCatalogo)((m) => p(m).publicaria(curto(head), aberto?.numero ?? null)) };
|
|
504
|
+
}
|
|
505
|
+
// A evidencia de cada efeito, com a prova ou a falta dela.
|
|
506
|
+
let pushFeito = false;
|
|
507
|
+
const gravar = (extra) => (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, t.id), t.id, ledger_1.TIPOS_DE_EVENTO.shipPublicado, {
|
|
508
|
+
branch: f.branch, base, remoto, head, remotaAntes: r.remotaAntes, shaRemoto: r.remota, pushFeito, pushVerificado: r.pushVerificado,
|
|
509
|
+
fonteDaProva: provaDoPush, corpo: r.corpo, modo: t.modo, tag: (0, modos_1.tagDoModo)(t.modo),
|
|
510
|
+
autorizadoPor: (0, locale_1.textoDoCatalogo)((m) => m.ship.porTag((0, modos_1.tagDoModo)(t.modo))), tipoDeAutorizacao: 'modo', passos: r.passos, ...extra,
|
|
511
|
+
});
|
|
512
|
+
// Efeito 1: o push sem forca do sha conferido, provado pelo ls-remote.
|
|
513
|
+
if (!noRemoto) {
|
|
514
|
+
const argv = ['push', '--', remoto, refspec];
|
|
515
|
+
const push = (opcoes.executorGit ?? ((args, prazoMs) => git(raiz, args, prazoMs)))(argv, PRAZO_DO_PUSH_MS);
|
|
516
|
+
r.passos.push(`git ${argv.join(' ')}`);
|
|
517
|
+
const depois = pontaNoRemoto(raiz, remoto, f.branch);
|
|
518
|
+
r.passos.push(provaDoPush);
|
|
519
|
+
r.remota = depois.sha;
|
|
520
|
+
r.pushVerificado = depois.sha === head;
|
|
521
|
+
// O push que saiu com erro mas chegou (o prazo venceu depois da escrita no remoto) conta: quem decide e o ls-remote.
|
|
522
|
+
pushFeito = push.ok || r.pushVerificado;
|
|
523
|
+
if (!pushFeito) {
|
|
524
|
+
const erro = deFora(push.stderr || push.stdout || `codigo ${push.code}`, 300);
|
|
525
|
+
const recusado = /rejected|non-fast-forward|fetch first|stale info|failed to update ref/i.test(erro);
|
|
526
|
+
return recusar(recusado ? 'tree.blocked' : 'runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).pushFalhou(remoto, f.branch, erro)), (0, locale_1.textoDoCatalogo)((m) => recusado ? p(m).remotoDivergiuCorrecao(remoto, f.branch) : p(m).redeCorrecao));
|
|
527
|
+
}
|
|
528
|
+
if (!r.pushVerificado) {
|
|
529
|
+
// B1 da revisao: o push aconteceu e o remoto nao mostra o HEAD; a tentativa vai ao ledger com o que ele respondeu.
|
|
530
|
+
gravar({ pr: null, ...(depois.erro !== null ? { erroDaProva: depois.erro } : {}) });
|
|
531
|
+
return recusar('runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).pushSemProva(depois.sha ? curto(depois.sha) : null, curto(head))), (0, locale_1.textoDoCatalogo)((m) => p(m).pushSemProvaCorrecao(t.id)));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// Efeito 2: o PR aberto da branch, ou um novo, provado pela releitura da lista.
|
|
535
|
+
let pr = aberto ? { numero: aberto.numero, url: aberto.url, criado: false, head: aberto.head } : null;
|
|
536
|
+
let erroDoPr = null;
|
|
537
|
+
if (!pr) {
|
|
538
|
+
const argv = ['pr', 'create', `--repo=${forja.host}/${forja.repositorio}`, `--base=${base}`, `--head=${f.branch}`, `--title=${titulo}`,
|
|
539
|
+
`--body=${textoDoCorpo}`];
|
|
540
|
+
const criado = executor(argv, PRAZO_DO_GH_MS);
|
|
541
|
+
r.passos.push(`gh pr create --base ${base} --head ${f.branch}`);
|
|
542
|
+
const relidos = prsDaBranch(executor, forja, base, f.branch);
|
|
543
|
+
r.passos.push(`gh pr list --head ${f.branch} --state all (prova do PR)`);
|
|
544
|
+
const achado = relidos.prs.find((x) => x.estado === 'aberto') ?? null;
|
|
545
|
+
if (achado)
|
|
546
|
+
pr = { numero: achado.numero, url: achado.url, criado: criado.status === 0, head: achado.head };
|
|
547
|
+
else {
|
|
548
|
+
erroDoPr = criado.status !== 0 ? deFora(criado.stderr || criado.stdout || `codigo ${criado.status ?? '-'}`)
|
|
549
|
+
: relidos.erro ?? (0, locale_1.textoDoCatalogo)((m) => p(m).prSemReleitura);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
r.pr = pr;
|
|
553
|
+
// A evidencia: so com efeito (push ou PR novo), com a prova.
|
|
554
|
+
if (pushFeito || pr?.criado || erroDoPr) {
|
|
555
|
+
gravar({ pr: pr ? { numero: pr.numero, url: pr.url, criado: pr.criado, head: pr.head } : null,
|
|
556
|
+
...(pr ? { provaDoPr: `gh pr list --head ${f.branch} --state all` } : {}), ...(erroDoPr ? { erroDoPr } : {}) });
|
|
557
|
+
}
|
|
558
|
+
if (erroDoPr) {
|
|
559
|
+
return recusar('runtime.unavailable', (0, locale_1.textoDoCatalogo)((m) => p(m).prFalhou(curto(head), erroDoPr ?? '-')), (0, locale_1.textoDoCatalogo)((m) => p(m).prFalhouCorrecao(t.id)));
|
|
560
|
+
}
|
|
561
|
+
// S7 da revisao: sem push e sem PR aberto por este processo, quem publicou foi outro; a entrega ja estava publicada.
|
|
562
|
+
if (!pushFeito && !pr?.criado) {
|
|
563
|
+
return { ...r, acao: 'ja-publicada', motivo: null, correcao: '', detalhe: (0, locale_1.textoDoCatalogo)((m) => p(m).jaPublicada(pr?.numero ?? 0)) };
|
|
564
|
+
}
|
|
565
|
+
return { ...r, acao: 'publicou', motivo: null, correcao: '',
|
|
566
|
+
detalhe: (0, locale_1.textoDoCatalogo)((m) => p(m).publicou(curto(head), pr?.numero ?? 0, pr?.criado === true)) };
|
|
567
|
+
}
|
|
568
|
+
/** As linhas da saida de texto do `ork ship publicar`, no locale ativo. */
|
|
569
|
+
function linhasDaPublicacao(r) {
|
|
570
|
+
const m = (0, locale_1.msg)().ship;
|
|
571
|
+
const p = m.publicar;
|
|
572
|
+
const curtoOuTraco = (sha) => (sha ? sha.slice(0, 7) : '-');
|
|
573
|
+
const linhas = [p.cabecalho(r.dryRun, r.thread, r.tag ?? '-')];
|
|
574
|
+
if (r.branch)
|
|
575
|
+
linhas.push(p.linhaBranch(r.branch, curtoOuTraco(r.head)));
|
|
576
|
+
if (r.passos.length) {
|
|
577
|
+
linhas.push(p.linhaRemoto(r.remoto, curtoOuTraco(r.remotaAntes), curtoOuTraco(r.remota)));
|
|
578
|
+
// O PR ja mesclado no HEAD com a branch apagada no remoto nao tem push a provar: a linha some.
|
|
579
|
+
if (!r.dryRun && r.acao !== 'recusada' && (r.pushVerificado || r.remota !== null)) {
|
|
580
|
+
linhas.push(r.pushVerificado ? p.linhaPushProvado : p.linhaPushSemProva);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (r.pr)
|
|
584
|
+
linhas.push(p.linhaPr(r.pr.numero, r.pr.criado, r.pr.url ?? ''));
|
|
585
|
+
if (r.corpo)
|
|
586
|
+
linhas.push(p.linhaCorpo(r.corpo));
|
|
587
|
+
if (r.acao === 'recusada')
|
|
588
|
+
linhas.push(m.bloqueado(r.motivo ?? '-'), m.detalhe((0, locale_1.remontar)(r.detalhe)));
|
|
589
|
+
else {
|
|
590
|
+
linhas.push(p.resultado(p.acoes[r.acao] ?? r.acao));
|
|
591
|
+
if (r.detalhe)
|
|
592
|
+
linhas.push(m.detalhe((0, locale_1.remontar)(r.detalhe)));
|
|
593
|
+
}
|
|
594
|
+
if (r.correcao)
|
|
595
|
+
linhas.push(m.correcao((0, locale_1.remontar)(r.correcao)));
|
|
596
|
+
if (r.passos.length) {
|
|
597
|
+
linhas.push(m.evidencia);
|
|
598
|
+
for (const passo of r.passos)
|
|
599
|
+
linhas.push(` ${(0, locale_1.remontar)(passo)}`);
|
|
600
|
+
}
|
|
601
|
+
return linhas;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* O PR que vale para a pista: o aberto mais novo, senao o mesclado no HEAD, senao o mais novo da branch quando ele foi
|
|
605
|
+
* fechado sem merge. O mesclado de outro head e de uma entrega anterior (A1 da revisao): nao vale para a ponta nova.
|
|
606
|
+
*/
|
|
607
|
+
function prDaBranchNaLista(prs, branch, head) {
|
|
608
|
+
const daBranch = prs.filter((x) => x.branch === branch).sort((a, b) => b.numero - a.numero);
|
|
609
|
+
const aberto = daBranch.find((x) => x.estado === 'aberto');
|
|
610
|
+
if (aberto)
|
|
611
|
+
return aberto;
|
|
612
|
+
const mesclado = daBranch.find((x) => x.estado === 'mesclado' && x.head === head);
|
|
613
|
+
if (mesclado)
|
|
614
|
+
return mesclado;
|
|
615
|
+
return daBranch[0]?.estado === 'fechado' ? daBranch[0] : null;
|
|
616
|
+
}
|
|
617
|
+
function entregaLocal(raiz, f, remotas, remoto, base) {
|
|
618
|
+
const remota = remotas?.get(f.branch) ?? null;
|
|
619
|
+
const ships = f.eventos.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido);
|
|
620
|
+
const entregue = (!!f.head && ships.some((e) => e.shaDe === f.head || e.mergeSha === f.head)) || (!f.comProduto && ships.length > 0);
|
|
621
|
+
const merge = f.head ? (0, entrega_pr_1.mergeDaEntrega)(raiz, f.t.id, remoto, base) : null;
|
|
622
|
+
const mergeSemRegistro = !!merge && !ships.some((e) => e.mergeSha === merge.mergeSha);
|
|
623
|
+
// So entra na pista quem ja esta na entrega: passou do CHECK (ciclo sem CHECK: o turno acabou) com produto, publicou,
|
|
624
|
+
// tem merge sem registro ou ja foi entregue e espera o MASTER.
|
|
625
|
+
const passouDoCheck = f.check.estado === 'nao-se-aplica' ? !f.conducao : f.checkPassou;
|
|
626
|
+
const naEntrega = (passouDoCheck && f.comProduto) || (!!remota && f.comProduto) || mergeSemRegistro || entregue;
|
|
627
|
+
return { remota, entregue, merge, mergeSemRegistro, naEntrega };
|
|
628
|
+
}
|
|
629
|
+
/** O que a pista de uma thread diz, das leituras ja feitas. */
|
|
630
|
+
function pistaDaThread(carregado, f, local, leitura) {
|
|
631
|
+
const { t } = f;
|
|
632
|
+
const raiz = carregado.raiz;
|
|
633
|
+
const { remota, entregue, merge, mergeSemRegistro } = local;
|
|
634
|
+
const pr = leitura.prs ? prDaBranchNaLista(leitura.prs, f.branch, f.head) : null;
|
|
635
|
+
const prMesclado = pr?.estado === 'mesclado' && pr.head === f.head;
|
|
636
|
+
if (!(local.naEntrega || !!pr) || !f.head)
|
|
637
|
+
return null;
|
|
638
|
+
const c = (m) => m.ship.publicar;
|
|
639
|
+
const publicavel = exports.MODOS_QUE_PUBLICAM.includes(t.modo);
|
|
640
|
+
// A branch no remoto. A3 da revisao: o sha do remoto que esta copia nem conhece tambem e divergencia.
|
|
641
|
+
let branchEtapa;
|
|
642
|
+
if (!leitura.remotasLidas)
|
|
643
|
+
branchEtapa = etapa('branch', 'nao-lida', (0, locale_1.textoDoCatalogo)((m) => c(m).remotoNaoLido(leitura.remoto)));
|
|
644
|
+
else if (remota === f.head)
|
|
645
|
+
branchEtapa = etapa('branch', 'feita', (0, locale_1.textoDoCatalogo)((m) => c(m).branchPublicada(curto(f.head))));
|
|
646
|
+
else if (!remota)
|
|
647
|
+
branchEtapa = etapa('branch', 'pendente', (0, locale_1.textoDoCatalogo)((m) => c(m).branchNaoPublicada));
|
|
648
|
+
else if (remotoContido(raiz, remota, f.head) === false) {
|
|
649
|
+
branchEtapa = etapa('branch', 'falhou', (0, locale_1.textoDoCatalogo)((m) => c(m).remotoDivergiu(leitura.remoto, f.branch, curto(remota))));
|
|
650
|
+
}
|
|
651
|
+
else
|
|
652
|
+
branchEtapa = etapa('branch', 'pendente', (0, locale_1.textoDoCatalogo)((m) => c(m).commitsSemPush(curto(remota), curto(f.head))));
|
|
653
|
+
// O PR, o CI e o conflito.
|
|
654
|
+
const corte = leitura.corte(f.branch);
|
|
655
|
+
let prEtapa, ciEtapa, conflitoEtapa;
|
|
656
|
+
if (!leitura.prs) {
|
|
657
|
+
prEtapa = etapa('pr', 'nao-lida', (0, locale_1.textoDoCatalogo)((m) => c(m).prsNaoLidos(leitura.prsNaoLidos ?? '-')));
|
|
658
|
+
ciEtapa = etapa('ci', 'nao-lida', (0, locale_1.textoDoCatalogo)((m) => c(m).prsNaoLidos(leitura.prsNaoLidos ?? '-')));
|
|
659
|
+
conflitoEtapa = etapa('conflito', 'nao-lida', (0, locale_1.textoDoCatalogo)((m) => c(m).prsNaoLidos(leitura.prsNaoLidos ?? '-')));
|
|
660
|
+
}
|
|
661
|
+
else if ((!pr || pr.estado === 'fechado') && corte) {
|
|
662
|
+
// A7 da revisao: a lista dos abertos cortada, ou a branch sem conferir nesta leitura, nao diz "sem PR" (a R1 do pulse).
|
|
663
|
+
const texto = (0, locale_1.textoDoCatalogo)((m) => corte === 'parcial' ? c(m).prsCortados(parado_no_condutor_1.LIMITE_DE_PRS) : c(m).branchSemConferir);
|
|
664
|
+
prEtapa = etapa('pr', 'nao-lida', texto);
|
|
665
|
+
ciEtapa = etapa('ci', 'nao-lida', texto);
|
|
666
|
+
conflitoEtapa = etapa('conflito', 'nao-lida', texto);
|
|
667
|
+
}
|
|
668
|
+
else if (!pr || pr.estado === 'fechado') {
|
|
669
|
+
prEtapa = etapa('pr', 'pendente', pr ? (0, locale_1.textoDoCatalogo)((m) => c(m).prFechado(pr.numero)) : (0, locale_1.textoDoCatalogo)((m) => c(m).semPr));
|
|
670
|
+
ciEtapa = etapa('ci', leitura.ciExigido ? 'pendente' : 'nao-se-aplica', (0, locale_1.textoDoCatalogo)((m) => leitura.ciExigido ? c(m).ciSemPr : c(m).ciNaoExigido));
|
|
671
|
+
conflitoEtapa = etapa('conflito', 'nao-se-aplica', (0, locale_1.textoDoCatalogo)((m) => c(m).conflitoSemPr));
|
|
672
|
+
}
|
|
673
|
+
else if (pr.estado === 'mesclado') {
|
|
674
|
+
prEtapa = etapa('pr', 'feita', (0, locale_1.textoDoCatalogo)((m) => c(m).prMesclado(pr.numero)));
|
|
675
|
+
ciEtapa = etapa('ci', 'nao-se-aplica', (0, locale_1.textoDoCatalogo)((m) => c(m).ciDoMesclado));
|
|
676
|
+
conflitoEtapa = etapa('conflito', 'feita', (0, locale_1.textoDoCatalogo)((m) => c(m).prMesclado(pr.numero)));
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
prEtapa = etapa('pr', 'feita', (0, locale_1.textoDoCatalogo)((m) => pr.rascunho ? c(m).prRascunho(pr.numero) : c(m).prAberto(pr.numero)));
|
|
680
|
+
const check = pr.checks.find((k) => k.nome === leitura.contexto);
|
|
681
|
+
ciEtapa = !leitura.ciExigido ? etapa('ci', 'nao-se-aplica', (0, locale_1.textoDoCatalogo)((m) => c(m).ciNaoExigido))
|
|
682
|
+
: !check ? etapa('ci', 'pendente', (0, locale_1.textoDoCatalogo)((m) => c(m).ciSemCheck(leitura.contexto, pr.numero)))
|
|
683
|
+
: check.situacao === 'verde' ? etapa('ci', 'feita', (0, locale_1.textoDoCatalogo)((m) => c(m).ciVerde(leitura.contexto, curto(pr.head))))
|
|
684
|
+
: check.situacao === 'vermelho' ? etapa('ci', 'falhou', (0, locale_1.textoDoCatalogo)((m) => c(m).ciVermelho(leitura.contexto, pr.numero)))
|
|
685
|
+
: etapa('ci', 'pendente', (0, locale_1.textoDoCatalogo)((m) => c(m).ciEsperando(leitura.contexto, pr.numero)));
|
|
686
|
+
conflitoEtapa = pr.conflito === true ? etapa('conflito', 'falhou', (0, locale_1.textoDoCatalogo)((m) => c(m).conflito(pr.numero)))
|
|
687
|
+
: pr.conflito === false ? etapa('conflito', 'feita', (0, locale_1.textoDoCatalogo)((m) => c(m).semConflito))
|
|
688
|
+
: etapa('conflito', 'nao-lida', (0, locale_1.textoDoCatalogo)((m) => c(m).conflitoNaoCalculado));
|
|
689
|
+
}
|
|
690
|
+
const entregaEtapa = entregue ? etapa('entrega', 'feita', (0, locale_1.textoDoCatalogo)((m) => c(m).entregue))
|
|
691
|
+
: etapa('entrega', 'pendente', (0, locale_1.textoDoCatalogo)((m) => mergeSemRegistro ? c(m).mergeSemRegistro(curto(merge?.mergeSha)) : c(m).semShipDone));
|
|
692
|
+
// O proximo passo, a primeira regra que vale.
|
|
693
|
+
const corpo = corpoDaThread(raiz, t.id);
|
|
694
|
+
const nada = (texto, comando = null) => ({ acao: null, disponivel: false, texto, comando });
|
|
695
|
+
let proximo;
|
|
696
|
+
if (entregue)
|
|
697
|
+
proximo = nada((0, locale_1.textoDoCatalogo)((m) => c(m).proximoEntregue));
|
|
698
|
+
else if (mergeSemRegistro || prMesclado) {
|
|
699
|
+
proximo = { acao: 'registrar', disponivel: true, texto: (0, locale_1.textoDoCatalogo)((m) => c(m).proximoRegistrar), comando: `ork ship registrar-pr ${t.id}` };
|
|
700
|
+
}
|
|
701
|
+
else if (f.conducao) {
|
|
702
|
+
const sessao = f.conducao.sessao ? f.conducao.sessao.slice(0, 8) : f.conducao.canal;
|
|
703
|
+
proximo = nada((0, locale_1.textoDoCatalogo)((m) => c(m).proximoConduzida(sessao)), `ork conducao status ${t.id}`);
|
|
704
|
+
}
|
|
705
|
+
else if (f.check.estado === 'pendente')
|
|
706
|
+
proximo = nada(f.check.texto, (0, locale_1.textoDoCatalogo)((m) => c(m).comandoDoCheck(t.id)));
|
|
707
|
+
else if (f.verify.estado === 'pendente')
|
|
708
|
+
proximo = nada(f.verify.texto, `ork verify ${t.id}`);
|
|
709
|
+
else if (f.bundle.estado === 'pendente')
|
|
710
|
+
proximo = nada(f.bundle.texto, `ork ci prepare ${t.id}`);
|
|
711
|
+
else if (branchEtapa.estado === 'falhou')
|
|
712
|
+
proximo = nada(branchEtapa.texto, null);
|
|
713
|
+
else if (branchEtapa.estado === 'nao-lida')
|
|
714
|
+
proximo = nada(branchEtapa.texto, `ork ship pista ${t.id}`);
|
|
715
|
+
else if (branchEtapa.estado === 'pendente' || (prEtapa.estado === 'pendente')) {
|
|
716
|
+
proximo = publicavel
|
|
717
|
+
? { acao: 'publicar', disponivel: true, comando: `ork ship publicar ${t.id}`,
|
|
718
|
+
texto: (0, locale_1.textoDoCatalogo)((m) => pr?.estado === 'aberto' ? c(m).proximoPublicarNoPr(pr.numero) : c(m).proximoPublicar) }
|
|
719
|
+
: { acao: 'publicar', disponivel: false, texto: (0, locale_1.textoDoCatalogo)((m) => c(m).proximoDoDono(t.id, (0, modos_1.tagDoModo)(t.modo))),
|
|
720
|
+
comando: comandoDoDono(leitura.remoto, leitura.base, f.branch, tituloDoPr(t, corpo?.texto ?? null), corpo ? path.relative(raiz, corpo.arquivo) : null) };
|
|
721
|
+
}
|
|
722
|
+
else if (prEtapa.estado === 'nao-lida')
|
|
723
|
+
proximo = nada(prEtapa.texto, `ork ship pista ${t.id}`);
|
|
724
|
+
else if (conflitoEtapa.estado === 'falhou')
|
|
725
|
+
proximo = nada((0, locale_1.textoDoCatalogo)((m) => c(m).proximoConflito(pr?.numero ?? 0)));
|
|
726
|
+
else if (ciEtapa.estado === 'falhou')
|
|
727
|
+
proximo = nada((0, locale_1.textoDoCatalogo)((m) => c(m).proximoCiVermelho(leitura.contexto, pr?.numero ?? 0)));
|
|
728
|
+
else if (ciEtapa.estado === 'pendente' || ciEtapa.estado === 'nao-lida') {
|
|
729
|
+
proximo = nada((0, locale_1.textoDoCatalogo)((m) => c(m).proximoEsperandoCi(leitura.contexto, pr?.numero ?? 0)), pr ? `ork ci status --sha ${pr.head}` : null);
|
|
730
|
+
}
|
|
731
|
+
else
|
|
732
|
+
proximo = nada((0, locale_1.textoDoCatalogo)((m) => pr?.rascunho ? c(m).proximoRascunho(pr.numero) : c(m).proximoMerge(pr?.numero ?? 0)));
|
|
733
|
+
return {
|
|
734
|
+
thread: t.id, nome: t.nome, modo: t.modo, tag: (0, modos_1.tagDoModo)(t.modo), publicavel, conduzida: !!f.conducao, branch: f.branch, head: f.head,
|
|
735
|
+
remota, pr: pr ? { numero: pr.numero, url: pr.url, estado: pr.estado, rascunho: pr.rascunho, head: pr.head } : null,
|
|
736
|
+
etapas: [f.check, f.verify, f.bundle, branchEtapa, prEtapa, ciEtapa, conflitoEtapa, entregaEtapa], proximo,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
/** `ork ship pista [<thread>]`: as etapas e o proximo passo de cada thread aberta que ja esta na entrega. Nao grava nada. */
|
|
740
|
+
function pistaDeEntrega(carregado, opcoes = {}) {
|
|
741
|
+
const raiz = carregado.raiz, base = carregado.manifesto.worktree.base_branch;
|
|
742
|
+
const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? 'origin', 'ship');
|
|
743
|
+
const lidoEm = opcoes.quando ?? new Date().toISOString();
|
|
744
|
+
const contexto = carregado.manifesto.ci.context || 'ork-verify';
|
|
745
|
+
const ciExigido = carregado.manifesto.ci.required_for_ship === true;
|
|
746
|
+
const refs = (0, parado_no_condutor_1.refsLocais)(raiz, remoto);
|
|
747
|
+
const baseRef = refDaBase(refs, remoto, base);
|
|
748
|
+
const ids = opcoes.thread ? [opcoes.thread] : (0, thread_1.listarIds)(raiz);
|
|
749
|
+
const fatos = [];
|
|
750
|
+
for (const id of ids) {
|
|
751
|
+
let t;
|
|
752
|
+
try {
|
|
753
|
+
t = (0, thread_1.lerThread)(raiz, id);
|
|
754
|
+
}
|
|
755
|
+
catch {
|
|
756
|
+
if (opcoes.thread)
|
|
757
|
+
throw new Error(`ship.pista: a thread ${id} nao existe`);
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (t.status === 'fechada')
|
|
761
|
+
continue;
|
|
762
|
+
fatos.push(fatosDaThread(carregado, t, refs, baseRef, lidoEm));
|
|
763
|
+
}
|
|
764
|
+
const comBranch = fatos.filter((f) => f.head && (0, branch_de_estado_1.branchValida)(f.branch));
|
|
765
|
+
const remotas = comBranch.length ? pontasNoRemoto(raiz, remoto) : new Map();
|
|
766
|
+
const locais = new Map(fatos.map((f) => [f.t.id, entregaLocal(raiz, f, remotas, remoto, base)]));
|
|
767
|
+
// A7 da revisao: so a branch da thread que ja esta na entrega pelo que se le sem a forja vai a leitura dos PRs, que
|
|
768
|
+
// confere uma a uma a que nao aparece nas listas; sem nenhuma, a forja nem e lida.
|
|
769
|
+
const candidatas = comBranch.filter((f) => locais.get(f.t.id)?.naEntrega === true).map((f) => f.branch);
|
|
770
|
+
let prs = [];
|
|
771
|
+
let prsNaoLidos = null;
|
|
772
|
+
let parcial = false;
|
|
773
|
+
let semConferir = new Set();
|
|
774
|
+
if (candidatas.length) {
|
|
775
|
+
const leitura = (0, parado_no_condutor_1.lerPrsDaForja)(carregado, { quando: lidoEm, executor: opcoes.executorGh, remoto, candidatas, forja: opcoes.forja });
|
|
776
|
+
if (leitura.ok) {
|
|
777
|
+
prs = leitura.retrato.prs;
|
|
778
|
+
parcial = leitura.retrato.parcial;
|
|
779
|
+
semConferir = new Set(leitura.retrato.semConferir ?? []);
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
prs = null;
|
|
783
|
+
prsNaoLidos = deFora(leitura.erro);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
// S8 da revisao (rodada 2): a candidata sem nenhum PR na leitura foi conferida uma a uma pelo `--head`, que a lista
|
|
787
|
+
// cortada nao limita; so a que nao foi conferida, ou que so tem PR achado na lista cortada, fica em duvida.
|
|
788
|
+
const comPrNaLeitura = new Set((prs ?? []).map((x) => x.branch));
|
|
789
|
+
const conferida = (branch) => candidatas.includes(branch) && !comPrNaLeitura.has(branch);
|
|
790
|
+
const corte = (branch) => (semConferir.has(branch) ? 'sem-conferir'
|
|
791
|
+
: parcial && !conferida(branch) ? 'parcial' : null);
|
|
792
|
+
const pistas = fatos.flatMap((f) => {
|
|
793
|
+
const local = locais.get(f.t.id);
|
|
794
|
+
const x = local ? pistaDaThread(carregado, f, local, { remoto, base, remotasLidas: remotas !== null, prs, prsNaoLidos, corte, ciExigido, contexto })
|
|
795
|
+
: null;
|
|
796
|
+
return x ? [x] : [];
|
|
797
|
+
});
|
|
798
|
+
return { contrato: exports.CONTRATO_DA_PISTA, lidoEm, base, remoto, remotoLido: remotas !== null, prsLidos: prs !== null, prsNaoLidos,
|
|
799
|
+
ciExigido, contextoDoCi: contexto, pistas };
|
|
800
|
+
}
|