From 5c23f21586cf8fb977261204e8afe4d69cb75db3 Mon Sep 17 00:00:00 2001 From: Julian lechner Date: Thu, 17 Sep 2026 08:43:12 +0200 Subject: [PATCH] feat(capabilities): add strict shell checks --- scripts/check-capabilities.ps1 | 32 ++++++++++++++++++++ scripts/check-capabilities.sh | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 scripts/check-capabilities.ps1 create mode 100755 scripts/check-capabilities.sh diff --git a/scripts/check-capabilities.ps1 b/scripts/check-capabilities.ps1 new file mode 100644 index 0000000..c984086 --- /dev/null +++ b/scripts/check-capabilities.ps1 @@ -0,0 +1,32 @@ +[CmdletBinding()] +param([switch]$SkipExternal) +$ErrorActionPreference = 'Continue' +$root = Split-Path $PSScriptRoot -Parent +$failed = $false +function Pass([string]$message) { Write-Output "PASS $message" } +function Fail([string]$message) { $script:failed = $true; Write-Error "FAIL $message" -ErrorAction Continue } +function Require-Command([string]$name) { if (Get-Command $name -ErrorAction SilentlyContinue) { Pass "$name available" } else { Fail "$name is required" } } +function Version-AtLeast([string]$version, [version]$minimum) { try { return ([version]$version -ge $minimum) } catch { return $false } } +function Check-Version([string]$name, [string]$version, [version]$minimum) { if (Version-AtLeast $version $minimum) { Pass "$name $version (minimum $minimum)" } else { Fail "$name $version is below minimum $minimum" } } + +Check-Version 'PowerShell' $PSVersionTable.PSVersion.ToString() ([version]'5.1') +Require-Command git; Require-Command python +if (Get-Command python -ErrorAction SilentlyContinue) { + $pythonVersion = (& python --version 2>$null | Select-Object -First 1) -replace '^Python\s+', '' + if ($LASTEXITCODE -eq 0) { Check-Version 'Python' $pythonVersion ([version]'3.11') } else { Fail 'Python 3.11 or newer is required' } +} +foreach ($path in @('AI-Instructions.md','shared/rules','shared/skills','shared/agents','adapters','scripts/build.sh','scripts/build.ps1','scripts/install.sh','scripts/install.ps1','scripts/update.sh','scripts/update.ps1','scripts/validate-config.py')) { + if (Test-Path -LiteralPath (Join-Path $root $path)) { Pass "repository path $path" } else { Fail "repository path is missing: $path" } +} + +if (-not $SkipExternal) { + foreach ($client in @('codex','claude')) { + if (Get-Command $client -ErrorAction SilentlyContinue) { & $client --version *> $null; if ($LASTEXITCODE -eq 0) { Pass "$client CLI available" } else { Fail "$client CLI could not report its version" } } else { Fail "$client CLI is required for the default setup" } + } + $remote = (& git -C $root config --get remote.origin.url 2>$null).Trim() + if ([string]::IsNullOrWhiteSpace($remote)) { Fail 'Git origin remote is required for network capability checks' } + else { & git -c http.lowSpeedLimit=1 -c http.lowSpeedTime=10 -C $root ls-remote --heads $remote *> $null; if ($LASTEXITCODE -eq 0) { Pass 'network access to the Git origin' } else { Fail 'network access to Git origin failed' } } +} + +if ($failed) { Write-Error 'Capability check: FAIL'; exit 1 } +Write-Output 'Capability check: PASS' diff --git a/scripts/check-capabilities.sh b/scripts/check-capabilities.sh new file mode 100755 index 0000000..616eb36 --- /dev/null +++ b/scripts/check-capabilities.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +skip_external=false +while [ $# -gt 0 ]; do + case "$1" in + --skip-external) skip_external=true; shift ;; + *) printf 'Unknown argument: %s\n' "$1" >&2; exit 2 ;; + esac +done + +root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) +failed=false +pass() { printf 'PASS %s\n' "$1"; } +fail() { printf 'FAIL %s\n' "$1" >&2; failed=true; } +require_command() { + if command -v "$1" >/dev/null 2>&1; then pass "$1 available"; else fail "$1 is required"; fi +} +version_at_least() { + awk -F. -v required="$2" 'BEGIN { split(required,r) } { split($1,v); if (v[1]+0 > r[1]+0 || (v[1]+0 == r[1]+0 && v[2]+0 >= r[2]+0)) exit 0; exit 1 }' <<<"$1" +} +check_version() { + local name=$1 version=$2 required=$3 + if version_at_least "$version" "$required"; then pass "$name $version (minimum $required)"; else fail "$name $version is below minimum $required"; fi +} + +check_version Bash "${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}" 4.3 +require_command awk; require_command sed; require_command sha256sum; require_command git; require_command jq +require_command python3 +if command -v python3 >/dev/null 2>&1 && python3 -c 'import sys; raise SystemExit(sys.version_info < (3, 11))' >/dev/null 2>&1; then + pass "Python $(python3 --version 2>&1 | awk '{print $2}') (minimum 3.11)" +else + fail 'Python 3.11 or newer is required' +fi + +for path in AI-Instructions.md shared/rules shared/skills shared/agents adapters scripts/build.sh scripts/build.ps1 scripts/install.sh scripts/install.ps1 scripts/update.sh scripts/update.ps1 scripts/validate-config.py; do + [ -e "$root/$path" ] && pass "repository path $path" || fail "repository path is missing: $path" +done + +if [ "$skip_external" = false ]; then + for client in codex claude; do + if command -v "$client" >/dev/null 2>&1 && "$client" --version >/dev/null 2>&1; then pass "$client CLI available"; else fail "$client CLI is required for the default setup"; fi + done + remote=$(git -C "$root" config --get remote.origin.url 2>/dev/null || true) + if [ -z "$remote" ]; then + fail 'Git origin remote is required for network capability checks' + elif git -c http.lowSpeedLimit=1 -c http.lowSpeedTime=10 -C "$root" ls-remote --heads "$remote" >/dev/null 2>&1; then + pass 'network access to the Git origin' + else + fail 'network access to Git origin failed' + fi +fi + +if [ "$failed" = true ]; then printf 'Capability check: FAIL\n' >&2; exit 1; fi +printf 'Capability check: PASS\n'