Skip to content

Commit e2f30b0

Browse files
committed
Add CI workflow to check for problems with shell scripts
On every push or pull request that modifies one of the shell scripts in the repository, and periodically, the workflow: - Runs ShellCheck to detect common problems. - Runs shfmt to check formatting. - Checks for forgotten executable script file permissions.
1 parent 0f0e986 commit e2f30b0

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-shell-task.md
2+
name: Check Shell Scripts
3+
4+
# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
5+
on:
6+
push:
7+
paths:
8+
- ".github/workflows/check-shell-task.ya?ml"
9+
- "Taskfile.ya?ml"
10+
- "**/.editorconfig"
11+
- "**.bash"
12+
- "**.sh"
13+
pull_request:
14+
paths:
15+
- ".github/workflows/check-shell-task.ya?ml"
16+
- "Taskfile.ya?ml"
17+
- "**/.editorconfig"
18+
- "**.bash"
19+
- "**.sh"
20+
schedule:
21+
# Run every Tuesday at 8 AM UTC to catch breakage caused by tool changes.
22+
- cron: "0 8 * * TUE"
23+
workflow_dispatch:
24+
repository_dispatch:
25+
26+
jobs:
27+
lint:
28+
name: ${{ matrix.configuration.name }}
29+
runs-on: ubuntu-latest
30+
31+
env:
32+
# See: https://github.com/koalaman/shellcheck/releases/latest
33+
SHELLCHECK_RELEASE_ASSET_SUFFIX: .linux.x86_64.tar.xz
34+
35+
strategy:
36+
fail-fast: false
37+
38+
matrix:
39+
configuration:
40+
- name: Generate problem matcher output
41+
# ShellCheck's "gcc" output format is required for annotated diffs, but inferior for humans reading the log.
42+
format: gcc
43+
# The other matrix job is used to set the result, so this job is configured to always pass.
44+
continue-on-error: true
45+
- name: ShellCheck
46+
# ShellCheck's "tty" output format is most suitable for humans reading the log.
47+
format: tty
48+
continue-on-error: false
49+
50+
steps:
51+
- name: Set environment variables
52+
run: |
53+
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
54+
echo "INSTALL_PATH=${{ runner.temp }}/shellcheck" >> "$GITHUB_ENV"
55+
56+
- name: Checkout repository
57+
uses: actions/checkout@v2
58+
59+
- name: Install Task
60+
uses: arduino/setup-task@v1
61+
with:
62+
repo-token: ${{ secrets.GITHUB_TOKEN }}
63+
version: 3.x
64+
65+
- name: Download latest ShellCheck release binary package
66+
id: download
67+
uses: MrOctopus/[email protected]
68+
with:
69+
repository: koalaman/shellcheck
70+
excludes: prerelease, draft
71+
asset: ${{ env.SHELLCHECK_RELEASE_ASSET_SUFFIX }}
72+
target: ${{ env.INSTALL_PATH }}
73+
74+
- name: Install ShellCheck
75+
run: |
76+
cd "${{ env.INSTALL_PATH }}"
77+
tar --extract --file="${{ steps.download.outputs.name }}"
78+
EXTRACTION_FOLDER="$(basename "${{ steps.download.outputs.name }}" "${{ env.SHELLCHECK_RELEASE_ASSET_SUFFIX }}")"
79+
# Add installation to PATH:
80+
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#adding-a-system-path
81+
echo "${{ env.INSTALL_PATH }}/$EXTRACTION_FOLDER" >> "$GITHUB_PATH"
82+
83+
- name: Run ShellCheck
84+
uses: liskin/gh-problem-matcher-wrap@v1
85+
continue-on-error: ${{ matrix.configuration.continue-on-error }}
86+
with:
87+
linters: gcc
88+
run: task --silent shell:check SHELLCHECK_FORMAT=${{ matrix.configuration.format }}
89+
90+
formatting:
91+
runs-on: ubuntu-latest
92+
93+
steps:
94+
- name: Set environment variables
95+
run: |
96+
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable
97+
echo "SHFMT_INSTALL_PATH=${{ runner.temp }}/shfmt" >> "$GITHUB_ENV"
98+
99+
- name: Checkout repository
100+
uses: actions/checkout@v2
101+
102+
- name: Install Task
103+
uses: arduino/setup-task@v1
104+
with:
105+
repo-token: ${{ secrets.GITHUB_TOKEN }}
106+
version: 3.x
107+
108+
- name: Download shfmt
109+
id: download
110+
uses: MrOctopus/[email protected]
111+
with:
112+
repository: mvdan/sh
113+
excludes: prerelease, draft
114+
asset: _linux_amd64
115+
target: ${{ env.SHFMT_INSTALL_PATH }}
116+
117+
- name: Install shfmt
118+
run: |
119+
# Executable permissions of release assets are lost
120+
chmod +x "${{ env.SHFMT_INSTALL_PATH }}/${{ steps.download.outputs.name }}"
121+
# Standardize binary name
122+
mv "${{ env.SHFMT_INSTALL_PATH }}/${{ steps.download.outputs.name }}" "${{ env.SHFMT_INSTALL_PATH }}/shfmt"
123+
# Add installation to PATH:
124+
# See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#adding-a-system-path
125+
echo "${{ env.SHFMT_INSTALL_PATH }}" >> "$GITHUB_PATH"
126+
127+
- name: Format shell scripts
128+
run: task --silent shell:format
129+
130+
- name: Check formatting
131+
run: git diff --color --exit-code
132+
133+
executable:
134+
runs-on: ubuntu-latest
135+
136+
steps:
137+
- name: Checkout repository
138+
uses: actions/checkout@v2
139+
140+
- name: Install Task
141+
uses: arduino/setup-task@v1
142+
with:
143+
repo-token: ${{ secrets.GITHUB_TOKEN }}
144+
version: 3.x
145+
146+
- name: Check for non-executable scripts
147+
run: task --silent shell:check-mode

Taskfile.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,71 @@ tasks:
1818
desc: Format all supported files with Prettier
1919
cmds:
2020
- npx prettier --write .
21+
22+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
23+
shell:check:
24+
desc: Check for problems with shell scripts
25+
cmds:
26+
- |
27+
if ! which shellcheck &>/dev/null; then
28+
echo "shellcheck not installed or not in PATH. Please install: https://github.com/koalaman/shellcheck#installing"
29+
exit 1
30+
fi
31+
- |
32+
# There is something odd about shellcheck that causes the task to always exit on the first fail, despite any
33+
# measures that would prevent this with any other command. So it's necessary to call shellcheck only once with
34+
# the list of script paths as an argument. This could lead to exceeding the maximum command length on Windows if
35+
# the repository contained a large number of scripts, but it's unlikely to happen in reality.
36+
shellcheck \
37+
--format={{default "tty" .SHELLCHECK_FORMAT}} \
38+
$(
39+
# The odd method for escaping . in the regex is required for windows compatibility because mvdan.cc/sh gives
40+
# \ characters special treatment on Windows in an attempt to support them as path separators.
41+
find . \
42+
-path ".git" -prune -or \
43+
\( \
44+
-regextype posix-extended \
45+
-regex '.*[.](bash|sh)' -and \
46+
-type f \
47+
\)
48+
)
49+
50+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
51+
shell:check-mode:
52+
desc: Check for non-executable shell scripts
53+
cmds:
54+
- |
55+
EXIT_STATUS=0
56+
while read -r nonExecutableScriptPath; do
57+
# The while loop always runs once, even if no file was found
58+
if [[ "$nonExecutableScriptPath" == "" ]]; then
59+
continue
60+
fi
61+
62+
echo "::error file=${nonExecutableScriptPath}::non-executable script file: $nonExecutableScriptPath";
63+
EXIT_STATUS=1
64+
done <<<"$(
65+
# The odd approach to escaping `.` in the regex is required for windows compatibility because mvdan.cc/sh
66+
# gives `\` characters special treatment on Windows in an attempt to support them as path separators.
67+
find . \
68+
-path ".git" -prune -or \
69+
\( \
70+
-regextype posix-extended \
71+
-regex '.*[.](bash|sh)' -and \
72+
-type f -and \
73+
-not -executable \
74+
-print \
75+
\)
76+
)"
77+
exit $EXIT_STATUS
78+
79+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
80+
shell:format:
81+
desc: Format shell script files
82+
cmds:
83+
- |
84+
if ! which shfmt &>/dev/null; then
85+
echo "shfmt not installed or not in PATH. Please install: https://github.com/mvdan/sh#shfmt"
86+
exit 1
87+
fi
88+
- shfmt -w .

0 commit comments

Comments
 (0)