valise-sh 0.2.0 → 0.3.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/CHANGELOG.md +152 -2
- package/README.md +99 -146
- package/bin/valise +57 -5376
- package/docs/architecture.md +77 -121
- package/docs/contrib-modules.md +172 -218
- package/docs/installation.md +137 -83
- package/docs/module-abi.md +125 -126
- package/docs/projects.md +249 -0
- package/docs/release-policy.md +68 -93
- package/docs/shared-modules.md +40 -39
- package/examples/README.md +12 -11
- package/examples/e2e/README.md +59 -66
- package/examples/e2e/bundles.tsv +6 -0
- package/examples/e2e/module.d/e2e_aliases.meta +1 -2
- package/examples/e2e/module.d/e2e_completions_bash.meta +0 -1
- package/examples/e2e/module.d/e2e_completions_zsh.meta +0 -1
- package/examples/e2e/module.d/e2e_env_setup.meta +2 -3
- package/examples/e2e/module.d/e2e_fish_features.meta +0 -1
- package/examples/e2e/module.d/e2e_generated_config.meta +1 -2
- package/examples/e2e/module.d/e2e_ksh_features.meta +0 -1
- package/examples/e2e/module.d/e2e_layer_probe.meta +1 -2
- package/examples/e2e/module.d/e2e_optional_network.meta +1 -2
- package/examples/e2e/module.d/e2e_platform_tools.meta +8 -0
- package/examples/e2e/module.d/e2e_platform_tools_bsd.meta +2 -3
- package/examples/e2e/module.d/e2e_platform_tools_darwin.meta +2 -3
- package/examples/e2e/module.d/e2e_platform_tools_linux.meta +2 -3
- package/examples/e2e/valise.conf +4 -0
- package/examples/e2e/variants.tsv +3 -0
- package/examples/local/module.d/hello_valise.meta +1 -2
- package/examples/profile-project/README.md +78 -108
- package/examples/profile-project/bundles.tsv +8 -0
- package/examples/profile-project/{shell/layers → layers}/demo/module.d/contrib_demo_greeting.meta +1 -2
- package/examples/profile-project/{shell/module.d → module.d}/greeting.meta +1 -2
- package/examples/profile-project/{shell/module.d → module.d}/profile_main.meta +0 -1
- package/examples/profile-project/{shell/modules → modules}/greeting.sh +1 -1
- package/examples/profile-project/valise.conf +4 -0
- package/examples/profile-project/variants.tsv +3 -0
- package/examples/shared/README.md +13 -34
- package/examples/shared/module.d/contrib_shared_paths.meta +2 -3
- package/examples/shared/module.d/contrib_shared_umask.meta +1 -2
- package/examples/shared/modules/shared/contrib_shared_paths.sh +2 -2
- package/lib/capabilities.sh +40 -13
- package/lib/cli/commands.sh +278 -0
- package/lib/cli/common.sh +326 -0
- package/lib/cli/devtools.sh +539 -0
- package/lib/cli/fish-hook.sh +986 -0
- package/lib/cli/integration.sh +337 -0
- package/lib/cli/lifecycle.sh +578 -0
- package/lib/cli/project.sh +1807 -0
- package/lib/cli/store.sh +1023 -0
- package/lib/cli/usage.sh +308 -0
- package/lib/compiler.awk +11 -4
- package/lib/fish/loader.fish +69 -9
- package/lib/fish/metadata.fish +49 -5
- package/lib/fish/profile.fish +170 -0
- package/lib/loader.sh +6 -8
- package/lib/metadata.sh +111 -5
- package/manifests/parser-support.tsv +2 -2
- package/module.d/valise_editor.meta +9 -0
- package/module.d/valise_history.meta +9 -0
- package/module.d/valise_pager.meta +9 -0
- package/module.d/valise_path.meta +9 -0
- package/module.d/{platform_bsd.meta → valise_platform_bsd.meta} +3 -4
- package/module.d/{platform_linux.meta → valise_platform_linux.meta} +3 -4
- package/module.d/{platform_macos.meta → valise_platform_macos.meta} +3 -4
- package/module.d/{platform_posix.meta → valise_platform_posix.meta} +2 -3
- package/module.d/valise_xdg.meta +9 -0
- package/modules/system/{contrib_editor.sh → valise_editor.sh} +2 -2
- package/modules/system/{contrib_history.sh → valise_history.sh} +2 -2
- package/modules/system/{contrib_pager.sh → valise_pager.sh} +2 -2
- package/modules/system/{contrib_path.sh → valise_path.sh} +3 -3
- package/modules/system/{contrib_xdg.sh → valise_xdg.sh} +2 -2
- package/package.json +91 -10
- package/docs/variants.md +0 -327
- package/entrypoints/.gitkeep +0 -0
- package/entrypoints/fish/profile.fish +0 -99
- package/examples/e2e/build-manifest.sh +0 -138
- package/examples/local/.gitkeep +0 -0
- package/examples/profile-project/shell/release/bundle-roots +0 -9
- package/examples/profile-project/shell/release/install-variants +0 -3
- package/examples/profile-project/shell/release/module-registry.tsv +0 -4
- package/examples/profile-project/shell/release/project.conf +0 -3
- package/lib/.gitkeep +0 -0
- package/manifests/.gitkeep +0 -0
- package/module.d/.gitkeep +0 -0
- package/module.d/contrib_editor.meta +0 -10
- package/module.d/contrib_history.meta +0 -10
- package/module.d/contrib_pager.meta +0 -10
- package/module.d/contrib_path.meta +0 -10
- package/module.d/contrib_xdg.meta +0 -10
- package/modules/.gitkeep +0 -0
- /package/examples/profile-project/{shell/layers → layers}/demo/modules/shared/contrib_demo_greeting.sh +0 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
# Copyright (c) 2026 Joseph Cumines
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
# valise CLI: developer commands: env, lint, test, and run. test and run load
|
|
4
|
+
# modules from a throwaway release built beside the project, so they exercise
|
|
5
|
+
# the same plans an installed profile uses.
|
|
6
|
+
# Sourced by bin/valise; not executable on its own.
|
|
7
|
+
|
|
8
|
+
_dev_tmp=""
|
|
9
|
+
|
|
10
|
+
# devtool_interpreter <class>
|
|
11
|
+
# Print the interpreter for a shell class, or fail. The sh class is dash, or
|
|
12
|
+
# an sh that is not bash, zsh, or ksh in disguise.
|
|
13
|
+
devtool_interpreter() {
|
|
14
|
+
case "$1" in
|
|
15
|
+
sh)
|
|
16
|
+
_di_path=$(command -v dash 2>/dev/null || :)
|
|
17
|
+
if [ -z "$_di_path" ]; then
|
|
18
|
+
_di_path=$(command -v sh 2>/dev/null || :)
|
|
19
|
+
[ -n "$_di_path" ] || return 1
|
|
20
|
+
# shellcheck disable=SC2016
|
|
21
|
+
"$_di_path" -c '[ -z "${BASH_VERSION-}${ZSH_VERSION-}${KSH_VERSION-}" ]' 2>/dev/null || return 1
|
|
22
|
+
fi
|
|
23
|
+
;;
|
|
24
|
+
bash | zsh | ksh | fish) _di_path=$(command -v "$1" 2>/dev/null || :) ;;
|
|
25
|
+
*) return 1 ;;
|
|
26
|
+
esac
|
|
27
|
+
case "$_di_path" in
|
|
28
|
+
/*) printf '%s\n' "$_di_path" ;;
|
|
29
|
+
*) return 1 ;;
|
|
30
|
+
esac
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# devtool_classes <shells-field>: the shell classes a module declares.
|
|
34
|
+
devtool_classes() {
|
|
35
|
+
_dc_out=
|
|
36
|
+
for _dc_word in $1; do
|
|
37
|
+
case "$_dc_word" in
|
|
38
|
+
posix) _dc_add='sh bash zsh ksh' ;;
|
|
39
|
+
*) _dc_add=$_dc_word ;;
|
|
40
|
+
esac
|
|
41
|
+
for _dc_class in $_dc_add; do
|
|
42
|
+
case " $_dc_out " in *" $_dc_class "*) ;; *) _dc_out="${_dc_out:+$_dc_out }$_dc_class" ;; esac
|
|
43
|
+
done
|
|
44
|
+
done
|
|
45
|
+
printf '%s\n' "$_dc_out"
|
|
46
|
+
unset _dc_out _dc_word _dc_add _dc_class
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# devtool_scratch: create the scratch directory for one command.
|
|
50
|
+
devtool_scratch() {
|
|
51
|
+
_dev_tmp=$(trusted_mktemp dir "${TMPDIR:-/tmp}/valise-dev.XXXXXX") || die "cannot create scratch directory"
|
|
52
|
+
_dev_tmp=$(cd "$_dev_tmp" && pwd -P) || die "cannot resolve scratch directory"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
devtool_cleanup() {
|
|
56
|
+
if [ -n "$_dev_tmp" ]; then
|
|
57
|
+
remove_tree "$_dev_tmp" || info "warning: cannot remove scratch directory: $_dev_tmp"
|
|
58
|
+
_dev_tmp=""
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# devtool_build <out> <log> <sh-roots> <bash-roots> <zsh-roots> <ksh-roots> <fish-roots>
|
|
63
|
+
# Build a release whose roots are given per shell, ignoring bundles and
|
|
64
|
+
# variants. Output goes to the log; fails when the build fails.
|
|
65
|
+
devtool_build() {
|
|
66
|
+
(
|
|
67
|
+
_pb_override=1
|
|
68
|
+
_pbo_sh=$3 _pbo_bash=$4 _pbo_zsh=$5 _pbo_ksh=$6 _pbo_fish=$7
|
|
69
|
+
project_build dev dev "$1" 0
|
|
70
|
+
) >"$2" 2>&1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# devtool_runner <class> <release> <file> <body-file>
|
|
74
|
+
# Write a script that loads the release for one shell class in strict mode,
|
|
75
|
+
# defines valise_test_reload, then runs the body. VALISE_TEST_RELEASE and
|
|
76
|
+
# VALISE_TEST_PLAN are exported so the body can load the same modules in a
|
|
77
|
+
# child shell.
|
|
78
|
+
devtool_runner() {
|
|
79
|
+
_dr_release=$(shell_quote "$2") || return 1
|
|
80
|
+
_dr_cat=$(trusted_utility_path cat) || return 1
|
|
81
|
+
if [ "$1" = fish ]; then
|
|
82
|
+
_dr_release=$(fish_quote "$2") || return 1
|
|
83
|
+
{
|
|
84
|
+
printf '%s\n' \
|
|
85
|
+
"set -gx SHELL_PROJECT_ROOT $_dr_release" \
|
|
86
|
+
"set -gx VALISE_TEST_RELEASE $_dr_release" \
|
|
87
|
+
'set -gx VALISE_TEST_PLAN "$VALISE_TEST_RELEASE/runtime/fish/profile.fish"' \
|
|
88
|
+
'source "$SHELL_PROJECT_ROOT/runtime/fish/profile.fish"' \
|
|
89
|
+
'or begin' \
|
|
90
|
+
" printf '%s\\n' 'valise: the modules failed to load' >&2" \
|
|
91
|
+
' exit 97' \
|
|
92
|
+
'end' \
|
|
93
|
+
'function valise_test_reload' \
|
|
94
|
+
' source "$SHELL_PROJECT_ROOT/runtime/fish/profile.fish"' \
|
|
95
|
+
'end'
|
|
96
|
+
"$_dr_cat" "$4"
|
|
97
|
+
} >"$3"
|
|
98
|
+
else
|
|
99
|
+
{
|
|
100
|
+
printf '%s\n' \
|
|
101
|
+
"_valise_dev_release=$_dr_release" \
|
|
102
|
+
'VALISE_TEST_RELEASE=$_valise_dev_release' \
|
|
103
|
+
"VALISE_TEST_PLAN=\$_valise_dev_release/plans/$1.sh" \
|
|
104
|
+
'export VALISE_TEST_RELEASE VALISE_TEST_PLAN' \
|
|
105
|
+
'valise_test_reload() {' \
|
|
106
|
+
' VALISE_MODULE_ROOT=$_valise_dev_release' \
|
|
107
|
+
' VALISE_MODE=strict' \
|
|
108
|
+
' VALISE_RELOAD=1' \
|
|
109
|
+
" . \"\$_valise_dev_release/plans/$1.sh\"" \
|
|
110
|
+
' _valise_dev_status=$?' \
|
|
111
|
+
' unset VALISE_RELOAD' \
|
|
112
|
+
' return "$_valise_dev_status"' \
|
|
113
|
+
'}' \
|
|
114
|
+
'VALISE_MODULE_ROOT=$_valise_dev_release' \
|
|
115
|
+
'VALISE_MODE=strict' \
|
|
116
|
+
". \"\$_valise_dev_release/plans/$1.sh\" || {" \
|
|
117
|
+
" printf '%s\\n' 'valise: the modules failed to load' >&2" \
|
|
118
|
+
' exit 97' \
|
|
119
|
+
'}'
|
|
120
|
+
"$_dr_cat" "$4"
|
|
121
|
+
} >"$3"
|
|
122
|
+
fi
|
|
123
|
+
_dr_status=$?
|
|
124
|
+
unset _dr_release _dr_cat
|
|
125
|
+
return "$_dr_status"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# devtool_quarantine <scratch> <interpreter>
|
|
129
|
+
# Create the test home and XDG tree; set _dev_path to the test PATH.
|
|
130
|
+
devtool_quarantine() {
|
|
131
|
+
_dq_scratch=$1
|
|
132
|
+
_dq_mkdir=$(trusted_utility_path mkdir) || die "no trusted mkdir utility available"
|
|
133
|
+
"$_dq_mkdir" -p "$_dq_scratch/home" "$_dq_scratch/tmp" "$_dq_scratch/xdg/config" \
|
|
134
|
+
"$_dq_scratch/xdg/data" "$_dq_scratch/xdg/state" "$_dq_scratch/xdg/cache" ||
|
|
135
|
+
die "cannot create the test home"
|
|
136
|
+
_dev_path="${VALISE_TEST_PATH:-/usr/bin:/bin:/usr/sbin:/sbin}:${2%/*}"
|
|
137
|
+
unset _dq_scratch _dq_mkdir
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# devtool_exec <scratch> <interpreter> <runner> [args...]
|
|
141
|
+
# Run a runner script in a quarantined environment.
|
|
142
|
+
devtool_exec() {
|
|
143
|
+
_de_scratch=$1
|
|
144
|
+
_de_interp=$2
|
|
145
|
+
_de_runner=$3
|
|
146
|
+
shift 3
|
|
147
|
+
devtool_quarantine "$_de_scratch" "$_de_interp"
|
|
148
|
+
env -i HOME="$_de_scratch/home" TMPDIR="$_de_scratch/tmp" PATH="$_dev_path" TERM=dumb \
|
|
149
|
+
LANG="${LANG:-C}" USER="${USER:-valise}" LOGNAME="${LOGNAME:-${USER:-valise}}" \
|
|
150
|
+
XDG_CONFIG_HOME="$_de_scratch/xdg/config" XDG_DATA_HOME="$_de_scratch/xdg/data" \
|
|
151
|
+
XDG_STATE_HOME="$_de_scratch/xdg/state" XDG_CACHE_HOME="$_de_scratch/xdg/cache" \
|
|
152
|
+
VALISE_TEST_ROOT="$_de_scratch" VALISE_PROJECT_ROOT="$_project_root" \
|
|
153
|
+
VALISE_TEST_MODULE="${_dev_module:-}" VALISE_TEST_MODULE_DIR="${_dev_module_dir:-}" \
|
|
154
|
+
VALISE_TEST_SHELL="${_dev_class:-}" \
|
|
155
|
+
"$_de_interp" "$_de_runner" "$@"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# --- env ---
|
|
159
|
+
|
|
160
|
+
cmd_env() {
|
|
161
|
+
case "${1:-}" in
|
|
162
|
+
--help | -h) usage_env; return 0 ;;
|
|
163
|
+
'') ;;
|
|
164
|
+
*) die "unknown env option: $1" ;;
|
|
165
|
+
esac
|
|
166
|
+
_env_engine=$(engine_root) || die "cannot locate the valise engine"
|
|
167
|
+
_env_project=$(project_find_root 2>/dev/null || :)
|
|
168
|
+
_env_bin=$(commands_bin_dir 2>/dev/null || :)
|
|
169
|
+
for _env_pair in \
|
|
170
|
+
"VALISE_VERSION=$VALISE_VERSION" \
|
|
171
|
+
"VALISE_ENGINE_ROOT=$_env_engine" \
|
|
172
|
+
"VALISE_PROJECT_ROOT=$_env_project" \
|
|
173
|
+
"VALISE_DATA_DIR=$VALISE_XDG_DATA" \
|
|
174
|
+
"VALISE_CONFIG_DIR=$VALISE_XDG_CONFIG" \
|
|
175
|
+
"VALISE_STATE_DIR=$VALISE_XDG_STATE" \
|
|
176
|
+
"VALISE_RELEASES_DIR=$VALISE_RELEASES_DIR" \
|
|
177
|
+
"VALISE_ACTIVE_LINK=$VALISE_ACTIVE_LINK" \
|
|
178
|
+
"VALISE_BIN_DIR=$_env_bin"; do
|
|
179
|
+
printf '%s=%s\n' "${_env_pair%%=*}" "$(shell_quote "${_env_pair#*=}")"
|
|
180
|
+
done
|
|
181
|
+
unset _env_engine _env_project _env_bin _env_pair
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# --- lint ---
|
|
185
|
+
|
|
186
|
+
# lint_report <ok|FAIL|skip> <message>
|
|
187
|
+
lint_report() {
|
|
188
|
+
printf '%s: %s\n' "$1" "$2"
|
|
189
|
+
[ "$1" != FAIL ] || _lint_problems=$((_lint_problems + 1))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# lint_parse <class> <file> <label>
|
|
193
|
+
lint_parse() {
|
|
194
|
+
if ! _lp_interp=$(devtool_interpreter "$1"); then
|
|
195
|
+
if [ "$_lint_require" -eq 1 ]; then
|
|
196
|
+
lint_report FAIL "$3: no $1 parser available"
|
|
197
|
+
else
|
|
198
|
+
case " $_lint_missing " in
|
|
199
|
+
*" $1 "*) ;;
|
|
200
|
+
*) lint_report skip "no $1 parser available; $1 syntax is not checked"; _lint_missing="$_lint_missing $1" ;;
|
|
201
|
+
esac
|
|
202
|
+
fi
|
|
203
|
+
return 0
|
|
204
|
+
fi
|
|
205
|
+
if [ "$1" = fish ]; then
|
|
206
|
+
_lp_out=$("$_lp_interp" --no-execute "$2" 2>&1) && _lp_rc=0 || _lp_rc=$?
|
|
207
|
+
else
|
|
208
|
+
_lp_out=$("$_lp_interp" -n "$2" 2>&1) && _lp_rc=0 || _lp_rc=$?
|
|
209
|
+
fi
|
|
210
|
+
if [ "$_lp_rc" -eq 0 ]; then
|
|
211
|
+
_lint_parsed=$((_lint_parsed + 1))
|
|
212
|
+
else
|
|
213
|
+
lint_report FAIL "$3: $1 syntax error: $(printf '%s' "$_lp_out" | head -n 3 | tr '\n' ' ')"
|
|
214
|
+
fi
|
|
215
|
+
unset _lp_interp _lp_out _lp_rc
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
cmd_lint() {
|
|
219
|
+
_lint_require=0
|
|
220
|
+
_lint_select=
|
|
221
|
+
for _lint_arg do
|
|
222
|
+
case "$_lint_arg" in
|
|
223
|
+
--require-parsers) _lint_require=1 ;;
|
|
224
|
+
--help | -h) usage_lint; return 0 ;;
|
|
225
|
+
-*) die "unknown lint option: $_lint_arg" ;;
|
|
226
|
+
*) _lint_select="$_lint_select $_lint_arg" ;;
|
|
227
|
+
esac
|
|
228
|
+
done
|
|
229
|
+
project_config_load
|
|
230
|
+
devtool_scratch
|
|
231
|
+
_lint_problems=0
|
|
232
|
+
_lint_parsed=0
|
|
233
|
+
_lint_missing=
|
|
234
|
+
_lint_awk=$(trusted_utility_path awk) || die "no trusted awk utility available"
|
|
235
|
+
|
|
236
|
+
# Metadata: registry generation validates every record, names, prefixes,
|
|
237
|
+
# and the files each module names.
|
|
238
|
+
_lint_registry="$_dev_tmp/registry"
|
|
239
|
+
if _lint_out=$( (registry_generate "$_project_root" "$_lint_registry") 2>&1); then
|
|
240
|
+
lint_report ok "metadata of $(($("$_lint_awk" 'END { print NR }' "$_lint_registry") - 1)) module(s)"
|
|
241
|
+
else
|
|
242
|
+
lint_report FAIL "$(printf '%s' "$_lint_out" | sed -n 's/^valise: error: //p' | head -n 1)"
|
|
243
|
+
printf 'valise lint: %d problem(s)\n' "$_lint_problems"
|
|
244
|
+
devtool_cleanup
|
|
245
|
+
exit 1
|
|
246
|
+
fi
|
|
247
|
+
|
|
248
|
+
# Layers: contrib_ names, versions, safe modes, and dependencies that stay
|
|
249
|
+
# inside the layer or the engine.
|
|
250
|
+
for _lint_layer in "$_project_root"/layers/*/; do
|
|
251
|
+
[ -d "$_lint_layer" ] || continue
|
|
252
|
+
_lint_layer=${_lint_layer%/}
|
|
253
|
+
_lint_name=${_lint_layer##*/}
|
|
254
|
+
if _lint_out=$(layer_lint_dir "$_lint_layer" "$_lint_name" 2>&1); then
|
|
255
|
+
lint_report ok "layer $_lint_name"
|
|
256
|
+
else
|
|
257
|
+
printf '%s\n' "$_lint_out" | sed -n 's/^ FAIL: //p' | while IFS= read -r _lint_line; do
|
|
258
|
+
printf 'FAIL: layers/%s: %s\n' "$_lint_name" "$_lint_line"
|
|
259
|
+
done
|
|
260
|
+
_lint_problems=$((_lint_problems + $(printf '%s\n' "$_lint_out" | grep -c '^ FAIL: ')))
|
|
261
|
+
fi
|
|
262
|
+
done
|
|
263
|
+
if [ -n "$_project_lint_forbid" ] && [ -d "$_project_root/layers" ]; then
|
|
264
|
+
if _lint_out=$(grep -rniE -- "$_project_lint_forbid" "$_project_root/layers" 2>/dev/null); then
|
|
265
|
+
printf '%s\n' "$_lint_out" | head -n 20 | while IFS= read -r _lint_line; do
|
|
266
|
+
printf 'FAIL: forbidden in layers (lint_layer_forbid): %s\n' "${_lint_line#"$_project_root"/}"
|
|
267
|
+
done
|
|
268
|
+
_lint_problems=$((_lint_problems + 1))
|
|
269
|
+
else
|
|
270
|
+
lint_report ok "layers contain nothing matching lint_layer_forbid"
|
|
271
|
+
fi
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
# Every bundle compiles for every variant and shell: unknown roots and
|
|
275
|
+
# exclusions, missing dependencies, and exclusions that break a remaining
|
|
276
|
+
# module all fail here.
|
|
277
|
+
_lint_bundles=$(project_bundle_names "$_project_root") || die "cannot read bundles.tsv"
|
|
278
|
+
_lint_variants=$(project_variant_names "$_project_root") || die "cannot read variants.tsv"
|
|
279
|
+
for _lint_bundle in $_lint_bundles; do
|
|
280
|
+
for _lint_variant in $_lint_variants; do
|
|
281
|
+
_lint_excl=$(project_variant_field "$_project_root" "$_lint_variant" excluded 2>/dev/null) || {
|
|
282
|
+
lint_report FAIL "variant $_lint_variant cannot be read"
|
|
283
|
+
continue
|
|
284
|
+
}
|
|
285
|
+
_lint_combo_ok=1
|
|
286
|
+
for _lint_shell in $VALISE_PROJECT_SHELLS fish; do
|
|
287
|
+
_lint_roots=$(project_bundle_roots "$_project_root" "$_lint_shell" "$_lint_bundle" 2>&1) || {
|
|
288
|
+
lint_report FAIL "bundle $_lint_bundle: $_lint_roots"
|
|
289
|
+
_lint_combo_ok=0
|
|
290
|
+
continue
|
|
291
|
+
}
|
|
292
|
+
[ -n "$_lint_roots" ] || continue
|
|
293
|
+
if ! _lint_out=$( (project_compile "$_lint_registry" "$_lint_shell" "$_lint_roots" "$_lint_excl" \
|
|
294
|
+
"$_dev_tmp/plan.$_lint_shell" closure) 2>&1); then
|
|
295
|
+
# The compiler's own reason comes first; the wrapper's message last.
|
|
296
|
+
lint_report FAIL "bundle $_lint_bundle variant $_lint_variant ($_lint_shell): $(printf '%s\n' "$_lint_out" |
|
|
297
|
+
sed -n 's/^valise: error: //p; s/^ERROR: //p' | head -n 1)"
|
|
298
|
+
_lint_combo_ok=0
|
|
299
|
+
fi
|
|
300
|
+
done
|
|
301
|
+
[ "$_lint_combo_ok" -eq 0 ] || lint_report ok "bundle $_lint_bundle variant $_lint_variant compiles"
|
|
302
|
+
done
|
|
303
|
+
done
|
|
304
|
+
|
|
305
|
+
# Syntax of every body and test with each parser its shells name.
|
|
306
|
+
# Tab is IFS whitespace, so read would merge empty columns; awk emits a
|
|
307
|
+
# separator that no validated name or path contains.
|
|
308
|
+
"$_lint_awk" -F '\t' 'NR > 1 && $13 != "valise" { print $1 "|" $6 "|" $9 "|" $12 "|" $13 }' \
|
|
309
|
+
"$_lint_registry" >"$_dev_tmp/lint-rows" || die "cannot read the registry"
|
|
310
|
+
while IFS='|' read -r _lint_mname _lint_shells _lint_load _lint_tests _lint_src; do
|
|
311
|
+
if [ -n "$_lint_select" ]; then
|
|
312
|
+
case " $_lint_select " in
|
|
313
|
+
*" $_lint_mname "* | *" $_lint_mname@$_lint_src "*) ;;
|
|
314
|
+
*) continue ;;
|
|
315
|
+
esac
|
|
316
|
+
fi
|
|
317
|
+
_lint_dir=$(project_source_dir "$_project_root" "$_lint_src") || continue
|
|
318
|
+
_lint_rel=${_lint_dir#"$_project_root"}
|
|
319
|
+
_lint_rel=${_lint_rel#/}
|
|
320
|
+
for _lint_file in $_lint_load $_lint_tests; do
|
|
321
|
+
for _lint_class in $(devtool_classes "$_lint_shells"); do
|
|
322
|
+
case "$_lint_class:$_lint_file" in
|
|
323
|
+
fish:*.fish | sh:* | bash:* | zsh:* | ksh:*) ;;
|
|
324
|
+
*) continue ;;
|
|
325
|
+
esac
|
|
326
|
+
case "$_lint_class:$_lint_file" in
|
|
327
|
+
fish:*) ;;
|
|
328
|
+
*:*.fish) continue ;;
|
|
329
|
+
esac
|
|
330
|
+
lint_parse "$_lint_class" "$_lint_dir/$_lint_file" "${_lint_rel:+$_lint_rel/}$_lint_file"
|
|
331
|
+
done
|
|
332
|
+
done
|
|
333
|
+
done <"$_dev_tmp/lint-rows"
|
|
334
|
+
lint_report ok "$_lint_parsed syntax check(s)"
|
|
335
|
+
if [ -n "$_lint_select" ]; then
|
|
336
|
+
for _lint_want in $_lint_select; do
|
|
337
|
+
"$_lint_awk" -F '\t' -v w="$_lint_want" 'NR > 1 && $13 != "valise" && ($1 == w || $1 "@" $13 == w) { f = 1 } END { exit !f }' \
|
|
338
|
+
"$_lint_registry" || lint_report FAIL "unknown module: $_lint_want"
|
|
339
|
+
done
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
devtool_cleanup
|
|
343
|
+
if [ "$_lint_problems" -ne 0 ]; then
|
|
344
|
+
printf 'valise lint: %d problem(s)\n' "$_lint_problems"
|
|
345
|
+
exit 1
|
|
346
|
+
fi
|
|
347
|
+
printf 'valise lint: no problems\n'
|
|
348
|
+
unset _lint_require _lint_select _lint_arg _lint_problems _lint_parsed _lint_missing _lint_awk _lint_registry \
|
|
349
|
+
_lint_out _lint_layer _lint_name _lint_line _lint_bundles _lint_variants _lint_bundle _lint_variant _lint_excl \
|
|
350
|
+
_lint_combo_ok _lint_shell _lint_roots _lint_mname _lint_shells _lint_load _lint_tests _lint_src \
|
|
351
|
+
_lint_dir _lint_rel _lint_file _lint_class _lint_want
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
# --- test ---
|
|
355
|
+
|
|
356
|
+
cmd_test() {
|
|
357
|
+
_test_shells=
|
|
358
|
+
_test_require=0
|
|
359
|
+
_test_select=
|
|
360
|
+
for _test_arg do
|
|
361
|
+
case "$_test_arg" in
|
|
362
|
+
--shell=*) _test_shells="$_test_shells $(printf '%s' "${_test_arg#--shell=}" | tr ',' ' ')" ;;
|
|
363
|
+
--require-shells) _test_require=1 ;;
|
|
364
|
+
--help | -h) usage_test; return 0 ;;
|
|
365
|
+
-*) die "unknown test option: $_test_arg" ;;
|
|
366
|
+
*) _test_select="$_test_select $_test_arg" ;;
|
|
367
|
+
esac
|
|
368
|
+
done
|
|
369
|
+
for _test_class in $_test_shells; do
|
|
370
|
+
case "$_test_class" in
|
|
371
|
+
sh | bash | zsh | ksh | fish) ;;
|
|
372
|
+
*) die "unknown shell class: $_test_class (use sh, bash, zsh, ksh, or fish)" ;;
|
|
373
|
+
esac
|
|
374
|
+
done
|
|
375
|
+
project_config_load
|
|
376
|
+
devtool_scratch
|
|
377
|
+
_test_awk=$(trusted_utility_path awk) || die "no trusted awk utility available"
|
|
378
|
+
registry_generate "$_project_root" "$_dev_tmp/registry"
|
|
379
|
+
for _test_want in $_test_select; do
|
|
380
|
+
"$_test_awk" -F '\t' -v w="$_test_want" 'NR > 1 && $13 != "valise" && ($1 == w || $1 "@" $13 == w) { f = 1 } END { exit !f }' \
|
|
381
|
+
"$_dev_tmp/registry" || die "unknown module: $_test_want"
|
|
382
|
+
done
|
|
383
|
+
_test_pass=0
|
|
384
|
+
_test_fail=0
|
|
385
|
+
_test_skip=0
|
|
386
|
+
_test_n=0
|
|
387
|
+
_test_any=0
|
|
388
|
+
_test_rows="$_dev_tmp/rows"
|
|
389
|
+
"$_test_awk" -F '\t' 'NR > 1 && $13 != "valise" && $12 != "" { print $1 "\t" $6 "\t" $12 "\t" $13 }' \
|
|
390
|
+
"$_dev_tmp/registry" >"$_test_rows"
|
|
391
|
+
while IFS="$(printf '\t')" read -r _dev_name _test_mshells _test_files _test_src; do
|
|
392
|
+
if [ -n "$_test_select" ]; then
|
|
393
|
+
case " $_test_select " in
|
|
394
|
+
*" $_dev_name "* | *" $_dev_name@$_test_src "*) ;;
|
|
395
|
+
*) continue ;;
|
|
396
|
+
esac
|
|
397
|
+
fi
|
|
398
|
+
_test_any=1
|
|
399
|
+
_dev_module=$_dev_name
|
|
400
|
+
_dev_module_dir=$(project_source_dir "$_project_root" "$_test_src") || die "unsupported registry source: $_test_src"
|
|
401
|
+
_test_id="$_dev_name@$_test_src"
|
|
402
|
+
_test_classes=$(devtool_classes "$_test_mshells")
|
|
403
|
+
_test_n=$((_test_n + 1))
|
|
404
|
+
_test_rel="$_dev_tmp/release-$_test_n"
|
|
405
|
+
# One release per module: the module is the only root for every shell it
|
|
406
|
+
# supports, and no variant applies.
|
|
407
|
+
_r_sh= _r_bash= _r_zsh= _r_ksh= _r_fish=
|
|
408
|
+
for _dev_class in $_test_classes; do
|
|
409
|
+
eval "_r_$_dev_class=\$_test_id"
|
|
410
|
+
done
|
|
411
|
+
if ! devtool_build "$_test_rel" "$_dev_tmp/build-$_test_n.log" "$_r_sh" "$_r_bash" "$_r_zsh" "$_r_ksh" "$_r_fish"; then
|
|
412
|
+
printf 'FAIL %s: cannot build a release for it\n' "$_dev_name"
|
|
413
|
+
sed 's/^/ /' "$_dev_tmp/build-$_test_n.log"
|
|
414
|
+
_test_fail=$((_test_fail + 1))
|
|
415
|
+
continue
|
|
416
|
+
fi
|
|
417
|
+
for _dev_class in $_test_classes; do
|
|
418
|
+
if [ -n "$_test_shells" ]; then
|
|
419
|
+
case " $_test_shells " in *" $_dev_class "*) ;; *) continue ;; esac
|
|
420
|
+
fi
|
|
421
|
+
if ! _test_interp=$(devtool_interpreter "$_dev_class"); then
|
|
422
|
+
if [ "$_test_require" -eq 1 ]; then
|
|
423
|
+
printf 'FAIL %s [%s]: no %s interpreter\n' "$_dev_name" "$_dev_class" "$_dev_class"
|
|
424
|
+
_test_fail=$((_test_fail + 1))
|
|
425
|
+
else
|
|
426
|
+
printf 'skip %s [%s]: no %s interpreter\n' "$_dev_name" "$_dev_class" "$_dev_class"
|
|
427
|
+
_test_skip=$((_test_skip + 1))
|
|
428
|
+
fi
|
|
429
|
+
continue
|
|
430
|
+
fi
|
|
431
|
+
for _test_file in $_test_files; do
|
|
432
|
+
# Fish tests run only in Fish; every other test only in POSIX shells.
|
|
433
|
+
case "$_dev_class:$_test_file" in
|
|
434
|
+
fish:*.fish) ;;
|
|
435
|
+
fish:* | *:*.fish) continue ;;
|
|
436
|
+
esac
|
|
437
|
+
_test_n=$((_test_n + 1))
|
|
438
|
+
_test_scratch="$_dev_tmp/run-$_test_n"
|
|
439
|
+
_test_runner="$_dev_tmp/runner-$_test_n"
|
|
440
|
+
devtool_runner "$_dev_class" "$_test_rel" "$_test_runner" "$_dev_module_dir/$_test_file" ||
|
|
441
|
+
die "cannot write the test runner"
|
|
442
|
+
if _test_out=$(devtool_exec "$_test_scratch" "$_test_interp" "$_test_runner" 2>&1 </dev/null); then
|
|
443
|
+
printf 'ok %s [%s] %s\n' "$_dev_name" "$_dev_class" "$_test_file"
|
|
444
|
+
_test_pass=$((_test_pass + 1))
|
|
445
|
+
else
|
|
446
|
+
printf 'FAIL %s [%s] %s\n' "$_dev_name" "$_dev_class" "$_test_file"
|
|
447
|
+
[ -z "$_test_out" ] || printf '%s\n' "$_test_out" | sed 's/^/ /'
|
|
448
|
+
_test_fail=$((_test_fail + 1))
|
|
449
|
+
fi
|
|
450
|
+
done
|
|
451
|
+
done
|
|
452
|
+
done <"$_test_rows"
|
|
453
|
+
devtool_cleanup
|
|
454
|
+
if [ "$_test_any" -eq 0 ]; then
|
|
455
|
+
printf 'valise test: no selected module declares tests=\n'
|
|
456
|
+
[ -z "$_test_select" ] || exit 1
|
|
457
|
+
return 0
|
|
458
|
+
fi
|
|
459
|
+
printf 'valise test: %d passed, %d failed, %d skipped\n' "$_test_pass" "$_test_fail" "$_test_skip"
|
|
460
|
+
[ "$_test_fail" -eq 0 ] || exit 1
|
|
461
|
+
unset _test_shells _test_require _test_select _test_arg _test_class _test_awk _test_want _test_pass _test_fail \
|
|
462
|
+
_test_skip _test_n _test_any _test_rows _dev_name _test_mshells _test_files _test_src _dev_module \
|
|
463
|
+
_dev_module_dir _test_id _test_classes _test_rel _r_sh _r_bash _r_zsh _r_ksh _r_fish _dev_class _test_interp \
|
|
464
|
+
_test_file _test_scratch _test_runner _test_out
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
# --- run ---
|
|
468
|
+
|
|
469
|
+
cmd_run() {
|
|
470
|
+
_run_class=
|
|
471
|
+
_run_modules=
|
|
472
|
+
_run_bundle=
|
|
473
|
+
_run_variant=
|
|
474
|
+
_run_keep=0
|
|
475
|
+
while [ "$#" -gt 0 ]; do
|
|
476
|
+
case "$1" in
|
|
477
|
+
--shell=*) _run_class=${1#--shell=} ;;
|
|
478
|
+
--module=*) _run_modules="${_run_modules:+$_run_modules }${1#--module=}" ;;
|
|
479
|
+
--bundle=*) _run_bundle=${1#--bundle=} ;;
|
|
480
|
+
--variant=*) _run_variant=${1#--variant=} ;;
|
|
481
|
+
--keep-env) _run_keep=1 ;;
|
|
482
|
+
--help | -h) usage_run; return 0 ;;
|
|
483
|
+
--) shift; break ;;
|
|
484
|
+
*) die "unknown run option: $1 (the script follows --)" ;;
|
|
485
|
+
esac
|
|
486
|
+
shift
|
|
487
|
+
done
|
|
488
|
+
[ "$#" -ge 1 ] || die "usage: valise run [options] -- <script> [args...]"
|
|
489
|
+
_run_script=$1
|
|
490
|
+
shift
|
|
491
|
+
if [ -z "$_run_class" ]; then
|
|
492
|
+
case "${SHELL:-}" in
|
|
493
|
+
*/bash | bash) _run_class=bash ;;
|
|
494
|
+
*/zsh | zsh) _run_class=zsh ;;
|
|
495
|
+
*/ksh | ksh) _run_class=ksh ;;
|
|
496
|
+
*/fish | fish) _run_class=fish ;;
|
|
497
|
+
*) _run_class=sh ;;
|
|
498
|
+
esac
|
|
499
|
+
fi
|
|
500
|
+
case "$_run_class" in
|
|
501
|
+
sh | bash | zsh | ksh | fish) ;;
|
|
502
|
+
*) die "unknown shell class: $_run_class (use sh, bash, zsh, ksh, or fish)" ;;
|
|
503
|
+
esac
|
|
504
|
+
[ -z "$_run_modules" ] || [ -z "$_run_bundle$_run_variant" ] ||
|
|
505
|
+
die "--module selects the roots itself; it cannot be combined with --bundle or --variant"
|
|
506
|
+
_run_interp=$(devtool_interpreter "$_run_class") || die "no $_run_class interpreter found"
|
|
507
|
+
project_config_load
|
|
508
|
+
devtool_scratch
|
|
509
|
+
_run_rel="$_dev_tmp/release"
|
|
510
|
+
if [ -n "$_run_modules" ]; then
|
|
511
|
+
_r_sh= _r_bash= _r_zsh= _r_ksh= _r_fish=
|
|
512
|
+
eval "_r_$_run_class=\$_run_modules"
|
|
513
|
+
devtool_build "$_run_rel" "$_dev_tmp/build.log" "$_r_sh" "$_r_bash" "$_r_zsh" "$_r_ksh" "$_r_fish" || {
|
|
514
|
+
sed 's/^/ /' "$_dev_tmp/build.log" >&2
|
|
515
|
+
die "cannot build the selected modules"
|
|
516
|
+
}
|
|
517
|
+
else
|
|
518
|
+
if ! (project_build "${_run_variant:-$_project_variant}" "${_run_bundle:-$_project_bundle}" "$_run_rel" 0) \
|
|
519
|
+
>"$_dev_tmp/build.log" 2>&1; then
|
|
520
|
+
sed 's/^/ /' "$_dev_tmp/build.log" >&2
|
|
521
|
+
die "cannot build the project"
|
|
522
|
+
fi
|
|
523
|
+
[ -f "$_run_rel/plans/$_run_class.sh" ] || [ "$_run_class" = fish ] ||
|
|
524
|
+
die "the bundle has no roots for $_run_class"
|
|
525
|
+
[ "$_run_class" != fish ] || [ -d "$_run_rel/runtime/fish" ] || die "the bundle has no roots for fish"
|
|
526
|
+
fi
|
|
527
|
+
printf '%s\n' "$_run_script" >"$_dev_tmp/script" || die "cannot write the script"
|
|
528
|
+
devtool_runner "$_run_class" "$_run_rel" "$_dev_tmp/runner" "$_dev_tmp/script" || die "cannot write the runner"
|
|
529
|
+
_run_rc=0
|
|
530
|
+
if [ "$_run_keep" -eq 1 ]; then
|
|
531
|
+
"$_run_interp" "$_dev_tmp/runner" "$@" || _run_rc=$?
|
|
532
|
+
else
|
|
533
|
+
devtool_exec "$_dev_tmp/home" "$_run_interp" "$_dev_tmp/runner" "$@" || _run_rc=$?
|
|
534
|
+
fi
|
|
535
|
+
devtool_cleanup
|
|
536
|
+
unset _run_class _run_modules _run_bundle _run_variant _run_keep _run_script _run_interp _run_rel \
|
|
537
|
+
_r_sh _r_bash _r_zsh _r_ksh _r_fish
|
|
538
|
+
return "$_run_rc"
|
|
539
|
+
}
|