@hybridlabor-api/aos 4.7.2 → 4.9.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/.agents/agents.md +1 -1
- package/.agents/nodes.json +1 -1
- package/.agents/vendor-manifest.json +23 -1
- package/.claude/agents/godmode-media-eventtech.md +1 -1
- package/.claude/hooks/conventional-commits.mjs +125 -0
- package/.claude/hooks/env-file-protection.mjs +105 -0
- package/.claude/hooks/go-gate.mjs +101 -81
- package/.claude/settings.json +13 -0
- package/.opencode/agents/godmode-media-eventtech.md +1 -1
- package/.opencode/plugins/bdb-aos.js +31 -4
- package/README.de.md +1 -1
- package/README.md +1 -1
- package/README.pt.md +1 -1
- package/THIRD_PARTY_NOTICES.md +88 -0
- package/docs/skills_table.md +1 -1
- package/installer.js +309 -101
- package/package.json +6 -2
- package/packages/aos-cli/README.md +80 -0
- package/packages/aos-cli/bin/aos-cli.mjs +134 -0
- package/packages/aos-cli/core-skills.json +12 -0
- package/packages/aos-cli/extensions/aos.ts +321 -0
- package/packages/aos-cli/package-lock.json +1923 -0
- package/packages/aos-cli/package.json +29 -0
- package/packages/aos-cli/scripts/check-theme.mjs +63 -0
- package/packages/aos-cli/themes/aos.json +97 -0
- package/scripts/build-plugin-manifest.mjs +131 -0
- package/scripts/validate-skills.mjs +81 -6
- package/skills/basic/ao-orchestrator/SKILL.md +116 -0
- package/skills/global_config/agenttrail/SKILL.md +6 -1
- package/skills/global_config/ask-tim/SKILL.md +4 -4
- package/skills/global_config/bash-script-generator/SKILL.md +201 -0
- package/skills/global_config/bash-script-generator/assets/templates/standard-template.sh +96 -0
- package/skills/global_config/bash-script-generator/docs/bash-scripting-guide.md +729 -0
- package/skills/global_config/bash-script-generator/docs/generation-best-practices.md +193 -0
- package/skills/global_config/bash-script-generator/docs/script-patterns.md +566 -0
- package/skills/global_config/bash-script-generator/docs/text-processing-guide.md +437 -0
- package/skills/global_config/bash-script-generator/examples/log-analyzer.sh +92 -0
- package/skills/global_config/bash-script-generator/scripts/generate_script_template.sh +123 -0
- package/skills/global_config/bash-script-generator/scripts/run_ci_checks.sh +172 -0
- package/skills/global_config/bash-script-generator/scripts/test_generator.sh +413 -0
- package/skills/global_config/bash-script-validator/SKILL.md +249 -0
- package/skills/global_config/bash-script-validator/docs/awk-reference.md +449 -0
- package/skills/global_config/bash-script-validator/docs/bash-reference.md +468 -0
- package/skills/global_config/bash-script-validator/docs/common-mistakes.md +623 -0
- package/skills/global_config/bash-script-validator/docs/grep-reference.md +395 -0
- package/skills/global_config/bash-script-validator/docs/regex-reference.md +391 -0
- package/skills/global_config/bash-script-validator/docs/sed-reference.md +454 -0
- package/skills/global_config/bash-script-validator/docs/shell-reference.md +463 -0
- package/skills/global_config/bash-script-validator/docs/shellcheck-reference.md +399 -0
- package/skills/global_config/bash-script-validator/examples/bad-bash.sh +55 -0
- package/skills/global_config/bash-script-validator/examples/bad-shell.sh +54 -0
- package/skills/global_config/bash-script-validator/examples/good-bash.sh +71 -0
- package/skills/global_config/bash-script-validator/examples/good-shell.sh +69 -0
- package/skills/global_config/bash-script-validator/scripts/run_ci_checks.sh +23 -0
- package/skills/global_config/bash-script-validator/scripts/shellcheck_wrapper.sh +174 -0
- package/skills/global_config/bash-script-validator/scripts/test_validate.sh +446 -0
- package/skills/global_config/bash-script-validator/scripts/validate.sh +512 -0
- package/skills/global_config/ci-pipeline/SKILL.md +135 -0
- package/skills/global_config/dispatching-parallel-agents/SKILL.md +170 -0
- package/skills/global_config/dockerfile-generator/SKILL.md +1038 -0
- package/skills/global_config/dockerfile-generator/examples/example.dockerignore +95 -0
- package/skills/global_config/dockerfile-generator/examples/golang-distroless.Dockerfile +34 -0
- package/skills/global_config/dockerfile-generator/examples/java-springboot.Dockerfile +45 -0
- package/skills/global_config/dockerfile-generator/examples/nextjs-production.Dockerfile +49 -0
- package/skills/global_config/dockerfile-generator/examples/nodejs-multistage.Dockerfile +55 -0
- package/skills/global_config/dockerfile-generator/examples/python-fastapi.Dockerfile +48 -0
- package/skills/global_config/dockerfile-generator/references/language_specific_guides.md +510 -0
- package/skills/global_config/dockerfile-generator/references/multistage_builds.md +570 -0
- package/skills/global_config/dockerfile-generator/references/optimization_patterns.md +492 -0
- package/skills/global_config/dockerfile-generator/references/security_best_practices.md +375 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_dockerignore.sh +199 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_golang.sh +172 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_java.sh +185 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_nodejs.sh +279 -0
- package/skills/global_config/dockerfile-generator/scripts/generate_python.sh +218 -0
- package/skills/global_config/dockerfile-generator/scripts/test_generator.sh +210 -0
- package/skills/global_config/dockerfile-validator/SKILL.md +300 -0
- package/skills/global_config/dockerfile-validator/examples/.dockerignore.example +34 -0
- package/skills/global_config/dockerfile-validator/examples/bad-example.Dockerfile +37 -0
- package/skills/global_config/dockerfile-validator/examples/golang-distroless.Dockerfile +50 -0
- package/skills/global_config/dockerfile-validator/examples/good-example.Dockerfile +52 -0
- package/skills/global_config/dockerfile-validator/examples/python-optimized.Dockerfile +53 -0
- package/skills/global_config/dockerfile-validator/examples/security-issues.Dockerfile +42 -0
- package/skills/global_config/dockerfile-validator/references/docker_best_practices.md +348 -0
- package/skills/global_config/dockerfile-validator/references/optimization_guide.md +473 -0
- package/skills/global_config/dockerfile-validator/references/security_checklist.md +208 -0
- package/skills/global_config/dockerfile-validator/scripts/dockerfile-validate.sh +699 -0
- package/skills/global_config/dockerfile-validator/scripts/test_validate.sh +35 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn-lock-read.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/copy-before-yarn.Dockerfile +6 -0
- package/skills/global_config/dockerfile-validator/tests/fixtures/from-platform-nonroot.Dockerfile +7 -0
- package/skills/global_config/dockerfile-validator/tests/test_regression.sh +164 -0
- package/skills/global_config/finishing-a-development-branch/SKILL.md +228 -0
- package/skills/global_config/github-actions-generator/SKILL.md +353 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/composite/action.yml +82 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/Dockerfile +25 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/action.yml +42 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/docker/entrypoint.sh +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/action.yml +33 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/index.js +50 -0
- package/skills/global_config/github-actions-generator/assets/templates/action/javascript/package.json +27 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/basic_workflow.yml +242 -0
- package/skills/global_config/github-actions-generator/assets/templates/workflow/reusable_workflow.yml +106 -0
- package/skills/global_config/github-actions-generator/examples/README.md +147 -0
- package/skills/global_config/github-actions-generator/examples/actions/setup-node-cached/action.yml +93 -0
- package/skills/global_config/github-actions-generator/examples/caching/docker-buildkit.yml +256 -0
- package/skills/global_config/github-actions-generator/examples/security/dependency-review.yml +62 -0
- package/skills/global_config/github-actions-generator/examples/security/sbom-attestation.yml +119 -0
- package/skills/global_config/github-actions-generator/examples/triggers/chatops-commands.yml +475 -0
- package/skills/global_config/github-actions-generator/examples/triggers/repository-dispatch.yml +418 -0
- package/skills/global_config/github-actions-generator/examples/triggers/workflow-orchestration.yml +404 -0
- package/skills/global_config/github-actions-generator/examples/workflows/docker-build-push.yml +68 -0
- package/skills/global_config/github-actions-generator/examples/workflows/go-ci.yml +161 -0
- package/skills/global_config/github-actions-generator/examples/workflows/monorepo-ci.yml +340 -0
- package/skills/global_config/github-actions-generator/examples/workflows/multi-environment-deploy.yml +406 -0
- package/skills/global_config/github-actions-generator/examples/workflows/nodejs-ci.yml +122 -0
- package/skills/global_config/github-actions-generator/examples/workflows/python-ci.yml +157 -0
- package/skills/global_config/github-actions-generator/examples/workflows/scheduled-tasks.yml +376 -0
- package/skills/global_config/github-actions-generator/references/advanced-triggers.md +917 -0
- package/skills/global_config/github-actions-generator/references/best-practices.md +755 -0
- package/skills/global_config/github-actions-generator/references/common-actions.md +715 -0
- package/skills/global_config/github-actions-generator/references/custom-actions.md +320 -0
- package/skills/global_config/github-actions-generator/references/expressions-and-contexts.md +688 -0
- package/skills/global_config/github-actions-generator/references/modern-features.md +421 -0
- package/skills/global_config/github-actions-generator/scripts/test_generator.sh +344 -0
- package/skills/global_config/github-actions-templates/SKILL.md +7 -0
- package/skills/global_config/github-actions-validator/SKILL.md +576 -0
- package/skills/global_config/github-actions-validator/examples/README.md +88 -0
- package/skills/global_config/github-actions-validator/examples/outdated-versions.yml +76 -0
- package/skills/global_config/github-actions-validator/examples/valid-ci.yml +79 -0
- package/skills/global_config/github-actions-validator/examples/with-errors.yml +47 -0
- package/skills/global_config/github-actions-validator/references/act_usage.md +233 -0
- package/skills/global_config/github-actions-validator/references/action_versions.md +122 -0
- package/skills/global_config/github-actions-validator/references/actionlint_usage.md +343 -0
- package/skills/global_config/github-actions-validator/references/common_errors.md +512 -0
- package/skills/global_config/github-actions-validator/references/modern_features.md +384 -0
- package/skills/global_config/github-actions-validator/references/runners.md +317 -0
- package/skills/global_config/github-actions-validator/scripts/install_tools.sh +113 -0
- package/skills/global_config/github-actions-validator/scripts/validate_workflow.sh +910 -0
- package/skills/global_config/github-actions-validator/tests/test_validate_workflow.sh +237 -0
- package/skills/global_config/makefile-generator/SKILL.md +614 -0
- package/skills/global_config/makefile-generator/assets/templates/.gitkeep +1 -0
- package/skills/global_config/makefile-generator/docs/makefile-structure.md +530 -0
- package/skills/global_config/makefile-generator/docs/optimization-guide.md +784 -0
- package/skills/global_config/makefile-generator/docs/patterns-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/security-guide.md +361 -0
- package/skills/global_config/makefile-generator/docs/targets-guide.md +642 -0
- package/skills/global_config/makefile-generator/docs/variables-guide.md +596 -0
- package/skills/global_config/makefile-generator/scripts/add_standard_targets.sh +539 -0
- package/skills/global_config/makefile-generator/scripts/generate_makefile_template.sh +690 -0
- package/skills/global_config/makefile-generator/test/test_helper_scripts.sh +190 -0
- package/skills/global_config/makefile-validator/SKILL.md +244 -0
- package/skills/global_config/makefile-validator/docs/bake-tool.md +1000 -0
- package/skills/global_config/makefile-validator/docs/best-practices.md +858 -0
- package/skills/global_config/makefile-validator/docs/common-mistakes.md +944 -0
- package/skills/global_config/makefile-validator/examples/bad-makefile.mk +77 -0
- package/skills/global_config/makefile-validator/examples/good-makefile.mk +103 -0
- package/skills/global_config/makefile-validator/scripts/test_validate.sh +382 -0
- package/skills/global_config/makefile-validator/scripts/validate_makefile.sh +712 -0
- package/skills/global_config/{MCP_Manage → mcp-manage}/SKILL.md +3 -3
- package/skills/global_config/requesting-code-review/SKILL.md +98 -0
- package/skills/global_config/requesting-code-review/code-reviewer.md +198 -0
- package/skills/global_config/using-git-worktrees/SKILL.md +170 -0
- package/skills/global_config/verification-before-completion/SKILL.md +123 -0
- package/skills/global_config/writing-plans/SKILL.md +126 -46
- package/skills/global_config/writing-plans-legacy/SKILL.md +139 -0
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Makefile Validator Script
|
|
4
|
+
# Validates Makefile syntax, best practices, security, and optimization using mbake tool
|
|
5
|
+
# Features: venv isolation, automatic cleanup via trap, comprehensive validation
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# Colors for output (supports NO_COLOR standard: https://no-color.org/)
|
|
10
|
+
if [ -n "${NO_COLOR:-}" ]; then
|
|
11
|
+
RED=''
|
|
12
|
+
YELLOW=''
|
|
13
|
+
GREEN=''
|
|
14
|
+
BLUE=''
|
|
15
|
+
NC=''
|
|
16
|
+
else
|
|
17
|
+
RED='\033[0;31m'
|
|
18
|
+
YELLOW='\033[1;33m'
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
BLUE='\033[0;34m'
|
|
21
|
+
NC='\033[0m' # No Color
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Counters
|
|
25
|
+
ERRORS=0
|
|
26
|
+
WARNINGS=0
|
|
27
|
+
INFO=0
|
|
28
|
+
|
|
29
|
+
# mbake availability flag — set to 1 by setup_venv when the venv+mbake are ready.
|
|
30
|
+
# Stays 0 when python3/pip3 are absent or the venv/install step fails, allowing
|
|
31
|
+
# the validator to fall back to GNU make + custom checks only.
|
|
32
|
+
MBAKE_AVAILABLE=0
|
|
33
|
+
MBAKE_SKIP_INSTALL_MODE=0
|
|
34
|
+
|
|
35
|
+
case "${MBAKE_SKIP_INSTALL:-0}" in
|
|
36
|
+
1|true|TRUE|yes|YES|on|ON)
|
|
37
|
+
MBAKE_SKIP_INSTALL_MODE=1
|
|
38
|
+
;;
|
|
39
|
+
0|false|FALSE|no|NO|off|OFF|"")
|
|
40
|
+
MBAKE_SKIP_INSTALL_MODE=0
|
|
41
|
+
;;
|
|
42
|
+
*)
|
|
43
|
+
echo -e "${YELLOW}[WARNING]${NC} Unsupported MBAKE_SKIP_INSTALL='${MBAKE_SKIP_INSTALL}', treating as disabled"
|
|
44
|
+
((WARNINGS+=1))
|
|
45
|
+
;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
# Temporary venv directory (unique per invocation, respects TMPDIR)
|
|
49
|
+
VENV_DIR="${TMPDIR:-/tmp}/makefile-validator-venv-$$"
|
|
50
|
+
CLEANUP_DONE=0
|
|
51
|
+
|
|
52
|
+
# Cleanup function - always runs on exit
|
|
53
|
+
cleanup() {
|
|
54
|
+
if [ "$CLEANUP_DONE" -eq 0 ]; then
|
|
55
|
+
CLEANUP_DONE=1
|
|
56
|
+
# Safety check: only remove if it's our temp venv (works with custom TMPDIR)
|
|
57
|
+
if [ -d "$VENV_DIR" ] && [[ "$VENV_DIR" == */makefile-validator-venv-* ]]; then
|
|
58
|
+
echo -e "${BLUE}[CLEANUP]${NC} Removing temporary venv..."
|
|
59
|
+
rm -rf "$VENV_DIR"
|
|
60
|
+
fi
|
|
61
|
+
fi
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Register cleanup trap for all exit scenarios
|
|
65
|
+
trap cleanup EXIT INT TERM
|
|
66
|
+
|
|
67
|
+
# Print error and exit
|
|
68
|
+
error_exit() {
|
|
69
|
+
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
70
|
+
exit 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Print section header
|
|
74
|
+
print_header() {
|
|
75
|
+
echo ""
|
|
76
|
+
echo "========================================"
|
|
77
|
+
echo "$1"
|
|
78
|
+
echo "========================================"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Print sub-header
|
|
82
|
+
print_subheader() {
|
|
83
|
+
echo -e "\n${BLUE}[$1]${NC}"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Check dependencies.
|
|
87
|
+
# python3 and pip3 are needed only for the mbake stages; their absence degrades
|
|
88
|
+
# coverage but does not abort — GNU make syntax and all custom checks still run.
|
|
89
|
+
check_dependencies() {
|
|
90
|
+
local mbake_prereqs_ok=1
|
|
91
|
+
|
|
92
|
+
if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 1 ]; then
|
|
93
|
+
echo -e "${BLUE}ℹ${NC} MBAKE_SKIP_INSTALL=1 enabled — deterministic mode will skip mbake stages"
|
|
94
|
+
((INFO+=1))
|
|
95
|
+
else
|
|
96
|
+
if ! command -v python3 &> /dev/null; then
|
|
97
|
+
echo -e "${YELLOW}[WARNING]${NC} python3 not found — mbake stages will be skipped"
|
|
98
|
+
echo " Install python3 to enable mbake validation and format-check coverage"
|
|
99
|
+
((WARNINGS+=1))
|
|
100
|
+
mbake_prereqs_ok=0
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if ! command -v pip3 &> /dev/null; then
|
|
104
|
+
echo -e "${YELLOW}[WARNING]${NC} pip3 not found — mbake stages will be skipped"
|
|
105
|
+
echo " Install pip3 to enable mbake validation and format-check coverage"
|
|
106
|
+
((WARNINGS+=1))
|
|
107
|
+
mbake_prereqs_ok=0
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if ! command -v make &> /dev/null; then
|
|
112
|
+
echo -e "${YELLOW}[WARNING]${NC} GNU make not found — syntax validation will be limited"
|
|
113
|
+
((WARNINGS+=1))
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Signal to setup_venv (and main) that it is worth attempting venv setup
|
|
117
|
+
if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 0 ] && [ "$mbake_prereqs_ok" -eq 1 ]; then
|
|
118
|
+
MBAKE_AVAILABLE=1
|
|
119
|
+
fi
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Setup virtual environment and install mbake.
|
|
123
|
+
# On failure (offline, proxy, bad python env) the function warns and clears
|
|
124
|
+
# MBAKE_AVAILABLE so the caller skips all mbake-dependent stages.
|
|
125
|
+
setup_venv() {
|
|
126
|
+
print_subheader "ENVIRONMENT SETUP"
|
|
127
|
+
echo "Creating temporary venv at: $VENV_DIR"
|
|
128
|
+
|
|
129
|
+
if ! python3 -m venv "$VENV_DIR" 2>&1; then
|
|
130
|
+
echo -e "${YELLOW}⚠${NC} Failed to create virtual environment — mbake stages will be skipped"
|
|
131
|
+
((WARNINGS+=1))
|
|
132
|
+
MBAKE_AVAILABLE=0
|
|
133
|
+
return 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Activate venv
|
|
137
|
+
# shellcheck source=/dev/null
|
|
138
|
+
source "$VENV_DIR/bin/activate"
|
|
139
|
+
|
|
140
|
+
echo "Installing mbake..."
|
|
141
|
+
if ! pip3 install --quiet mbake 2>&1; then
|
|
142
|
+
echo -e "${YELLOW}⚠${NC} Failed to install mbake — mbake stages will be skipped"
|
|
143
|
+
echo " Ensure network access or an internal PyPI mirror is available, then rerun"
|
|
144
|
+
((WARNINGS+=1))
|
|
145
|
+
MBAKE_AVAILABLE=0
|
|
146
|
+
return 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
MBAKE_AVAILABLE=1
|
|
150
|
+
echo -e "${GREEN}✓${NC} Environment ready"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# Validate Makefile exists and is readable
|
|
154
|
+
validate_file() {
|
|
155
|
+
local file=$1
|
|
156
|
+
|
|
157
|
+
if [ ! -f "$file" ]; then
|
|
158
|
+
error_exit "File not found: $file"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
if [ ! -r "$file" ]; then
|
|
162
|
+
error_exit "File not readable: $file"
|
|
163
|
+
fi
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# Basic syntax check using GNU make
|
|
167
|
+
syntax_check() {
|
|
168
|
+
local file=$1
|
|
169
|
+
print_subheader "SYNTAX CHECK (GNU make)"
|
|
170
|
+
|
|
171
|
+
if ! command -v make &> /dev/null; then
|
|
172
|
+
echo -e "${YELLOW}⚠${NC} Skipped - GNU make not installed"
|
|
173
|
+
return 0
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
# Get absolute path to Makefile
|
|
177
|
+
local abs_file
|
|
178
|
+
abs_file=$(cd "$(dirname "$file")" && pwd)/$(basename "$file")
|
|
179
|
+
local makefile_dir
|
|
180
|
+
makefile_dir=$(dirname "$abs_file")
|
|
181
|
+
local makefile_name
|
|
182
|
+
makefile_name=$(basename "$abs_file")
|
|
183
|
+
|
|
184
|
+
# Run make from the Makefile's directory to resolve relative paths correctly
|
|
185
|
+
# Capture output and exit code in a single invocation (avoids running make twice)
|
|
186
|
+
local make_output make_exit=0
|
|
187
|
+
make_output=$(cd "$makefile_dir" && make -f "$makefile_name" -n 2>&1) || make_exit=$?
|
|
188
|
+
|
|
189
|
+
if [ "$make_exit" -eq 0 ]; then
|
|
190
|
+
echo -e "${GREEN}✓${NC} No syntax errors found"
|
|
191
|
+
else
|
|
192
|
+
echo -e "${RED}✗${NC} Syntax errors detected:"
|
|
193
|
+
echo "$make_output"
|
|
194
|
+
((ERRORS+=1))
|
|
195
|
+
return 1
|
|
196
|
+
fi
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Run mbake validation
|
|
200
|
+
mbake_validation() {
|
|
201
|
+
local file=$1
|
|
202
|
+
print_subheader "MBAKE VALIDATION"
|
|
203
|
+
|
|
204
|
+
echo "Running mbake validate..."
|
|
205
|
+
if mbake validate "$file" 2>&1; then
|
|
206
|
+
echo -e "${GREEN}✓${NC} mbake validation passed"
|
|
207
|
+
else
|
|
208
|
+
echo -e "${RED}✗${NC} mbake validation failed"
|
|
209
|
+
((ERRORS+=1))
|
|
210
|
+
fi
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# Run mbake format check
|
|
214
|
+
mbake_format_check() {
|
|
215
|
+
local file=$1
|
|
216
|
+
print_subheader "MBAKE FORMAT CHECK"
|
|
217
|
+
|
|
218
|
+
echo "Checking formatting consistency..."
|
|
219
|
+
# Capture output - mbake may wrap long lines
|
|
220
|
+
local format_output
|
|
221
|
+
format_output=$(mbake format --check "$file" 2>&1)
|
|
222
|
+
local format_exit=$?
|
|
223
|
+
|
|
224
|
+
# Join multi-line output for easier pattern matching (mbake wraps at ~80 chars)
|
|
225
|
+
# Also normalize whitespace (collapse multiple spaces to single space)
|
|
226
|
+
local format_oneline
|
|
227
|
+
format_oneline=$(echo "$format_output" | tr '\n' ' ' | tr -s ' ')
|
|
228
|
+
|
|
229
|
+
# Check for known false positives (mbake limitation with GNU Make special targets).
|
|
230
|
+
# mbake does not recognise many valid GNU Make special targets (.DELETE_ON_ERROR,
|
|
231
|
+
# .SUFFIXES, .ONESHELL, .POSIX, .PRECIOUS, .NOTPARALLEL, .INTERMEDIATE, etc.).
|
|
232
|
+
# The sed cleaning below already strips all of them; this flag just controls
|
|
233
|
+
# whether the "known mbake limitation" info note is printed.
|
|
234
|
+
local has_unknown_special_target=0
|
|
235
|
+
if echo "$format_oneline" | grep -qE "Unknown special target '\.[A-Z_]+'"; then
|
|
236
|
+
has_unknown_special_target=1
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# Check if there are real formatting issues ("Would reformat:" indicates changes needed)
|
|
240
|
+
local has_reformat=0
|
|
241
|
+
if echo "$format_oneline" | grep -q "Would reformat:"; then
|
|
242
|
+
has_reformat=1
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# Check for other errors (not related to unknown special targets)
|
|
246
|
+
local has_other_errors=0
|
|
247
|
+
# Remove known false positive patterns completely (including "Error:" before them)
|
|
248
|
+
# Pattern matches: "some_file.mk:0: Error: Unknown special target '.DELETE_ON_ERROR'"
|
|
249
|
+
local cleaned_output
|
|
250
|
+
cleaned_output=$(echo "$format_oneline" | sed -E "s/[^ ]*:[0-9]+: Error: Unknown special target '\.[A-Z_]+'//g")
|
|
251
|
+
if echo "$cleaned_output" | grep -qE "Error:|Fatal error"; then
|
|
252
|
+
has_other_errors=1
|
|
253
|
+
fi
|
|
254
|
+
|
|
255
|
+
# Decision logic
|
|
256
|
+
if [ $format_exit -eq 0 ]; then
|
|
257
|
+
echo -e "${GREEN}✓${NC} Formatting is consistent"
|
|
258
|
+
elif [ $has_unknown_special_target -eq 1 ] && [ $has_reformat -eq 0 ] && [ $has_other_errors -eq 0 ]; then
|
|
259
|
+
# Only false positive about unknown special targets - treat as success
|
|
260
|
+
echo -e "${GREEN}✓${NC} Formatting is consistent"
|
|
261
|
+
echo -e "${BLUE}ℹ${NC} mbake reported unknown special targets (this is a known mbake limitation)"
|
|
262
|
+
elif [ $has_reformat -eq 1 ] || [ $has_other_errors -eq 1 ]; then
|
|
263
|
+
# Real formatting issues or other errors exist
|
|
264
|
+
echo "$format_output" | grep -v "Unknown special target" | grep -v "^$" || true
|
|
265
|
+
echo -e "${YELLOW}⚠${NC} Formatting issues found"
|
|
266
|
+
echo ""
|
|
267
|
+
echo "Run 'mbake format $file' to fix formatting issues"
|
|
268
|
+
echo "Or run 'mbake format --diff $file' to preview changes"
|
|
269
|
+
((WARNINGS+=1))
|
|
270
|
+
else
|
|
271
|
+
# Unknown case - show as info, don't warn
|
|
272
|
+
echo -e "${GREEN}✓${NC} Formatting is consistent"
|
|
273
|
+
if [ $has_unknown_special_target -eq 1 ]; then
|
|
274
|
+
echo -e "${BLUE}ℹ${NC} mbake reported unknown special targets (this is a known mbake limitation)"
|
|
275
|
+
fi
|
|
276
|
+
fi
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
# Custom security and best practice checks
|
|
280
|
+
custom_checks() {
|
|
281
|
+
local file=$1
|
|
282
|
+
print_subheader "CUSTOM CHECKS"
|
|
283
|
+
|
|
284
|
+
local found_issues=0
|
|
285
|
+
|
|
286
|
+
# ============================================================
|
|
287
|
+
# CRITICAL: Check for .DELETE_ON_ERROR (GNU Make best practice)
|
|
288
|
+
# ============================================================
|
|
289
|
+
if ! grep -q "^\.DELETE_ON_ERROR:" "$file"; then
|
|
290
|
+
echo -e "${YELLOW}⚠${NC} Missing .DELETE_ON_ERROR declaration"
|
|
291
|
+
echo " GNU Make recommends this to delete targets on recipe failure"
|
|
292
|
+
echo " Add '.DELETE_ON_ERROR:' at the top of your Makefile"
|
|
293
|
+
echo " See: https://www.gnu.org/software/make/manual/html_node/Special-Targets.html"
|
|
294
|
+
((WARNINGS+=1))
|
|
295
|
+
found_issues=1
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
# ============================================================
|
|
299
|
+
# Check for explicit SHELL setting (modern best practice)
|
|
300
|
+
# ============================================================
|
|
301
|
+
if ! grep -qE "^SHELL\s*:?=\s*(bash|/bin/bash|/usr/bin/bash)" "$file"; then
|
|
302
|
+
echo -e "${BLUE}ℹ${NC} No explicit SHELL setting"
|
|
303
|
+
echo " Consider 'SHELL := bash' for predictable behavior"
|
|
304
|
+
echo " See: https://tech.davis-hansson.com/p/make/"
|
|
305
|
+
((INFO+=1))
|
|
306
|
+
found_issues=1
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
# ============================================================
|
|
310
|
+
# Check for recommended MAKEFLAGS settings
|
|
311
|
+
# ============================================================
|
|
312
|
+
if ! grep -q "MAKEFLAGS.*--warn-undefined-variables" "$file"; then
|
|
313
|
+
echo -e "${BLUE}ℹ${NC} Consider 'MAKEFLAGS += --warn-undefined-variables'"
|
|
314
|
+
echo " This alerts you to undefined Make variable references"
|
|
315
|
+
((INFO+=1))
|
|
316
|
+
found_issues=1
|
|
317
|
+
fi
|
|
318
|
+
if ! grep -q "MAKEFLAGS.*--no-builtin-rules" "$file"; then
|
|
319
|
+
echo -e "${BLUE}ℹ${NC} Consider 'MAKEFLAGS += --no-builtin-rules'"
|
|
320
|
+
echo " This disables built-in implicit rules for faster builds"
|
|
321
|
+
((INFO+=1))
|
|
322
|
+
found_issues=1
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
# Check for .PHONY declarations
|
|
326
|
+
if ! grep -q "^\.PHONY:" "$file"; then
|
|
327
|
+
echo -e "${YELLOW}⚠${NC} No .PHONY declarations found"
|
|
328
|
+
echo " Consider adding .PHONY for targets that don't create files"
|
|
329
|
+
echo " Example: .PHONY: clean test install"
|
|
330
|
+
((WARNINGS+=1))
|
|
331
|
+
found_issues=1
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
# Check for tabs vs spaces in recipes (improved regex)
|
|
335
|
+
# Catches 2, 4, or 8 space indentation that should be tabs
|
|
336
|
+
local space_lines
|
|
337
|
+
space_lines=$(grep -nE "^( | | )[a-zA-Z@\$\(]" "$file" 2>/dev/null | head -5) || true
|
|
338
|
+
if [ -n "$space_lines" ]; then
|
|
339
|
+
echo -e "${RED}✗${NC} Potential spaces instead of tabs in recipes detected:"
|
|
340
|
+
echo "$space_lines"
|
|
341
|
+
echo " Makefiles require TAB characters for recipe indentation"
|
|
342
|
+
((ERRORS+=1))
|
|
343
|
+
found_issues=1
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
# Check for hardcoded credentials (expanded pattern for common credential names)
|
|
347
|
+
# Note: grep -n adds "N:" prefix, so filter must match "N:# comment" format, not "# comment"
|
|
348
|
+
local cred_lines
|
|
349
|
+
cred_lines=$(grep -niE '(password|secret|api[_-]?key|apikey|token|private[_-]?key|aws_access_key|aws_secret_access_key|github_token|auth_token|credentials|azure_client_secret|database_url|db_password|ssh_key|ssl_key|encryption_key)\s*[:?]?=' "$file" 2>/dev/null | grep -vE "^[0-9]+:[[:space:]]*#" | head -3) || true
|
|
350
|
+
if [ -n "$cred_lines" ]; then
|
|
351
|
+
echo -e "${RED}✗${NC} Potential hardcoded credentials detected:"
|
|
352
|
+
echo "$cred_lines"
|
|
353
|
+
echo " Use environment variables or secret management instead"
|
|
354
|
+
((ERRORS+=1))
|
|
355
|
+
found_issues=1
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
# Check for TRULY unsafe variable expansion patterns
|
|
359
|
+
# Only flag variables that are NOT defined with defaults in the same file
|
|
360
|
+
# Look for rm/sudo/curl/wget with variables that could be empty or user-controlled
|
|
361
|
+
# Handles both UPPERCASE and lowercase variable names
|
|
362
|
+
local unsafe_vars=""
|
|
363
|
+
while IFS= read -r line; do
|
|
364
|
+
# Extract variable name from $(VAR) or $(var) pattern (any case)
|
|
365
|
+
var_name=$(echo "$line" | grep -oE '\$\([a-zA-Z_][a-zA-Z0-9_]*\)' | head -1 | tr -d '$()')
|
|
366
|
+
if [ -n "$var_name" ]; then
|
|
367
|
+
# Check if variable has a default value with := or ?=
|
|
368
|
+
if ! grep -qE "^${var_name}\s*[:?]=" "$file"; then
|
|
369
|
+
# Variable has no default, this is potentially unsafe
|
|
370
|
+
unsafe_vars="${unsafe_vars}${line}\n"
|
|
371
|
+
fi
|
|
372
|
+
fi
|
|
373
|
+
done < <(grep -E '\$\([a-zA-Z_][a-zA-Z0-9_]*\)' "$file" | grep -E '(rm -rf|sudo|curl|wget)')
|
|
374
|
+
|
|
375
|
+
if [ -n "$unsafe_vars" ]; then
|
|
376
|
+
echo -e "${YELLOW}⚠${NC} Variables without defaults used in dangerous commands:"
|
|
377
|
+
echo -e "$unsafe_vars" | head -3
|
|
378
|
+
echo " Consider adding default values (VAR := value) or validation"
|
|
379
|
+
((WARNINGS+=1))
|
|
380
|
+
found_issues=1
|
|
381
|
+
fi
|
|
382
|
+
|
|
383
|
+
# Check for missing error handling in recipes
|
|
384
|
+
if grep -E "^\t[^@#-]" "$file" | grep -vE "set -e|pipefail|\|\||&&" | grep -q .; then
|
|
385
|
+
echo -e "${BLUE}ℹ${NC} Some recipe commands may lack error handling"
|
|
386
|
+
echo " Consider using 'set -e', '||', '&&' or '-' prefix for error control"
|
|
387
|
+
((INFO+=1))
|
|
388
|
+
found_issues=1
|
|
389
|
+
fi
|
|
390
|
+
|
|
391
|
+
# Check for missing .INTERMEDIATE or .SECONDARY for temporary files
|
|
392
|
+
if grep -E "\.o|\.tmp|\.temp" "$file" | grep -q ":"; then
|
|
393
|
+
if ! grep -qE "^\.(INTERMEDIATE|SECONDARY):" "$file"; then
|
|
394
|
+
echo -e "${BLUE}ℹ${NC} Intermediate files detected (.o, .tmp, .temp)"
|
|
395
|
+
echo " Consider using .INTERMEDIATE or .SECONDARY for automatic cleanup"
|
|
396
|
+
((INFO+=1))
|
|
397
|
+
found_issues=1
|
|
398
|
+
fi
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
# Check for missing default target documentation
|
|
402
|
+
# Look for various patterns: "Default target", "(default)", "Main target", "default:" before all:
|
|
403
|
+
if ! grep -qE "^##?.*(([Dd]efault|[Mm]ain|[Ff]irst).*target|target.*(default|main)|^\s*all:.*#.*default|\(default\))" "$file"; then
|
|
404
|
+
# Also check if there's a ## comment right before "all:" target
|
|
405
|
+
if ! grep -B1 "^all:" "$file" | grep -qE "^##"; then
|
|
406
|
+
echo -e "${BLUE}ℹ${NC} No documentation for default target"
|
|
407
|
+
echo " Consider adding a comment explaining the default target"
|
|
408
|
+
((INFO+=1))
|
|
409
|
+
found_issues=1
|
|
410
|
+
fi
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
# Check for recursive variable expansion with shell commands (performance issue)
|
|
414
|
+
# Pattern uses single quotes so \$ reaches grep as a literal-dollar matcher,
|
|
415
|
+
# not as an end-of-line anchor (which double-quote expansion would produce).
|
|
416
|
+
local shell_lines
|
|
417
|
+
shell_lines=$(grep -nE '^\s*[A-Z_]+\s*=\s*\$\(shell' "$file" 2>/dev/null | head -3) || true
|
|
418
|
+
if [ -n "$shell_lines" ]; then
|
|
419
|
+
echo -e "${YELLOW}⚠${NC} Shell commands with recursive expansion '=' found:"
|
|
420
|
+
echo "$shell_lines"
|
|
421
|
+
echo " Use ':=' for immediate expansion to avoid repeated shell calls"
|
|
422
|
+
((WARNINGS+=1))
|
|
423
|
+
found_issues=1
|
|
424
|
+
fi
|
|
425
|
+
|
|
426
|
+
# Check for .SUFFIXES (optimization for disabling built-in rules)
|
|
427
|
+
# This is informational - not all Makefiles need this
|
|
428
|
+
if grep -qE "^%\." "$file" || grep -qE "^\.[a-z]+\.[a-z]+:" "$file"; then
|
|
429
|
+
if ! grep -q "^\.SUFFIXES:" "$file"; then
|
|
430
|
+
echo -e "${BLUE}ℹ${NC} Pattern/suffix rules found but no .SUFFIXES declaration"
|
|
431
|
+
echo " Consider adding '.SUFFIXES:' to disable built-in rules for faster builds"
|
|
432
|
+
((INFO+=1))
|
|
433
|
+
found_issues=1
|
|
434
|
+
fi
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
# Check for using 'make' instead of '$(MAKE)' in recursive calls
|
|
438
|
+
# Exclude: echo statements, comments, and string literals
|
|
439
|
+
# Match 'make' followed by whitespace OR at end of line (bare 'make' with no args)
|
|
440
|
+
local make_lines
|
|
441
|
+
make_lines=$(grep -nE "^\t[^#@]*\bmake(\s|$)" "$file" 2>/dev/null | grep -vE '(echo|printf|MAKE\)|".*make.*"|'"'"'.*make.*'"'"')' | head -3) || true
|
|
442
|
+
if [ -n "$make_lines" ]; then
|
|
443
|
+
echo -e "${YELLOW}⚠${NC} Direct 'make' call in recipe (should use \$(MAKE)):"
|
|
444
|
+
echo "$make_lines"
|
|
445
|
+
echo " Use '\$(MAKE)' for recursive make calls to preserve flags and options"
|
|
446
|
+
((WARNINGS+=1))
|
|
447
|
+
found_issues=1
|
|
448
|
+
fi
|
|
449
|
+
|
|
450
|
+
# Check for .SUFFIXES recommendation for large Makefiles
|
|
451
|
+
local target_count
|
|
452
|
+
target_count=$(grep -cE "^[a-zA-Z_][a-zA-Z0-9_-]*:" "$file" 2>/dev/null) || target_count=0
|
|
453
|
+
if [ "$target_count" -gt 10 ] && ! grep -q "^\.SUFFIXES:" "$file"; then
|
|
454
|
+
echo -e "${BLUE}ℹ${NC} Large Makefile ($target_count targets) without .SUFFIXES"
|
|
455
|
+
echo " Consider adding '.SUFFIXES:' to disable built-in rules for faster builds"
|
|
456
|
+
((INFO+=1))
|
|
457
|
+
found_issues=1
|
|
458
|
+
fi
|
|
459
|
+
|
|
460
|
+
# ============================================================
|
|
461
|
+
# Check for .ONESHELL without proper error handling
|
|
462
|
+
# ============================================================
|
|
463
|
+
if grep -q "^\.ONESHELL:" "$file"; then
|
|
464
|
+
# .ONESHELL is used - check if .SHELLFLAGS includes recommended flags
|
|
465
|
+
local has_shellflags=0
|
|
466
|
+
local has_e_flag=0
|
|
467
|
+
local has_u_flag=0
|
|
468
|
+
local has_pipefail=0
|
|
469
|
+
|
|
470
|
+
if grep -qE "^\.?SHELLFLAGS\s*:?=" "$file"; then
|
|
471
|
+
has_shellflags=1
|
|
472
|
+
local shellflags_line
|
|
473
|
+
shellflags_line=$(grep -E "^\.?SHELLFLAGS\s*:?=" "$file" | head -1)
|
|
474
|
+
# Check for -e flag (can be standalone -e or combined like -eu, -euo, etc.)
|
|
475
|
+
# Match: -e, -eu, -euo, -eux, etc. (e after dash, possibly with other letters)
|
|
476
|
+
if [[ "$shellflags_line" =~ -[a-zA-Z]*e[a-zA-Z]* ]] || [[ "$shellflags_line" == *"-e "* ]] || [[ "$shellflags_line" == *"-e\""* ]]; then
|
|
477
|
+
has_e_flag=1
|
|
478
|
+
fi
|
|
479
|
+
# Check for -u flag (can be standalone -u or combined like -eu, -euo, etc.)
|
|
480
|
+
if [[ "$shellflags_line" =~ -[a-zA-Z]*u[a-zA-Z]* ]] || [[ "$shellflags_line" == *"-u "* ]] || [[ "$shellflags_line" == *"-u\""* ]]; then
|
|
481
|
+
has_u_flag=1
|
|
482
|
+
fi
|
|
483
|
+
# Check for pipefail (always as -o pipefail)
|
|
484
|
+
[[ "$shellflags_line" == *"pipefail"* ]] && has_pipefail=1
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
if [ "$has_shellflags" -eq 0 ]; then
|
|
488
|
+
# No SHELLFLAGS at all, check if recipes commonly use set -e
|
|
489
|
+
local oneshell_recipes
|
|
490
|
+
oneshell_recipes=$(grep -cE "^\t" "$file" 2>/dev/null) || oneshell_recipes=0
|
|
491
|
+
local set_e_count
|
|
492
|
+
set_e_count=$(grep -cE "^\t.*set -e" "$file" 2>/dev/null) || set_e_count=0
|
|
493
|
+
|
|
494
|
+
# If less than 33% of recipe blocks have set -e, warn
|
|
495
|
+
if [ "$oneshell_recipes" -gt 0 ] && [ "$set_e_count" -lt $((oneshell_recipes / 3)) ]; then
|
|
496
|
+
echo -e "${YELLOW}⚠${NC} .ONESHELL used without .SHELLFLAGS"
|
|
497
|
+
echo " With .ONESHELL, recipe errors (except the last line) are silently ignored"
|
|
498
|
+
echo " Fix: Add '.SHELLFLAGS := -eu -o pipefail -c'"
|
|
499
|
+
echo " See: https://www.gnu.org/software/make/manual/html_node/One-Shell.html"
|
|
500
|
+
((WARNINGS+=1))
|
|
501
|
+
found_issues=1
|
|
502
|
+
fi
|
|
503
|
+
elif [ "$has_e_flag" -eq 0 ]; then
|
|
504
|
+
echo -e "${YELLOW}⚠${NC} .ONESHELL with SHELLFLAGS missing -e flag"
|
|
505
|
+
echo " Without -e, errors in recipe commands are ignored"
|
|
506
|
+
echo " Consider: .SHELLFLAGS := -eu -o pipefail -c"
|
|
507
|
+
((WARNINGS+=1))
|
|
508
|
+
found_issues=1
|
|
509
|
+
else
|
|
510
|
+
# Has -e, but recommend full flags as info
|
|
511
|
+
if [ "$has_u_flag" -eq 0 ] || [ "$has_pipefail" -eq 0 ]; then
|
|
512
|
+
echo -e "${BLUE}ℹ${NC} .SHELLFLAGS could include additional safety flags"
|
|
513
|
+
echo " Recommended: .SHELLFLAGS := -eu -o pipefail -c"
|
|
514
|
+
echo " -u: error on undefined variables, -o pipefail: catch pipe failures"
|
|
515
|
+
((INFO+=1))
|
|
516
|
+
found_issues=1
|
|
517
|
+
fi
|
|
518
|
+
fi
|
|
519
|
+
fi
|
|
520
|
+
|
|
521
|
+
# ============================================================
|
|
522
|
+
# Check for .EXPORT_ALL_VARIABLES (security concern)
|
|
523
|
+
# ============================================================
|
|
524
|
+
if grep -q "^\.EXPORT_ALL_VARIABLES:" "$file"; then
|
|
525
|
+
echo -e "${YELLOW}⚠${NC} .EXPORT_ALL_VARIABLES used - security consideration"
|
|
526
|
+
echo " This exports ALL Make variables to subprocesses, potentially leaking sensitive data"
|
|
527
|
+
echo " Consider using 'export VAR' for specific variables instead"
|
|
528
|
+
((WARNINGS+=1))
|
|
529
|
+
found_issues=1
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
# ============================================================
|
|
533
|
+
# Check for order-only prerequisites (directory best practice)
|
|
534
|
+
# ============================================================
|
|
535
|
+
# If mkdir -p is used in recipes and order-only | syntax is not used
|
|
536
|
+
if grep -qE "^\t.*mkdir.*-p" "$file"; then
|
|
537
|
+
if ! grep -qE '\| \$\(' "$file"; then
|
|
538
|
+
echo -e "${BLUE}ℹ${NC} mkdir in recipes without order-only prerequisites"
|
|
539
|
+
echo " Consider using order-only prerequisites for directories:"
|
|
540
|
+
echo " Example: \$(BUILD_DIR)/app: \$(SOURCES) | \$(BUILD_DIR)"
|
|
541
|
+
echo " This prevents unnecessary rebuilds when only timestamps change"
|
|
542
|
+
((INFO+=1))
|
|
543
|
+
found_issues=1
|
|
544
|
+
fi
|
|
545
|
+
fi
|
|
546
|
+
|
|
547
|
+
# ============================================================
|
|
548
|
+
# Check for parallel-unsafe patterns without .NOTPARALLEL
|
|
549
|
+
# ============================================================
|
|
550
|
+
if grep -qE "^\t.*(docker build|npm install|pip install|yarn install|bundle install)" "$file"; then
|
|
551
|
+
if ! grep -q "^\.NOTPARALLEL:" "$file"; then
|
|
552
|
+
echo -e "${BLUE}ℹ${NC} Parallel-sensitive commands detected (npm/docker/pip install)"
|
|
553
|
+
echo " Consider using .NOTPARALLEL for targets with these commands"
|
|
554
|
+
echo " Or add proper dependencies to prevent race conditions"
|
|
555
|
+
((INFO+=1))
|
|
556
|
+
found_issues=1
|
|
557
|
+
fi
|
|
558
|
+
fi
|
|
559
|
+
|
|
560
|
+
if [ "$found_issues" -eq 0 ]; then
|
|
561
|
+
echo -e "${GREEN}✓${NC} No additional issues found"
|
|
562
|
+
fi
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
# Run checkmake if available
|
|
566
|
+
checkmake_validation() {
|
|
567
|
+
local file=$1
|
|
568
|
+
print_subheader "CHECKMAKE VALIDATION (optional)"
|
|
569
|
+
|
|
570
|
+
if ! command -v checkmake &> /dev/null; then
|
|
571
|
+
echo -e "${BLUE}ℹ${NC} checkmake not installed - skipping additional linting"
|
|
572
|
+
echo " Install with: go install github.com/checkmake/checkmake/cmd/checkmake@latest"
|
|
573
|
+
return 0
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
echo "Running checkmake..."
|
|
577
|
+
local checkmake_output
|
|
578
|
+
checkmake_output=$(checkmake "$file" 2>&1) || true
|
|
579
|
+
if [ -n "$checkmake_output" ]; then
|
|
580
|
+
echo "$checkmake_output"
|
|
581
|
+
# Count warnings from checkmake
|
|
582
|
+
local cm_warnings
|
|
583
|
+
cm_warnings=$(echo "$checkmake_output" | grep -c "WARN" 2>/dev/null) || cm_warnings=0
|
|
584
|
+
if [ "$cm_warnings" -gt 0 ]; then
|
|
585
|
+
((WARNINGS+=cm_warnings))
|
|
586
|
+
fi
|
|
587
|
+
else
|
|
588
|
+
echo -e "${GREEN}✓${NC} checkmake validation passed"
|
|
589
|
+
fi
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
# Run unmake if available (for POSIX portability checks)
|
|
593
|
+
unmake_validation() {
|
|
594
|
+
local file=$1
|
|
595
|
+
print_subheader "UNMAKE VALIDATION (optional)"
|
|
596
|
+
|
|
597
|
+
if ! command -v unmake &> /dev/null; then
|
|
598
|
+
echo -e "${BLUE}ℹ${NC} unmake not installed - skipping POSIX portability checks"
|
|
599
|
+
echo " See: https://github.com/mcandre/unmake"
|
|
600
|
+
return 0
|
|
601
|
+
fi
|
|
602
|
+
|
|
603
|
+
echo "Running unmake for POSIX portability..."
|
|
604
|
+
local unmake_output
|
|
605
|
+
unmake_output=$(unmake "$file" 2>&1) || true
|
|
606
|
+
if [ -n "$unmake_output" ]; then
|
|
607
|
+
echo "$unmake_output"
|
|
608
|
+
# Count warnings from unmake
|
|
609
|
+
local um_warnings
|
|
610
|
+
um_warnings=$(echo "$unmake_output" | grep -cE "(warning|Warning)" 2>/dev/null) || um_warnings=0
|
|
611
|
+
if [ "$um_warnings" -gt 0 ]; then
|
|
612
|
+
((WARNINGS+=um_warnings))
|
|
613
|
+
fi
|
|
614
|
+
else
|
|
615
|
+
echo -e "${GREEN}✓${NC} unmake validation passed (POSIX compatible)"
|
|
616
|
+
fi
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
# Print summary
|
|
620
|
+
print_summary() {
|
|
621
|
+
local file=$1
|
|
622
|
+
print_header "VALIDATION SUMMARY"
|
|
623
|
+
echo "File: $file"
|
|
624
|
+
echo ""
|
|
625
|
+
echo -e "${RED}Errors: ${NC} $ERRORS"
|
|
626
|
+
echo -e "${YELLOW}Warnings:${NC} $WARNINGS"
|
|
627
|
+
echo -e "${BLUE}Info: ${NC} $INFO"
|
|
628
|
+
echo ""
|
|
629
|
+
|
|
630
|
+
if [ "$ERRORS" -gt 0 ]; then
|
|
631
|
+
echo -e "${RED}⚠ Validation FAILED - errors must be fixed${NC}"
|
|
632
|
+
return 2
|
|
633
|
+
elif [ "$WARNINGS" -gt 0 ]; then
|
|
634
|
+
echo -e "${YELLOW}⚠ Validation PASSED with warnings${NC}"
|
|
635
|
+
return 1
|
|
636
|
+
else
|
|
637
|
+
echo -e "${GREEN}✓ Validation PASSED${NC}"
|
|
638
|
+
return 0
|
|
639
|
+
fi
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
# Main execution
|
|
643
|
+
main() {
|
|
644
|
+
if [ $# -eq 0 ]; then
|
|
645
|
+
echo "Usage: $0 <Makefile>"
|
|
646
|
+
echo ""
|
|
647
|
+
echo "Validates Makefile for syntax errors, best practices, security issues,"
|
|
648
|
+
echo "and optimization opportunities using mbake and custom checks."
|
|
649
|
+
echo ""
|
|
650
|
+
echo "Examples:"
|
|
651
|
+
echo " $0 Makefile"
|
|
652
|
+
echo " $0 path/to/Makefile"
|
|
653
|
+
echo " $0 project.mk"
|
|
654
|
+
exit 1
|
|
655
|
+
fi
|
|
656
|
+
|
|
657
|
+
local makefile=$1
|
|
658
|
+
|
|
659
|
+
print_header "MAKEFILE VALIDATOR"
|
|
660
|
+
echo "File: $makefile"
|
|
661
|
+
|
|
662
|
+
# Validation pipeline
|
|
663
|
+
check_dependencies
|
|
664
|
+
validate_file "$makefile"
|
|
665
|
+
|
|
666
|
+
# Set up mbake venv only when python3+pip3 are present and skip mode is disabled.
|
|
667
|
+
if [ "$MBAKE_AVAILABLE" -eq 1 ]; then
|
|
668
|
+
setup_venv || true
|
|
669
|
+
fi
|
|
670
|
+
|
|
671
|
+
# Run syntax check — always runs (uses GNU make, not mbake)
|
|
672
|
+
syntax_check "$makefile" || true
|
|
673
|
+
|
|
674
|
+
# Run mbake stages only when the venv is ready
|
|
675
|
+
if [ "$MBAKE_AVAILABLE" -eq 1 ]; then
|
|
676
|
+
mbake_validation "$makefile" || true
|
|
677
|
+
mbake_format_check "$makefile" || true
|
|
678
|
+
else
|
|
679
|
+
print_subheader "MBAKE VALIDATION"
|
|
680
|
+
if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 1 ]; then
|
|
681
|
+
echo -e "${BLUE}ℹ${NC} Skipped — MBAKE_SKIP_INSTALL=1 (deterministic mode)"
|
|
682
|
+
else
|
|
683
|
+
echo -e "${BLUE}ℹ${NC} Skipped — mbake not available (python3, pip3, and network required)"
|
|
684
|
+
fi
|
|
685
|
+
print_subheader "MBAKE FORMAT CHECK"
|
|
686
|
+
if [ "$MBAKE_SKIP_INSTALL_MODE" -eq 1 ]; then
|
|
687
|
+
echo -e "${BLUE}ℹ${NC} Skipped — MBAKE_SKIP_INSTALL=1 (deterministic mode)"
|
|
688
|
+
else
|
|
689
|
+
echo -e "${BLUE}ℹ${NC} Skipped — mbake not available (python3, pip3, and network required)"
|
|
690
|
+
fi
|
|
691
|
+
((INFO+=2))
|
|
692
|
+
fi
|
|
693
|
+
|
|
694
|
+
# Run custom checks — always runs (pure bash + grep, no external tools)
|
|
695
|
+
custom_checks "$makefile" || true
|
|
696
|
+
|
|
697
|
+
# Run checkmake if available (optional, continue even if it fails)
|
|
698
|
+
checkmake_validation "$makefile" || true
|
|
699
|
+
|
|
700
|
+
# Run unmake if available (optional, continue even if it fails)
|
|
701
|
+
unmake_validation "$makefile" || true
|
|
702
|
+
|
|
703
|
+
# Print summary and return appropriate exit code
|
|
704
|
+
print_summary "$makefile"
|
|
705
|
+
local exit_code=$?
|
|
706
|
+
|
|
707
|
+
return $exit_code
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
# Run main with all arguments and exit with its return code
|
|
711
|
+
main "$@"
|
|
712
|
+
exit $?
|