Skip to content

Commit a96ca47

Browse files
Merge pull request #22 from arduino/check-shell
Add CI workflow to check for problems with shell scripts
2 parents 984debd + 929273e commit a96ca47

File tree

5 files changed

+314
-1
lines changed

5 files changed

+314
-1
lines changed

.editorconfig

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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
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 "::set-output name=result::$RESULT"
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@v1
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
@@ -5,6 +5,7 @@
55
[![Check License status](https://github.com/arduino/imgtool-packing/actions/workflows/check-license.yml/badge.svg)](https://github.com/arduino/imgtool-packing/actions/workflows/check-license.yml)
66
[![Check Taskfiles status](https://github.com/arduino/imgtool-packing/actions/workflows/check-taskfiles.yml/badge.svg)](https://github.com/arduino/imgtool-packing/actions/workflows/check-taskfiles.yml)
77
[![Check Workflows status](https://github.com/arduino/imgtool-packing/actions/workflows/check-workflows-task.yml/badge.svg)](https://github.com/arduino/imgtool-packing/actions/workflows/check-workflows-task.yml)
8+
[![Check Shell Scripts status](https://github.com/arduino/imgtool-packing/actions/workflows/check-shell-task.yml/badge.svg)](https://github.com/arduino/imgtool-packing/actions/workflows/check-shell-task.yml)
89

910
This repo does not contain the source code, but only the patches, and the release workflow
1011

Taskfile.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,78 @@ tasks:
149149
else
150150
echo "{{.RAW_PATH}}"
151151
fi
152+
153+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
154+
shell:check:
155+
desc: Check for problems with shell scripts
156+
cmds:
157+
- |
158+
if ! which shellcheck &>/dev/null; then
159+
echo "shellcheck not installed or not in PATH. Please install: https://github.com/koalaman/shellcheck#installing"
160+
exit 1
161+
fi
162+
- |
163+
# There is something odd about shellcheck that causes the task to always exit on the first fail, despite any
164+
# measures that would prevent this with any other command. So it's necessary to call shellcheck only once with
165+
# the list of script paths as an argument. This could lead to exceeding the maximum command length on Windows if
166+
# the repository contained a large number of scripts, but it's unlikely to happen in reality.
167+
shellcheck \
168+
--format={{default "tty" .SHELLCHECK_FORMAT}} \
169+
$(
170+
# The odd method for escaping . in the regex is required for windows compatibility because mvdan.cc/sh gives
171+
# \ characters special treatment on Windows in an attempt to support them as path separators.
172+
find . \
173+
-type d -name '.git' -prune -or \
174+
-type d -name '.licenses' -prune -or \
175+
-type d -name '__pycache__' -prune -or \
176+
-type d -name 'node_modules' -prune -or \
177+
\( \
178+
-regextype posix-extended \
179+
-regex '.*[.](bash|sh)' -and \
180+
-type f \
181+
\) \
182+
-print
183+
)
184+
185+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
186+
shell:check-mode:
187+
desc: Check for non-executable shell scripts
188+
cmds:
189+
- |
190+
EXIT_STATUS=0
191+
while read -r nonExecutableScriptPath; do
192+
# The while loop always runs once, even if no file was found
193+
if [[ "$nonExecutableScriptPath" == "" ]]; then
194+
continue
195+
fi
196+
197+
echo "::error file=${nonExecutableScriptPath}::non-executable script file: $nonExecutableScriptPath";
198+
EXIT_STATUS=1
199+
done <<<"$(
200+
# The odd approach to escaping `.` in the regex is required for windows compatibility because mvdan.cc/sh
201+
# gives `\` characters special treatment on Windows in an attempt to support them as path separators.
202+
find . \
203+
-type d -name '.git' -prune -or \
204+
-type d -name '.licenses' -prune -or \
205+
-type d -name '__pycache__' -prune -or \
206+
-type d -name 'node_modules' -prune -or \
207+
\( \
208+
-regextype posix-extended \
209+
-regex '.*[.](bash|sh)' -and \
210+
-type f -and \
211+
-not -executable \
212+
-print \
213+
\)
214+
)"
215+
exit $EXIT_STATUS
216+
217+
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
218+
shell:format:
219+
desc: Format shell script files
220+
cmds:
221+
- |
222+
if ! which shfmt &>/dev/null; then
223+
echo "shfmt not installed or not in PATH. Please install: https://github.com/mvdan/sh#shfmt"
224+
exit 1
225+
fi
226+
- shfmt -w .

build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
apt-get update
44
apt-get install -y upx
55
python -m pip install --upgrade pip setuptools wheel
6-
cd workspace/
6+
cd workspace/ || exit
77
pip install -r requirements.txt
88
pip install pyinstaller==5.0.1
99
echo "

0 commit comments

Comments
 (0)