xp-gate 0.5.1

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.
Files changed (90) hide show
  1. package/adapter-common.sh +192 -0
  2. package/adapters/cpp.sh +76 -0
  3. package/adapters/dart.sh +41 -0
  4. package/adapters/flutter.sh +41 -0
  5. package/adapters/go.sh +59 -0
  6. package/adapters/iac.sh +189 -0
  7. package/adapters/java.sh +191 -0
  8. package/adapters/kotlin.sh +77 -0
  9. package/adapters/objectivec.sh +38 -0
  10. package/adapters/powershell.sh +138 -0
  11. package/adapters/python.sh +104 -0
  12. package/adapters/shell.sh +55 -0
  13. package/adapters/swift.sh +44 -0
  14. package/adapters/typescript.sh +61 -0
  15. package/bin/xp-gate.js +157 -0
  16. package/hooks/adapter-common.sh +192 -0
  17. package/hooks/pre-commit +1667 -0
  18. package/hooks/pre-push +395 -0
  19. package/lib/__tests__/detect-deps.test.js +209 -0
  20. package/lib/__tests__/doctor.test.js +448 -0
  21. package/lib/__tests__/download-skill.test.js +281 -0
  22. package/lib/__tests__/init.test.js +327 -0
  23. package/lib/__tests__/install-skill.test.js +326 -0
  24. package/lib/__tests__/migrate.test.js +212 -0
  25. package/lib/__tests__/rollback.test.js +183 -0
  26. package/lib/__tests__/ui-detector.test.ts +200 -0
  27. package/lib/__tests__/uninstall-skill.test.js +189 -0
  28. package/lib/__tests__/uninstall.test.js +589 -0
  29. package/lib/__tests__/update-skill.test.js +276 -0
  30. package/lib/detect-deps.js +157 -0
  31. package/lib/doctor.js +370 -0
  32. package/lib/download-skill.js +96 -0
  33. package/lib/init.js +367 -0
  34. package/lib/install-skill.js +184 -0
  35. package/lib/migrate.js +120 -0
  36. package/lib/rollback.js +78 -0
  37. package/lib/ui-detector.ts +99 -0
  38. package/lib/uninstall-skill.js +69 -0
  39. package/lib/uninstall.js +401 -0
  40. package/lib/update-skill.js +90 -0
  41. package/package.json +39 -0
  42. package/plugins/claude-code/.claude-plugin/plugin.json +21 -0
  43. package/plugins/claude-code/bin/delphi-review-guard.sh +68 -0
  44. package/plugins/claude-code/bin/xp-gate-check +47 -0
  45. package/plugins/claude-code/hooks/hooks.json +37 -0
  46. package/skills/delphi-review/.delphi-config.json.example +45 -0
  47. package/skills/delphi-review/AGENTS.md +54 -0
  48. package/skills/delphi-review/INSTALL.md +152 -0
  49. package/skills/delphi-review/SKILL.md +371 -0
  50. package/skills/delphi-review/evals/evals.json +82 -0
  51. package/skills/delphi-review/opencode.json.delphi.example +56 -0
  52. package/skills/delphi-review/references/code-walkthrough.md +486 -0
  53. package/skills/ralph-loop/SKILL.md +330 -0
  54. package/skills/ralph-loop/evals/evals.json +311 -0
  55. package/skills/ralph-loop/evolution-history.json +59 -0
  56. package/skills/ralph-loop/evolution-log.md +16 -0
  57. package/skills/ralph-loop/references/components/memory.md +55 -0
  58. package/skills/ralph-loop/references/components/middleware.md +54 -0
  59. package/skills/ralph-loop/references/components/skill-invocations.md +39 -0
  60. package/skills/ralph-loop/references/components/system-prompt.md +24 -0
  61. package/skills/ralph-loop/references/components/tool-descriptions.md +32 -0
  62. package/skills/ralph-loop/references/phase-2-build-ralph.md +89 -0
  63. package/skills/ralph-loop/templates/progress-log.md +36 -0
  64. package/skills/sprint-flow/SKILL.md +600 -0
  65. package/skills/sprint-flow/evals/evals.json +78 -0
  66. package/skills/sprint-flow/evolution-history.json +39 -0
  67. package/skills/sprint-flow/evolution-log.md +23 -0
  68. package/skills/sprint-flow/references/components/memory.md +87 -0
  69. package/skills/sprint-flow/references/components/middleware.md +72 -0
  70. package/skills/sprint-flow/references/components/skill-invocations.md +104 -0
  71. package/skills/sprint-flow/references/components/system-prompt.md +27 -0
  72. package/skills/sprint-flow/references/components/tool-descriptions.md +96 -0
  73. package/skills/sprint-flow/references/phase-0-think.md +115 -0
  74. package/skills/sprint-flow/references/phase-1-plan.md +178 -0
  75. package/skills/sprint-flow/references/phase-2-build.md +198 -0
  76. package/skills/sprint-flow/references/phase-3-review.md +213 -0
  77. package/skills/sprint-flow/references/phase-4-uat.md +125 -0
  78. package/skills/sprint-flow/references/phase-5-feedback.md +100 -0
  79. package/skills/sprint-flow/references/phase-6-ship.md +193 -0
  80. package/skills/sprint-flow/references/phase-7-land.md +140 -0
  81. package/skills/sprint-flow/references/phase-8-cleanup.md +192 -0
  82. package/skills/sprint-flow/templates/emergent-issues-template.md +120 -0
  83. package/skills/sprint-flow/templates/pain-document-template.md +115 -0
  84. package/skills/sprint-flow/templates/sprint-summary-template.md +120 -0
  85. package/skills/test-specification-alignment/AGENTS.md +59 -0
  86. package/skills/test-specification-alignment/SKILL.md +605 -0
  87. package/skills/test-specification-alignment/evals/evals.json +75 -0
  88. package/skills/test-specification-alignment/references/alignment-verification-algorithm.md +493 -0
  89. package/skills/test-specification-alignment/references/phase2-constraint-enforcement.md +431 -0
  90. package/skills/test-specification-alignment/references/specification-format.md +348 -0
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Common adapter functions for language detection and routing
4
+
5
+ detect_project_lang() {
6
+ if [[ -f "tsconfig.json" ]]; then
7
+ echo "typescript"
8
+ elif [[ -f "pyproject.toml" ]] || [[ -f "requirements.txt" ]] || [[ -f "setup.py" ]]; then
9
+ echo "python"
10
+ elif [[ -f "go.mod" ]]; then
11
+ echo "go"
12
+ elif [[ -f "build.gradle" ]] || [[ -f "build.gradle.kts" ]]; then
13
+ if [[ -n "$(find . -name "*.kt" -type f | head -n 1)" ]]; then
14
+ echo "kotlin"
15
+ else
16
+ echo "java"
17
+ fi
18
+ elif [[ -f "pom.xml" ]]; then
19
+ echo "java"
20
+ elif [[ -f "pubspec.yaml" ]]; then
21
+ if grep -q "flutter:" "pubspec.yaml" 2>/dev/null || [[ -f ".metadata" ]]; then
22
+ echo "flutter"
23
+ else
24
+ echo "dart"
25
+ fi
26
+ elif [[ -n "$(find . -name "*.ps1" -type f | head -n 1)" ]]; then
27
+ echo "powershell"
28
+ elif [[ -f "Package.swift" ]]; then
29
+ echo "swift"
30
+ elif [[ -f "CMakeLists.txt" ]] || [[ -n "$(find . -name "*.cpp" -o -name "*.cc" -type f | head -n 1)" ]]; then
31
+ echo "cpp"
32
+ elif [[ -n "$(find . -name "*.m" -o -name "*.mm" -type f | head -n 1)" ]]; then
33
+ echo "objectivec"
34
+ elif [[ -n "$(find . -name "*.sh" -type f | head -n 1)" ]] || [[ -n "$(find . -name "Dockerfile" -o -name "*.dockerfile" -type f | head -n 1)" ]]; then
35
+ echo "shell"
36
+ elif [[ -n "$(find . -name "*.ps1" -type f -not -path "./.git/*" | head -n 1)" ]]; then
37
+ echo "powershell"
38
+ else
39
+ if [[ -n "$(find . -name "*.ts" -o -name "*.tsx" -type f | head -n 1)" ]]; then
40
+ echo "typescript"
41
+ elif [[ -n "$(find . -name "*.py" -type f | head -n 1)" ]]; then
42
+ echo "python"
43
+ elif [[ -n "$(find . -name "*.go" -type f | head -n 1)" ]]; then
44
+ echo "go"
45
+ elif [[ -n "$(find . -name "*.kt" -type f | head -n 1)" ]]; then
46
+ echo "kotlin"
47
+ elif [[ -n "$(find . -name "*.java" -type f | head -n 1)" ]]; then
48
+ echo "java"
49
+ elif [[ -n "$(find . -name "*.dart" -type f | head -n 1)" ]]; then
50
+ if grep -q "flutter:" "pubspec.yaml" 2>/dev/null || [[ -f ".flutter" ]]; then
51
+ echo "flutter"
52
+ else
53
+ echo "dart"
54
+ fi
55
+ elif [[ -n "$(find . -name "*.swift" -type f | head -n 1)" ]]; then
56
+ echo "swift"
57
+ elif [[ -n "$(find . -name "*.cpp" -o -name "*.cc" -o -name "*.c" -o -name "*.h" -type f | head -n 1)" ]]; then
58
+ echo "cpp"
59
+ elif [[ -n "$(find . -name "*.m" -o -name "*.mm" -type f | head -n 1)" ]]; then
60
+ echo "objectivec"
61
+ elif [[ -n "$(find . -name "*.sh" -type f | head -n 1)" ]]; then
62
+ echo "shell"
63
+ elif [[ -n "$(find . -name "*.ps1" -type f -not -path "./.git/*" | head -n 1)" ]]; then
64
+ echo "powershell"
65
+ else
66
+ echo "unknown"
67
+ fi
68
+ fi
69
+ }
70
+
71
+ route_to_adapter() {
72
+ local action="$1"
73
+ local lang
74
+ lang=$(detect_project_lang)
75
+
76
+ # Source the appropriate adapter
77
+ if [[ -f "githooks/adapters/${lang}.sh" ]]; then
78
+ # shellcheck source=githooks/adapters/"${lang}".sh
79
+ source "githooks/adapters/${lang}.sh"
80
+
81
+ # Execute the requested action
82
+ case "$action" in
83
+ "static_analysis") run_static_analysis ;;
84
+ "lint") run_lint ;;
85
+ "tests") run_tests ;;
86
+ "coverage") run_coverage ;;
87
+ *) return 1 ;;
88
+ esac
89
+ elif [[ -f "./githooks/adapters/${lang}.sh" ]]; then
90
+ # Alternative: source with ./ prefix
91
+ # shellcheck source=./githooks/adapters/"${lang}".sh
92
+ source "./githooks/adapters/${lang}.sh"
93
+
94
+ # Execute the requested action
95
+ case "$action" in
96
+ "static_analysis") run_static_analysis ;;
97
+ "lint") run_lint ;;
98
+ "tests") run_tests ;;
99
+ "coverage") run_coverage ;;
100
+ *) return 1 ;;
101
+ esac
102
+ else
103
+ echo "No adapter found for language: $lang"
104
+ return 1
105
+ fi
106
+ }
107
+
108
+ check_if_tool_available() {
109
+ local tool_name="$1"
110
+
111
+ # Check if command exists
112
+ if command -v "$tool_name" >/dev/null 2>&1; then
113
+ return 0
114
+ else
115
+ return 1
116
+ fi
117
+ }
118
+
119
+ require_tool() {
120
+ local tool_name="$1"
121
+ local gate_name="${2:-Gate}"
122
+ local install_hint="${3:-}"
123
+
124
+ if command -v "$tool_name" >/dev/null 2>&1; then
125
+ return 0
126
+ fi
127
+
128
+ if command -v npx >/dev/null 2>&1 && npx --no-install "$tool_name" --version >/dev/null 2>&1; then
129
+ return 0
130
+ fi
131
+
132
+ echo "❌ BLOCKED - Required tool '$tool_name' not available for $gate_name"
133
+ if [[ -n "$install_hint" ]]; then
134
+ echo " Install: $install_hint"
135
+ fi
136
+ echo " Per QUALITY-GATES-CODE-OF-CONDUCT.md: tool unavailable = BLOCK, not SKIP"
137
+ return 1
138
+ }
139
+
140
+ # Detect if project has IaC files (Terraform, Kubernetes, Docker)
141
+ detect_iac_project() {
142
+ local has_iac=false
143
+
144
+ # Check for Terraform files
145
+ if [[ -n "$(find . -maxdepth 2 -name "*.tf" -not -path "./.git/*" 2>/dev/null | head -1)" ]]; then
146
+ has_iac=true
147
+ fi
148
+
149
+ # Check for Kubernetes manifests (YAML with apiVersion/kind)
150
+ if [[ -n "$(find . -maxdepth 2 \( -name "*.yaml" -o -name "*.yml" \) -not -path "./.git/*" 2>/dev/null | head -1)" ]]; then
151
+ local yaml_file=$(find . -maxdepth 2 \( -name "*.yaml" -o -name "*.yml" \) -not -path "./.git/*" 2>/dev/null | head -1)
152
+ if grep -qE "^(apiVersion|kind):" "$yaml_file" 2>/dev/null; then
153
+ has_iac=true
154
+ fi
155
+ fi
156
+
157
+ # Check for Dockerfiles
158
+ if [[ -n "$(find . -maxdepth 2 -name "Dockerfile" -o -name "*.dockerfile" -not -path "./.git/*" 2>/dev/null | head -1)" ]]; then
159
+ has_iac=true
160
+ fi
161
+
162
+ if [ "$has_iac" = true ]; then
163
+ echo "iac"
164
+ else
165
+ echo ""
166
+ fi
167
+ }
168
+
169
+ # Stryker 9.x config files (new format, takes priority)
170
+ detect_mutation_testable() {
171
+ if [[ -f "stryker.config.mjs" ]] || [[ -f "stryker.config.js" ]] || \
172
+ [[ -f "stryker.config.cjs" ]] || [[ -f "stryker.config.json" ]]; then
173
+ if [[ -f "package.json" ]] && grep -qE '"@stryker-mutator[^"]*"' package.json 2>/dev/null; then
174
+ return 0
175
+ fi
176
+ if command -v npx >/dev/null 2>&1 && npx --no-install stryker --version >/dev/null 2>&1; then
177
+ return 0
178
+ fi
179
+ fi
180
+
181
+ # Legacy Stryker config files (backwards compatibility)
182
+ if [[ -f "stryker.conf.json" ]] || [[ -f "stryker.prepush.conf.json" ]]; then
183
+ if [[ -f "package.json" ]] && grep -qE '"@stryker-mutator[^"]*"' package.json 2>/dev/null; then
184
+ return 0
185
+ fi
186
+ if command -v npx >/dev/null 2>&1 && npx --no-install stryker --version >/dev/null 2>&1; then
187
+ return 0
188
+ fi
189
+ fi
190
+
191
+ return 1
192
+ }
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env bash
2
+
3
+ _detect_cpp_build() {
4
+ if command -v cmake &>/dev/null && [ -f "CMakeLists.txt" ]; then
5
+ echo "cmake"
6
+ elif [ -f "Makefile" ]; then
7
+ echo "make"
8
+ else
9
+ echo "none"
10
+ fi
11
+ }
12
+
13
+ run_static_analysis() {
14
+ if command -v clang-tidy &>/dev/null; then
15
+ local files
16
+ files=$(find . -name "*.cpp" -o -name "*.cc" -o -name "*.c" -o -name "*.h" 2>/dev/null | head -20)
17
+ if [ -n "$files" ]; then
18
+ echo "$files" | xargs clang-tidy 2>&1 | head -20
19
+ fi
20
+ elif command -v cppcheck &>/dev/null; then
21
+ cppcheck --enable=all --inline-suppr . 2>&1 | head -20
22
+ else
23
+ echo "No C++ analysis tools (clang-tidy/cppcheck)"
24
+ return 1
25
+ fi
26
+ return 0
27
+ }
28
+
29
+ run_lint() {
30
+ run_static_analysis
31
+ }
32
+
33
+ run_tests() {
34
+ local build_system
35
+ build_system=$(_detect_cpp_build)
36
+
37
+ if [ "$build_system" = "cmake" ]; then
38
+ if [ -d "build" ]; then
39
+ cmake --build build/ 2>&1 | tail -5
40
+ cd build && ctest --output-on-failure 2>&1 | tail -15
41
+ return ${PIPESTATUS[1]}
42
+ else
43
+ cmake -B build -G Ninja 2>/dev/null || cmake -B build 2>/dev/null
44
+ cmake --build build/ 2>&1 | tail -5
45
+ cd build && ctest --output-on-failure 2>&1 | tail -15
46
+ return ${PIPESTATUS[1]}
47
+ fi
48
+ elif [ "$build_system" = "make" ]; then
49
+ make 2>&1 | tail -5
50
+ make test 2>&1 | tail -15
51
+ return ${PIPESTATUS[1]}
52
+ else
53
+ echo "No C++ build system detected"
54
+ return 1
55
+ fi
56
+ }
57
+
58
+ run_coverage() {
59
+ local build_system
60
+ build_system=$(_detect_cpp_build)
61
+
62
+ if command -v gcovr &>/dev/null; then
63
+ if [ "$build_system" = "cmake" ] && [ -d "build" ]; then
64
+ cd build && gcovr --root .. 2>&1 | tail -10
65
+ return $?
66
+ fi
67
+ gcovr --root . 2>&1 | tail -10
68
+ return $?
69
+ elif command -v llvm-cov &>/dev/null; then
70
+ llvm-cov report 2>&1 | tail -10
71
+ return $?
72
+ else
73
+ echo "No C++ coverage tools (gcovr/llvm-cov)"
74
+ return 1
75
+ fi
76
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+
3
+ run_static_analysis() {
4
+ if command -v dart &>/dev/null; then
5
+ dart analyze 2>&1 | head -30
6
+ return $?
7
+ else
8
+ echo "Dart SDK not available"
9
+ return 1
10
+ fi
11
+ }
12
+
13
+ run_lint() {
14
+ if command -v dart &>/dev/null; then
15
+ dart format --output=none --set-exit-if-changed . 2>&1 | head -30
16
+ return $?
17
+ else
18
+ echo "Dart SDK not available"
19
+ return 1
20
+ fi
21
+ }
22
+
23
+ run_tests() {
24
+ if command -v dart &>/dev/null; then
25
+ dart test 2>&1 | tail -20
26
+ return $?
27
+ else
28
+ echo "Dart SDK not available"
29
+ return 1
30
+ fi
31
+ }
32
+
33
+ run_coverage() {
34
+ if command -v dart &>/dev/null; then
35
+ dart test --coverage=coverage 2>&1 | tail -10
36
+ return $?
37
+ else
38
+ echo "Dart SDK not available"
39
+ return 1
40
+ fi
41
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+
3
+ run_static_analysis() {
4
+ if command -v flutter &>/dev/null; then
5
+ flutter analyze 2>&1 | head -30
6
+ return $?
7
+ else
8
+ echo "Flutter SDK not available"
9
+ return 1
10
+ fi
11
+ }
12
+
13
+ run_lint() {
14
+ if command -v flutter &>/dev/null; then
15
+ flutter format --output=none --set-exit-if-changed . 2>&1 | head -30
16
+ return $?
17
+ else
18
+ echo "Flutter SDK not available"
19
+ return 1
20
+ fi
21
+ }
22
+
23
+ run_tests() {
24
+ if command -v flutter &>/dev/null; then
25
+ flutter test 2>&1 | tail -20
26
+ return $?
27
+ else
28
+ echo "Flutter SDK not available"
29
+ return 1
30
+ fi
31
+ }
32
+
33
+ run_coverage() {
34
+ if command -v flutter &>/dev/null; then
35
+ flutter test --coverage 2>&1 | tail -10
36
+ return $?
37
+ else
38
+ echo "Flutter SDK not available"
39
+ return 1
40
+ fi
41
+ }
package/adapters/go.sh ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Go adapter for quality gates
4
+ # Tools: go vet, golangci-lint, arch-go (architecture), go test
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ source "$SCRIPT_DIR/../adapter-common.sh" 2>/dev/null || true
8
+
9
+ run_static_analysis() {
10
+ require_tool go "Go SDK" || return 1
11
+
12
+ echo "Running Go static analysis (go vet)..."
13
+ go vet ./...
14
+ return $?
15
+ }
16
+
17
+ run_lint() {
18
+ if ! require_tool golangci-lint "golangci-lint" 2>/dev/null; then
19
+ echo "⚠ golangci-lint not available, installing..."
20
+ go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest 2>/dev/null
21
+ require_tool golangci-lint "golangci-lint" || return 0
22
+ fi
23
+
24
+ echo "Running Go linting (golangci-lint)..."
25
+ golangci-lint run ./...
26
+ return $?
27
+ }
28
+
29
+ run_architecture() {
30
+ if ! require_tool arch-go "arch-go (Go architecture checker)" 2>/dev/null; then
31
+ echo "⚠ arch-go not available, installing..."
32
+ go install github.com/arch-go/arch-go@latest 2>/dev/null
33
+ require_tool arch-go "arch-go" || return 0
34
+ fi
35
+
36
+ echo "Running Go architecture checks (arch-go)..."
37
+ arch-go check
38
+ return $?
39
+ }
40
+
41
+ run_tests() {
42
+ require_tool go "Go SDK" || return 1
43
+
44
+ echo "Running Go tests..."
45
+ go test ./...
46
+ return $?
47
+ }
48
+
49
+ run_coverage() {
50
+ require_tool go "Go SDK" || return 1
51
+
52
+ echo "Running Go coverage..."
53
+ go test -coverprofile=coverage.out ./...
54
+ local rc=$?
55
+ if [ $rc -eq 0 ]; then
56
+ go tool cover -func=coverage.out 2>/dev/null | tail -1
57
+ fi
58
+ return $rc
59
+ }
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # IaC (Infrastructure as Code) adapter for quality gates
4
+ # Supports: Terraform (.tf), Kubernetes (K8s *.yaml), Docker, CloudFormation
5
+ # Tools: checkov (primary), hadolint (Docker), kube-score (K8s)
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ source "$SCRIPT_DIR/../adapter-common.sh" 2>/dev/null || true
9
+
10
+ # Detect IaC files in changed files
11
+ detect_iac_files() {
12
+ local files="$1"
13
+ local tf_files=""
14
+ local yaml_files=""
15
+ local dockerfile_files=""
16
+
17
+ for file in $files; do
18
+ case "$file" in
19
+ *.tf)
20
+ tf_files="$tf_files $file"
21
+ ;;
22
+ *.yaml|*.yml)
23
+ # Check if it's a K8s manifest (has apiVersion and kind)
24
+ if grep -qE "^(apiVersion|kind):" "$file" 2>/dev/null; then
25
+ yaml_files="$yaml_files $file"
26
+ fi
27
+ ;;
28
+ Dockerfile|*.dockerfile|Dockerfile.*)
29
+ dockerfile_files="$dockerfile_files $file"
30
+ ;;
31
+ esac
32
+ done
33
+
34
+ echo "TERRAFORM:$tf_files"
35
+ echo "KUBERNETES:$yaml_files"
36
+ echo "DOCKER:$dockerfile_files"
37
+ }
38
+
39
+ run_static_analysis() {
40
+ local changed_files="$1"
41
+ local iac_files=$(detect_iac_files "$changed_files")
42
+
43
+ # Check if any IaC files are present
44
+ local has_iac=false
45
+ if echo "$iac_files" | grep -qE "TERRAFORM:.*[^\s]|KUBERNETES:.*[^\s]|DOCKER:.*[^\s]"; then
46
+ has_iac=true
47
+ fi
48
+
49
+ if [ "$has_iac" = false ]; then
50
+ return 0 # No IaC files, skip
51
+ fi
52
+
53
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
54
+ echo " GATE: IaC Security Scan"
55
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
56
+
57
+ local exit_code=0
58
+
59
+ # Try checkov first (recommended - supports multiple platforms)
60
+ if command -v checkov >/dev/null 2>&1; then
61
+ echo "Running checkov IaC security scan..."
62
+
63
+ # Run checkov on all detected IaC files
64
+ local tf_files=$(echo "$iac_files" | grep "TERRAFORM:" | sed 's/TERRAFORM://')
65
+ local k8s_files=$(echo "$iac_files" | grep "KUBERNETES:" | sed 's/KUBERNETES://')
66
+ local docker_files=$(echo "$iac_files" | grep "DOCKER:" | sed 's/DOCKER://')
67
+
68
+ if [ -n "$tf_files" ]; then
69
+ echo "Scanning Terraform files..."
70
+ for file in $tf_files; do
71
+ if [ -f "$file" ]; then
72
+ checkov --file "$file" --compact 2>&1 | tail -20
73
+ local tf_exit=${PIPESTATUS[0]}
74
+ if [ $tf_exit -ne 0 ]; then
75
+ exit_code=$tf_exit
76
+ fi
77
+ fi
78
+ done
79
+ fi
80
+
81
+ if [ -n "$k8s_files" ]; then
82
+ echo "Scanning Kubernetes manifests..."
83
+ for file in $k8s_files; do
84
+ if [ -f "$file" ]; then
85
+ checkov --file "$file" --compact 2>&1 | tail -20
86
+ local k8s_exit=${PIPESTATUS[0]}
87
+ if [ $k8s_exit -ne 0 ]; then
88
+ exit_code=$k8s_exit
89
+ fi
90
+ fi
91
+ done
92
+ fi
93
+
94
+ if [ -n "$docker_files" ]; then
95
+ echo "Scanning Dockerfiles..."
96
+ for file in $docker_files; do
97
+ if [ -f "$file" ]; then
98
+ checkov --file "$file" --compact 2>&1 | tail -20
99
+ local docker_exit=${PIPESTATUS[0]}
100
+ if [ $docker_exit -ne 0 ]; then
101
+ exit_code=$docker_exit
102
+ fi
103
+ fi
104
+ done
105
+ fi
106
+
107
+ echo "checkov scan completed with exit code: $exit_code"
108
+ return $exit_code
109
+ fi
110
+
111
+ # Fallback to individual tools if checkov not available
112
+
113
+ # Try hadolint for Docker
114
+ local docker_files=$(echo "$iac_files" | grep "DOCKER:" | sed 's/DOCKER://')
115
+ if [ -n "$docker_files" ] && command -v hadolint >/dev/null 2>&1; then
116
+ echo "Running hadolint for Dockerfiles..."
117
+ for file in $docker_files; do
118
+ if [ -f "$file" ]; then
119
+ hadolint "$file" 2>&1 | tail -20
120
+ local hadolint_exit=${PIPESTATUS[0]}
121
+ if [ $hadolint_exit -ne 0 ]; then
122
+ exit_code=$hadolint_exit
123
+ fi
124
+ fi
125
+ done
126
+ fi
127
+
128
+ # Try kube-score for Kubernetes
129
+ local k8s_files=$(echo "$iac_files" | grep "KUBERNETES:" | sed 's/KUBERNETES://')
130
+ if [ -n "$k8s_files" ] && command -v kube-score >/dev/null 2>&1; then
131
+ echo "Running kube-score for Kubernetes..."
132
+ for file in $k8s_files; do
133
+ if [ -f "$file" ]; then
134
+ kube-score score "$file" 2>&1 | tail -20
135
+ local kube_exit=${PIPESTATUS[0]}
136
+ if [ $kube_exit -ne 0 ]; then
137
+ exit_code=$kube_exit
138
+ fi
139
+ fi
140
+ done
141
+ fi
142
+
143
+ # Try tflint for Terraform
144
+ local tf_files=$(echo "$iac_files" | grep "TERRAFORM:" | sed 's/TERRAFORM://')
145
+ if [ -n "$tf_files" ] && command -v tflint >/dev/null 2>&1; then
146
+ echo "Running tflint for Terraform..."
147
+ for file in $tf_files; do
148
+ if [ -f "$file" ]; then
149
+ tflint --chdir "$(dirname "$file")" 2>&1 | tail -20
150
+ local tflint_exit=${PIPESTATUS[0]}
151
+ if [ $tflint_exit -ne 0 ]; then
152
+ exit_code=$tflint_exit
153
+ fi
154
+ fi
155
+ done
156
+ fi
157
+
158
+ # If no tools available but IaC files detected, warn user
159
+ if [ $exit_code -eq 0 ]; then
160
+ echo "⚠ IaC files detected but no scanning tools available."
161
+ echo " Install checkov (recommended) or individual tools:"
162
+ echo " - checkov: pip install checkov"
163
+ echo " - hadolint: https://github.com/hadolint/hadolint"
164
+ echo " - kube-score: https://github.com/zegl/kube-score"
165
+ echo " - tflint: https://github.com/terraform-linters/tflint"
166
+ echo ""
167
+ echo " Per QUALITY-GATES-CODE-OF-CONDUCT.md: tool unavailable = BLOCK, not SKIP"
168
+ return 1
169
+ fi
170
+
171
+ return $exit_code
172
+ }
173
+
174
+ run_lint() {
175
+ # IaC linting is handled by run_static_analysis
176
+ run_static_analysis "$1"
177
+ }
178
+
179
+ run_tests() {
180
+ # IaC typically doesn't have unit tests in the traditional sense
181
+ # Could add integration tests with terratest (Go) or pytest (Python)
182
+ echo "IaC tests not configured"
183
+ return 0
184
+ }
185
+
186
+ run_coverage() {
187
+ echo "IaC coverage not available"
188
+ return 0
189
+ }