audit-code install-tools.sh:
- Buffer the opengrep release JSON before grep -m1; curl died with (23)
under pipefail when grep quit early.
- Use ${m}: in the PowerShell block; $m: parsed as a scope-qualified var.
- On Arch, skip paru/yay when pacman -Q shows every package installed,
since --needed still invokes sudo.
- Add --check-only (fast, installs nothing, non-zero naming missing tools)
and --user-only (no system package managers, no sudo).
log-run.py (both skills): put the skill dir on sys.path so running it as
a script from any cwd no longer raises ModuleNotFoundError.
audit-terraform: move deps from requirements.txt into pyproject
dependency groups and add scripts/install-tools.sh (uv sync --group tools,
then check trivy, tflint, tofu, terragrunt, gh).
Both SKILL.md files gain a 0.5 Preflight step and call scripts through
uv run --project ${SKILL_DIR}. tools_unavailable is now a map of tool to
exact install command; audit-terraform skips trivy when absent and stops
with an install hint instead of crashing when tofu/terragrunt is missing.
211 lines
7.1 KiB
Bash
Executable File
211 lines
7.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
SKILL_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
|
say() { printf '\n\033[1m▶ %s\033[0m\n' "$*"; }
|
|
warn() { printf '\033[33m! %s\033[0m\n' "$*"; }
|
|
|
|
|
|
install_python_tools() {
|
|
if ! command -v uv >/dev/null 2>&1; then
|
|
warn "uv not installed. Install: https://docs.astral.sh/uv/getting-started/installation/"
|
|
warn "Falling back to plain pip — tools will install into the active environment."
|
|
if ! command -v pip >/dev/null 2>&1; then
|
|
echo "Neither uv nor pip available. Aborting Python-tools install."
|
|
exit 1
|
|
fi
|
|
pip install bandit ruff mypy pip-audit
|
|
else
|
|
say "Installing Python tools into $SKILL_DIR/.venv/ via uv"
|
|
uv sync --group tools
|
|
fi
|
|
}
|
|
|
|
|
|
install_opengrep() {
|
|
if [[ -x "$SKILL_DIR/.venv/bin/opengrep" ]]; then
|
|
echo " opengrep: already installed (.venv/bin)"
|
|
return
|
|
fi
|
|
say "Installing opengrep into $SKILL_DIR/.venv/bin/"
|
|
local os arch asset
|
|
os="$(uname -s)"
|
|
arch="$(uname -m)"
|
|
case "$os-$arch" in
|
|
Linux-x86_64) asset="opengrep_manylinux_x86" ;;
|
|
Linux-aarch64) asset="opengrep_manylinux_aarch64" ;;
|
|
Darwin-x86_64) asset="opengrep_osx_x86" ;;
|
|
Darwin-arm64) asset="opengrep_osx_arm64" ;;
|
|
*)
|
|
warn "opengrep: unsupported platform $os-$arch, install manually from https://github.com/opengrep/opengrep/releases"
|
|
return
|
|
;;
|
|
esac
|
|
local release tag url
|
|
# Buffer the response: piping curl into an early-exiting `grep -m1` makes curl die with (23), which pipefail turns fatal.
|
|
release="$(curl -fsSL https://api.github.com/repos/opengrep/opengrep/releases/latest)"
|
|
tag="$(grep -m1 '"tag_name"' <<<"$release" | sed -E 's/.*"([^"]+)".*/\1/')"
|
|
if [[ -z "$tag" ]]; then
|
|
warn "opengrep: could not resolve latest release tag, install manually"
|
|
return
|
|
fi
|
|
url="https://github.com/opengrep/opengrep/releases/download/$tag/$asset"
|
|
mkdir -p "$SKILL_DIR/.venv/bin"
|
|
curl -fsSL "$url" -o "$SKILL_DIR/.venv/bin/opengrep"
|
|
chmod +x "$SKILL_DIR/.venv/bin/opengrep"
|
|
}
|
|
|
|
|
|
install_powershell_modules() {
|
|
if ! command -v pwsh >/dev/null 2>&1; then
|
|
warn "pwsh not found — PowerShell review (PSScriptAnalyzer, InjectionHunter) will be skipped."
|
|
warn " Install: https://learn.microsoft.com/powershell/scripting/install/installing-powershell"
|
|
return
|
|
fi
|
|
say "Installing PowerShell modules for the current user"
|
|
pwsh -NoProfile -NonInteractive -Command '
|
|
foreach ($m in "PSScriptAnalyzer", "InjectionHunter") {
|
|
if (Get-Module -ListAvailable -Name $m) {
|
|
Write-Host " ${m}: already installed"
|
|
} else {
|
|
Install-Module -Name $m -Scope CurrentUser -Force -AcceptLicense -Repository PSGallery
|
|
Write-Host " ${m}: installed"
|
|
}
|
|
}'
|
|
}
|
|
|
|
|
|
install_native_brew() {
|
|
say "Installing native tools via Homebrew"
|
|
for pkg in gitleaks osv-scanner gh; do
|
|
if brew list --formula | grep -qx "$pkg"; then
|
|
echo " $pkg: already installed"
|
|
else
|
|
brew install "$pkg"
|
|
fi
|
|
done
|
|
}
|
|
|
|
install_native_apt() {
|
|
say "Installing native tools via apt-get"
|
|
if ! command -v gh >/dev/null 2>&1; then
|
|
warn "gh: follow https://github.com/cli/cli/blob/trunk/docs/install_linux.md"
|
|
fi
|
|
if ! command -v gitleaks >/dev/null 2>&1; then
|
|
warn "gitleaks: download from https://github.com/gitleaks/gitleaks/releases"
|
|
fi
|
|
if ! command -v osv-scanner >/dev/null 2>&1; then
|
|
warn "osv-scanner: install via 'go install github.com/google/osv-scanner/cmd/osv-scanner@latest' or download from https://github.com/google/osv-scanner/releases"
|
|
fi
|
|
}
|
|
|
|
install_native_arch() {
|
|
local helper
|
|
if command -v paru >/dev/null 2>&1; then
|
|
helper="paru"
|
|
elif command -v yay >/dev/null 2>&1; then
|
|
helper="yay"
|
|
else
|
|
helper="pacman"
|
|
fi
|
|
say "Installing native tools via $helper"
|
|
|
|
# --needed still invokes sudo, so skip the helper entirely when nothing is missing.
|
|
local pkgs=() pkg
|
|
for pkg in gitleaks github-cli osv-scanner; do
|
|
pacman -Q "$pkg" >/dev/null 2>&1 || pkgs+=("$pkg")
|
|
done
|
|
if [[ ${#pkgs[@]} -eq 0 ]]; then
|
|
echo " gitleaks, github-cli, osv-scanner: already installed"
|
|
return
|
|
fi
|
|
if [[ "$helper" == "pacman" ]]; then
|
|
sudo pacman -S --needed --noconfirm gitleaks github-cli || true
|
|
if ! command -v osv-scanner >/dev/null 2>&1; then
|
|
warn "osv-scanner is AUR-only; pacman can't install it. Use paru/yay or install manually:"
|
|
warn " go install github.com/google/osv-scanner/cmd/osv-scanner@latest"
|
|
fi
|
|
else
|
|
"$helper" -S --needed --noconfirm "${pkgs[@]}"
|
|
fi
|
|
}
|
|
|
|
install_native() {
|
|
if command -v brew >/dev/null 2>&1; then
|
|
install_native_brew
|
|
elif command -v pacman >/dev/null 2>&1; then
|
|
install_native_arch
|
|
elif command -v apt-get >/dev/null 2>&1; then
|
|
install_native_apt
|
|
else
|
|
warn "No supported native package manager found (brew/pacman/apt). Install gitleaks, osv-scanner, gh manually."
|
|
fi
|
|
}
|
|
|
|
# Checked in .venv/bin only: each plugin version gets its own venv, and a copy on PATH says nothing about this one.
|
|
VENV_TOOLS=(bandit ruff mypy pip-audit vulture radon interrogate lizard opengrep)
|
|
NATIVE_TOOLS=(gitleaks osv-scanner gh)
|
|
|
|
verify_tools() {
|
|
say "Verifying tool availability"
|
|
local tool missing=()
|
|
for tool in "${VENV_TOOLS[@]}"; do
|
|
if [[ -x "$SKILL_DIR/.venv/bin/$tool" ]]; then
|
|
printf ' %-15s %s\n' "$tool" "(.venv/bin)"
|
|
else
|
|
missing+=("$tool")
|
|
fi
|
|
done
|
|
for tool in "${NATIVE_TOOLS[@]}" pwsh; do
|
|
if command -v "$tool" >/dev/null 2>&1; then
|
|
printf ' %-15s %s\n' "$tool" "$(command -v "$tool")"
|
|
elif [[ "$tool" == pwsh ]]; then
|
|
printf ' %-15s %s\n' "$tool" "missing (optional: PowerShell review only)"
|
|
else
|
|
missing+=("$tool")
|
|
fi
|
|
done
|
|
[[ ${#missing[@]} -eq 0 ]] && return
|
|
printf ' %-15s \033[31mmissing\033[0m\n' "${missing[@]}"
|
|
return 1
|
|
}
|
|
|
|
main() {
|
|
local user_only=0
|
|
case "${1:-}" in
|
|
"") ;;
|
|
--check-only) verify_tools; return ;;
|
|
--user-only) user_only=1 ;;
|
|
*) echo "usage: install-tools.sh [--check-only | --user-only]" >&2; return 2 ;;
|
|
esac
|
|
|
|
cd "$SKILL_DIR"
|
|
install_python_tools
|
|
install_opengrep
|
|
install_powershell_modules
|
|
if [[ $user_only -eq 1 ]]; then
|
|
warn "--user-only: skipped system packages (gitleaks, osv-scanner, gh). Re-run without it to install them."
|
|
else
|
|
install_native
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
Per-project tools (not installed here — must live in the target repo):
|
|
- eslint + eslint-plugin-security (npm i -D)
|
|
- typescript (tsc) (npm i -D)
|
|
- SecurityCodeScan + dotnet (dotnet add package SecurityCodeScan.VS2019)
|
|
- knip (npm i -D)
|
|
- jscpd (npm i -D)
|
|
|
|
Run /audit-code to use the skill.
|
|
EOF
|
|
verify_tools
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
main "$@"
|
|
fi
|