create-harness-btp 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI/.opencode/agents/deploy-cf.md +7 -7
- package/AI/.opencode/agents/orchestrator.md +1 -1
- package/AI/.opencode/commands/verify.md +1 -1
- package/AI/.opencode/skills/cf-cli/SKILL.md +41 -0
- package/AI/.opencode/skills/mta-build/SKILL.md +4 -4
- package/AI/AGENTS.md +22 -21
- package/AI/INICIO.md +34 -15
- package/AI/docs/cf-safe.md +5 -5
- package/AI/docs/flujo-etapas.md +1 -1
- package/AI/docs/fuentes-oficiales.md +27 -0
- package/AI/docs/habilitacion-trial.md +2 -2
- package/AI/docs/prerequisitos.md +60 -0
- package/AI/scripts/cf-safe.ps1 +14 -14
- package/AI/scripts/check-docs.ps1 +78 -0
- package/AI/scripts/check-docs.sh +42 -0
- package/AI/scripts/check-prereqs.ps1 +129 -0
- package/AI/scripts/check-prereqs.sh +136 -0
- package/AI/scripts/provision-espacio.template.ps1 +10 -10
- package/README.md +106 -38
- package/VERSION +1 -1
- package/install.ps1 +6 -0
- package/install.sh +15 -2
- package/package.json +1 -1
- package/AI/.opencode/skills/sap-cf-cli/SKILL.md +0 -41
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<# Verificación de fuentes oficiales del harness BTP (PowerShell 5.1).
|
|
2
|
+
Uso:
|
|
3
|
+
powershell -ExecutionPolicy Bypass -File AI/scripts/check-docs.ps1
|
|
4
|
+
- Comprueba cada URL de AI/docs/fuentes-oficiales.md con UN solo intento
|
|
5
|
+
HTTP GET (timeout 15 s, sin reintentos ni bucles).
|
|
6
|
+
- Emite OK / ROTO / PENDIENTE por fuente + fecha.
|
|
7
|
+
- OK: HTTP 2xx-3xx. ROTO: HTTP 4xx-5xx confirmado. PENDIENTE: sin respuesta
|
|
8
|
+
(timeout/DNS; avisa pero no falla: re-verificar y actualizar la fecha).
|
|
9
|
+
- exit 1 si alguna fuente REQUERIDA está ROTA. exit 0 en otro caso.
|
|
10
|
+
- Cero secretos: solo peticiones GET anónimas, sin credenciales. #>
|
|
11
|
+
|
|
12
|
+
$ErrorActionPreference = "Continue"
|
|
13
|
+
$Fecha = (Get-Date).ToString("yyyy-MM-dd")
|
|
14
|
+
$FallosReq = 0
|
|
15
|
+
|
|
16
|
+
function Test-Doc {
|
|
17
|
+
param([string]$Nombre, [string]$Url, [string]$Nivel)
|
|
18
|
+
try {
|
|
19
|
+
$r = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 15 -MaximumRedirection 5
|
|
20
|
+
$code = [int]$r.StatusCode
|
|
21
|
+
if ($code -ge 200 -and $code -lt 400) {
|
|
22
|
+
Write-Output ("{0} | OK | {1} | {2}" -f $Nombre, $Fecha, $Url)
|
|
23
|
+
} else {
|
|
24
|
+
Write-Output ("{0} | ROTO (HTTP {1}) | {2} | {3}" -f $Nombre, $code, $Fecha, $Url)
|
|
25
|
+
if ($Nivel -eq "REQ") { $script:FallosReq++ }
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
$resp = $_.Exception.Response
|
|
29
|
+
if ($resp -ne $null) {
|
|
30
|
+
try { $code = [int]$resp.StatusCode } catch { $code = 0 }
|
|
31
|
+
# .NET Framework (PS 5.1) no sigue HTTP 308 automáticamente aunque se pida
|
|
32
|
+
# -MaximumRedirection: se sigue la redirección UNA sola vez, sin bucles.
|
|
33
|
+
if ($code -eq 308) {
|
|
34
|
+
try {
|
|
35
|
+
$loc = $resp.Headers["Location"]
|
|
36
|
+
if ($loc -notmatch "^https?://") {
|
|
37
|
+
$base = ([System.Uri]$Url).GetLeftPart([System.UriPartial]::Authority)
|
|
38
|
+
$loc = $base + $loc
|
|
39
|
+
}
|
|
40
|
+
$r2 = Invoke-WebRequest -Uri $loc -UseBasicParsing -TimeoutSec 15 -MaximumRedirection 5
|
|
41
|
+
$code2 = [int]$r2.StatusCode
|
|
42
|
+
if ($code2 -ge 200 -and $code2 -lt 400) {
|
|
43
|
+
Write-Output ("{0} | OK (redirige HTTP 308 a {1}) | {2} | {3}" -f $Nombre, $loc, $Fecha, $Url)
|
|
44
|
+
} else {
|
|
45
|
+
Write-Output ("{0} | ROTO (HTTP {1} tras 308) | {2} | {3}" -f $Nombre, $code2, $Fecha, $Url)
|
|
46
|
+
if ($Nivel -eq "REQ") { $script:FallosReq++ }
|
|
47
|
+
}
|
|
48
|
+
} catch {
|
|
49
|
+
Write-Output ("{0} | PENDIENTE (308 sin destino legible) | {1} | {2}" -f $Nombre, $Fecha, $Url)
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
Write-Output ("{0} | ROTO (HTTP {1}) | {2} | {3}" -f $Nombre, $code, $Fecha, $Url)
|
|
53
|
+
if ($Nivel -eq "REQ") { $script:FallosReq++ }
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
Write-Output ("{0} | PENDIENTE (sin respuesta: timeout/DNS) | {1} | {2}" -f $Nombre, $Fecha, $Url)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Write-Output ""
|
|
62
|
+
Write-Output ("Fuentes oficiales del harness BTP ({0})" -f $Fecha)
|
|
63
|
+
Test-Doc "cf-cli" "https://docs.cloudfoundry.org/cf-cli/install-go-cli.html" "REQ"
|
|
64
|
+
Test-Doc "mbt" "https://github.com/SAP/cloud-mta-build-tool" "REQ"
|
|
65
|
+
Test-Doc "cap" "https://cap.cloud.sap/docs/get-started/" "REQ"
|
|
66
|
+
Test-Doc "ui5-cli" "https://ui5.github.io/cli/" "REQ"
|
|
67
|
+
Test-Doc "opencode" "https://docs.opencode.ai/docs" "REQ"
|
|
68
|
+
Test-Doc "npm-trusted-publishers" "https://docs.npmjs.com/trusted-publishers/" "REQ"
|
|
69
|
+
Test-Doc "op-1password" "https://developer.1password.com/docs/cli/get-started/" "OPT"
|
|
70
|
+
Write-Output ""
|
|
71
|
+
|
|
72
|
+
if ($FallosReq -gt 0) {
|
|
73
|
+
Write-Output ("RESULTADO: {0} fuente(s) REQUERIDA(s) ROTA(s). Revisa AI/docs/fuentes-oficiales.md y actualiza la fecha tras corregir." -f $FallosReq)
|
|
74
|
+
exit 1
|
|
75
|
+
} else {
|
|
76
|
+
Write-Output "RESULTADO: ninguna fuente REQUERIDA rota. Actualiza la columna de fecha en AI/docs/fuentes-oficiales.md."
|
|
77
|
+
exit 0
|
|
78
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Verificación de fuentes oficiales del harness BTP (POSIX sh).
|
|
3
|
+
# Uso: sh AI/scripts/check-docs.sh
|
|
4
|
+
# - Comprueba cada URL de AI/docs/fuentes-oficiales.md con UN solo intento
|
|
5
|
+
# HTTP GET (timeout 15 s, --retry 0, sin reintentos ni bucles).
|
|
6
|
+
# - Emite OK / ROTO / PENDIENTE por fuente + fecha.
|
|
7
|
+
# - OK: HTTP 2xx-3xx. ROTO: HTTP 4xx-5xx confirmado. PENDIENTE: sin respuesta
|
|
8
|
+
# (timeout/DNS; avisa pero no falla: re-verificar y actualizar la fecha).
|
|
9
|
+
# - exit 1 si alguna fuente REQUERIDA está ROTA. exit 0 en otro caso.
|
|
10
|
+
# - Cero secretos: solo peticiones GET anónimas, sin credenciales.
|
|
11
|
+
FECHA=$(date +%F)
|
|
12
|
+
FALLOS_REQ=0
|
|
13
|
+
# check_doc <nombre> <url> <nivel>: un solo intento, sin bucles.
|
|
14
|
+
check_doc() {
|
|
15
|
+
_code=$(curl -s -o /dev/null -w "%{http_code}" -L --max-time 15 --retry 0 "$2" 2>/dev/null)
|
|
16
|
+
_rc=$?
|
|
17
|
+
if [ $_rc -ne 0 ]; then
|
|
18
|
+
echo "$1 | PENDIENTE (sin respuesta: timeout/DNS) | $FECHA | $2"
|
|
19
|
+
elif [ "$_code" -ge 200 ] && [ "$_code" -lt 400 ]; then
|
|
20
|
+
echo "$1 | OK | $FECHA | $2"
|
|
21
|
+
else
|
|
22
|
+
echo "$1 | ROTO (HTTP $_code) | $FECHA | $2"
|
|
23
|
+
if [ "$3" = "REQ" ]; then FALLOS_REQ=$((FALLOS_REQ + 1)); fi
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
echo ""
|
|
27
|
+
echo "Fuentes oficiales del harness BTP ($FECHA)"
|
|
28
|
+
check_doc "cf-cli" "https://docs.cloudfoundry.org/cf-cli/install-go-cli.html" "REQ"
|
|
29
|
+
check_doc "mbt" "https://github.com/SAP/cloud-mta-build-tool" "REQ"
|
|
30
|
+
check_doc "cap" "https://cap.cloud.sap/docs/get-started/" "REQ"
|
|
31
|
+
check_doc "ui5-cli" "https://ui5.github.io/cli/" "REQ"
|
|
32
|
+
check_doc "opencode" "https://docs.opencode.ai/docs" "REQ"
|
|
33
|
+
check_doc "npm-trusted-publishers" "https://docs.npmjs.com/trusted-publishers/" "REQ"
|
|
34
|
+
check_doc "op-1password" "https://developer.1password.com/docs/cli/get-started/" "OPT"
|
|
35
|
+
echo ""
|
|
36
|
+
if [ "$FALLOS_REQ" -gt 0 ]; then
|
|
37
|
+
echo "RESULTADO: $FALLOS_REQ fuente(s) REQUERIDA(s) ROTA(s). Revisa AI/docs/fuentes-oficiales.md y actualiza la fecha tras corregir."
|
|
38
|
+
exit 1
|
|
39
|
+
else
|
|
40
|
+
echo "RESULTADO: ninguna fuente REQUERIDA rota. Actualiza la columna de fecha en AI/docs/fuentes-oficiales.md."
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<# Verificación de prerrequisitos del harness BTP (PowerShell 5.1).
|
|
2
|
+
Uso:
|
|
3
|
+
powershell -ExecutionPolicy Bypass -File AI/scripts/check-prereqs.ps1 [-WarnOnly] [-NeedMtar] [-NeedJava] [-NeedCds] [-NeedGh] [-NeedOp]
|
|
4
|
+
- Sin parámetros: comprueba REQUERIDO siempre + OPCIONAL como aviso.
|
|
5
|
+
- -NeedMtar/-NeedJava/-NeedCds/-NeedGh/-NeedOp: promocionan ese opcional a
|
|
6
|
+
REQUERIDO (usar cuando la spec del MVP lo exija).
|
|
7
|
+
- -WarnOnly: nunca falla (exit 0); lo usan install.ps1/install.sh al final.
|
|
8
|
+
NUNCA imprime secretos: solo ejecuta "<comando> --version" y cuenta archivos.
|
|
9
|
+
REQUERIDO faltante -> exit 1 (salvo -WarnOnly). Solo opcionales -> exit 0. #>
|
|
10
|
+
param(
|
|
11
|
+
[switch]$WarnOnly,
|
|
12
|
+
[switch]$NeedMtar,
|
|
13
|
+
[switch]$NeedJava,
|
|
14
|
+
[switch]$NeedCds,
|
|
15
|
+
[switch]$NeedGh,
|
|
16
|
+
[switch]$NeedOp
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
$ErrorActionPreference = "Continue"
|
|
20
|
+
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
21
|
+
$HarnessRoot = Split-Path -Parent (Split-Path -Parent $ScriptDir)
|
|
22
|
+
|
|
23
|
+
$rows = New-Object System.Collections.ArrayList
|
|
24
|
+
function Add-Row($tool, $need, $level, $state, $detail, $url) {
|
|
25
|
+
[void]$rows.Add([pscustomobject]@{
|
|
26
|
+
Herramienta = $tool; NecesariaPara = $need; Nivel = $level
|
|
27
|
+
Estado = $state; Detalle = $detail; Instalacion = $url
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Test-Tool($name, $versionArgs) {
|
|
32
|
+
try {
|
|
33
|
+
$cmd = Get-Command $name -ErrorAction Stop
|
|
34
|
+
try {
|
|
35
|
+
$out = & $cmd.Source @versionArgs 2>&1 | Select-Object -First 1
|
|
36
|
+
if ($out) { $out = "$out".Trim() } else { $out = "instalado" }
|
|
37
|
+
} catch { $out = "instalado (version no legible)" }
|
|
38
|
+
return @{ Found = $true; Detail = $out }
|
|
39
|
+
} catch { return @{ Found = $false; Detail = "no encontrado en PATH" } }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# --- REQUERIDO siempre ---
|
|
43
|
+
$r = Test-Tool "git" @("--version")
|
|
44
|
+
Add-Row "git" "control de versiones del MVP" "REQ" $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://git-scm.com/downloads"
|
|
45
|
+
|
|
46
|
+
$node = Test-Tool "node" @("--version")
|
|
47
|
+
$nodeOk = $false
|
|
48
|
+
if ($node.Found) {
|
|
49
|
+
try {
|
|
50
|
+
$m = [regex]::Match($node.Detail, "v?(\d+)\.")
|
|
51
|
+
if ($m.Success -and [int]$m.Groups[1].Value -ge 18) { $nodeOk = $true }
|
|
52
|
+
else { $node.Detail = "$($node.Detail) (se requiere Node 18+)" }
|
|
53
|
+
} catch { $node.Detail = "$($node.Detail) (no se pudo verificar major >= 18)" }
|
|
54
|
+
}
|
|
55
|
+
Add-Row "node 18+" "CAP, UI5, mbt, scripts del harness" "REQ" $(if ($nodeOk) { "OK" } else { "FALTA" }) $node.Detail "https://nodejs.org/"
|
|
56
|
+
|
|
57
|
+
$r = Test-Tool "cf" @("--version")
|
|
58
|
+
$cfState = if ($r.Found) { "OK" } else { "FALTA" }
|
|
59
|
+
if ($r.Found -and ($r.Detail -notmatch "(?i)cf version|cloudfoundry")) {
|
|
60
|
+
$r.Detail = "$($r.Detail) (AVISO: no parece el CLI de Cloud Foundry; si 'cf' es otra herramienta, define un alias al CLI real)"
|
|
61
|
+
$cfState = "FALTA"
|
|
62
|
+
}
|
|
63
|
+
Add-Row "cf" "despliegues y lecturas Cloud Foundry" "REQ" $cfState $r.Detail "https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"
|
|
64
|
+
|
|
65
|
+
$agentsOk = (Test-Path (Join-Path $HarnessRoot "AI\AGENTS.md")) -and ((Test-Path (Join-Path $HarnessRoot "AGENTS.md")) -or (Test-Path (Join-Path $HarnessRoot "templates\AGENTS.pointer.md")))
|
|
66
|
+
Add-Row "cliente agentico" "leer AGENTS.md y orquestar el flujo" "REQ" $(if ($agentsOk) { "OK" } else { "FALTA" }) $(if ($agentsOk) { "AGENTS.md + AI/AGENTS.md presentes; verifica que tu cliente los lea" } else { "falta AGENTS.md o AI/AGENTS.md (reinstala con install.ps1)" }) "https://docs.opencode.ai/ (o tu cliente agéntico)"
|
|
67
|
+
|
|
68
|
+
# --- OPCIONAL según MVP (promocionable con -Need*) ---
|
|
69
|
+
function Opt-Level($flag) { if ($flag) { "REQ" } else { "OPT" } }
|
|
70
|
+
|
|
71
|
+
$r = Test-Tool "mbt" @("--version")
|
|
72
|
+
Add-Row "mbt" "construir MTAR (solo si el MVP lo exige)" (Opt-Level $NeedMtar) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://github.com/SAP/cloud-mta-build-tool"
|
|
73
|
+
|
|
74
|
+
$r = Test-Tool "make" @("--version")
|
|
75
|
+
Add-Row "make" "requerido por mbt en el SO" $(if ($NeedMtar) { "REQ" } else { "OPT" }) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://www.gnu.org/software/make/"
|
|
76
|
+
|
|
77
|
+
$r = Test-Tool "java" @("-version")
|
|
78
|
+
Add-Row "java" "builds MTA Java (solo si la spec los pide)" (Opt-Level $NeedJava) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://adoptium.net/"
|
|
79
|
+
|
|
80
|
+
$r = Test-Tool "cds" @("--version")
|
|
81
|
+
if (-not $r.Found) { $r.Detail = "no global; valido via npx -p @sap/cds-dk (solo si la spec exige CAP)" }
|
|
82
|
+
Add-Row "cds" "proyectos CAP (solo si la spec lo exige)" (Opt-Level $NeedCds) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://cap.cloud.sap/docs/get-started/"
|
|
83
|
+
|
|
84
|
+
$r = Test-Tool "ui5" @("--version")
|
|
85
|
+
Add-Row "ui5" "proyectos UI5/Fiori (solo si la spec lo exige)" $(if ($NeedCds) { "REQ" } else { "OPT" }) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://ui5.github.io/cli/"
|
|
86
|
+
|
|
87
|
+
$r = Test-Tool "gh" @("--version")
|
|
88
|
+
Add-Row "gh" "repo remoto/PR (solo si la spec pide remoto)" (Opt-Level $NeedGh) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://cli.github.com/"
|
|
89
|
+
|
|
90
|
+
$r = Test-Tool "op" @("--version")
|
|
91
|
+
Add-Row "op (1Password)" "secretos de equipo (solo si el equipo lo adopta)" (Opt-Level $NeedOp) $(if ($r.Found) { "OK" } else { "FALTA" }) $r.Detail "https://developer.1password.com/docs/cli/get-started/"
|
|
92
|
+
|
|
93
|
+
# --- Harness: skills, agentes, fragmento MCP (presencia de archivos) ---
|
|
94
|
+
$skills = @()
|
|
95
|
+
$skillsDir = Join-Path $HarnessRoot "AI\.opencode\skills"
|
|
96
|
+
if (Test-Path $skillsDir) { $skills = Get-ChildItem $skillsDir -Directory | Where-Object { Test-Path (Join-Path $_.FullName "SKILL.md") } }
|
|
97
|
+
Add-Row "skills" "capacidades del harness (cf-cli, mta-build, ...)" "OPT" $(if ($skills.Count -gt 0) { "OK" } else { "FALTA" }) "$($skills.Count) skills con SKILL.md" "(vienen con AI/; reinstala con install.ps1 si FALTA)"
|
|
98
|
+
|
|
99
|
+
$agents = @()
|
|
100
|
+
$agentsDir = Join-Path $HarnessRoot "AI\.opencode\agents"
|
|
101
|
+
if (Test-Path $agentsDir) { $agents = Get-ChildItem $agentsDir -Filter "*.md" }
|
|
102
|
+
Add-Row "agents" "orquestador y especialistas" "OPT" $(if ($agents.Count -gt 0) { "OK" } else { "FALTA" }) "$($agents.Count) agentes .md" "(vienen con AI/; reinstala con install.ps1 si FALTA)"
|
|
103
|
+
|
|
104
|
+
$frag = Test-Path (Join-Path $HarnessRoot "AI\opencode.fragment.jsonc")
|
|
105
|
+
Add-Row "fragmento MCP" "plantilla MCP del proyecto destino" "OPT" $(if ($frag) { "OK" } else { "FALTA" }) $(if ($frag) { "AI/opencode.fragment.jsonc presente" } else { "no encontrado" }) "(viene con AI/; reinstala con install.ps1 si FALTA)"
|
|
106
|
+
|
|
107
|
+
# --- Tabla ---
|
|
108
|
+
Write-Output ""
|
|
109
|
+
Write-Output "Prerrequisitos del harness BTP (harness: $HarnessRoot)"
|
|
110
|
+
Write-Output ("{0,-14} | {1,-42} | {2,-5} | {3,-6} | {4}" -f "Herramienta", "Necesaria para", "Nivel", "Estado", "Detalle / Instalacion")
|
|
111
|
+
$rows | ForEach-Object {
|
|
112
|
+
Write-Output ("{0,-14} | {1,-42} | {2,-5} | {3,-6} | {4} [{5}]" -f $_.Herramienta, $_.NecesariaPara, $_.Nivel, $_.Estado, $_.Detalle, $_.Instalacion)
|
|
113
|
+
}
|
|
114
|
+
Write-Output ""
|
|
115
|
+
Write-Output "NOTA MCPs (no detectables desde shell): tu cliente agentico debe tener configurados"
|
|
116
|
+
Write-Output "sus propios MCPs: context7, codebase-memory, github, playwright, eslint, chrome-devtools."
|
|
117
|
+
Write-Output "Detalle y docs oficiales: AI/docs/prerequisitos.md"
|
|
118
|
+
|
|
119
|
+
$reqMissing = @($rows | Where-Object { $_.Nivel -eq "REQ" -and $_.Estado -eq "FALTA" })
|
|
120
|
+
$optMissing = @($rows | Where-Object { $_.Nivel -eq "OPT" -and $_.Estado -eq "FALTA" })
|
|
121
|
+
if ($reqMissing.Count -gt 0) {
|
|
122
|
+
Write-Output ("RESULTADO: FALTAN {0} REQUERIDOS ({1}). Instala y re-ejecuta este script." -f $reqMissing.Count, (($reqMissing | ForEach-Object { $_.Herramienta }) -join ", "))
|
|
123
|
+
if ($WarnOnly) { exit 0 } else { exit 1 }
|
|
124
|
+
} else {
|
|
125
|
+
if ($optMissing.Count -gt 0) {
|
|
126
|
+
Write-Output ("RESULTADO: OK en requeridos. Avisos opcionales ({0}): {1}." -f $optMissing.Count, (($optMissing | ForEach-Object { $_.Herramienta }) -join ", "))
|
|
127
|
+
} else { Write-Output "RESULTADO: todo OK (requeridos + opcionales presentes)." }
|
|
128
|
+
exit 0
|
|
129
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Verificación de prerrequisitos del harness BTP (POSIX sh).
|
|
3
|
+
# Uso: ./check-prereqs.sh [--warn-only] [--need-mtar] [--need-java] [--need-cds] [--need-gh] [--need-op]
|
|
4
|
+
# --need-*: promociona ese opcional a REQUERIDO (cuando la spec del MVP lo exija).
|
|
5
|
+
# --warn-only: nunca falla (exit 0); lo usan install.ps1/install.sh al final.
|
|
6
|
+
# NUNCA imprime secretos: solo ejecuta "<comando> --version" y cuenta archivos.
|
|
7
|
+
# REQUERIDO faltante -> exit 1 (salvo --warn-only). Solo opcionales -> exit 0.
|
|
8
|
+
WARN_ONLY=0; NEED_MTAR=0; NEED_JAVA=0; NEED_CDS=0; NEED_GH=0; NEED_OP=0
|
|
9
|
+
for a in "$@"; do
|
|
10
|
+
case "$a" in
|
|
11
|
+
--warn-only) WARN_ONLY=1 ;;
|
|
12
|
+
--need-mtar) NEED_MTAR=1 ;;
|
|
13
|
+
--need-java) NEED_JAVA=1 ;;
|
|
14
|
+
--need-cds) NEED_CDS=1 ;;
|
|
15
|
+
--need-gh) NEED_GH=1 ;;
|
|
16
|
+
--need-op) NEED_OP=1 ;;
|
|
17
|
+
*) echo "ABORT: opción desconocida $a"; exit 1 ;;
|
|
18
|
+
esac
|
|
19
|
+
done
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
21
|
+
HARNESS_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
22
|
+
|
|
23
|
+
N_REQ_MISSING=""; N_OPT_MISSING=0; OPT_NAMES=""
|
|
24
|
+
row() { printf '%-14s | %-42s | %-5s | %-6s | %s [%s]\n' "$1" "$2" "$3" "$4" "$5" "$6"; }
|
|
25
|
+
record() {
|
|
26
|
+
# record <nivel> <estado> <nombre>
|
|
27
|
+
if [ "$1" = "REQ" ] && [ "$2" = "FALTA" ]; then
|
|
28
|
+
if [ -z "$N_REQ_MISSING" ]; then N_REQ_MISSING="$3"; else N_REQ_MISSING="$N_REQ_MISSING, $3"; fi
|
|
29
|
+
elif [ "$1" = "OPT" ] && [ "$2" = "FALTA" ]; then
|
|
30
|
+
N_OPT_MISSING=$((N_OPT_MISSING + 1))
|
|
31
|
+
if [ -z "$OPT_NAMES" ]; then OPT_NAMES="$3"; else OPT_NAMES="$OPT_NAMES, $3"; fi
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
check_cmd() {
|
|
35
|
+
# check_cmd <nombre> <args...>: imprime "versión" o "no encontrado en PATH"
|
|
36
|
+
name="$1"; shift
|
|
37
|
+
if command -v "$name" >/dev/null 2>&1; then
|
|
38
|
+
v=$("$name" "$@" 2>&1 | head -n 1)
|
|
39
|
+
[ -z "$v" ] && v="instalado"
|
|
40
|
+
printf '%s' "$v"
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
printf 'no encontrado en PATH'
|
|
44
|
+
return 1
|
|
45
|
+
}
|
|
46
|
+
opt_level() { if [ "$1" -eq 1 ]; then printf 'REQ'; else printf 'OPT'; fi; }
|
|
47
|
+
|
|
48
|
+
echo ""
|
|
49
|
+
echo "Prerrequisitos del harness BTP (harness: $HARNESS_ROOT)"
|
|
50
|
+
printf '%-14s | %-42s | %-5s | %-6s | %s\n' "Herramienta" "Necesaria para" "Nivel" "Estado" "Detalle / Instalacion"
|
|
51
|
+
|
|
52
|
+
if v=$(check_cmd git --version); then S=OK; else S=FALTA; fi
|
|
53
|
+
row "git" "control de versiones del MVP" "REQ" "$S" "$v" "https://git-scm.com/downloads"; record REQ "$S" git
|
|
54
|
+
|
|
55
|
+
if v=$(check_cmd node --version); then
|
|
56
|
+
major=$(printf '%s' "$v" | sed -n 's/^v\?\([0-9][0-9]*\).*/\1/p')
|
|
57
|
+
if [ -n "$major" ] && [ "$major" -ge 18 ]; then S=OK; else S=FALTA; v="$v (se requiere Node 18+)"; fi
|
|
58
|
+
else S=FALTA; fi
|
|
59
|
+
row "node 18+" "CAP, UI5, mbt, scripts del harness" "REQ" "$S" "$v" "https://nodejs.org/"; record REQ "$S" "node 18+"
|
|
60
|
+
|
|
61
|
+
if v=$(check_cmd cf --version); then
|
|
62
|
+
case "$v" in *[Cc][Ff]*version*|*[Cc]loud*[Ff]oundry*) S=OK ;; *) S=FALTA; v="$v (AVISO: no parece el CLI de Cloud Foundry; si 'cf' es otra herramienta, define un alias al CLI real)" ;; esac
|
|
63
|
+
else S=FALTA; fi
|
|
64
|
+
row "cf" "despliegues y lecturas Cloud Foundry" "REQ" "$S" "$v" "https://docs.cloudfoundry.org/cf-cli/install-go-cli.html"; record REQ "$S" cf
|
|
65
|
+
|
|
66
|
+
if [ -f "$HARNESS_ROOT/AI/AGENTS.md" ] && { [ -f "$HARNESS_ROOT/AGENTS.md" ] || [ -f "$HARNESS_ROOT/templates/AGENTS.pointer.md" ]; }; then
|
|
67
|
+
S=OK; v="AGENTS.md + AI/AGENTS.md presentes; verifica que tu cliente los lea"
|
|
68
|
+
else
|
|
69
|
+
S=FALTA; v="falta AGENTS.md o AI/AGENTS.md (reinstala con install.sh)"
|
|
70
|
+
fi
|
|
71
|
+
row "cliente agentico" "leer AGENTS.md y orquestar el flujo" "REQ" "$S" "$v" "https://docs.opencode.ai/ (o tu cliente agéntico)"; record REQ "$S" "cliente agentico"
|
|
72
|
+
|
|
73
|
+
LVL=$(opt_level $NEED_MTAR)
|
|
74
|
+
if v=$(check_cmd mbt --version); then S=OK; else S=FALTA; fi
|
|
75
|
+
row "mbt" "construir MTAR (solo si el MVP lo exige)" "$LVL" "$S" "$v" "https://github.com/SAP/cloud-mta-build-tool"; record "$LVL" "$S" mbt
|
|
76
|
+
|
|
77
|
+
if [ "$NEED_MTAR" -eq 1 ]; then LVL=REQ; else LVL=OPT; fi
|
|
78
|
+
if v=$(check_cmd make --version); then S=OK; else S=FALTA; fi
|
|
79
|
+
row "make" "requerido por mbt" "$LVL" "$S" "$v" "https://www.gnu.org/software/make/"; record "$LVL" "$S" make
|
|
80
|
+
|
|
81
|
+
LVL=$(opt_level $NEED_JAVA)
|
|
82
|
+
if v=$(check_cmd java -version); then S=OK; else S=FALTA; fi
|
|
83
|
+
row "java" "builds MTA Java (solo si la spec los pide)" "$LVL" "$S" "$v" "https://adoptium.net/"; record "$LVL" "$S" java
|
|
84
|
+
|
|
85
|
+
LVL=$(opt_level $NEED_CDS)
|
|
86
|
+
if v=$(check_cmd cds --version); then S=OK; else S=FALTA; v="no global; valido via npx -p @sap/cds-dk (solo si la spec exige CAP)"; fi
|
|
87
|
+
row "cds" "proyectos CAP (solo si la spec lo exige)" "$LVL" "$S" "$v" "https://cap.cloud.sap/docs/get-started/"; record "$LVL" "$S" cds
|
|
88
|
+
|
|
89
|
+
if [ "$NEED_CDS" -eq 1 ]; then LVL=REQ; else LVL=OPT; fi
|
|
90
|
+
if v=$(check_cmd ui5 --version); then S=OK; else S=FALTA; fi
|
|
91
|
+
row "ui5" "proyectos UI5/Fiori (solo si la spec lo exige)" "$LVL" "$S" "$v" "https://ui5.github.io/cli/"; record "$LVL" "$S" ui5
|
|
92
|
+
|
|
93
|
+
LVL=$(opt_level $NEED_GH)
|
|
94
|
+
if v=$(check_cmd gh --version); then S=OK; else S=FALTA; fi
|
|
95
|
+
row "gh" "repo remoto/PR (solo si la spec pide remoto)" "$LVL" "$S" "$v" "https://cli.github.com/"; record "$LVL" "$S" gh
|
|
96
|
+
|
|
97
|
+
LVL=$(opt_level $NEED_OP)
|
|
98
|
+
if v=$(check_cmd op --version); then S=OK; else S=FALTA; fi
|
|
99
|
+
row "op (1Password)" "secretos de equipo (solo si el equipo lo adopta)" "$LVL" "$S" "$v" "https://developer.1password.com/docs/cli/get-started/"; record "$LVL" "$S" "op (1Password)"
|
|
100
|
+
|
|
101
|
+
NSKILLS=0
|
|
102
|
+
if [ -d "$HARNESS_ROOT/AI/.opencode/skills" ]; then
|
|
103
|
+
for d in "$HARNESS_ROOT"/AI/.opencode/skills/*/; do
|
|
104
|
+
[ -f "${d}SKILL.md" ] && NSKILLS=$((NSKILLS + 1))
|
|
105
|
+
done
|
|
106
|
+
fi
|
|
107
|
+
if [ "$NSKILLS" -gt 0 ]; then S=OK; else S=FALTA; fi
|
|
108
|
+
row "skills" "capacidades del harness (cf-cli, mta-build, ...)" "OPT" "$S" "$NSKILLS skills con SKILL.md" "(vienen con AI/; reinstala con install.sh si FALTA)"; record OPT "$S" skills
|
|
109
|
+
|
|
110
|
+
NAGENTS=0
|
|
111
|
+
if [ -d "$HARNESS_ROOT/AI/.opencode/agents" ]; then
|
|
112
|
+
# shellcheck disable=SC2012
|
|
113
|
+
NAGENTS=$(ls "$HARNESS_ROOT"/AI/.opencode/agents/*.md 2>/dev/null | wc -l | tr -d ' ')
|
|
114
|
+
fi
|
|
115
|
+
if [ "$NAGENTS" -gt 0 ]; then S=OK; else S=FALTA; fi
|
|
116
|
+
row "agents" "orquestador y especialistas" "OPT" "$S" "$NAGENTS agentes .md" "(vienen con AI/; reinstala con install.sh si FALTA)"; record OPT "$S" agents
|
|
117
|
+
|
|
118
|
+
if [ -f "$HARNESS_ROOT/AI/opencode.fragment.jsonc" ]; then S=OK; v="AI/opencode.fragment.jsonc presente"; else S=FALTA; v="no encontrado"; fi
|
|
119
|
+
row "fragmento MCP" "plantilla MCP del proyecto destino" "OPT" "$S" "$v" "(viene con AI/; reinstala con install.sh si FALTA)"; record OPT "$S" "fragmento MCP"
|
|
120
|
+
|
|
121
|
+
echo ""
|
|
122
|
+
echo "NOTA MCPs (no detectables desde shell): tu cliente agéntico debe tener configurados"
|
|
123
|
+
echo "sus propios MCPs: context7, codebase-memory, github, playwright, eslint, chrome-devtools."
|
|
124
|
+
echo "Detalle y docs oficiales: AI/docs/prerequisitos.md"
|
|
125
|
+
|
|
126
|
+
if [ -n "$N_REQ_MISSING" ]; then
|
|
127
|
+
echo "RESULTADO: FALTAN REQUERIDOS ($N_REQ_MISSING). Instala y re-ejecuta este script."
|
|
128
|
+
if [ "$WARN_ONLY" -eq 1 ]; then exit 0; else exit 1; fi
|
|
129
|
+
else
|
|
130
|
+
if [ "$N_OPT_MISSING" -gt 0 ]; then
|
|
131
|
+
echo "RESULTADO: OK en requeridos. Avisos opcionales ($N_OPT_MISSING): $OPT_NAMES."
|
|
132
|
+
else
|
|
133
|
+
echo "RESULTADO: todo OK (requeridos + opcionales presentes)."
|
|
134
|
+
fi
|
|
135
|
+
exit 0
|
|
136
|
+
fi
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
faltan -> asigna rol solo si falta. Emite SKIP: si existe, CREATE: si crea.
|
|
7
7
|
Allowlist cerrada: target, spaces, space-users, services, marketplace,
|
|
8
8
|
create-space, create-service, set-space-role.
|
|
9
|
-
PROHIBIDO: delete-*, unbind-*, purge-*, restage-*. Usa
|
|
9
|
+
PROHIBIDO: delete-*, unbind-*, purge-*, restage-*. Usa cf.
|
|
10
10
|
Cero secretos en repo: solo nombres. #>
|
|
11
11
|
param(
|
|
12
12
|
[string]$Space = "TODO(<paisaje>)",
|
|
@@ -17,7 +17,7 @@ param(
|
|
|
17
17
|
$ErrorActionPreference = "Stop"
|
|
18
18
|
|
|
19
19
|
function Target-Ok {
|
|
20
|
-
$t = &
|
|
20
|
+
$t = & cf target 2>&1 | Out-String
|
|
21
21
|
if ($t -notmatch [regex]::Escape($ExpectedApi)) { Write-Output "ABORT: endpoint no coincide (esperado $ExpectedApi)"; return $false }
|
|
22
22
|
if ($t -notmatch [regex]::Escape($ExpectedOrg)) { Write-Output "ABORT: org no coincide (esperada $ExpectedOrg)"; return $false }
|
|
23
23
|
return $true
|
|
@@ -26,19 +26,19 @@ function Target-Ok {
|
|
|
26
26
|
if (-not (Target-Ok)) { exit 1 }
|
|
27
27
|
|
|
28
28
|
# 1. Espacio: crear solo si falta
|
|
29
|
-
$spaces = &
|
|
29
|
+
$spaces = & cf spaces 2>&1 | Out-String
|
|
30
30
|
if ($spaces -match "(?m)^\s*$([regex]::Escape($Space))\s*$") {
|
|
31
31
|
Write-Output "SKIP: space $Space ya existe"
|
|
32
32
|
} else {
|
|
33
|
-
&
|
|
33
|
+
& cf create-space $Space
|
|
34
34
|
Write-Output "CREATE: space $Space"
|
|
35
35
|
}
|
|
36
|
-
&
|
|
36
|
+
& cf target -s $Space | Out-Null
|
|
37
37
|
|
|
38
38
|
# 2. Guardia de marketplace: aborta antes de crear si falta un offering
|
|
39
39
|
# TODO: lista los offerings requeridos, p. ej. @("xsuaa", "destination")
|
|
40
40
|
$RequiredOfferings = @()
|
|
41
|
-
$market = &
|
|
41
|
+
$market = & cf marketplace 2>&1 | Out-String
|
|
42
42
|
foreach ($o in $RequiredOfferings) {
|
|
43
43
|
if ($market -notmatch [regex]::Escape($o)) {
|
|
44
44
|
Write-Output "ABORT: offering $o no disponible en marketplace"
|
|
@@ -49,12 +49,12 @@ foreach ($o in $RequiredOfferings) {
|
|
|
49
49
|
# 3. Instancias: crear solo si faltan
|
|
50
50
|
# TODO: anadir una entrada por instancia: @{ Name="TODO"; Offering="TODO"; Plan="TODO" }
|
|
51
51
|
$Instances = @()
|
|
52
|
-
$services = &
|
|
52
|
+
$services = & cf services 2>&1 | Out-String
|
|
53
53
|
foreach ($i in $Instances) {
|
|
54
54
|
if ($services -match [regex]::Escape($i.Name)) {
|
|
55
55
|
Write-Output ("SKIP: service {0} ya existe" -f $i.Name)
|
|
56
56
|
} else {
|
|
57
|
-
&
|
|
57
|
+
& cf create-service $i.Offering $i.Plan $i.Name
|
|
58
58
|
Write-Output ("CREATE: service {0}" -f $i.Name)
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -62,11 +62,11 @@ foreach ($i in $Instances) {
|
|
|
62
62
|
# 4. Rol: asignar solo si falta
|
|
63
63
|
# TODO: fijar usuario, p. ej. $User = "TODO"
|
|
64
64
|
$User = "TODO"
|
|
65
|
-
$users = &
|
|
65
|
+
$users = & cf space-users $ExpectedOrg $Space 2>&1 | Out-String
|
|
66
66
|
if ($users -match [regex]::Escape($User)) {
|
|
67
67
|
Write-Output "SKIP: rol de $User ya asignado"
|
|
68
68
|
} else {
|
|
69
|
-
&
|
|
69
|
+
& cf set-space-role $User $ExpectedOrg $Space SpaceDeveloper
|
|
70
70
|
Write-Output "CREATE: rol SpaceDeveloper para $User"
|
|
71
71
|
}
|
|
72
72
|
|