Skip to content

Commit ad800ef

Browse files
committed
Target specific scripts in "Check Scripts" template
Background ---------- Previously, the approach taken shell script tasks was to recursively search the entire repository for shell scripts. There were several problems with that system: The `-regextype` flag used in the `find` commands of the `shell:check` and `shell:check-mode` tasks is not supported by the BSD/macOS version of `find`, meaning the tasks would fail if a contributor using a macOS machine tried to run them: ``` find: -regextype: unknown primary or operator ``` The `shell:check` and `shell:check-mode` tasks only provided coverage for files with `.sh` and `.bash` file extensions, whereas the practice of omitting a file extension on shell scripts is unfortunately quite common. There was no obvious way to exclude paths of externally maintained files from coverage by the `shell:format` task. Alternative Solutions --------------------- The first of the problems listed above could be overcome by configuring the tasks to adjust the `find` commands to use different flags depending on the operating system of the machine. The second could be overcome by using `shfmt --files` (which searches recursively for shell scripts by checking for the presence of a shebang inside files and outputs a list of the paths) as a replacement for `find`. The third could be overcome by documenting the usage of shfmt's poorly advertised feature of ignoring the paths assigned an `ignore` attribute in the .editorconfig file. Chosen Solution --------------- After careful consideration, the decision was made to abandon the previous approach of attempting to automatically discover script files and instead instead use the strategy of configuring the template with the specific paths of the scripts to be checked for each project it is installed into. Although such an approach would not be appropriate in the case of a check on files that tend to be present in greater abundance and regularly added and moved in a project, this is not the case for shell scripts in Arduino projects. A project is more likely to contain a few scripts at most and their paths tend to be reasonably stable. Code Duplication in Workflow ---------------------------- In order to make it easier to interpret results, navigate logs, and reduce duration of workflow runs, a separate workflow job is used for each of the distinct checks. Unfortunately it is necessary for the project maintainer to configure the script paths redundantly in the matrix of each of the three jobs. Intuitively we would expect this could be avoided by defining the paths at workflow scope via the env key. However, this is not feasible due to the env context not being available for use in the jobs.<job name>.strategy.matrix key. It is technically possible to accomplish this by adding a job that converts the data from the `env` context into a job output (the `needs` context is available for use in the `matrix` key). However the significant increase in complexity this would bring to the workflow outweighs the benefit of avoiding duplication.
1 parent 4fb2baa commit ad800ef

File tree

2 files changed

+60
-43
lines changed

2 files changed

+60
-43
lines changed

Diff for: .github/workflows/check-shell-task.yml

+33-4
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
echo "result=$RESULT" >> $GITHUB_OUTPUT
5151
5252
lint:
53-
name: ${{ matrix.configuration.name }}
53+
name: ${{ matrix.configuration.name }} (${{ matrix.script }})
5454
needs: run-determination
5555
if: needs.run-determination.outputs.result == 'true'
5656
runs-on: ubuntu-latest
@@ -75,6 +75,8 @@ jobs:
7575
# ShellCheck's "tty" output format is most suitable for humans reading the log.
7676
format: tty
7777
continue-on-error: false
78+
script:
79+
- etc/install.sh
7880

7981
steps:
8082
- name: Set environment variables
@@ -114,15 +116,26 @@ jobs:
114116
continue-on-error: ${{ matrix.configuration.continue-on-error }}
115117
with:
116118
linters: gcc
117-
run: task --silent shell:check SHELLCHECK_FORMAT=${{ matrix.configuration.format }}
119+
# Due to a quirk of the "liskin/gh-problem-matcher-wrap" action, the entire command must be on a single line
120+
# (instead of being broken into multiple lines for readability).
121+
run: |
122+
task --silent shell:check SCRIPT_PATH="${{ matrix.script }}" SHELLCHECK_FORMAT=${{ matrix.configuration.format }}
118123
119124
formatting:
125+
name: formatting (${{ matrix.script }})
120126
needs: run-determination
121127
if: needs.run-determination.outputs.result == 'true'
122128
runs-on: ubuntu-latest
123129
permissions:
124130
contents: read
125131

132+
strategy:
133+
fail-fast: false
134+
135+
matrix:
136+
script:
137+
- etc/install.sh
138+
126139
steps:
127140
- name: Set environment variables
128141
run: |
@@ -158,18 +171,30 @@ jobs:
158171
echo "${{ env.SHFMT_INSTALL_PATH }}" >> "$GITHUB_PATH"
159172
160173
- name: Format shell scripts
161-
run: task --silent shell:format
174+
run: |
175+
task \
176+
--silent \
177+
shell:format \
178+
SCRIPT_PATH="${{ matrix.script }}"
162179
163180
- name: Check formatting
164181
run: git diff --color --exit-code
165182

166183
executable:
184+
name: executable (${{ matrix.script }})
167185
needs: run-determination
168186
if: needs.run-determination.outputs.result == 'true'
169187
runs-on: ubuntu-latest
170188
permissions:
171189
contents: read
172190

191+
strategy:
192+
fail-fast: false
193+
194+
matrix:
195+
script:
196+
- etc/install.sh
197+
173198
steps:
174199
- name: Checkout repository
175200
uses: actions/checkout@v4
@@ -181,4 +206,8 @@ jobs:
181206
version: 3.x
182207

183208
- name: Check for non-executable scripts
184-
run: task --silent shell:check-mode
209+
run: |
210+
task \
211+
--silent \
212+
shell:check-mode \
213+
SCRIPT_PATH="${{ matrix.script }}"

Diff for: Taskfile.yml

+27-39
Original file line numberDiff line numberDiff line change
@@ -396,73 +396,61 @@ tasks:
396396
cmds:
397397
- poetry run flake8 --show-source
398398

399+
# Parameter variables:
400+
# - SCRIPT_PATH: path of the script to be checked.
399401
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
400402
shell:check:
401403
desc: Check for problems with shell scripts
402404
cmds:
405+
- |
406+
if [[ "{{.SCRIPT_PATH}}" == "" ]]; then
407+
echo "Path to script file must be passed to this task via the SCRIPT_PATH taskfile variable."
408+
echo "See: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-shell-task.md#usage"
409+
exit 1
410+
fi
403411
- |
404412
if ! which shellcheck &>/dev/null; then
405413
echo "shellcheck not installed or not in PATH. Please install: https://github.com/koalaman/shellcheck#installing"
406414
exit 1
407415
fi
408416
- |
409-
# There is something odd about shellcheck that causes the task to always exit on the first fail, despite any
410-
# measures that would prevent this with any other command. So it's necessary to call shellcheck only once with
411-
# the list of script paths as an argument. This could lead to exceeding the maximum command length on Windows if
412-
# the repository contained a large number of scripts, but it's unlikely to happen in reality.
413417
shellcheck \
414418
--format={{default "tty" .SHELLCHECK_FORMAT}} \
415-
$(
416-
# The odd method for escaping . in the regex is required for windows compatibility because mvdan.cc/sh gives
417-
# \ characters special treatment on Windows in an attempt to support them as path separators.
418-
find . \
419-
-path ".git" -prune -or \
420-
\( \
421-
-regextype posix-extended \
422-
-regex '.*[.](bash|sh)' -and \
423-
-type f \
424-
\)
425-
)
419+
"{{.SCRIPT_PATH}}"
426420
421+
# Parameter variables:
422+
# - SCRIPT_PATH: path of the script to be checked.
427423
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
428424
shell:check-mode:
429425
desc: Check for non-executable shell scripts
430426
cmds:
431427
- |
432-
EXIT_STATUS=0
433-
while read -r nonExecutableScriptPath; do
434-
# The while loop always runs once, even if no file was found
435-
if [[ "$nonExecutableScriptPath" == "" ]]; then
436-
continue
437-
fi
438-
439-
echo "::error file=${nonExecutableScriptPath}::non-executable script file: $nonExecutableScriptPath";
440-
EXIT_STATUS=1
441-
done <<<"$(
442-
# The odd approach to escaping `.` in the regex is required for windows compatibility because mvdan.cc/sh
443-
# gives `\` characters special treatment on Windows in an attempt to support them as path separators.
444-
find . \
445-
-path ".git" -prune -or \
446-
\( \
447-
-regextype posix-extended \
448-
-regex '.*[.](bash|sh)' -and \
449-
-type f -and \
450-
-not -executable \
451-
-print \
452-
\)
453-
)"
454-
exit $EXIT_STATUS
428+
if [[ "{{.SCRIPT_PATH}}" == "" ]]; then
429+
echo "Path to script file must be passed to this task via the SCRIPT_PATH taskfile variable."
430+
echo "See: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-shell-task.md#usage"
431+
exit 1
432+
fi
433+
- |
434+
test -x "{{.SCRIPT_PATH}}"
455435
436+
# Parameter variables:
437+
# - SCRIPT_PATH: path of the script to be formatted.
456438
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
457439
shell:format:
458440
desc: Format shell script files
459441
cmds:
442+
- |
443+
if [[ "{{.SCRIPT_PATH}}" == "" ]]; then
444+
echo "Path to script file must be passed to this task via the SCRIPT_PATH taskfile variable."
445+
echo "See: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-shell-task.md#usage"
446+
exit 1
447+
fi
460448
- |
461449
if ! which shfmt &>/dev/null; then
462450
echo "shfmt not installed or not in PATH. Please install: https://github.com/mvdan/sh#shfmt"
463451
exit 1
464452
fi
465-
- shfmt -w .
453+
- shfmt -w "{{.SCRIPT_PATH}}"
466454

467455
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-mkdocs-task/Taskfile.yml
468456
website:check:

0 commit comments

Comments
 (0)