@zairakai/js-dev-tools 1.0.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/.editorconfig +86 -0
- package/.gitlab/ci/pipeline-js-app.yml +75 -0
- package/.gitlab/ci/pipeline-js-package.yml +360 -0
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/config/.markdownlint.json +7 -0
- package/config/.markdownlintignore +5 -0
- package/config/.prettierignore +11 -0
- package/config/.stylelintignore +7 -0
- package/config/eslint.config.js +198 -0
- package/config/knip.config.js +40 -0
- package/config/prettier.config.js +121 -0
- package/config/stylelint.config.js +56 -0
- package/config/tsconfig.base.json +20 -0
- package/config/vitest.config.js +25 -0
- package/index.js +24 -0
- package/package.json +148 -0
- package/scripts/build.sh +58 -0
- package/scripts/ci-quality.sh +51 -0
- package/scripts/config.sh +254 -0
- package/scripts/doctor.sh +74 -0
- package/scripts/eslint-fix.sh +38 -0
- package/scripts/eslint.sh +49 -0
- package/scripts/install-bats.sh +227 -0
- package/scripts/install-hooks.sh +167 -0
- package/scripts/install-markdownlint.sh +31 -0
- package/scripts/install-shellcheck.sh +232 -0
- package/scripts/knip.sh +40 -0
- package/scripts/markdownlint-fix.sh +8 -0
- package/scripts/markdownlint.sh +84 -0
- package/scripts/outdated.sh +70 -0
- package/scripts/prettier-fix.sh +52 -0
- package/scripts/prettier.sh +53 -0
- package/scripts/setup-project.sh +816 -0
- package/scripts/stylelint-fix.sh +64 -0
- package/scripts/stylelint.sh +66 -0
- package/scripts/test.sh +40 -0
- package/scripts/typecheck.sh +41 -0
- package/scripts/validate-shellcheck.sh +70 -0
- package/stubs/githooks/commit-msg +34 -0
- package/stubs/githooks/pre-commit +58 -0
- package/stubs/githooks/pre-push +57 -0
- package/stubs/githooks/prepare-commit-msg +39 -0
- package/stubs/gitlab-ci/gitlab-ci.yml.stub +18 -0
- package/stubs/gitlab-ci/gitlab-pipeline-js-app.yml.stub +16 -0
- package/stubs/governance/CODE_OF_CONDUCT.md.stub +8 -0
- package/stubs/governance/CONTRIBUTING.md.stub +39 -0
- package/stubs/governance/SECURITY.md.stub +41 -0
- package/stubs/quality/eslint.config.js.stub +18 -0
- package/stubs/quality/knip.config.js.stub +14 -0
- package/stubs/style/prettier.config.js.stub +16 -0
- package/stubs/style/stylelint.config.js.stub +17 -0
- package/stubs/testing/vitest.config.js.stub +18 -0
- package/stubs/typescript/tsconfig.json.stub +10 -0
- package/tools/make/bats.mk +49 -0
- package/tools/make/code-style.mk +20 -0
- package/tools/make/core.mk +56 -0
- package/tools/make/help.mk +32 -0
- package/tools/make/knip.mk +7 -0
- package/tools/make/markdownlint.mk +17 -0
- package/tools/make/quality.mk +19 -0
- package/tools/make/shellcheck.mk +14 -0
- package/tools/make/stylelint.mk +11 -0
- package/tools/make/test.mk +19 -0
- package/tools/make/typescript.mk +10 -0
- package/tools/make/utils.mk +44 -0
- package/tools/make/variables.mk +42 -0
- package/tools/scripts/git-cleanup.sh +53 -0
- package/tools/scripts/git-update.sh +37 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
# shellcheck disable=SC1091
|
|
6
|
+
source "$SCRIPT_DIR/config.sh"
|
|
7
|
+
|
|
8
|
+
log_header "ESLint Check"
|
|
9
|
+
|
|
10
|
+
ensure_bin "$ESLINT_BIN" "eslint" || exit 1
|
|
11
|
+
|
|
12
|
+
if [[ -n "$ESLINT_CONFIG" ]]; then
|
|
13
|
+
log_step "Using configuration: ${ESLINT_CONFIG#"$PROJECT_ROOT"/}"
|
|
14
|
+
else
|
|
15
|
+
log_warning "No ESLint configuration found — using ESLint defaults"
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Lint target depends on project type (overridable via env)
|
|
19
|
+
if [[ "$PROJECT_TYPE" == "laravel-app" ]]; then
|
|
20
|
+
LINT_TARGET="${LINT_TARGET:-resources/js/}"
|
|
21
|
+
else
|
|
22
|
+
# Default to src/, but fallback to . if src/ doesn't exist
|
|
23
|
+
if [[ -z "${LINT_TARGET:-}" ]]; then
|
|
24
|
+
if [[ -d "${PROJECT_ROOT}/src" ]]; then
|
|
25
|
+
LINT_TARGET="src/"
|
|
26
|
+
else
|
|
27
|
+
LINT_TARGET="."
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
log_step "Checking: $LINT_TARGET"
|
|
33
|
+
|
|
34
|
+
# Skip gracefully if no matching files exist
|
|
35
|
+
if ! has_files "${PROJECT_ROOT}/${LINT_TARGET}**/*.{js,ts,vue,jsx,tsx}"; then
|
|
36
|
+
log_info "No JS/TS files found in ${LINT_TARGET} — skipping"
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
ESLINT_ARGS=()
|
|
41
|
+
|
|
42
|
+
# Only pass --config when the config is not at project root (ESLint 9 auto-discovers root)
|
|
43
|
+
if [[ -n "$ESLINT_CONFIG" ]] && [[ "$ESLINT_CONFIG" != "${PROJECT_ROOT}/eslint.config.js" ]]; then
|
|
44
|
+
ESLINT_ARGS+=(--config "$ESLINT_CONFIG")
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
"$ESLINT_BIN" "${ESLINT_ARGS[@]}" "$LINT_TARGET"
|
|
48
|
+
|
|
49
|
+
log_success "ESLint check passed"
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Zairakai NPM Dev Tools - BATS Installer
|
|
4
|
+
# Installs BATS (Bash Automated Testing System) for shell script testing
|
|
5
|
+
#
|
|
6
|
+
# Platform: Linux/macOS/WSL
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash scripts/install-bats.sh
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
# shellcheck disable=SC1091
|
|
16
|
+
source "${SCRIPT_DIR}/config.sh"
|
|
17
|
+
|
|
18
|
+
# ============================================================================
|
|
19
|
+
# Configuration
|
|
20
|
+
# ============================================================================
|
|
21
|
+
|
|
22
|
+
CI_MODE="${CI:-false}"
|
|
23
|
+
TOOL_VERSIONS_FILE="${DEV_TOOLS_ROOT}/.tool-versions"
|
|
24
|
+
INSTALL_DIR="${HOME}/.local/share/bats"
|
|
25
|
+
BIN_DIR="${HOME}/.local/bin"
|
|
26
|
+
|
|
27
|
+
# ============================================================================
|
|
28
|
+
# Helpers
|
|
29
|
+
# ============================================================================
|
|
30
|
+
|
|
31
|
+
get_bats_version() {
|
|
32
|
+
local version=""
|
|
33
|
+
|
|
34
|
+
if [[ -f "${TOOL_VERSIONS_FILE}" ]]; then
|
|
35
|
+
version="$(grep "^bats=" "${TOOL_VERSIONS_FILE}" | awk -F "=" '{print $2}' || echo "")"
|
|
36
|
+
if [[ -n "${version}" ]]; then
|
|
37
|
+
log_info "Found .tool-versions: ${version}"
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [[ -z "${version}" ]]; then
|
|
42
|
+
version="1.11.0"
|
|
43
|
+
log_info "Using default version: ${version}"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [[ ! "${version}" =~ ^v ]]; then
|
|
47
|
+
version="v${version}"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "${version}"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
version_ge() {
|
|
54
|
+
local ver1="${1#v}"
|
|
55
|
+
local ver2="${2#v}"
|
|
56
|
+
|
|
57
|
+
if [[ "${ver1}" == "${ver2}" ]]; then
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
local IFS=.
|
|
62
|
+
# shellcheck disable=SC2206
|
|
63
|
+
local ver1_arr=($ver1) ver2_arr=($ver2)
|
|
64
|
+
|
|
65
|
+
local i
|
|
66
|
+
for ((i=${#ver1_arr[@]}; i<${#ver2_arr[@]}; i++)); do
|
|
67
|
+
ver1_arr[i]=0
|
|
68
|
+
done
|
|
69
|
+
|
|
70
|
+
for ((i=0; i<${#ver1_arr[@]}; i++)); do
|
|
71
|
+
if [[ -z "${ver2_arr[i]:-}" ]]; then
|
|
72
|
+
ver2_arr[i]=0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if ((10#${ver1_arr[i]} > 10#${ver2_arr[i]})); then
|
|
76
|
+
return 0
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
if ((10#${ver1_arr[i]} < 10#${ver2_arr[i]})); then
|
|
80
|
+
return 1
|
|
81
|
+
fi
|
|
82
|
+
done
|
|
83
|
+
|
|
84
|
+
return 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
check_bats_installed() {
|
|
88
|
+
local required_version="$1"
|
|
89
|
+
|
|
90
|
+
if ! command_exists bats; then
|
|
91
|
+
return 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
local current_version
|
|
95
|
+
current_version="$(bats --version 2>/dev/null | awk '{print $2}' || echo "")"
|
|
96
|
+
|
|
97
|
+
if [[ -z "${current_version}" ]]; then
|
|
98
|
+
log_warning "Could not determine BATS version"
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
log_info "Current version: ${current_version}"
|
|
103
|
+
|
|
104
|
+
if version_ge "${current_version}" "${required_version}"; then
|
|
105
|
+
log_success "BATS ${current_version} is sufficient (need >=${required_version})"
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
log_warning "Version ${current_version} is outdated (need >=${required_version})"
|
|
110
|
+
|
|
111
|
+
if [[ "${CI_MODE}" != "true" ]]; then
|
|
112
|
+
echo ""
|
|
113
|
+
read -rp "Upgrade to ${required_version}? (y/N) " -n 1
|
|
114
|
+
echo ""
|
|
115
|
+
|
|
116
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
117
|
+
log_info "Keeping existing installation"
|
|
118
|
+
return 0
|
|
119
|
+
fi
|
|
120
|
+
else
|
|
121
|
+
log_info "CI mode: upgrading automatically"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
return 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
install_bats_core() {
|
|
128
|
+
local version="$1"
|
|
129
|
+
|
|
130
|
+
log_step "Installing BATS ${version} from source…"
|
|
131
|
+
mkdir -p "${INSTALL_DIR}" "${BIN_DIR}"
|
|
132
|
+
|
|
133
|
+
if [[ -d "${INSTALL_DIR}/bats-core" ]]; then
|
|
134
|
+
log_info "BATS repository exists, updating…"
|
|
135
|
+
git -C "${INSTALL_DIR}/bats-core" fetch --tags --quiet
|
|
136
|
+
git -C "${INSTALL_DIR}/bats-core" checkout "${version}" --quiet
|
|
137
|
+
else
|
|
138
|
+
log_info "Cloning BATS repository…"
|
|
139
|
+
git clone --quiet --depth 1 --branch "${version}" \
|
|
140
|
+
https://github.com/bats-core/bats-core.git "${INSTALL_DIR}/bats-core"
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
"${INSTALL_DIR}/bats-core/install.sh" "${HOME}/.local"
|
|
144
|
+
log_success "BATS core installed to: ${BIN_DIR}/bats"
|
|
145
|
+
|
|
146
|
+
if [[ ":$PATH:" != *":${BIN_DIR}:"* ]]; then
|
|
147
|
+
echo ""
|
|
148
|
+
log_warning "${BIN_DIR} is not in your PATH"
|
|
149
|
+
echo ""
|
|
150
|
+
echo "Add this to your ~/.bashrc or ~/.zshrc:"
|
|
151
|
+
echo ""
|
|
152
|
+
echo -e " ${CYAN}export PATH=\"\${HOME}/.local/bin:\${PATH}\"${NC}"
|
|
153
|
+
echo ""
|
|
154
|
+
fi
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
install_bats_libraries() {
|
|
158
|
+
log_step "Installing BATS support libraries…"
|
|
159
|
+
|
|
160
|
+
local libraries=(
|
|
161
|
+
"bats-support:https://github.com/bats-core/bats-support.git"
|
|
162
|
+
"bats-assert:https://github.com/bats-core/bats-assert.git"
|
|
163
|
+
"bats-file:https://github.com/bats-core/bats-file.git"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
local lib_info lib_name lib_url lib_path
|
|
167
|
+
for lib_info in "${libraries[@]}"; do
|
|
168
|
+
lib_name="${lib_info%%:*}"
|
|
169
|
+
lib_url="${lib_info#*:}"
|
|
170
|
+
lib_path="${INSTALL_DIR}/${lib_name}"
|
|
171
|
+
|
|
172
|
+
if [[ -d "${lib_path}" ]]; then
|
|
173
|
+
log_info "${lib_name} already installed"
|
|
174
|
+
else
|
|
175
|
+
log_info "Installing ${lib_name}…"
|
|
176
|
+
git clone --quiet --depth 1 "${lib_url}" "${lib_path}"
|
|
177
|
+
log_success "${lib_name} installed"
|
|
178
|
+
fi
|
|
179
|
+
done
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# ============================================================================
|
|
183
|
+
# Main
|
|
184
|
+
# ============================================================================
|
|
185
|
+
|
|
186
|
+
main() {
|
|
187
|
+
local required_version
|
|
188
|
+
required_version="$(get_bats_version)"
|
|
189
|
+
|
|
190
|
+
echo ""
|
|
191
|
+
log_header "BATS Installer"
|
|
192
|
+
echo ""
|
|
193
|
+
|
|
194
|
+
if check_bats_installed "${required_version}"; then
|
|
195
|
+
echo ""
|
|
196
|
+
log_info "No action needed — BATS is already available"
|
|
197
|
+
echo ""
|
|
198
|
+
exit 0
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
echo ""
|
|
202
|
+
log_warning "BATS not found or outdated — installing…"
|
|
203
|
+
echo ""
|
|
204
|
+
|
|
205
|
+
install_bats_core "${required_version}"
|
|
206
|
+
install_bats_libraries
|
|
207
|
+
|
|
208
|
+
echo ""
|
|
209
|
+
log_success "BATS installation complete"
|
|
210
|
+
echo ""
|
|
211
|
+
|
|
212
|
+
if command_exists bats; then
|
|
213
|
+
bats --version
|
|
214
|
+
echo ""
|
|
215
|
+
log_info "Libraries installed in: ${INSTALL_DIR}"
|
|
216
|
+
echo ""
|
|
217
|
+
log_info "Run tests with:"
|
|
218
|
+
echo -e " ${CYAN}make bats${NC}"
|
|
219
|
+
echo -e " ${CYAN}bats tests/bats/ --recursive${NC}"
|
|
220
|
+
echo ""
|
|
221
|
+
else
|
|
222
|
+
log_error "BATS installation verification failed"
|
|
223
|
+
exit 1
|
|
224
|
+
fi
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
main "$@"
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Zairakai NPM Dev Tools - Git Hooks Installer
|
|
4
|
+
# Installs quality check hooks into .git/hooks
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# bash scripts/install-hooks.sh # Install hooks
|
|
8
|
+
# bash scripts/install-hooks.sh --remove # Remove hooks
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# Load central configuration
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
# shellcheck disable=SC1091
|
|
16
|
+
source "${SCRIPT_DIR}/config.sh"
|
|
17
|
+
|
|
18
|
+
# ============================================================================
|
|
19
|
+
# Configuration
|
|
20
|
+
# ============================================================================
|
|
21
|
+
|
|
22
|
+
HOOKS_SOURCE_DIR="${DEV_TOOLS_ROOT}/stubs/githooks"
|
|
23
|
+
HOOKS_TARGET_DIR="${PROJECT_ROOT}/.git/hooks"
|
|
24
|
+
|
|
25
|
+
# Available hooks (auto-discovered from stubs/githooks directory)
|
|
26
|
+
HOOKS=(
|
|
27
|
+
"commit-msg"
|
|
28
|
+
"prepare-commit-msg"
|
|
29
|
+
"pre-commit"
|
|
30
|
+
"pre-push"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# ============================================================================
|
|
34
|
+
# Functions
|
|
35
|
+
# ============================================================================
|
|
36
|
+
|
|
37
|
+
install_hooks() {
|
|
38
|
+
log_header "📦 Installing Git Hooks"
|
|
39
|
+
|
|
40
|
+
if [[ ! -d ".git" ]]; then
|
|
41
|
+
log_error "Not a git repository"
|
|
42
|
+
log_info "Git hooks can only be installed in a git repository"
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if [[ ! -d "$HOOKS_SOURCE_DIR" ]]; then
|
|
47
|
+
log_error "Hooks source directory not found: $HOOKS_SOURCE_DIR"
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
mkdir -p "$HOOKS_TARGET_DIR"
|
|
52
|
+
|
|
53
|
+
local installed=0
|
|
54
|
+
|
|
55
|
+
for hook in "${HOOKS[@]}"; do
|
|
56
|
+
local source="${HOOKS_SOURCE_DIR}/${hook}"
|
|
57
|
+
local target="${HOOKS_TARGET_DIR}/${hook}"
|
|
58
|
+
|
|
59
|
+
if [[ ! -f "$source" ]]; then
|
|
60
|
+
log_warning "Hook not found: ${hook} (skipping)"
|
|
61
|
+
continue
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
echo -e " ${BLUE}•${NC} ${hook}"
|
|
65
|
+
|
|
66
|
+
# Backup existing hook if present
|
|
67
|
+
if [[ -f "$target" ]] && [[ ! -L "$target" ]]; then
|
|
68
|
+
local backup
|
|
69
|
+
backup="${target}.backup-$(date +%Y%m%d-%H%M%S)"
|
|
70
|
+
mv "$target" "$backup"
|
|
71
|
+
log_warning " Existing hook backed up to: ${backup}"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Remove existing symlink
|
|
75
|
+
rm -f "$target"
|
|
76
|
+
|
|
77
|
+
# Install hook
|
|
78
|
+
ln -s "$source" "$target"
|
|
79
|
+
log_success " ${hook} installed"
|
|
80
|
+
|
|
81
|
+
installed=$((installed + 1))
|
|
82
|
+
done
|
|
83
|
+
|
|
84
|
+
echo ""
|
|
85
|
+
|
|
86
|
+
if [[ $installed -eq 0 ]]; then
|
|
87
|
+
log_warning "No hooks were installed"
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
log_header "✅ ${installed} Hook(s) Installed Successfully"
|
|
92
|
+
|
|
93
|
+
echo ""
|
|
94
|
+
log_info "Installed hooks:"
|
|
95
|
+
for hook in "${HOOKS[@]}"; do
|
|
96
|
+
case "$hook" in
|
|
97
|
+
commit-msg)
|
|
98
|
+
echo -e " ${GREEN}✓${NC} ${hook} ${YELLOW}→${NC} Validate Conventional Commits format"
|
|
99
|
+
;;
|
|
100
|
+
prepare-commit-msg)
|
|
101
|
+
echo -e " ${GREEN}✓${NC} ${hook} ${YELLOW}→${NC} Auto-prefix ticket number from branch"
|
|
102
|
+
;;
|
|
103
|
+
pre-commit)
|
|
104
|
+
echo -e " ${GREEN}✓${NC} ${hook} ${YELLOW}→${NC} Quality checks (ESLint, Prettier, Markdown)"
|
|
105
|
+
;;
|
|
106
|
+
pre-push)
|
|
107
|
+
echo -e " ${GREEN}✓${NC} ${hook} ${YELLOW}→${NC} Full quality gate before push"
|
|
108
|
+
;;
|
|
109
|
+
*)
|
|
110
|
+
echo -e " ${GREEN}✓${NC} ${hook}"
|
|
111
|
+
;;
|
|
112
|
+
esac
|
|
113
|
+
done
|
|
114
|
+
|
|
115
|
+
echo ""
|
|
116
|
+
log_info "To bypass hooks (emergency only):"
|
|
117
|
+
echo -e " ${CYAN}git commit --no-verify${NC}"
|
|
118
|
+
echo ""
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
remove_hooks() {
|
|
122
|
+
log_header "🗑️ Removing Git Hooks"
|
|
123
|
+
|
|
124
|
+
if [[ ! -d ".git" ]]; then
|
|
125
|
+
log_error "Not a git repository"
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
local removed=0
|
|
130
|
+
|
|
131
|
+
for hook in "${HOOKS[@]}"; do
|
|
132
|
+
local target="${HOOKS_TARGET_DIR}/${hook}"
|
|
133
|
+
|
|
134
|
+
if [[ -L "$target" ]]; then
|
|
135
|
+
# Only remove if it's a symlink (managed by us)
|
|
136
|
+
echo -e " ${BLUE}•${NC} ${hook}"
|
|
137
|
+
rm -f "$target"
|
|
138
|
+
log_success " ${hook} removed"
|
|
139
|
+
removed=$((removed + 1))
|
|
140
|
+
elif [[ -f "$target" ]]; then
|
|
141
|
+
log_warning " ${hook} exists but is not a symlink (skipping)"
|
|
142
|
+
fi
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
echo ""
|
|
146
|
+
|
|
147
|
+
if [[ $removed -eq 0 ]]; then
|
|
148
|
+
log_info "No hooks were removed"
|
|
149
|
+
exit 0
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
log_header "✅ ${removed} Hook(s) Removed"
|
|
153
|
+
echo ""
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# ============================================================================
|
|
157
|
+
# Main
|
|
158
|
+
# ============================================================================
|
|
159
|
+
|
|
160
|
+
case "${1:-install}" in
|
|
161
|
+
--remove|-r)
|
|
162
|
+
remove_hooks
|
|
163
|
+
;;
|
|
164
|
+
--install|-i|install|*)
|
|
165
|
+
install_hooks
|
|
166
|
+
;;
|
|
167
|
+
esac
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Zairakai NPM Dev Tools - Markdownlint Installer
|
|
4
|
+
# Installs markdownlint-cli2 as a local npm dev dependency.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# bash scripts/install-markdownlint.sh
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
13
|
+
# shellcheck source=config.sh
|
|
14
|
+
source "${SCRIPT_DIR}/config.sh"
|
|
15
|
+
|
|
16
|
+
log_header "Markdownlint Installer"
|
|
17
|
+
|
|
18
|
+
if [[ -f "$MARKDOWNLINT_BIN" ]]; then
|
|
19
|
+
version=$("$MARKDOWNLINT_BIN" --version 2>/dev/null || echo "unknown")
|
|
20
|
+
log_success "markdownlint-cli2 already installed: ${version}"
|
|
21
|
+
log_info "Run: make markdownlint"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
log_info "markdownlint-cli2 not found in node_modules/.bin/"
|
|
26
|
+
log_step "Installing markdownlint-cli2 as a dev dependency…"
|
|
27
|
+
|
|
28
|
+
"$PM" install --save-dev markdownlint-cli2
|
|
29
|
+
|
|
30
|
+
log_success "markdownlint-cli2 installed"
|
|
31
|
+
log_info "Run: make markdownlint"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Zairakai NPM Dev Tools - ShellCheck Installer
|
|
4
|
+
# Installs ShellCheck for shell script static analysis
|
|
5
|
+
#
|
|
6
|
+
# Platform: Linux/WSL only
|
|
7
|
+
#
|
|
8
|
+
# Features:
|
|
9
|
+
# - Reads version from .tool-versions
|
|
10
|
+
# - Supports apt/dnf/pacman/apk
|
|
11
|
+
# - CI-aware
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# bash scripts/install-shellcheck.sh
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# Load central configuration
|
|
20
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
21
|
+
# shellcheck source=config.sh
|
|
22
|
+
source "${SCRIPT_DIR}/config.sh"
|
|
23
|
+
|
|
24
|
+
# ============
|
|
25
|
+
# Configuration
|
|
26
|
+
# ============
|
|
27
|
+
|
|
28
|
+
CI_MODE="${CI:-false}"
|
|
29
|
+
TOOL_VERSIONS_FILE="${SCRIPT_DIR}/../.tool-versions"
|
|
30
|
+
|
|
31
|
+
# ============
|
|
32
|
+
# Functions
|
|
33
|
+
# ============
|
|
34
|
+
|
|
35
|
+
# Get ShellCheck version from .tool-versions or use default
|
|
36
|
+
get_shellcheck_version() {
|
|
37
|
+
local version=""
|
|
38
|
+
|
|
39
|
+
if [[ -f "${TOOL_VERSIONS_FILE}" ]]; then
|
|
40
|
+
version=$(grep "^shellcheck=" "${TOOL_VERSIONS_FILE}" | awk -F "=" '{print $2}' || echo "")
|
|
41
|
+
|
|
42
|
+
if [[ -n "${version}" ]]; then
|
|
43
|
+
log_info "Found .tool-versions: ${version}"
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Fallback to default if not found
|
|
48
|
+
if [[ -z "${version}" ]]; then
|
|
49
|
+
version="0.10.0"
|
|
50
|
+
log_info "Using default version: ${version}"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
echo "${version}"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Compare versions (returns 0 if ver1 >= ver2)
|
|
57
|
+
version_ge() {
|
|
58
|
+
local ver1="$1"
|
|
59
|
+
local ver2="$2"
|
|
60
|
+
|
|
61
|
+
if [[ "${ver1}" == "${ver2}" ]]; then
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
local IFS=.
|
|
66
|
+
local i ver1_arr=("$ver1") ver2_arr=("$ver2")
|
|
67
|
+
|
|
68
|
+
# Fill empty positions with zeros
|
|
69
|
+
for ((i=${#ver1_arr[@]}; i<${#ver2_arr[@]}; i++)); do
|
|
70
|
+
ver1_arr[i]=0
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
for ((i=0; i<${#ver1_arr[@]}; i++)); do
|
|
74
|
+
if [[ -z ${ver2_arr[i]} ]]; then
|
|
75
|
+
ver2_arr[i]=0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if ((10#${ver1_arr[i]} > 10#${ver2_arr[i]})); then
|
|
79
|
+
return 0
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if ((10#${ver1_arr[i]} < 10#${ver2_arr[i]})); then
|
|
83
|
+
return 1
|
|
84
|
+
fi
|
|
85
|
+
done
|
|
86
|
+
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Check if ShellCheck is already installed with sufficient version
|
|
91
|
+
check_shellcheck_installed() {
|
|
92
|
+
local required_version="$1"
|
|
93
|
+
|
|
94
|
+
if ! command_exists shellcheck; then
|
|
95
|
+
return 1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
local current_version
|
|
99
|
+
current_version=$(shellcheck --version | grep "version:" | awk '{print $2}' || echo "")
|
|
100
|
+
|
|
101
|
+
if [[ -z "${current_version}" ]]; then
|
|
102
|
+
log_warning "Could not determine ShellCheck version"
|
|
103
|
+
return 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
log_info "Current version: ${current_version}"
|
|
107
|
+
|
|
108
|
+
if version_ge "${current_version}" "${required_version}"; then
|
|
109
|
+
log_success "ShellCheck ${current_version} is sufficient (need >=${required_version})"
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
log_warning "Version ${current_version} is outdated (need >=${required_version})"
|
|
114
|
+
|
|
115
|
+
# Ask for upgrade confirmation if not in CI
|
|
116
|
+
if [[ "${CI_MODE}" != "true" ]]; then
|
|
117
|
+
echo ""
|
|
118
|
+
read -rp "Upgrade to ${required_version}? (y/N) " -n 1
|
|
119
|
+
echo ""
|
|
120
|
+
|
|
121
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
122
|
+
log_info "Keeping existing installation"
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
else
|
|
126
|
+
log_info "CI mode: upgrading automatically"
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
return 1
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Detect package manager and install ShellCheck
|
|
133
|
+
install_shellcheck() {
|
|
134
|
+
local version="$1"
|
|
135
|
+
|
|
136
|
+
log_header "🐚 Installing ShellCheck ${version}"
|
|
137
|
+
echo ""
|
|
138
|
+
|
|
139
|
+
local use_sudo=true
|
|
140
|
+
|
|
141
|
+
# Don't use sudo in CI or if running as root
|
|
142
|
+
if [[ "${CI_MODE}" == "true" ]] || [[ "$(id -u)" -eq 0 ]]; then
|
|
143
|
+
use_sudo=false
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# Detect and use package manager
|
|
147
|
+
if command_exists apt-get; then
|
|
148
|
+
log_step "Installing via apt-get..."
|
|
149
|
+
|
|
150
|
+
if [[ "${use_sudo}" == "true" ]]; then
|
|
151
|
+
sudo apt-get update -qq
|
|
152
|
+
sudo apt-get install -y -qq shellcheck
|
|
153
|
+
else
|
|
154
|
+
apt-get update -qq
|
|
155
|
+
apt-get install -y -qq shellcheck
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
elif command_exists dnf; then
|
|
159
|
+
log_step "Installing via dnf..."
|
|
160
|
+
|
|
161
|
+
if [[ "${use_sudo}" == "true" ]]; then
|
|
162
|
+
sudo dnf install -y -q ShellCheck
|
|
163
|
+
else
|
|
164
|
+
dnf install -y -q ShellCheck
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
elif command_exists pacman; then
|
|
168
|
+
log_step "Installing via pacman..."
|
|
169
|
+
|
|
170
|
+
if [[ "${use_sudo}" == "true" ]]; then
|
|
171
|
+
sudo pacman -S --noconfirm --quiet shellcheck
|
|
172
|
+
else
|
|
173
|
+
pacman -S --noconfirm --quiet shellcheck
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
elif command_exists apk; then
|
|
177
|
+
log_step "Installing via apk..."
|
|
178
|
+
apk add --no-cache -q shellcheck
|
|
179
|
+
|
|
180
|
+
else
|
|
181
|
+
log_error "No supported package manager found (apt-get, dnf, pacman, apk)"
|
|
182
|
+
echo ""
|
|
183
|
+
log_info "Install manually from: https://github.com/koalaman/shellcheck"
|
|
184
|
+
return 1
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
log_success "ShellCheck installed successfully"
|
|
188
|
+
|
|
189
|
+
echo ""
|
|
190
|
+
log_header "✅ ShellCheck Installation Complete"
|
|
191
|
+
echo ""
|
|
192
|
+
|
|
193
|
+
# Verify installation
|
|
194
|
+
if command_exists shellcheck; then
|
|
195
|
+
shellcheck --version
|
|
196
|
+
echo ""
|
|
197
|
+
log_info "Run linter with:"
|
|
198
|
+
echo -e " ${CYAN}make shellcheck${NC}"
|
|
199
|
+
echo -e " ${CYAN}shellcheck scripts/*.sh${NC}"
|
|
200
|
+
echo ""
|
|
201
|
+
else
|
|
202
|
+
log_error "ShellCheck installation verification failed"
|
|
203
|
+
return 1
|
|
204
|
+
fi
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# ============
|
|
208
|
+
# Main
|
|
209
|
+
# ============
|
|
210
|
+
|
|
211
|
+
main() {
|
|
212
|
+
local required_version
|
|
213
|
+
required_version=$(get_shellcheck_version)
|
|
214
|
+
|
|
215
|
+
echo ""
|
|
216
|
+
|
|
217
|
+
# Check if already installed with sufficient version
|
|
218
|
+
if check_shellcheck_installed "${required_version}"; then
|
|
219
|
+
echo ""
|
|
220
|
+
log_info "No action needed - ShellCheck is already available"
|
|
221
|
+
echo ""
|
|
222
|
+
exit 0
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
echo ""
|
|
226
|
+
log_warning "ShellCheck not found or outdated - installing..."
|
|
227
|
+
echo ""
|
|
228
|
+
|
|
229
|
+
install_shellcheck "${required_version}"
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
main "$@"
|