@hybridlabor-api/aos 4.8.0 → 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 +174 -46
- 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,174 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# ShellCheck Wrapper with Temporary Virtual Environment
|
|
4
|
+
#
|
|
5
|
+
# This script creates a temporary Python virtual environment, installs shellcheck-py,
|
|
6
|
+
# runs shellcheck, and cleans up afterwards.
|
|
7
|
+
#
|
|
8
|
+
# Usage: ./shellcheck_wrapper.sh [shellcheck-options] <script-file>
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# Colors
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
YELLOW='\033[1;33m'
|
|
17
|
+
BLUE='\033[0;34m'
|
|
18
|
+
NC='\033[0m' # No Color
|
|
19
|
+
|
|
20
|
+
# Configuration
|
|
21
|
+
VENV_DIR=""
|
|
22
|
+
CLEANUP_ON_EXIT=true
|
|
23
|
+
CACHE_VENV=false
|
|
24
|
+
VENV_CACHE_DIR="${HOME}/.cache/bash-script-validator/shellcheck-venv"
|
|
25
|
+
|
|
26
|
+
# Cleanup function
|
|
27
|
+
cleanup() {
|
|
28
|
+
if [[ "$CLEANUP_ON_EXIT" == "true" ]] && [[ -n "$VENV_DIR" ]] && [[ -d "$VENV_DIR" ]]; then
|
|
29
|
+
echo -e "${BLUE}[INFO]${NC} Cleaning up temporary virtual environment..." >&2
|
|
30
|
+
rm -rf "$VENV_DIR"
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
trap cleanup EXIT INT TERM
|
|
35
|
+
|
|
36
|
+
# Check if Python 3 is available
|
|
37
|
+
check_python() {
|
|
38
|
+
if ! command -v python3 &>/dev/null; then
|
|
39
|
+
echo -e "${RED}[ERROR]${NC} python3 not found. Please install Python 3." >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Check if shellcheck is already available system-wide
|
|
45
|
+
check_system_shellcheck() {
|
|
46
|
+
if command -v shellcheck &>/dev/null; then
|
|
47
|
+
# ShellCheck is available, use it directly
|
|
48
|
+
shellcheck "$@"
|
|
49
|
+
exit $?
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Create and activate virtual environment
|
|
54
|
+
setup_venv() {
|
|
55
|
+
if [[ "$CACHE_VENV" == "true" ]] && [[ -d "$VENV_CACHE_DIR" ]]; then
|
|
56
|
+
echo -e "${GREEN}[INFO]${NC} Using cached virtual environment..." >&2
|
|
57
|
+
VENV_DIR="$VENV_CACHE_DIR"
|
|
58
|
+
CLEANUP_ON_EXIT=false
|
|
59
|
+
return 0
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
echo -e "${BLUE}[INFO]${NC} Creating temporary virtual environment..." >&2
|
|
63
|
+
|
|
64
|
+
if [[ "$CACHE_VENV" == "true" ]]; then
|
|
65
|
+
mkdir -p "$(dirname "$VENV_CACHE_DIR")"
|
|
66
|
+
VENV_DIR="$VENV_CACHE_DIR"
|
|
67
|
+
CLEANUP_ON_EXIT=false
|
|
68
|
+
else
|
|
69
|
+
VENV_DIR=$(mktemp -d -t shellcheck-venv.XXXXXX)
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
python3 -m venv "$VENV_DIR"
|
|
73
|
+
|
|
74
|
+
# Activate virtual environment
|
|
75
|
+
# shellcheck source=/dev/null
|
|
76
|
+
source "$VENV_DIR/bin/activate"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Install shellcheck-py
|
|
80
|
+
install_shellcheck() {
|
|
81
|
+
local marker_file="$VENV_DIR/.shellcheck_installed"
|
|
82
|
+
|
|
83
|
+
if [[ -f "$marker_file" ]]; then
|
|
84
|
+
echo -e "${GREEN}[INFO]${NC} ShellCheck already installed in cached venv" >&2
|
|
85
|
+
return 0
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
echo -e "${BLUE}[INFO]${NC} Installing shellcheck-py..." >&2
|
|
89
|
+
|
|
90
|
+
# Upgrade pip first (suppress output)
|
|
91
|
+
pip3 install --upgrade pip &>/dev/null
|
|
92
|
+
|
|
93
|
+
# Install shellcheck-py
|
|
94
|
+
if pip3 install shellcheck-py &>/dev/null; then
|
|
95
|
+
echo -e "${GREEN}[INFO]${NC} ShellCheck installed successfully" >&2
|
|
96
|
+
touch "$marker_file"
|
|
97
|
+
return 0
|
|
98
|
+
else
|
|
99
|
+
echo -e "${RED}[ERROR]${NC} Failed to install shellcheck-py" >&2
|
|
100
|
+
return 1
|
|
101
|
+
fi
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Run shellcheck
|
|
105
|
+
run_shellcheck() {
|
|
106
|
+
if [[ ! -f "$VENV_DIR/bin/shellcheck" ]]; then
|
|
107
|
+
echo -e "${RED}[ERROR]${NC} ShellCheck binary not found in virtual environment" >&2
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Run shellcheck with all provided arguments
|
|
112
|
+
"$VENV_DIR/bin/shellcheck" "$@"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Main function
|
|
116
|
+
main() {
|
|
117
|
+
# If no arguments, show usage
|
|
118
|
+
if [[ $# -eq 0 ]]; then
|
|
119
|
+
echo "Usage: $0 [--cache] [--no-cache] [shellcheck-options] <script-file>"
|
|
120
|
+
echo ""
|
|
121
|
+
echo "Options:"
|
|
122
|
+
echo " --cache Cache the virtual environment for faster subsequent runs"
|
|
123
|
+
echo " --no-cache Don't use cached venv (default)"
|
|
124
|
+
echo " --clear-cache Clear the cached virtual environment"
|
|
125
|
+
echo ""
|
|
126
|
+
echo "Examples:"
|
|
127
|
+
echo " $0 script.sh"
|
|
128
|
+
echo " $0 --cache -s bash script.sh"
|
|
129
|
+
echo " $0 -f gcc script.sh"
|
|
130
|
+
exit 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Parse wrapper-specific options
|
|
134
|
+
local args=()
|
|
135
|
+
while [[ $# -gt 0 ]]; do
|
|
136
|
+
case "$1" in
|
|
137
|
+
--cache)
|
|
138
|
+
CACHE_VENV=true
|
|
139
|
+
shift
|
|
140
|
+
;;
|
|
141
|
+
--no-cache)
|
|
142
|
+
CACHE_VENV=false
|
|
143
|
+
shift
|
|
144
|
+
;;
|
|
145
|
+
--clear-cache)
|
|
146
|
+
if [[ -d "$VENV_CACHE_DIR" ]]; then
|
|
147
|
+
echo -e "${BLUE}[INFO]${NC} Clearing cached virtual environment..."
|
|
148
|
+
rm -rf "$VENV_CACHE_DIR"
|
|
149
|
+
echo -e "${GREEN}[INFO]${NC} Cache cleared"
|
|
150
|
+
else
|
|
151
|
+
echo -e "${YELLOW}[INFO]${NC} No cache to clear"
|
|
152
|
+
fi
|
|
153
|
+
exit 0
|
|
154
|
+
;;
|
|
155
|
+
*)
|
|
156
|
+
args+=("$1")
|
|
157
|
+
shift
|
|
158
|
+
;;
|
|
159
|
+
esac
|
|
160
|
+
done
|
|
161
|
+
|
|
162
|
+
# Check if system shellcheck is available
|
|
163
|
+
check_system_shellcheck "${args[@]}"
|
|
164
|
+
|
|
165
|
+
# System shellcheck not found, use venv approach
|
|
166
|
+
check_python
|
|
167
|
+
setup_venv
|
|
168
|
+
install_shellcheck
|
|
169
|
+
|
|
170
|
+
# Run shellcheck with remaining arguments
|
|
171
|
+
run_shellcheck "${args[@]}"
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main "$@"
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Regression test suite for validate.sh
|
|
4
|
+
#
|
|
5
|
+
# Runs the validator against each example file and asserts the expected exit code.
|
|
6
|
+
# Exit 0 when all assertions pass; non-zero otherwise.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
readonly SCRIPT_DIR
|
|
13
|
+
VALIDATOR="$SCRIPT_DIR/validate.sh"
|
|
14
|
+
EXAMPLES_DIR="$SCRIPT_DIR/../examples"
|
|
15
|
+
TMP_DIR="$(mktemp -d)"
|
|
16
|
+
FAKE_SHELLCHECK_BIN="$TMP_DIR/fake-shellcheck-bin"
|
|
17
|
+
|
|
18
|
+
cleanup() {
|
|
19
|
+
rm -rf "$TMP_DIR"
|
|
20
|
+
}
|
|
21
|
+
trap cleanup EXIT
|
|
22
|
+
|
|
23
|
+
# Counters
|
|
24
|
+
PASS=0
|
|
25
|
+
FAIL=0
|
|
26
|
+
|
|
27
|
+
# ─── helpers ────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
|
|
30
|
+
fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
|
|
31
|
+
|
|
32
|
+
# Create a deterministic shellcheck stub so regression tests do not depend on host tooling.
|
|
33
|
+
mkdir -p "$FAKE_SHELLCHECK_BIN"
|
|
34
|
+
cat > "$FAKE_SHELLCHECK_BIN/shellcheck" <<'EOF'
|
|
35
|
+
#!/usr/bin/env bash
|
|
36
|
+
set -euo pipefail
|
|
37
|
+
|
|
38
|
+
mode="${FAKE_SHELLCHECK_MODE:-ok}"
|
|
39
|
+
target="${@: -1}"
|
|
40
|
+
|
|
41
|
+
case "$mode" in
|
|
42
|
+
ok)
|
|
43
|
+
exit 0
|
|
44
|
+
;;
|
|
45
|
+
warning)
|
|
46
|
+
echo "${target}:1:1: warning: fake shellcheck warning [SC9999]"
|
|
47
|
+
exit 1
|
|
48
|
+
;;
|
|
49
|
+
infra)
|
|
50
|
+
echo "fake shellcheck infrastructure failure" >&2
|
|
51
|
+
exit 3
|
|
52
|
+
;;
|
|
53
|
+
*)
|
|
54
|
+
echo "unknown FAKE_SHELLCHECK_MODE: $mode" >&2
|
|
55
|
+
exit 2
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
EOF
|
|
59
|
+
chmod +x "$FAKE_SHELLCHECK_BIN/shellcheck"
|
|
60
|
+
|
|
61
|
+
# Run the validator and return its exit code without aborting this script.
|
|
62
|
+
# Uses || to prevent set -e from treating a non-zero validator exit as fatal.
|
|
63
|
+
run_validator() {
|
|
64
|
+
local file="$1"
|
|
65
|
+
shift
|
|
66
|
+
|
|
67
|
+
local exit_code=0
|
|
68
|
+
if [[ $# -gt 0 ]]; then
|
|
69
|
+
env "$@" bash "$VALIDATOR" "$file" >/dev/null 2>&1 || exit_code=$?
|
|
70
|
+
else
|
|
71
|
+
bash "$VALIDATOR" "$file" >/dev/null 2>&1 || exit_code=$?
|
|
72
|
+
fi
|
|
73
|
+
echo "$exit_code"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
validator_output() {
|
|
77
|
+
local file="$1"
|
|
78
|
+
shift
|
|
79
|
+
|
|
80
|
+
if [[ $# -gt 0 ]]; then
|
|
81
|
+
env "$@" bash "$VALIDATOR" "$file" 2>&1 || true
|
|
82
|
+
else
|
|
83
|
+
bash "$VALIDATOR" "$file" 2>&1 || true
|
|
84
|
+
fi
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# Assert that the validator exits with a specific code for the given file.
|
|
88
|
+
assert_exit_code() {
|
|
89
|
+
local label="$1"
|
|
90
|
+
local file="$2"
|
|
91
|
+
local expected="$3"
|
|
92
|
+
shift 3
|
|
93
|
+
|
|
94
|
+
local actual
|
|
95
|
+
actual=$(run_validator "$file" "$@")
|
|
96
|
+
|
|
97
|
+
if [[ "$actual" -eq "$expected" ]]; then
|
|
98
|
+
pass "$label (exit $actual)"
|
|
99
|
+
else
|
|
100
|
+
fail "$label — expected exit $expected, got $actual"
|
|
101
|
+
# Re-run with output visible so the failure is diagnosable.
|
|
102
|
+
echo " --- validator output ---"
|
|
103
|
+
validator_output "$file" "$@" | sed 's/^/ /'
|
|
104
|
+
echo " --- end output ---"
|
|
105
|
+
fi
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
assert_exit_code_in() {
|
|
109
|
+
local label="$1"
|
|
110
|
+
local file="$2"
|
|
111
|
+
local expected_csv="$3"
|
|
112
|
+
shift 3
|
|
113
|
+
|
|
114
|
+
local actual
|
|
115
|
+
actual=$(run_validator "$file" "$@")
|
|
116
|
+
|
|
117
|
+
if [[ ",$expected_csv," == *",$actual,"* ]]; then
|
|
118
|
+
pass "$label (exit $actual)"
|
|
119
|
+
else
|
|
120
|
+
fail "$label — expected one of [$expected_csv], got $actual"
|
|
121
|
+
echo " --- validator output ---"
|
|
122
|
+
validator_output "$file" "$@" | sed 's/^/ /'
|
|
123
|
+
echo " --- end output ---"
|
|
124
|
+
fi
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Assert that a pattern IS found in the validator output for a given file.
|
|
128
|
+
assert_output_contains() {
|
|
129
|
+
local label="$1"
|
|
130
|
+
local file="$2"
|
|
131
|
+
local pattern="$3"
|
|
132
|
+
shift 3
|
|
133
|
+
|
|
134
|
+
local output
|
|
135
|
+
output=$(validator_output "$file" "$@")
|
|
136
|
+
|
|
137
|
+
if echo "$output" | grep -qE "$pattern"; then
|
|
138
|
+
pass "$label"
|
|
139
|
+
else
|
|
140
|
+
fail "$label — pattern not found: $pattern"
|
|
141
|
+
echo " --- validator output ---"
|
|
142
|
+
echo "$output" | sed 's/^/ /'
|
|
143
|
+
echo " --- end output ---"
|
|
144
|
+
fi
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Assert that a pattern is NOT found in the validator output for a given file.
|
|
148
|
+
assert_output_not_contains() {
|
|
149
|
+
local label="$1"
|
|
150
|
+
local file="$2"
|
|
151
|
+
local pattern="$3"
|
|
152
|
+
shift 3
|
|
153
|
+
|
|
154
|
+
local output
|
|
155
|
+
output=$(validator_output "$file" "$@")
|
|
156
|
+
|
|
157
|
+
if echo "$output" | grep -qE "$pattern"; then
|
|
158
|
+
fail "$label — unexpected pattern found: $pattern"
|
|
159
|
+
echo " --- validator output ---"
|
|
160
|
+
echo "$output" | sed 's/^/ /'
|
|
161
|
+
echo " --- end output ---"
|
|
162
|
+
else
|
|
163
|
+
pass "$label"
|
|
164
|
+
fi
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
assert_shellcheck_stage_verifiable() {
|
|
168
|
+
local label="$1"
|
|
169
|
+
local file="$2"
|
|
170
|
+
shift 2
|
|
171
|
+
|
|
172
|
+
local output
|
|
173
|
+
output=$(validator_output "$file" "$@")
|
|
174
|
+
|
|
175
|
+
if ! echo "$output" | grep -q '\[SHELLCHECK\]'; then
|
|
176
|
+
fail "$label — missing [SHELLCHECK] stage"
|
|
177
|
+
echo " --- validator output ---"
|
|
178
|
+
echo "$output" | sed 's/^/ /'
|
|
179
|
+
echo " --- end output ---"
|
|
180
|
+
return
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
if echo "$output" | grep -qE 'ShellCheck unavailable|ShellCheck not installed'; then
|
|
184
|
+
fail "$label — ShellCheck stage unavailable"
|
|
185
|
+
echo " --- validator output ---"
|
|
186
|
+
echo "$output" | sed 's/^/ /'
|
|
187
|
+
echo " --- end output ---"
|
|
188
|
+
return
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
if echo "$output" | grep -qE 'No ShellCheck issues found|SC[0-9]{4}|: (error|warning|note|style):'; then
|
|
192
|
+
pass "$label"
|
|
193
|
+
return
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
fail "$label — ShellCheck produced no verifiable signal"
|
|
197
|
+
echo " --- validator output ---"
|
|
198
|
+
echo "$output" | sed 's/^/ /'
|
|
199
|
+
echo " --- end output ---"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# ─── test cases ─────────────────────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
echo "Running bash-script-validator tests..."
|
|
205
|
+
echo ""
|
|
206
|
+
|
|
207
|
+
# --- good-bash.sh: well-written bash, must exit 0 ---
|
|
208
|
+
echo "[good-bash.sh]"
|
|
209
|
+
assert_exit_code \
|
|
210
|
+
"exits cleanly (code 0)" \
|
|
211
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
212
|
+
0
|
|
213
|
+
|
|
214
|
+
assert_output_not_contains \
|
|
215
|
+
"no false-positive errors" \
|
|
216
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
217
|
+
"✗"
|
|
218
|
+
|
|
219
|
+
assert_shellcheck_stage_verifiable \
|
|
220
|
+
"shellcheck stage is verifiable" \
|
|
221
|
+
"$EXAMPLES_DIR/good-bash.sh"
|
|
222
|
+
|
|
223
|
+
# --- good-shell.sh: well-written POSIX sh, must exit 0 ---
|
|
224
|
+
echo ""
|
|
225
|
+
echo "[good-shell.sh]"
|
|
226
|
+
assert_exit_code \
|
|
227
|
+
"exits cleanly (code 0)" \
|
|
228
|
+
"$EXAMPLES_DIR/good-shell.sh" \
|
|
229
|
+
0
|
|
230
|
+
|
|
231
|
+
assert_output_not_contains \
|
|
232
|
+
"no false-positive [[ ]] error from comment on line 31" \
|
|
233
|
+
"$EXAMPLES_DIR/good-shell.sh" \
|
|
234
|
+
"\[\["
|
|
235
|
+
|
|
236
|
+
assert_output_not_contains \
|
|
237
|
+
"no false-positive errors" \
|
|
238
|
+
"$EXAMPLES_DIR/good-shell.sh" \
|
|
239
|
+
"✗"
|
|
240
|
+
|
|
241
|
+
assert_shellcheck_stage_verifiable \
|
|
242
|
+
"shellcheck stage is verifiable" \
|
|
243
|
+
"$EXAMPLES_DIR/good-shell.sh"
|
|
244
|
+
|
|
245
|
+
# --- bad-bash.sh: intentionally bad bash, must be non-clean ---
|
|
246
|
+
echo ""
|
|
247
|
+
echo "[bad-bash.sh]"
|
|
248
|
+
assert_exit_code_in \
|
|
249
|
+
"exits non-clean (code 1 or 2)" \
|
|
250
|
+
"$EXAMPLES_DIR/bad-bash.sh" \
|
|
251
|
+
"1,2"
|
|
252
|
+
|
|
253
|
+
assert_output_contains \
|
|
254
|
+
"detects eval with variable" \
|
|
255
|
+
"$EXAMPLES_DIR/bad-bash.sh" \
|
|
256
|
+
"eval with variable"
|
|
257
|
+
|
|
258
|
+
assert_output_contains \
|
|
259
|
+
"detects useless cat" \
|
|
260
|
+
"$EXAMPLES_DIR/bad-bash.sh" \
|
|
261
|
+
"Useless use of cat"
|
|
262
|
+
|
|
263
|
+
# --- bad-shell.sh: intentionally bad POSIX sh, must exit 2 ---
|
|
264
|
+
echo ""
|
|
265
|
+
echo "[bad-shell.sh]"
|
|
266
|
+
assert_exit_code \
|
|
267
|
+
"exits with errors (code 2)" \
|
|
268
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
269
|
+
2
|
|
270
|
+
|
|
271
|
+
assert_output_contains \
|
|
272
|
+
"detects [[ ]] in sh script (line 7, actual code)" \
|
|
273
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
274
|
+
"Bash-specific \[\[ \]\]"
|
|
275
|
+
|
|
276
|
+
assert_output_contains \
|
|
277
|
+
"detects bash arrays in sh script" \
|
|
278
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
279
|
+
"Bash-specific arrays"
|
|
280
|
+
|
|
281
|
+
assert_output_contains \
|
|
282
|
+
"detects function keyword in sh script" \
|
|
283
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
284
|
+
"function.*keyword"
|
|
285
|
+
|
|
286
|
+
assert_output_contains \
|
|
287
|
+
"detects source command in sh script" \
|
|
288
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
289
|
+
"source.*command"
|
|
290
|
+
|
|
291
|
+
assert_output_contains \
|
|
292
|
+
"detects eval with variable" \
|
|
293
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
294
|
+
"eval with variable"
|
|
295
|
+
|
|
296
|
+
assert_output_contains \
|
|
297
|
+
"detects useless cat" \
|
|
298
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
299
|
+
"Useless use of cat"
|
|
300
|
+
|
|
301
|
+
# Only real code lines flagged — not comment lines for [[ check
|
|
302
|
+
assert_output_not_contains \
|
|
303
|
+
"[[ check does not flag comment lines in bad-shell.sh" \
|
|
304
|
+
"$EXAMPLES_DIR/bad-shell.sh" \
|
|
305
|
+
"Line [0-9]*:# Bad: using bash-specific"
|
|
306
|
+
|
|
307
|
+
# --- p1 regression fixtures ---
|
|
308
|
+
INDENTED_SH="$TMP_DIR/indented-bashisms.sh"
|
|
309
|
+
cat > "$INDENTED_SH" <<'EOF'
|
|
310
|
+
#!/bin/sh
|
|
311
|
+
set -e
|
|
312
|
+
function helper {
|
|
313
|
+
echo "bad"
|
|
314
|
+
}
|
|
315
|
+
source /etc/profile
|
|
316
|
+
helper
|
|
317
|
+
EOF
|
|
318
|
+
|
|
319
|
+
COMMENT_ONLY_ERR_HANDLING="$TMP_DIR/comment-only-error-handling.sh"
|
|
320
|
+
cat > "$COMMENT_ONLY_ERR_HANDLING" <<'EOF'
|
|
321
|
+
#!/bin/sh
|
|
322
|
+
# set -e
|
|
323
|
+
# set -o errexit
|
|
324
|
+
# trap 'echo failed' ERR
|
|
325
|
+
echo "hello"
|
|
326
|
+
EOF
|
|
327
|
+
|
|
328
|
+
echo ""
|
|
329
|
+
echo "[p1 regressions]"
|
|
330
|
+
assert_output_contains \
|
|
331
|
+
"indented function in sh is detected" \
|
|
332
|
+
"$INDENTED_SH" \
|
|
333
|
+
"function.*keyword"
|
|
334
|
+
|
|
335
|
+
assert_output_contains \
|
|
336
|
+
"indented source in sh is detected" \
|
|
337
|
+
"$INDENTED_SH" \
|
|
338
|
+
"source.*command"
|
|
339
|
+
|
|
340
|
+
assert_output_contains \
|
|
341
|
+
"comment-only set -e does not suppress warning" \
|
|
342
|
+
"$COMMENT_ONLY_ERR_HANDLING" \
|
|
343
|
+
"Consider adding error handling"
|
|
344
|
+
|
|
345
|
+
# --- p2 deterministic shellcheck stage regressions ---
|
|
346
|
+
echo ""
|
|
347
|
+
echo "[p2 shellcheck-mode regressions]"
|
|
348
|
+
|
|
349
|
+
assert_exit_code \
|
|
350
|
+
"system mode accepts deterministic clean shellcheck run" \
|
|
351
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
352
|
+
0 \
|
|
353
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
354
|
+
VALIDATOR_SHELLCHECK_MODE=system \
|
|
355
|
+
PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
|
|
356
|
+
|
|
357
|
+
assert_output_contains \
|
|
358
|
+
"system mode reports clean shellcheck output" \
|
|
359
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
360
|
+
"No ShellCheck issues found" \
|
|
361
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
362
|
+
VALIDATOR_SHELLCHECK_MODE=system \
|
|
363
|
+
PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
|
|
364
|
+
|
|
365
|
+
assert_exit_code \
|
|
366
|
+
"system mode returns warning on shellcheck findings" \
|
|
367
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
368
|
+
1 \
|
|
369
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
370
|
+
VALIDATOR_SHELLCHECK_MODE=system \
|
|
371
|
+
FAKE_SHELLCHECK_MODE=warning \
|
|
372
|
+
PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
|
|
373
|
+
|
|
374
|
+
assert_output_contains \
|
|
375
|
+
"system mode surfaces shellcheck issue codes" \
|
|
376
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
377
|
+
"SC9999" \
|
|
378
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
379
|
+
VALIDATOR_SHELLCHECK_MODE=system \
|
|
380
|
+
FAKE_SHELLCHECK_MODE=warning \
|
|
381
|
+
PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
|
|
382
|
+
|
|
383
|
+
assert_exit_code \
|
|
384
|
+
"system mode fails on shellcheck infrastructure error" \
|
|
385
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
386
|
+
2 \
|
|
387
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
388
|
+
VALIDATOR_SHELLCHECK_MODE=system \
|
|
389
|
+
FAKE_SHELLCHECK_MODE=infra \
|
|
390
|
+
PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
|
|
391
|
+
|
|
392
|
+
assert_output_contains \
|
|
393
|
+
"system mode reports shellcheck infrastructure error" \
|
|
394
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
395
|
+
"ShellCheck execution failed \(exit 3\)" \
|
|
396
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
397
|
+
VALIDATOR_SHELLCHECK_MODE=system \
|
|
398
|
+
FAKE_SHELLCHECK_MODE=infra \
|
|
399
|
+
PATH="$FAKE_SHELLCHECK_BIN:/usr/bin:/bin"
|
|
400
|
+
|
|
401
|
+
assert_exit_code \
|
|
402
|
+
"invalid shellcheck mode exits with error" \
|
|
403
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
404
|
+
2 \
|
|
405
|
+
VALIDATOR_SHELLCHECK_MODE=invalid-mode
|
|
406
|
+
|
|
407
|
+
assert_output_contains \
|
|
408
|
+
"invalid shellcheck mode prints actionable error" \
|
|
409
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
410
|
+
"Invalid VALIDATOR_SHELLCHECK_MODE" \
|
|
411
|
+
VALIDATOR_SHELLCHECK_MODE=invalid-mode
|
|
412
|
+
|
|
413
|
+
# --- edge cases ---
|
|
414
|
+
echo ""
|
|
415
|
+
echo "[edge cases]"
|
|
416
|
+
|
|
417
|
+
# P0: strict mode must fail when ShellCheck is unavailable
|
|
418
|
+
assert_exit_code \
|
|
419
|
+
"strict mode fails when shellcheck is unavailable" \
|
|
420
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
421
|
+
2 \
|
|
422
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
423
|
+
VALIDATOR_DISABLE_SHELLCHECK=1
|
|
424
|
+
|
|
425
|
+
assert_output_contains \
|
|
426
|
+
"strict mode reports shellcheck unavailable" \
|
|
427
|
+
"$EXAMPLES_DIR/good-bash.sh" \
|
|
428
|
+
"ShellCheck (unavailable|disabled by configuration)" \
|
|
429
|
+
VALIDATOR_REQUIRE_SHELLCHECK=1 \
|
|
430
|
+
VALIDATOR_DISABLE_SHELLCHECK=1
|
|
431
|
+
|
|
432
|
+
# Missing file → exit 1 from the validator's own error path
|
|
433
|
+
assert_exit_code \
|
|
434
|
+
"missing file exits non-zero" \
|
|
435
|
+
"/nonexistent/path/script.sh" \
|
|
436
|
+
1
|
|
437
|
+
|
|
438
|
+
# ─── summary ────────────────────────────────────────────────────────────────
|
|
439
|
+
|
|
440
|
+
echo ""
|
|
441
|
+
echo "Results: $PASS passed, $FAIL failed"
|
|
442
|
+
echo ""
|
|
443
|
+
|
|
444
|
+
if [[ $FAIL -gt 0 ]]; then
|
|
445
|
+
exit 1
|
|
446
|
+
fi
|