Skip to content

Commit 6a65d3a

Browse files
authored
feat: improve zsh scripts support (#7674)
- Enable formatting ZSH scripts with the .zsh extension using shfmt - Enable formatting ZSH scripts without extension using shfmt - Enable checking ZSH scripts with or without extension using bash-exec - Don't lint ZSH scripts without extension with shellcheck Close #7618
1 parent eb8ddc7 commit 6a65d3a

9 files changed

Lines changed: 151 additions & 12 deletions

File tree

lib/functions/buildFileList.sh

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,25 @@ AddToPythonFileArrays() {
284284
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-PYTHON_RUFF_FORMAT"
285285
}
286286

287+
AddToShfmtFileArray() {
288+
local FILE="${1}"
289+
debug "Adding ${FILE} to SHELL_SHFMT file array"
290+
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-SHELL_SHFMT"
291+
}
292+
293+
AddToBashExecFileArray() {
294+
local FILE="${1}"
295+
debug "Adding ${FILE} to BASH_EXEC file array"
296+
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-BASH_EXEC"
297+
}
298+
287299
AddToShellFileArrays() {
288300
local FILE="${1}"
301+
debug "Adding ${FILE} to shell file arrays"
289302

290303
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-BASH"
291-
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-BASH_EXEC"
292-
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-SHELL_SHFMT"
304+
AddToBashExecFileArray "${FILE}"
305+
AddToShfmtFileArray "${FILE}"
293306
}
294307

295308
AddToPerlFileArrays() {
@@ -308,12 +321,18 @@ CheckFileType() {
308321
local FILE
309322
FILE="$1"
310323

324+
local FILE_EXTENSION
325+
if ! FILE_EXTENSION="$(GetFileExtension "$FILE" 2>&1)"; then
326+
error "Error while getting the file extension for $FILE: $FILE_EXTENSION"
327+
return 1
328+
fi
329+
311330
local GET_FILE_TYPE_CMD
312331
if ! GET_FILE_TYPE_CMD="$(file --brief "${FILE}" 2>&1)"; then
313332
error "Error while checking file type: ${GET_FILE_TYPE_CMD}"
314333
return 1
315334
fi
316-
debug "Detected file type for ${FILE}: ${GET_FILE_TYPE_CMD}"
335+
debug "Detected file type for ${FILE}: ${GET_FILE_TYPE_CMD}. File extension: ${FILE_EXTENSION:-"not set"}"
317336

318337
local FILE_TYPE_MESSAGE
319338

@@ -331,12 +350,24 @@ CheckFileType() {
331350
AddToRubyFileArrays "${FILE}"
332351
;;
333352
*"zsh script"*)
334-
FILE_TYPE_MESSAGE="Found a ZSH script without extension: ${FILE}"
353+
FILE_TYPE_MESSAGE="Found a ZSH script: ${FILE}"
354+
AddToBashExecFileArray "${FILE}"
355+
AddToShfmtFileArray "${FILE}"
335356
;;
336357
*"POSIX shell script"* | *"Bourne-Again shell script"* | *"Dash shell script"* | *"Korn shell script"* | *"sh script"*)
337-
FILE_TYPE_MESSAGE="Found a Shell script without extension: ${FILE}"
358+
FILE_TYPE_MESSAGE="Found a Shell script: ${FILE}"
338359
AddToShellFileArrays "${FILE}"
339360
;;
361+
*"ASCII text"*)
362+
FILE_TYPE_MESSAGE="Found ASCII text: ${FILE}"
363+
# Check if the file is a shell script with the sh extension, without shebang
364+
if [[ "$FILE_EXTENSION" == "sh" ]]; then
365+
FILE_TYPE_MESSAGE="Found a shell script without shebang: ${FILE}"
366+
AddToShellFileArrays "${FILE}"
367+
else
368+
return 1
369+
fi
370+
;;
340371
*)
341372
FILE_TYPE_MESSAGE="Failed to get file type for: ${FILE}. Output: ${GET_FILE_TYPE_CMD}"
342373
return 1
@@ -528,12 +559,17 @@ BuildFileArrays() {
528559

529560
# Select files by extension or file name
530561

531-
if [ "${FILE_TYPE}" == "sh" ] ||
532-
[ "${FILE_TYPE}" == "bash" ] ||
562+
# Don't include .sh scripts so they fall in the 'else' clause where we
563+
# dynamically check their file type to avoid that we add ZSH scripts when
564+
# they are not supported.
565+
if [ "${FILE_TYPE}" == "bash" ] ||
533566
[ "${FILE_TYPE}" == "bats" ] ||
534567
[ "${FILE_TYPE}" == "dash" ] ||
535568
[ "${FILE_TYPE}" == "ksh" ]; then
536569
AddToShellFileArrays "${FILE}"
570+
elif [ "${FILE_TYPE}" == "zsh" ]; then
571+
AddToBashExecFileArray "${FILE}"
572+
AddToShfmtFileArray "${FILE}"
537573
elif [ "${FILE_TYPE}" == "clj" ] || [ "${FILE_TYPE}" == "cljs" ] ||
538574
[ "${FILE_TYPE}" == "cljc" ] || [ "${FILE_TYPE}" == "edn" ]; then # codespell:ignore edn
539575
echo "${FILE}" >>"${FILE_ARRAYS_DIRECTORY_PATH}/file-array-CLOJURE"
@@ -813,9 +849,11 @@ BuildFileArrays() {
813849
}
814850

815851
# We need so subprocesses (such as GNU Parallel) have access to these functions
816-
export -f AddToPythonFileArrays
817-
export -f AddToShellFileArrays
852+
export -f AddToBashExecFileArray
818853
export -f AddToPerlFileArrays
854+
export -f AddToPythonFileArrays
819855
export -f AddToRubyFileArrays
856+
export -f AddToShellFileArrays
857+
export -f AddToShfmtFileArray
820858
export -f BuildFileArrays
821859
export -f CheckFileType

test/lib/buildFileListTest.sh

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ CheckFileTypeTest() {
480480
echo "#!/usr/bin/env ksh" >"${ENV_KSH_SCRIPT_PATH}"
481481
chmod +x "${ENV_KSH_SCRIPT_PATH}"
482482

483+
local SHELL_SCRIPT_WITHOUT_SHEBANG_PATH="${GITHUB_WORKSPACE}/shell-script-no-shebang.sh"
484+
echo "echo 'hello'" >"${SHELL_SCRIPT_WITHOUT_SHEBANG_PATH}"
485+
chmod +x "${SHELL_SCRIPT_WITHOUT_SHEBANG_PATH}"
486+
483487
local UNKNOWN_FILE_PATH="${GITHUB_WORKSPACE}/unknown-file"
484488
echo "some text" >"${UNKNOWN_FILE_PATH}"
485489

@@ -496,6 +500,7 @@ CheckFileTypeTest() {
496500
"${ENV_BASH_SCRIPT_PATH}"
497501
"${ENV_DASH_SCRIPT_PATH}"
498502
"${ENV_KSH_SCRIPT_PATH}"
503+
"${SHELL_SCRIPT_WITHOUT_SHEBANG_PATH}"
499504
)
500505

501506
# Run CheckFileType on created files
@@ -536,8 +541,15 @@ CheckFileTypeTest() {
536541

537542
# Assertions for ZSH shell scripts
538543

539-
if AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${ZSH_SCRIPT_PATH}"; then
540-
fatal "${ZSH_SCRIPT_PATH} should NOT be in file-array-${shell_language}"
544+
# bash-exec supports ZSH scripts
545+
# shfmt supports ZSH scripts on versions >= 3.13.0
546+
if [[ "${shell_language}" == "BASH_EXEC" ]] ||
547+
[[ "${shell_language}" == "SHELL_SHFMT" ]]; then
548+
AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${ZSH_SCRIPT_PATH}"
549+
else
550+
if AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${ZSH_SCRIPT_PATH}"; then
551+
fatal "${ZSH_SCRIPT_PATH} should NOT be in file-array-${shell_language}"
552+
fi
541553
fi
542554

543555
# Assertions for all Shell scripts (direct and env variants)
@@ -550,6 +562,7 @@ CheckFileTypeTest() {
550562
AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${ENV_BASH_SCRIPT_PATH}"
551563
AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${ENV_DASH_SCRIPT_PATH}"
552564
AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${ENV_KSH_SCRIPT_PATH}"
565+
AssertFileContains "${FILE_ARRAYS_DIRECTORY_PATH}/file-array-${shell_language}" "${SHELL_SCRIPT_WITHOUT_SHEBANG_PATH}"
553566
done
554567

555568
unset SUPPRESS_FILE_TYPE_WARN

test/linters/bash/shell_good_2.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env zsh
2+
3+
# This script should be ignored by Super-linter because shellcheck doesn't
4+
# support ZSH scripts
5+
6+
# Make sure that the terminal is in application mode when zle is active, since
7+
# only then values from $terminfo are valid
8+
if ((${+terminfo[smkx]})) && ((${+terminfo[rmkx]})); then
9+
function zle-line-init() {
10+
echoti smkx
11+
}
12+
function zle-line-finish() {
13+
echoti rmkx
14+
}
15+
zle -N zle-line-init
16+
zle -N zle-line-finish
17+
fi

test/linters/bash/shell_good_3.zsh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/usr/bin/env zsh
2+
3+
# This script should be ignored by Super-linter because shellcheck doesn't
4+
# support ZSH scripts
5+
6+
if ((${+terminfo[smkx]})) && ((${+terminfo[rmkx]})); then
7+
function zle-line-init() {
8+
echoti smkx
9+
}
10+
function zle-line-finish() {
11+
echoti rmkx
12+
}
13+
zle -N zle-line-init
14+
zle -N zle-line-finish
15+
fi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env zsh
2+
3+
# Make sure that the terminal is in application mode when zle is active, since
4+
# only then values from $terminfo are valid
5+
if ((${+terminfo[smkx]})) && ((${+terminfo[rmkx]})); then
6+
function zle-line-init() {
7+
echoti smkx
8+
}
9+
function zle-line-finish() {
10+
echoti rmkx
11+
}
12+
zle -N zle-line-init
13+
zle -N zle-line-finish
14+
fi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env zsh
2+
3+
# Make sure that the terminal is in application mode when zle is active, since
4+
# only then values from $terminfo are valid
5+
if ((${+terminfo[smkx]})) && ((${+terminfo[rmkx]})); then
6+
function zle-line-init() {
7+
echoti smkx
8+
}
9+
function zle-line-finish() {
10+
echoti rmkx
11+
}
12+
zle -N zle-line-init
13+
zle -N zle-line-finish
14+
fi
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# top-most EditorConfig file
22
root = true
33

4-
[*.sh]
4+
[*.{sh,zsh}]
55
indent_style = space
66
indent_size = 4
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env zsh
2+
3+
# Make sure that the terminal is in application mode when zle is active, since
4+
# only then values from $terminfo are valid
5+
if ((${+terminfo[smkx]})) && ((${+terminfo[rmkx]})); then
6+
function zle-line-init() {
7+
echoti smkx
8+
}
9+
function zle-line-finish() {
10+
echoti rmkx
11+
}
12+
zle -N zle-line-init
13+
zle -N zle-line-finish
14+
fi
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env zsh
2+
3+
# Make sure that the terminal is in application mode when zle is active, since
4+
# only then values from $terminfo are valid
5+
if ((${+terminfo[smkx]})) && ((${+terminfo[rmkx]})); then
6+
function zle-line-init() {
7+
echoti smkx
8+
}
9+
function zle-line-finish() {
10+
echoti rmkx
11+
}
12+
zle -N zle-line-init
13+
zle -N zle-line-finish
14+
fi

0 commit comments

Comments
 (0)