Skip to content

Commit 609f7e9

Browse files
Merge pull request #11 from MatteoPologruto/check-shell
Add CI workflow to check for problems with shell scripts
2 parents 60a5883 + cf396e3 commit 609f7e9

File tree

5 files changed

+370
-44
lines changed

5 files changed

+370
-44
lines changed

.editorconfig

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/general/.editorconfig
2+
# See: https://editorconfig.org/
3+
# The formatting style defined in this file is the official standardized style to be used in all Arduino Tooling
4+
# projects and should not be modified.
5+
# Note: indent style for each file type is defined even when it matches the universal config in order to make it clear
6+
# that this type has an official style.
7+
8+
[*]
9+
charset = utf-8
10+
end_of_line = lf
11+
indent_size = 2
12+
indent_style = space
13+
insert_final_newline = true
14+
trim_trailing_whitespace = true
15+
16+
[*.{adoc,asc,asciidoc}]
17+
indent_size = 2
18+
indent_style = space
19+
20+
[*.{bash,sh}]
21+
indent_size = 2
22+
indent_style = space
23+
24+
[*.{c,cc,cp,cpp,cxx,h,hh,hpp,hxx,ii,inl,ino,ixx,pde,tpl,tpp,txx}]
25+
indent_size = 2
26+
indent_style = space
27+
28+
[*.{go,mod}]
29+
indent_style = tab
30+
31+
[*.java]
32+
indent_size = 2
33+
indent_style = space
34+
35+
[*.{js,jsx,json,jsonc,json5,ts,tsx}]
36+
indent_size = 2
37+
indent_style = space
38+
39+
[*.{md,mdx,mkdn,mdown,markdown}]
40+
indent_size = unset
41+
indent_style = space
42+
43+
[*.proto]
44+
indent_size = 2
45+
indent_style = space
46+
47+
[*.py]
48+
indent_size = 4
49+
indent_style = space
50+
51+
[*.svg]
52+
indent_size = 2
53+
indent_style = space
54+
55+
[*.{yaml,yml}]
56+
indent_size = 2
57+
indent_style = space
58+
59+
[{.gitconfig,.gitmodules}]
60+
indent_style = tab
61+
62+
[deps/*/*]
63+
ignore = true
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
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/actions/using-workflows/events-that-trigger-workflows
5+
on:
6+
create:
7+
push:
8+
paths:
9+
- ".github/workflows/check-shell-task.ya?ml"
10+
- "Taskfile.ya?ml"
11+
- "**/.editorconfig"
12+
- "**.bash"
13+
- "**.sh"
14+
pull_request:
15+
paths:
16+
- ".github/workflows/check-shell-task.ya?ml"
17+
- "Taskfile.ya?ml"
18+
- "**/.editorconfig"
19+
- "**.bash"
20+
- "**.sh"
21+
schedule:
22+
# Run every Tuesday at 8 AM UTC to catch breakage caused by tool changes.
23+
- cron: "0 8 * * TUE"
24+
workflow_dispatch:
25+
repository_dispatch:
26+
27+
jobs:
28+
run-determination:
29+
runs-on: ubuntu-latest
30+
outputs:
31+
result: ${{ steps.determination.outputs.result }}
32+
steps:
33+
- name: Determine if the rest of the workflow should run
34+
id: determination
35+
run: |
36+
RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x"
37+
# The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead.
38+
if [[
39+
"${{ github.event_name }}" != "create" ||
40+
"${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX
41+
]]; then
42+
# Run the other jobs.
43+
RESULT="true"
44+
else
45+
# There is no need to run the other jobs.
46+
RESULT="false"
47+
fi
48+
49+
echo "result=$RESULT" >> $GITHUB_OUTPUT
50+
51+
lint:
52+
name: ${{ matrix.configuration.name }}
53+
needs: run-determination
54+
if: needs.run-determination.outputs.result == 'true'
55+
runs-on: ubuntu-latest
56+
57+
env:
58+
# See: https://github.com/koalaman/shellcheck/releases/latest
59+
SHELLCHECK_RELEASE_ASSET_SUFFIX: .linux.x86_64.tar.xz
60+
61+
strategy:
62+
fail-fast: false
63+
64+
matrix:
65+
configuration:
66+
- name: Generate problem matcher output
67+
# ShellCheck's "gcc" output format is required for annotated diffs, but inferior for humans reading the log.
68+
format: gcc
69+
# The other matrix job is used to set the result, so this job is configured to always pass.
70+
continue-on-error: true
71+
- name: ShellCheck
72+
# ShellCheck's "tty" output format is most suitable for humans reading the log.
73+
format: tty
74+
continue-on-error: false
75+
76+
steps:
77+
- name: Set environment variables
78+
run: |
79+
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
80+
echo "INSTALL_PATH=${{ runner.temp }}/shellcheck" >> "$GITHUB_ENV"
81+
82+
- name: Checkout repository
83+
uses: actions/checkout@v3
84+
85+
- name: Install Task
86+
uses: arduino/setup-task@v1
87+
with:
88+
repo-token: ${{ secrets.GITHUB_TOKEN }}
89+
version: 3.x
90+
91+
- name: Download latest ShellCheck release binary package
92+
id: download
93+
uses: MrOctopus/[email protected]
94+
with:
95+
repository: koalaman/shellcheck
96+
excludes: prerelease, draft
97+
asset: ${{ env.SHELLCHECK_RELEASE_ASSET_SUFFIX }}
98+
target: ${{ env.INSTALL_PATH }}
99+
100+
- name: Install ShellCheck
101+
run: |
102+
cd "${{ env.INSTALL_PATH }}"
103+
tar --extract --file="${{ steps.download.outputs.name }}"
104+
EXTRACTION_FOLDER="$(basename "${{ steps.download.outputs.name }}" "${{ env.SHELLCHECK_RELEASE_ASSET_SUFFIX }}")"
105+
# Add installation to PATH:
106+
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
107+
echo "${{ env.INSTALL_PATH }}/$EXTRACTION_FOLDER" >> "$GITHUB_PATH"
108+
109+
- name: Run ShellCheck
110+
uses: liskin/gh-problem-matcher-wrap@v2
111+
continue-on-error: ${{ matrix.configuration.continue-on-error }}
112+
with:
113+
linters: gcc
114+
run: task --silent shell:check SHELLCHECK_FORMAT=${{ matrix.configuration.format }}
115+
116+
formatting:
117+
needs: run-determination
118+
if: needs.run-determination.outputs.result == 'true'
119+
runs-on: ubuntu-latest
120+
121+
steps:
122+
- name: Set environment variables
123+
run: |
124+
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
125+
echo "SHFMT_INSTALL_PATH=${{ runner.temp }}/shfmt" >> "$GITHUB_ENV"
126+
127+
- name: Checkout repository
128+
uses: actions/checkout@v3
129+
130+
- name: Install Task
131+
uses: arduino/setup-task@v1
132+
with:
133+
repo-token: ${{ secrets.GITHUB_TOKEN }}
134+
version: 3.x
135+
136+
- name: Download shfmt
137+
id: download
138+
uses: MrOctopus/[email protected]
139+
with:
140+
repository: mvdan/sh
141+
excludes: prerelease, draft
142+
asset: _linux_amd64
143+
target: ${{ env.SHFMT_INSTALL_PATH }}
144+
145+
- name: Install shfmt
146+
run: |
147+
# Executable permissions of release assets are lost
148+
chmod +x "${{ env.SHFMT_INSTALL_PATH }}/${{ steps.download.outputs.name }}"
149+
# Standardize binary name
150+
mv "${{ env.SHFMT_INSTALL_PATH }}/${{ steps.download.outputs.name }}" "${{ env.SHFMT_INSTALL_PATH }}/shfmt"
151+
# Add installation to PATH:
152+
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
153+
echo "${{ env.SHFMT_INSTALL_PATH }}" >> "$GITHUB_PATH"
154+
155+
- name: Format shell scripts
156+
run: task --silent shell:format
157+
158+
- name: Check formatting
159+
run: git diff --color --exit-code
160+
161+
executable:
162+
needs: run-determination
163+
if: needs.run-determination.outputs.result == 'true'
164+
runs-on: ubuntu-latest
165+
166+
steps:
167+
- name: Checkout repository
168+
uses: actions/checkout@v3
169+
170+
- name: Install Task
171+
uses: arduino/setup-task@v1
172+
with:
173+
repo-token: ${{ secrets.GITHUB_TOKEN }}
174+
version: 3.x
175+
176+
- name: Check for non-executable scripts
177+
run: task --silent shell:check-mode

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
[![Check Markdown status](https://github.com/arduino/crossbuild/actions/workflows/check-markdown-task.yml/badge.svg)](https://github.com/arduino/crossbuild/actions/workflows/check-markdown-task.yml)
55
[![Check License status](https://github.com/arduino/crossbuild/actions/workflows/check-license.yml/badge.svg)](https://github.com/arduino/crossbuild/actions/workflows/check-license.yml)
66
[![Check Taskfiles status](https://github.com/arduino/crossbuild/actions/workflows/check-taskfiles.yml/badge.svg)](https://github.com/arduino/crossbuild/actions/workflows/check-taskfiles.yml)
7+
[![Check Shell Scripts status](https://github.com/arduino/crossbuild/actions/workflows/check-shell-task.yml/badge.svg)](https://github.com/arduino/crossbuild/actions/workflows/check-shell-task.yml)
78

89
This docker container has been created to allow us to easily crosscompile our c++ tools. The idea comes from [multiarch/crossbuild](https://github.com/multiarch/crossbuild), but that container unfortunately is outdated and the apt sources are no longer available.
910

Taskfile.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,80 @@ tasks:
8484
desc: Install dependencies managed by npm
8585
cmds:
8686
- npm install
87+
88+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
89+
shell:check:
90+
desc: Check for problems with shell scripts
91+
cmds:
92+
- |
93+
if ! which shellcheck &>/dev/null; then
94+
echo "shellcheck not installed or not in PATH. Please install: https://github.com/koalaman/shellcheck#installing"
95+
exit 1
96+
fi
97+
- |
98+
# There is something odd about shellcheck that causes the task to always exit on the first fail, despite any
99+
# measures that would prevent this with any other command. So it's necessary to call shellcheck only once with
100+
# the list of script paths as an argument. This could lead to exceeding the maximum command length on Windows if
101+
# the repository contained a large number of scripts, but it's unlikely to happen in reality.
102+
shellcheck \
103+
--format={{default "tty" .SHELLCHECK_FORMAT}} \
104+
$(
105+
# The odd method for escaping . in the regex is required for windows compatibility because mvdan.cc/sh gives
106+
# \ characters special treatment on Windows in an attempt to support them as path separators.
107+
find . \
108+
-type d -name '.git' -prune -or \
109+
-type d -name '.licenses' -prune -or \
110+
-type d -name '__pycache__' -prune -or \
111+
-type d -name 'node_modules' -prune -or \
112+
-type d -path './deps/*' -prune -or \
113+
\( \
114+
-regextype posix-extended \
115+
-regex '.*[.](bash|sh)' -and \
116+
-type f \
117+
\) \
118+
-print
119+
)
120+
121+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
122+
shell:check-mode:
123+
desc: Check for non-executable shell scripts
124+
cmds:
125+
- |
126+
EXIT_STATUS=0
127+
while read -r nonExecutableScriptPath; do
128+
# The while loop always runs once, even if no file was found
129+
if [[ "$nonExecutableScriptPath" == "" ]]; then
130+
continue
131+
fi
132+
133+
echo "::error file=${nonExecutableScriptPath}::non-executable script file: $nonExecutableScriptPath";
134+
EXIT_STATUS=1
135+
done <<<"$(
136+
# The odd approach to escaping `.` in the regex is required for windows compatibility because mvdan.cc/sh
137+
# gives `\` characters special treatment on Windows in an attempt to support them as path separators.
138+
find . \
139+
-type d -name '.git' -prune -or \
140+
-type d -name '.licenses' -prune -or \
141+
-type d -name '__pycache__' -prune -or \
142+
-type d -name 'node_modules' -prune -or \
143+
-type d -path './deps/*' -prune -or \
144+
\( \
145+
-regextype posix-extended \
146+
-regex '.*[.](bash|sh)' -and \
147+
-type f -and \
148+
-not -executable \
149+
-print \
150+
\)
151+
)"
152+
exit $EXIT_STATUS
153+
154+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
155+
shell:format:
156+
desc: Format shell script files
157+
cmds:
158+
- |
159+
if ! which shfmt &>/dev/null; then
160+
echo "shfmt not installed or not in PATH. Please install: https://github.com/mvdan/sh#shfmt"
161+
exit 1
162+
fi
163+
- shfmt -w .

0 commit comments

Comments
 (0)