@orkastery/cli 0.6.6 → 0.6.7
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/agents/ork-check.md +4 -0
- package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +1 -1
- package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +2 -1
- package/adapters/openclaw/openclaw.plugin.json +1 -1
- package/adapters/openclaw/package.json +1 -1
- package/assets/adapters-en/claude-code/agents/ork-check.md +4 -0
- package/assets/skills-en/phases/check-quality/SKILL.md +14 -0
- package/dist/adapters/claude-bg.js +1 -1
- package/dist/agendador-cpu.js +27 -4
- package/dist/ambiente-funcional.js +72 -0
- package/dist/cenarios-sensores.js +3 -7
- package/dist/check-agil.js +204 -60
- package/dist/check-paralelo.js +101 -36
- package/dist/claim-lint.js +19 -0
- package/dist/claims.js +77 -11
- package/dist/fila-pesada.js +8 -6
- package/dist/fix.js +152 -6
- package/dist/hitl-gates.js +1 -1
- package/dist/index.js +53 -4
- package/dist/locale-ajuda.js +10 -4
- package/dist/locale-claims.js +22 -6
- package/dist/locale-operacao-restante.js +12 -0
- package/dist/locale-prompts.js +4 -2
- package/dist/locale-pulse.js +2 -0
- package/dist/locale-retry.js +8 -0
- package/dist/locale-verify.js +8 -0
- package/dist/mcp-artifacts.js +24 -0
- package/dist/mcp-install.js +1 -1
- package/dist/mcp-server.js +11 -1
- package/dist/mcp-verify-async.js +2 -1
- package/dist/noctum-provas.js +56 -30
- package/dist/recibo-prova.js +87 -0
- package/dist/retry.js +6 -0
- package/dist/security-scan/executor.js +9 -3
- package/dist/security-scan/guard.js +5 -2
- package/dist/ship.js +1 -1
- package/dist/temporario-das-lanes.js +94 -0
- package/dist/verify.js +662 -410
- package/eval/fixtures/fx-sensores-runtime/caso.json +1 -1
- package/package.json +1 -1
- package/skills/phases/check-quality/SKILL.md +14 -0
package/dist/check-agil.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.PECA_SEM_RELATORIO = exports.RelatorioDoCheck = exports.TETO_DO_RELATORIO_BYTES = exports.MAXIMO_DE_PREPAROS_REFEITOS = exports.GuardaDoProduto = void 0;
|
|
36
|
+
exports.assinaturaDoAmbiente = exports.PECA_SEM_RELATORIO = exports.RelatorioDoCheck = exports.TETO_DO_RELATORIO_BYTES = exports.MAXIMO_DE_PREPAROS_REFEITOS = exports.GuardaDoProduto = void 0;
|
|
37
37
|
exports.paralelismoDaVarreduraNoGrupo = paralelismoDaVarreduraNoGrupo;
|
|
38
38
|
exports.passosDoComando = passosDoComando;
|
|
39
39
|
exports.elosDoComando = elosDoComando;
|
|
@@ -45,11 +45,13 @@ exports.executorDaClaimNoAgile = executorDaClaimNoAgile;
|
|
|
45
45
|
exports.refazerPreparo = refazerPreparo;
|
|
46
46
|
exports.testDasLanes = testDasLanes;
|
|
47
47
|
exports.porQueDasLanes = porQueDasLanes;
|
|
48
|
+
exports.comandoDaClaimDependeDasLanes = comandoDaClaimDependeDasLanes;
|
|
49
|
+
exports.planejarClaimsNoAgile = planejarClaimsNoAgile;
|
|
48
50
|
exports.comandosNoAgile = comandosNoAgile;
|
|
49
51
|
exports.pecasDoRelatorio = pecasDoRelatorio;
|
|
50
52
|
exports.listasDasLanes = listasDasLanes;
|
|
51
53
|
exports.resultadosDasLanes = resultadosDasLanes;
|
|
52
|
-
exports.
|
|
54
|
+
exports.classificarClaimPelasLanes = classificarClaimPelasLanes;
|
|
53
55
|
exports.executorDaClaimPelasLanes = executorDaClaimPelasLanes;
|
|
54
56
|
/**
|
|
55
57
|
* RM-072 (D3 a D8 da ork-check5x): o motor do Agile Check no verify.
|
|
@@ -79,6 +81,7 @@ exports.executorDaClaimPelasLanes = executorDaClaimPelasLanes;
|
|
|
79
81
|
* ok e e o nome que a baseline compara). Nada aqui decide veredito: a classificacao e a do verify.
|
|
80
82
|
*/
|
|
81
83
|
const node_crypto_1 = require("node:crypto");
|
|
84
|
+
const ambiente_funcional_1 = require("./ambiente-funcional");
|
|
82
85
|
const fs = __importStar(require("node:fs"));
|
|
83
86
|
const os = __importStar(require("node:os"));
|
|
84
87
|
const path = __importStar(require("node:path"));
|
|
@@ -494,11 +497,78 @@ function porQueDasLanes(c) {
|
|
|
494
497
|
};
|
|
495
498
|
return caidas.map((l) => `lane ${l.lane}: ${porque(l)}`).join('; ');
|
|
496
499
|
}
|
|
500
|
+
/** A mesma gramatica fechada que o reaproveitamento usa, sem precisar esperar a lane para classificar a dependencia. */
|
|
501
|
+
function comandoDaClaimDependeDasLanes(manifesto, comando, cwd) {
|
|
502
|
+
const passosDoPreparo = manifesto.verify.preparo ? passosDoComando(manifesto.verify.preparo) : [];
|
|
503
|
+
const daFrente = tirarPassosDoPreparo(comando, passosDoPreparo);
|
|
504
|
+
const { resto } = daFrente.passos.length > 0 ? daFrente : tirarPassosDoPreparoAposLeituras(comando, passosDoPreparo);
|
|
505
|
+
if (!resto)
|
|
506
|
+
return false;
|
|
507
|
+
const elos = elosDoComando(resto);
|
|
508
|
+
return !!elos?.length && elos.every((elo) => comandoGrepQ(elo) || !!auditoriaTap(elo, cwd) || !!comandoNodeTest(elo, cwd))
|
|
509
|
+
&& elos.some((elo) => !!auditoriaTap(elo, cwd) || !!comandoNodeTest(elo, cwd));
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Claims dependentes de lane conservam a ordem antiga. As demais entram no grupo; a ordem interna e claims sobre o
|
|
513
|
+
* mesmo arquivo formam cadeias, enquanto arquivos distintos podem usar vagas diferentes do pool de claims.
|
|
514
|
+
*/
|
|
515
|
+
function planejarClaimsNoAgile(manifesto, claims, cwd, guarda, preparo) {
|
|
516
|
+
if (guarda.conferir())
|
|
517
|
+
return { execucoes: [], ids: [], cadeias: 0 };
|
|
518
|
+
const passosDoPreparo = manifesto.verify.preparo ? passosDoComando(manifesto.verify.preparo) : [];
|
|
519
|
+
const ultimosPorArquivo = new Map();
|
|
520
|
+
const execucoes = [];
|
|
521
|
+
const ids = [];
|
|
522
|
+
for (const claim of claims) {
|
|
523
|
+
const usaPreparo = (comando) => {
|
|
524
|
+
const daFrente = tirarPassosDoPreparo(comando, passosDoPreparo);
|
|
525
|
+
return (daFrente.passos.length > 0 ? daFrente : tirarPassosDoPreparoAposLeituras(comando, passosDoPreparo)).passos.length > 0;
|
|
526
|
+
};
|
|
527
|
+
// A guarda e o teto de refacoes sao seriais: claim que consome o preparo continua no caminho anterior.
|
|
528
|
+
if (!claim.verificar.length || claim.verificar.some((comando) => comandoDaClaimDependeDasLanes(manifesto, comando, cwd)
|
|
529
|
+
|| usaPreparo(comando)))
|
|
530
|
+
continue;
|
|
531
|
+
ids.push(claim.id);
|
|
532
|
+
let anterior = ultimosPorArquivo.get(claim.arquivo);
|
|
533
|
+
claim.verificar.forEach((comando, i) => {
|
|
534
|
+
const nome = `${claim.id}.${i + 1}`;
|
|
535
|
+
const daFrente = tirarPassosDoPreparo(comando, passosDoPreparo);
|
|
536
|
+
const tirado = daFrente.passos.length > 0 ? daFrente : tirarPassosDoPreparoAposLeituras(comando, passosDoPreparo);
|
|
537
|
+
if (tirado.passos.length > 0 && tirado.resto === '') {
|
|
538
|
+
execucoes.push({ nome, comando, resultado: doPreparo(nome, comando, preparo, tirado.passos) });
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
const executado = tirado.passos.length > 0 ? tirado.resto : comando;
|
|
542
|
+
const item = { nome: `claim:${nome}`, comando: executado, papelCpu: 'claim',
|
|
543
|
+
...(anterior ? { apos: [anterior] } : {}) };
|
|
544
|
+
execucoes.push({ nome, comando, item,
|
|
545
|
+
...(executado !== comando ? { comandoExecutado: executado,
|
|
546
|
+
preparo: { reaproveitado: true, passos: tirado.passos } } : {}) });
|
|
547
|
+
anterior = item.nome;
|
|
548
|
+
});
|
|
549
|
+
if (anterior)
|
|
550
|
+
ultimosPorArquivo.set(claim.arquivo, anterior);
|
|
551
|
+
}
|
|
552
|
+
return { execucoes, ids, cadeias: ultimosPorArquivo.size };
|
|
553
|
+
}
|
|
554
|
+
function resultadosDasClaimsAntecipadas(plano, porItem) {
|
|
555
|
+
if (!plano)
|
|
556
|
+
return {};
|
|
557
|
+
return Object.fromEntries(plano.execucoes.map((e) => {
|
|
558
|
+
if (e.resultado)
|
|
559
|
+
return [e.nome, { ...e.resultado, agendamento: 'antecipada' }];
|
|
560
|
+
const bruto = porItem.get(e.item.nome);
|
|
561
|
+
return [e.nome, { ...bruto, nome: e.nome, comando: e.comando,
|
|
562
|
+
agendamento: 'antecipada',
|
|
563
|
+
...(e.comandoExecutado ? { comandoExecutado: e.comandoExecutado } : {}),
|
|
564
|
+
...(e.preparo ? { preparo: e.preparo } : {}) }];
|
|
565
|
+
}));
|
|
566
|
+
}
|
|
497
567
|
/**
|
|
498
568
|
* Os comandos do manifesto no Agile (D4): `build` reaproveitado do preparo com a guarda valendo; o resto num grupo
|
|
499
569
|
* paralelo, com as lanes no lugar do `verify.test`, so com a guarda valendo; sem ela, tudo em serie (o Classic).
|
|
500
570
|
*/
|
|
501
|
-
function comandosNoAgile(manifesto, cwd, lista, executor, grupo, guarda, preparo, selecao = { comandos: {}, lanes: {} }, passagemParcial, selecaoPorMudanca = {}) {
|
|
571
|
+
function comandosNoAgile(manifesto, cwd, lista, executor, grupo, guarda, preparo, selecao = { comandos: {}, lanes: {} }, passagemParcial, selecaoPorMudanca = {}, claimsAntecipadas) {
|
|
502
572
|
const pecas = [];
|
|
503
573
|
const passosDoPreparo = manifesto.verify.preparo ? passosDoComando(manifesto.verify.preparo) : [];
|
|
504
574
|
const lanesDeclaradas = Object.entries(manifesto.check?.agile?.test ?? {});
|
|
@@ -517,7 +587,7 @@ function comandosNoAgile(manifesto, cwd, lista, executor, grupo, guarda, preparo
|
|
|
517
587
|
? passagemParcial?.(c.nome, c.comando, cwd, c.filtro, selecao.comandos[c.nome]) : null;
|
|
518
588
|
return parcial && !parcial.ok ? parcial : executor(c.nome, c.comando, cwd);
|
|
519
589
|
};
|
|
520
|
-
return { comandos: lista.map(executar), pecas, paralelo: false, lanes: [] };
|
|
590
|
+
return { comandos: lista.map(executar), claimsAntecipadas: {}, pecas, paralelo: false, lanes: [] };
|
|
521
591
|
}
|
|
522
592
|
// Com a guarda: o build primeiro (do preparo, quando e so passos dele), e o resto junto.
|
|
523
593
|
const build = lista.find((c) => c.nome === 'build');
|
|
@@ -560,7 +630,7 @@ function comandosNoAgile(manifesto, cwd, lista, executor, grupo, guarda, preparo
|
|
|
560
630
|
...(c.filtro ? { filtro: c.filtro } : {}), ...(selecao.comandos[c.nome] ? { testesFiltrados: selecao.comandos[c.nome] } : {}),
|
|
561
631
|
papelCpu: c.nome === 'security_scan' ? 'varredura' : 'outro' });
|
|
562
632
|
}
|
|
563
|
-
itens.push(...encadeados);
|
|
633
|
+
itens.push(...(claimsAntecipadas?.execucoes.flatMap((e) => e.item ? [e.item] : []) ?? []), ...encadeados);
|
|
564
634
|
const parciaisVermelhas = new Map();
|
|
565
635
|
for (const item of itens) {
|
|
566
636
|
if (!item.filtro || !item.testesFiltrados?.length || !passagemParcial)
|
|
@@ -590,7 +660,7 @@ function comandosNoAgile(manifesto, cwd, lista, executor, grupo, guarda, preparo
|
|
|
590
660
|
itens.forEach((item, i) => {
|
|
591
661
|
if (item.nome.startsWith('test:'))
|
|
592
662
|
lanes.push({ lane: item.nome.slice('test:'.length), resultado: resultados[i] });
|
|
593
|
-
else
|
|
663
|
+
else if (!item.nome.startsWith('claim:'))
|
|
594
664
|
porNome.set(item.nome, resultados[i]);
|
|
595
665
|
});
|
|
596
666
|
// As lanes voltam na ordem do manifesto, mesmo com a encadeada no fim do grupo.
|
|
@@ -602,7 +672,9 @@ function comandosNoAgile(manifesto, cwd, lista, executor, grupo, guarda, preparo
|
|
|
602
672
|
}
|
|
603
673
|
if (itens.length > 1)
|
|
604
674
|
pecas.push({ peca: 'grupo', estado: 'agile' });
|
|
605
|
-
return { comandos: lista.map((c) => porNome.get(c.nome)),
|
|
675
|
+
return { comandos: lista.map((c) => porNome.get(c.nome)),
|
|
676
|
+
claimsAntecipadas: resultadosDasClaimsAntecipadas(claimsAntecipadas, porItemInteiro),
|
|
677
|
+
pecas, paralelo: itens.length > 1, lanes: lanes.map((l) => l.lane) };
|
|
606
678
|
}
|
|
607
679
|
/** O teto da leitura do relatorio da rodada. */
|
|
608
680
|
exports.TETO_DO_RELATORIO_BYTES = 4 * 1024 * 1024;
|
|
@@ -700,16 +772,8 @@ function resultadosDasLanes(linhas) {
|
|
|
700
772
|
estado: x.estado }];
|
|
701
773
|
}) }));
|
|
702
774
|
}
|
|
703
|
-
|
|
704
|
-
function
|
|
705
|
-
const ignorada = (nome) => nome.startsWith('npm_') || nome.startsWith('NPM_')
|
|
706
|
-
|| ['ORK_CHECK_RELATORIO', 'ORK_CHECK_CPUS_DA_LANE', 'ORK_CHECK_CPUS_DA_LANE_APOS_VARREDURA',
|
|
707
|
-
'ORK_CHECK_CPUS_DA_LANE_APOS_RESERVA', 'ORK_CHECK_RESERVA_DEVOLVIDA', 'ORK_CHECK_CONTAINERS_DA_LANE', 'ORK_CHECK_CPUS_DOCKER',
|
|
708
|
-
'NODE_TEST_CONTEXT', 'NODE_COMPILE_CACHE', 'PWD', 'OLDPWD', 'SHLVL', '_', 'INIT_CWD'].includes(nome);
|
|
709
|
-
const pares = Object.entries(env).filter(([nome, valor]) => valor !== undefined && !ignorada(nome))
|
|
710
|
-
.sort(([a], [b]) => a.localeCompare(b));
|
|
711
|
-
return (0, node_crypto_1.createHash)('sha256').update(JSON.stringify(pares)).digest('hex');
|
|
712
|
-
}
|
|
775
|
+
var ambiente_funcional_2 = require("./ambiente-funcional");
|
|
776
|
+
Object.defineProperty(exports, "assinaturaDoAmbiente", { enumerable: true, get: function () { return ambiente_funcional_2.assinaturaDoAmbiente; } });
|
|
713
777
|
/** Le somente a gramatica aceita para reaproveitar: `node --test [--test-name-pattern P] arquivo...`. */
|
|
714
778
|
function comandoNodeTest(comando, cwd) {
|
|
715
779
|
const palavras = palavrasDoComando(comando);
|
|
@@ -774,68 +838,148 @@ function resultadoReaproveitado(nome, comando, pedido, usadas, esperado, nomes)
|
|
|
774
838
|
nomes: nomes ?? (pedido.padrao === undefined ? pedido.arquivos.map((a) => `${a}:todos`) : pedido.arquivos.map((a) => `${a}:/${pedido.padrao}/`)),
|
|
775
839
|
head: esperado.head, ambiente: esperado.ambiente } };
|
|
776
840
|
}
|
|
841
|
+
function recusaPelasLanes(motivo, arquivos, esperado, assinatura, candidata, usarExecutorEquivalente = candidata) {
|
|
842
|
+
return { decisao: { reaproveitada: false, motivo, arquivos: [...new Set(arquivos)], head: esperado.head, ambiente: assinatura },
|
|
843
|
+
candidata, usarExecutorEquivalente, elos: [] };
|
|
844
|
+
}
|
|
845
|
+
/** Casos nominais ficam exatamente um nivel abaixo do evento que representa o arquivo no runner isolado. */
|
|
846
|
+
function casosNominaisDoArquivo(vistos, arquivo) {
|
|
847
|
+
const niveisDoArquivo = vistos.filter((t) => path.basename(t.nome) === arquivo).map((t) => t.nesting);
|
|
848
|
+
if (niveisDoArquivo.length === 0)
|
|
849
|
+
return [];
|
|
850
|
+
const nivelDosCasos = Math.min(...niveisDoArquivo) + 1;
|
|
851
|
+
return vistos.filter((t) => t.nesting === nivelDosCasos);
|
|
852
|
+
}
|
|
853
|
+
/** Reconhece especificamente o TAP com filtro: ele e fechado, mas a lane inteira nao prova a auditoria filtrada. */
|
|
854
|
+
function tapComPadraoDeNome(comando, cwd) {
|
|
855
|
+
const divisor = ' | ';
|
|
856
|
+
const i = comando.indexOf(divisor);
|
|
857
|
+
if (i < 0 || comando.indexOf(divisor, i + divisor.length) >= 0)
|
|
858
|
+
return null;
|
|
859
|
+
const node = comando.slice(0, i).trim();
|
|
860
|
+
if (!/^node --test-reporter=tap\b/.test(node))
|
|
861
|
+
return null;
|
|
862
|
+
const pedido = comandoNodeTest(node.replace(/^node --test-reporter=tap\b/, 'node --test'), cwd);
|
|
863
|
+
return pedido?.padrao !== undefined ? pedido : null;
|
|
864
|
+
}
|
|
865
|
+
function planejarClaimPelasLanes(comando, cwd, evidencias, esperado) {
|
|
866
|
+
const assinatura = (0, ambiente_funcional_1.assinaturaDoAmbiente)(esperado.ambiente);
|
|
867
|
+
const elos = elosDoComando(comando);
|
|
868
|
+
if (!elos)
|
|
869
|
+
return recusaPelasLanes('comando-aberto', [], esperado, assinatura, false, false);
|
|
870
|
+
const planejados = [];
|
|
871
|
+
const arquivos = [];
|
|
872
|
+
for (const elo of elos) {
|
|
873
|
+
if (comandoGrepQ(elo)) {
|
|
874
|
+
planejados.push({ tipo: 'grep', comando: elo });
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
const tap = auditoriaTap(elo, cwd);
|
|
878
|
+
const comPadrao = tap ? null : tapComPadraoDeNome(elo, cwd);
|
|
879
|
+
if (comPadrao)
|
|
880
|
+
return recusaPelasLanes('tap-com-test-name-pattern', [...arquivos, ...comPadrao.arquivos], esperado, assinatura, true);
|
|
881
|
+
const pedido = tap?.pedido ?? comandoNodeTest(elo, cwd);
|
|
882
|
+
if (!pedido)
|
|
883
|
+
return recusaPelasLanes('comando-nao-elegivel', arquivos, esperado, assinatura, false, false);
|
|
884
|
+
arquivos.push(...pedido.arquivos);
|
|
885
|
+
planejados.push({ tipo: 'lane', comando: elo, pedido, usadas: [],
|
|
886
|
+
...(tap ? { nomes: pedido.arquivos.map((a) => `${a}:${tap.prefixo}>=${tap.minimo};sem-falha;sem-skip`) } : {}) });
|
|
887
|
+
}
|
|
888
|
+
if (!planejados.some((p) => p.tipo === 'lane')) {
|
|
889
|
+
return recusaPelasLanes('comando-nao-elegivel', arquivos, esperado, assinatura, false, false);
|
|
890
|
+
}
|
|
891
|
+
const vigencia = esperado.podeReaproveitar?.();
|
|
892
|
+
if (vigencia === false)
|
|
893
|
+
return recusaPelasLanes('lanes-invigentes', arquivos, esperado, assinatura, true);
|
|
894
|
+
if (typeof vigencia === 'string')
|
|
895
|
+
return recusaPelasLanes(vigencia, arquivos, esperado, assinatura, true);
|
|
896
|
+
if (evidencias.length === 0)
|
|
897
|
+
return recusaPelasLanes('evidencia-vazia', arquivos, esperado, assinatura, true);
|
|
898
|
+
for (const item of planejados) {
|
|
899
|
+
if (item.tipo !== 'lane')
|
|
900
|
+
continue;
|
|
901
|
+
const tap = auditoriaTap(item.comando, cwd);
|
|
902
|
+
const usadasDoElo = new Set();
|
|
903
|
+
for (const arquivo of item.pedido.arquivos) {
|
|
904
|
+
const comArquivo = evidencias.filter((e) => e.testes.some((t) => t.arquivo === arquivo));
|
|
905
|
+
if (comArquivo.length === 0)
|
|
906
|
+
return recusaPelasLanes('arquivo-nao-executado', arquivos, esperado, assinatura, true);
|
|
907
|
+
const noHead = comArquivo.filter((e) => e.head === esperado.head);
|
|
908
|
+
if (noHead.length === 0)
|
|
909
|
+
return recusaPelasLanes('head-divergente', arquivos, esperado, assinatura, true);
|
|
910
|
+
const noAmbiente = noHead.filter((e) => e.ambiente === assinatura);
|
|
911
|
+
if (noAmbiente.length === 0) {
|
|
912
|
+
return recusaPelasLanes('ambiente-funcional-divergente', arquivos, esperado, assinatura, true);
|
|
913
|
+
}
|
|
914
|
+
const vistos = noAmbiente.flatMap((e) => e.testes.filter((t) => t.arquivo === arquivo));
|
|
915
|
+
if (vistos.some((t) => t.estado === 'fail'))
|
|
916
|
+
return recusaPelasLanes('falha-na-lane', arquivos, esperado, assinatura, true);
|
|
917
|
+
if (vistos.some((t) => t.estado === 'skip' || t.estado === 'todo')) {
|
|
918
|
+
return recusaPelasLanes('skip-ou-todo-na-lane', arquivos, esperado, assinatura, true);
|
|
919
|
+
}
|
|
920
|
+
if (tap) {
|
|
921
|
+
const doArquivo = casosNominaisDoArquivo(vistos, arquivo).filter((t) => t.estado === 'pass');
|
|
922
|
+
const doPrefixo = doArquivo.filter((t) => new RegExp(`^${tap.prefixo}[0-9]+:`).test(t.nome));
|
|
923
|
+
if (doPrefixo.length === 0)
|
|
924
|
+
return recusaPelasLanes('prefixo-divergente', arquivos, esperado, assinatura, true);
|
|
925
|
+
if (doPrefixo.length < tap.minimo) {
|
|
926
|
+
return recusaPelasLanes('contagem-abaixo-do-minimo', arquivos, esperado, assinatura, true);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
noAmbiente.forEach((e) => usadasDoElo.add(e));
|
|
930
|
+
}
|
|
931
|
+
item.usadas = [...usadasDoElo];
|
|
932
|
+
}
|
|
933
|
+
const usadas = planejados.flatMap((p) => p.tipo === 'lane' ? p.usadas : []);
|
|
934
|
+
const lanes = [...new Set(usadas.map((e) => e.lane))];
|
|
935
|
+
const nomes = planejados.flatMap((p) => p.tipo !== 'lane' ? [] : p.nomes ?? (p.pedido.padrao === undefined
|
|
936
|
+
? p.pedido.arquivos.map((a) => `${a}:todos`) : p.pedido.arquivos.map((a) => `${a}:/${p.pedido.padrao}/`)));
|
|
937
|
+
return { decisao: { reaproveitada: true, lanes, arquivos: [...new Set(arquivos)], nomes: [...new Set(nomes)],
|
|
938
|
+
head: esperado.head, ambiente: assinatura }, candidata: true, usarExecutorEquivalente: false, elos: planejados };
|
|
939
|
+
}
|
|
940
|
+
/** A classificacao pura usada pelo executor e, na M12, pelo planejador dos lotes de claims. */
|
|
941
|
+
function classificarClaimPelasLanes(comando, cwd, evidencias, esperado) {
|
|
942
|
+
const p = planejarClaimPelasLanes(comando, cwd, evidencias, esperado);
|
|
943
|
+
return { decisao: p.decisao, candidata: p.candidata, usarExecutorEquivalente: p.usarExecutorEquivalente };
|
|
944
|
+
}
|
|
777
945
|
/**
|
|
778
946
|
* Reaproveita uma claim somente quando a lane terminou todos os arquivos pedidos no mesmo HEAD e ambiente. Um arquivo
|
|
779
947
|
* inteiro verde e evidencia mais forte que `--test-name-pattern`: o padrao fica registrado em `nomes`.
|
|
780
948
|
*/
|
|
781
949
|
function executorDaClaimPelasLanes(base, evidencias, esperado) {
|
|
782
|
-
const assinatura = assinaturaDoAmbiente(esperado.ambiente);
|
|
783
950
|
return (nome, comando, cwd) => {
|
|
784
951
|
const executar = esperado.executorEquivalente ?? base;
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
return
|
|
790
|
-
const planejados = [];
|
|
791
|
-
for (const elo of elos) {
|
|
792
|
-
if (comandoGrepQ(elo)) {
|
|
793
|
-
planejados.push({ tipo: 'grep', comando: elo });
|
|
794
|
-
continue;
|
|
795
|
-
}
|
|
796
|
-
const tap = auditoriaTap(elo, cwd);
|
|
797
|
-
const pedido = tap?.pedido ?? comandoNodeTest(elo, cwd);
|
|
798
|
-
if (!pedido)
|
|
799
|
-
return base(nome, comando, cwd);
|
|
800
|
-
const usadas = evidencias.filter((e) => e.head === esperado.head && e.ambiente === assinatura
|
|
801
|
-
&& e.testes.some((t) => pedido.arquivos.includes(t.arquivo)));
|
|
802
|
-
const cobertos = pedido.arquivos.every((arquivo) => {
|
|
803
|
-
const vistos = usadas.flatMap((e) => e.testes.filter((t) => t.arquivo === arquivo));
|
|
804
|
-
if (vistos.length === 0 || vistos.some((t) => t.estado === 'fail'))
|
|
805
|
-
return false;
|
|
806
|
-
if (vistos.some((t) => t.estado === 'skip' || t.estado === 'todo'))
|
|
807
|
-
return false;
|
|
808
|
-
if (!tap)
|
|
809
|
-
return true;
|
|
810
|
-
const doArquivo = vistos.filter((t) => t.nesting === 0);
|
|
811
|
-
return doArquivo.filter((t) => t.estado === 'pass' && new RegExp(`^${tap.prefixo}[0-9]+:`).test(t.nome)).length >= tap.minimo;
|
|
812
|
-
});
|
|
813
|
-
if (!cobertos)
|
|
814
|
-
return executar(nome, comando, cwd);
|
|
815
|
-
const nomes = tap ? pedido.arquivos.map((a) => `${a}:${tap.prefixo}>=${tap.minimo};sem-falha;sem-skip`) : undefined;
|
|
816
|
-
planejados.push({ tipo: 'lane', resultado: resultadoReaproveitado(nome, elo, pedido, usadas, { head: esperado.head, ambiente: assinatura }, nomes) });
|
|
952
|
+
const plano = planejarClaimPelasLanes(comando, cwd, evidencias, esperado);
|
|
953
|
+
if (!plano.decisao.reaproveitada) {
|
|
954
|
+
const r = (plano.usarExecutorEquivalente ? executar : base)(nome, comando, cwd);
|
|
955
|
+
// Comando fora da gramatica das lanes nao foi recusado por elas: ele segue sem inventar uma decisao nominal.
|
|
956
|
+
return plano.candidata ? { ...r, lane: plano.decisao } : r;
|
|
817
957
|
}
|
|
818
958
|
let duracaoMs = 0;
|
|
819
959
|
const resumos = [];
|
|
820
960
|
let ultimo = null;
|
|
821
961
|
const lanes = new Set(), arquivos = new Set(), nomes = new Set();
|
|
822
|
-
for (const item of
|
|
823
|
-
const r = item.tipo === 'grep' ? base(nome, item.comando, cwd)
|
|
962
|
+
for (const item of plano.elos) {
|
|
963
|
+
const r = item.tipo === 'grep' ? base(nome, item.comando, cwd)
|
|
964
|
+
: resultadoReaproveitado(nome, item.comando, item.pedido, item.usadas, { head: esperado.head, ambiente: plano.decisao.ambiente }, item.nomes);
|
|
824
965
|
duracaoMs += r.duracaoMs ?? 0;
|
|
825
966
|
resumos.push(r.resumo);
|
|
826
967
|
ultimo = r;
|
|
827
968
|
if (!r.ok)
|
|
828
|
-
return { ...r, comando, comandoExecutado: item.
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
969
|
+
return { ...r, comando, comandoExecutado: item.comando, duracaoMs, lane: plano.decisao };
|
|
970
|
+
if (r.lane?.reaproveitada) {
|
|
971
|
+
for (const lane of r.lane.lanes)
|
|
972
|
+
lanes.add(lane);
|
|
973
|
+
for (const arquivo of r.lane.arquivos)
|
|
974
|
+
arquivos.add(arquivo);
|
|
975
|
+
for (const n of r.lane.nomes)
|
|
976
|
+
nomes.add(n);
|
|
977
|
+
}
|
|
835
978
|
}
|
|
836
979
|
if (!ultimo || lanes.size === 0)
|
|
837
980
|
return base(nome, comando, cwd);
|
|
838
981
|
return { ...ultimo, nome, comando, duracaoMs, resumo: resumos.filter(Boolean).join('\n'),
|
|
839
|
-
lane: { reaproveitada: true, lanes: [...lanes], arquivos: [...arquivos], nomes: [...nomes], head: esperado.head,
|
|
982
|
+
lane: { reaproveitada: true, lanes: [...lanes], arquivos: [...arquivos], nomes: [...nomes], head: esperado.head,
|
|
983
|
+
ambiente: plano.decisao.ambiente } };
|
|
840
984
|
};
|
|
841
985
|
}
|
package/dist/check-paralelo.js
CHANGED
|
@@ -34,10 +34,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.argvDoItem = argvDoItem;
|
|
37
|
+
exports.limiteDeVagas = limiteDeVagas;
|
|
37
38
|
exports.workersDaLane = workersDaLane;
|
|
38
39
|
exports.chaveDoPerfilIntegracao = chaveDoPerfilIntegracao;
|
|
39
40
|
exports.picoDaArvoreParaPerfil = picoDaArvoreParaPerfil;
|
|
40
41
|
exports.picoCpuPorFonte = picoCpuPorFonte;
|
|
42
|
+
exports.devolucoesDaReserva = devolucoesDaReserva;
|
|
43
|
+
exports.aplicarDevolucaoDaReserva = aplicarDevolucaoDaReserva;
|
|
44
|
+
exports.aplicarPlanoCpu = aplicarPlanoCpu;
|
|
41
45
|
exports.esperaDoGrupo = esperaDoGrupo;
|
|
42
46
|
exports.rodarEmParalelo = rodarEmParalelo;
|
|
43
47
|
/**
|
|
@@ -58,19 +62,21 @@ const os = __importStar(require("node:os"));
|
|
|
58
62
|
const path = __importStar(require("node:path"));
|
|
59
63
|
const agendador_cpu_1 = require("./agendador-cpu");
|
|
60
64
|
const integracoes_locais_1 = require("./integracoes-locais");
|
|
65
|
+
const ambiente_funcional_1 = require("./ambiente-funcional");
|
|
61
66
|
const TIMEOUT_BIN = '/usr/bin/timeout';
|
|
62
67
|
const NICE_BIN = '/usr/bin/nice';
|
|
63
68
|
const TIME_BIN = '/usr/bin/time';
|
|
69
|
+
const BASH_BIN = '/usr/bin/bash';
|
|
64
70
|
const CONTRATO_PERFIL_INTEGRACAO = 'ork.check.cpu-integracao/v2';
|
|
65
71
|
const INTERVALO_CPU_MS = 100;
|
|
66
72
|
/** O argv de um item: `nice` (se pedido e existir), `timeout(1)` (se existir) e o `argv` do item ou `bash -c`. */
|
|
67
73
|
function argvDoItem(item, comGrupo = fs.existsSync(TIMEOUT_BIN), comNice = fs.existsSync(NICE_BIN)) {
|
|
68
|
-
const programa = item.argv?.length ? item.argv : ['
|
|
74
|
+
const programa = item.argv?.length ? item.argv : [BASH_BIN, '--noprofile', '--norc', '-c', item.comando];
|
|
69
75
|
const base = comGrupo ? [TIMEOUT_BIN, '--kill-after=15', `${Math.ceil(item.prazoMs / 1000)}`, ...programa] : programa;
|
|
70
76
|
return item.nice && comNice ? [NICE_BIN, '-n', String(item.nice), ...base] : base;
|
|
71
77
|
}
|
|
72
78
|
/** As vagas do grupo: no maximo `max` comandos ao mesmo tempo, na ordem em que pediram. */
|
|
73
|
-
function
|
|
79
|
+
function limiteDeVagas(max) {
|
|
74
80
|
let livres = max;
|
|
75
81
|
const fila = [];
|
|
76
82
|
return {
|
|
@@ -125,7 +131,7 @@ function workersDaLane(item, plano, cpus, totalDeLanes) {
|
|
|
125
131
|
if (item.papelCpu === 'hermetica')
|
|
126
132
|
return (0, agendador_cpu_1.workersDaReserva)(plano.cpusHermetica);
|
|
127
133
|
if (item.papelCpu === 'integracao')
|
|
128
|
-
return (0, agendador_cpu_1.workersDaReserva)(plano.
|
|
134
|
+
return (0, agendador_cpu_1.workersDaReserva)(plano.cpusIntegracaoEmUso);
|
|
129
135
|
// Outros fecham a cadeia depois que as reservas da varredura e da integracao voltaram.
|
|
130
136
|
return capacidade;
|
|
131
137
|
}
|
|
@@ -255,6 +261,25 @@ function nomesDosContainers(arquivo) {
|
|
|
255
261
|
return [];
|
|
256
262
|
}
|
|
257
263
|
}
|
|
264
|
+
function evidenciaDaCotaDocker(arquivo) {
|
|
265
|
+
try {
|
|
266
|
+
const evidencias = fs.readFileSync(arquivo, 'utf8').split('\n').filter(Boolean).flatMap((linha) => {
|
|
267
|
+
try {
|
|
268
|
+
const r = JSON.parse(linha);
|
|
269
|
+
return r.tipo === 'cota-docker' && ['aplicada', 'fallback'].includes(String(r.estado))
|
|
270
|
+
&& Number.isFinite(r.cota) && Number(r.cota) > 0 && typeof r.motivo === 'string'
|
|
271
|
+
? [{ estado: r.estado, cota: Number(r.cota), motivo: r.motivo }] : [];
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return evidencias.find((x) => x.estado === 'fallback') ?? evidencias.at(-1);
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
258
283
|
function arquivoCpuDoContainer(nome) {
|
|
259
284
|
const r = (0, node_child_process_1.spawnSync)('docker', ['inspect', '--format', '{{.Id}} {{.State.Pid}}', nome], { encoding: 'utf8', timeout: 1000 });
|
|
260
285
|
const [id = '', pidTexto = ''] = r.stdout?.trim().split(/\s+/) ?? [];
|
|
@@ -394,11 +419,15 @@ async function rodarEspecificacao(esp) {
|
|
|
394
419
|
Object.assign(medicao, { cpusIntegracao: perfil.picoCpusDaArvore, fonteCpusIntegracao: 'medida' });
|
|
395
420
|
else if (medicao.cpusIntegracao === undefined)
|
|
396
421
|
Object.assign(medicao, { cpusIntegracao: null, fonteCpusIntegracao: 'conservadora' });
|
|
397
|
-
const
|
|
422
|
+
const nomesDasClaims = new Set(esp.itens.filter((i) => i.papelCpu === 'claim').map((i) => i.nome));
|
|
423
|
+
const cadeiasDeClaims = esp.itens.filter((i) => i.papelCpu === 'claim'
|
|
424
|
+
&& !(i.apos ?? []).some((nome) => nomesDasClaims.has(nome))).length;
|
|
425
|
+
const plano = usaAgendador ? (0, agendador_cpu_1.planejarCpuDoGrupo)(cpus, medicao, { cadeiasDeClaims }) : undefined;
|
|
398
426
|
const sinalDaReserva = plano && !plano.saturado ? path.join(esp.dir, 'reserva-devolvida.json') : undefined;
|
|
399
|
-
const itens = plano ?
|
|
427
|
+
const itens = plano ? aplicarPlanoCpu(esp.itens, plano, sinalDaReserva) : esp.itens;
|
|
400
428
|
const tetoInformado = esp.maxParalelo && esp.maxParalelo > 0 ? esp.maxParalelo : itens.length;
|
|
401
|
-
const limite =
|
|
429
|
+
const limite = limiteDeVagas(Math.max(1, Math.min(tetoInformado, (0, agendador_cpu_1.workersDaReserva)(cpus))));
|
|
430
|
+
const limiteDasClaims = plano?.cpusClaims ? limiteDeVagas(plano.cpusClaims) : null;
|
|
402
431
|
const exclusivos = recursosExclusivos();
|
|
403
432
|
const totalDeLanes = itens.filter((item) => item.nome.startsWith('test:')).length;
|
|
404
433
|
const porNome = new Map();
|
|
@@ -416,12 +445,16 @@ async function rodarEspecificacao(esp) {
|
|
|
416
445
|
await vezDoItem(itens[j], j);
|
|
417
446
|
}
|
|
418
447
|
const soltarRecursos = await exclusivos.tomar(item.recursos ?? []);
|
|
448
|
+
if (item.papelCpu === 'claim')
|
|
449
|
+
await limiteDasClaims?.tomar();
|
|
419
450
|
await limite.tomar();
|
|
420
451
|
try {
|
|
421
452
|
return await lancar(item, i);
|
|
422
453
|
}
|
|
423
454
|
finally {
|
|
424
455
|
limite.soltar();
|
|
456
|
+
if (item.papelCpu === 'claim')
|
|
457
|
+
limiteDasClaims?.soltar();
|
|
425
458
|
soltarRecursos();
|
|
426
459
|
}
|
|
427
460
|
});
|
|
@@ -429,35 +462,33 @@ async function rodarEspecificacao(esp) {
|
|
|
429
462
|
return vez;
|
|
430
463
|
};
|
|
431
464
|
const todos = Promise.all(itens.map(vezDoItem));
|
|
432
|
-
const reservados = itens.filter((item) =>
|
|
465
|
+
const reservados = itens.filter((item) => ['varredura', 'integracao', 'claim'].includes(item.papelCpu ?? ''));
|
|
433
466
|
const devolverReserva = async () => {
|
|
434
|
-
if (!sinalDaReserva || !reservados.
|
|
435
|
-
|| !reservados.some((item) => item.papelCpu === 'integracao'))
|
|
467
|
+
if (!sinalDaReserva || !reservados.length)
|
|
436
468
|
return;
|
|
437
469
|
const varreduras = reservados.filter((item) => item.papelCpu === 'varredura');
|
|
438
470
|
const integracoes = reservados.filter((item) => item.papelCpu === 'integracao');
|
|
439
|
-
const
|
|
471
|
+
const claims = reservados.filter((item) => item.papelCpu === 'claim');
|
|
472
|
+
let vagas = plano.cpusHermetica;
|
|
473
|
+
const devolucoes = devolucoesDaReserva(plano);
|
|
474
|
+
const degrau = async (origem, pecas) => {
|
|
475
|
+
if (!pecas.length)
|
|
476
|
+
return;
|
|
440
477
|
await Promise.all(pecas.map((item) => vezDoItem(item, indice.get(item.nome))));
|
|
441
|
-
|
|
442
|
-
|
|
478
|
+
const registro = aplicarDevolucaoDaReserva(vagas, plano.capacidadeEfetiva, origem, devolucoes[origem], new Date().toISOString());
|
|
479
|
+
vagas = registro.vagas;
|
|
480
|
+
fs.appendFileSync(sinalDaReserva, JSON.stringify(registro) + '\n', { mode: 0o600 });
|
|
443
481
|
};
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
vagas = Math.min(plano.cpus, vagas + libera);
|
|
455
|
-
fs.appendFileSync(sinalDaReserva, JSON.stringify({ contrato: 'ork.check.reserva-devolvida/v2', origem,
|
|
456
|
-
em: new Date().toISOString(), vagasGanhas: vagas - antes, vagas }) + '\n', { mode: 0o600 });
|
|
457
|
-
};
|
|
458
|
-
await Promise.all([aoTerminar('varredura', varreduras, plano.cpusDaFatia),
|
|
459
|
-
aoTerminar('integracao', integracoes, plano.cpusIntegracao)]);
|
|
460
|
-
}
|
|
482
|
+
const devolverInfraestrutura = plano.integracaoEmParalelo
|
|
483
|
+
? Promise.all([degrau('varredura', varreduras), degrau('integracao', integracoes)])
|
|
484
|
+
: (async () => {
|
|
485
|
+
await degrau('varredura', varreduras);
|
|
486
|
+
await degrau('integracao', integracoes);
|
|
487
|
+
})();
|
|
488
|
+
await Promise.all([
|
|
489
|
+
degrau('claims', claims),
|
|
490
|
+
devolverInfraestrutura,
|
|
491
|
+
]);
|
|
461
492
|
};
|
|
462
493
|
const [, terminados] = await Promise.all([devolverReserva(), todos]);
|
|
463
494
|
const medidaDaIntegracao = terminados.find((t) => t.medicaoCpu)?.medicaoCpu;
|
|
@@ -465,6 +496,10 @@ async function rodarEspecificacao(esp) {
|
|
|
465
496
|
plano.medicaoCpuIntegracao = medidaDaIntegracao;
|
|
466
497
|
gravarPerfilIntegracao(chaveDoPerfil, cpus, medidaDaIntegracao);
|
|
467
498
|
}
|
|
499
|
+
if (plano) {
|
|
500
|
+
const cotas = terminados.flatMap((t) => t.cotaDocker ? [t.cotaDocker] : []);
|
|
501
|
+
plano.cotaDosContainersDaIntegracao = cotas.find((x) => x.estado === 'fallback') ?? cotas.at(-1);
|
|
502
|
+
}
|
|
468
503
|
fs.writeFileSync(path.join(esp.dir, 'resultado.json'), JSON.stringify({ terminados, ...(plano ? { agendamentoCpu: plano } : {}) }), { mode: 0o600 });
|
|
469
504
|
function lancar(item, i) {
|
|
470
505
|
return new Promise((resolve) => {
|
|
@@ -497,14 +532,18 @@ async function rodarEspecificacao(esp) {
|
|
|
497
532
|
catch { /* medida incompleta abaixo */ }
|
|
498
533
|
}
|
|
499
534
|
const medicaoCpu = medirIntegracao && sonda ? sonda.fechar(arvoreMs, t.duracaoMs) : undefined;
|
|
500
|
-
|
|
535
|
+
const cotaDocker = medirIntegracao ? evidenciaDaCotaDocker(registro) : undefined;
|
|
536
|
+
fim = { ...t, ...(medicaoCpu ? { medicaoCpu } : {}), ...(cotaDocker ? { cotaDocker } : {}) };
|
|
501
537
|
fs.closeSync(saida);
|
|
502
538
|
fs.closeSync(erro);
|
|
503
539
|
resolve(fim);
|
|
504
540
|
};
|
|
505
541
|
// Sem o `timeout(1)`, o proprio auxiliar mata pelo prazo, como o `spawnSync` do `exec` faria.
|
|
506
542
|
const workers = workersDaLane(item, plano, cpus, totalDeLanes);
|
|
507
|
-
const
|
|
543
|
+
const baseDoFilho = item.ambienteFuncional
|
|
544
|
+
? (0, ambiente_funcional_1.ambienteFuncionalDosTestes)(process.env, item.ambiente ?? {})
|
|
545
|
+
: { ...process.env, ...item.ambiente };
|
|
546
|
+
const ambiente = { ...baseDoFilho, ...(workers === null ? {} : { [agendador_cpu_1.ENV_WORKERS]: String(workers) }),
|
|
508
547
|
...(medirIntegracao ? { [agendador_cpu_1.ENV_CONTAINERS_DA_LANE]: registro } : {}) };
|
|
509
548
|
const filho = (0, node_child_process_1.spawn)(bin, args, { cwd: item.cwd, env: ambiente, stdio: ['ignore', saida, erro],
|
|
510
549
|
...(comGrupo ? {} : { timeout: item.prazoMs, killSignal: 'SIGTERM' }) });
|
|
@@ -521,13 +560,26 @@ async function rodarEspecificacao(esp) {
|
|
|
521
560
|
});
|
|
522
561
|
}
|
|
523
562
|
}
|
|
524
|
-
/**
|
|
525
|
-
function
|
|
563
|
+
/** Reservas que cada peça devolve; a integração encadeada usa a fatia da varredura desde que ela termina. */
|
|
564
|
+
function devolucoesDaReserva(plano) {
|
|
565
|
+
return {
|
|
566
|
+
varredura: plano.integracaoEmParalelo ? plano.cpusDaFatia : 0,
|
|
567
|
+
integracao: plano.cpusIntegracaoEmUso,
|
|
568
|
+
claims: plano.cpusClaims,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/** Transição pura do sinal; relógio e estado entram por argumento para a prova não depender da parede da máquina. */
|
|
572
|
+
function aplicarDevolucaoDaReserva(vagasAtuais, capacidade, origem, libera, em) {
|
|
573
|
+
const vagas = Math.min(capacidade, vagasAtuais + Math.max(0, libera));
|
|
574
|
+
return { contrato: 'ork.check.reserva-devolvida/v2', origem, em, vagasGanhas: vagas - vagasAtuais, vagas };
|
|
575
|
+
}
|
|
576
|
+
function aplicarPlanoCpu(originais, plano, sinalDaReserva) {
|
|
526
577
|
const itens = originais.map((item) => ({ ...item, ambiente: { ...item.ambiente } }));
|
|
527
578
|
const porPapel = (papel) => itens.filter((i) => i.papelCpu === papel);
|
|
528
579
|
const hermetica = porPapel('hermetica')[0];
|
|
529
580
|
const varredura = porPapel('varredura')[0];
|
|
530
581
|
const integracoes = porPapel('integracao');
|
|
582
|
+
const claims = porPapel('claim');
|
|
531
583
|
const outros = porPapel('outro');
|
|
532
584
|
const depender = (item, nomes) => {
|
|
533
585
|
if (!item)
|
|
@@ -540,7 +592,7 @@ function aplicarPlano(originais, plano, sinalDaReserva) {
|
|
|
540
592
|
};
|
|
541
593
|
const cpusContainer = String(plano.cpusPorContainer);
|
|
542
594
|
ambiente(hermetica, { [agendador_cpu_1.ENV_CPUS_DA_LANE]: String(plano.cpusHermetica), [agendador_cpu_1.ENV_CPUS_DOCKER]: cpusContainer,
|
|
543
|
-
...(sinalDaReserva && varredura
|
|
595
|
+
...(sinalDaReserva && (varredura || integracoes.length > 0 || claims.length > 0) ? {
|
|
544
596
|
[agendador_cpu_1.ENV_CPUS_DA_LANE_APOS_VARREDURA]: String(plano.cpusHermeticaAposVarredura),
|
|
545
597
|
[agendador_cpu_1.ENV_CPUS_DA_LANE_APOS_RESERVA]: String(plano.cpusHermeticaAposReserva), [agendador_cpu_1.ENV_SINAL_DA_RESERVA]: sinalDaReserva,
|
|
546
598
|
} : {}) });
|
|
@@ -549,11 +601,21 @@ function aplicarPlano(originais, plano, sinalDaReserva) {
|
|
|
549
601
|
[agendador_cpu_1.ENV_CPUS_DOCKER]: cpusContainer,
|
|
550
602
|
});
|
|
551
603
|
let anteriorIntegracao;
|
|
604
|
+
const workersDaIntegracao = (0, agendador_cpu_1.workersDaReserva)(plano.cpusIntegracaoEmUso);
|
|
605
|
+
const cotaDoContainerDaIntegracao = String(Math.floor((plano.cpusIntegracaoEmUso / workersDaIntegracao) * 100) / 100);
|
|
552
606
|
for (const item of integracoes) {
|
|
553
|
-
ambiente(item, { [agendador_cpu_1.ENV_CPUS_DA_LANE]: String(plano.
|
|
607
|
+
ambiente(item, { [agendador_cpu_1.ENV_CPUS_DA_LANE]: String(plano.cpusIntegracaoEmUso),
|
|
608
|
+
[agendador_cpu_1.ENV_CPUS_DOCKER]: cotaDoContainerDaIntegracao });
|
|
554
609
|
depender(item, [anteriorIntegracao ?? (plano.integracaoEmParalelo ? undefined : varredura?.nome)]);
|
|
555
610
|
anteriorIntegracao = item.nome;
|
|
556
611
|
}
|
|
612
|
+
if (plano.cpusClaims === 0) {
|
|
613
|
+
let anteriorClaim = anteriorIntegracao;
|
|
614
|
+
for (const item of claims) {
|
|
615
|
+
depender(item, [anteriorClaim ?? varredura?.nome, hermetica?.nome]);
|
|
616
|
+
anteriorClaim = item.nome;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
557
619
|
if (plano.saturado)
|
|
558
620
|
depender(varredura, [hermetica?.nome]);
|
|
559
621
|
// Comandos sem papel conhecido nao disputam CPU com as tres pecas: fecham a cadeia, um por vez.
|
|
@@ -585,7 +647,10 @@ function rodarEmParalelo(itens, ambiente = process.env, opcoes = {}) {
|
|
|
585
647
|
...(opcoes.cpus ? { cpus: opcoes.cpus } : {}), ...(opcoes.medicaoCpu ? { medicaoCpu: opcoes.medicaoCpu } : {}) };
|
|
586
648
|
fs.writeFileSync(especificacao, JSON.stringify(esp), { mode: 0o600 });
|
|
587
649
|
const prazo = esperaDoGrupo(itens, opcoes.maxParalelo) + 60_000;
|
|
588
|
-
const
|
|
650
|
+
const ambienteDoAuxiliar = { ...ambiente };
|
|
651
|
+
// Contexto privado do harness: herdá-lo faz este Node comum falar o protocolo binário de um filho de `node --test`.
|
|
652
|
+
delete ambienteDoAuxiliar.NODE_TEST_CONTEXT;
|
|
653
|
+
const r = (0, node_child_process_1.spawnSync)(process.execPath, [__filename, especificacao], { env: ambienteDoAuxiliar, encoding: 'utf8', timeout: prazo,
|
|
589
654
|
killSignal: 'SIGKILL' });
|
|
590
655
|
let lido = { terminados: [] };
|
|
591
656
|
try {
|