Skip to content

Commit

Permalink
Merge pull request #11 from MatteoPologruto/check-shell
Browse files Browse the repository at this point in the history
Add CI workflow to check for problems with shell scripts
  • Loading branch information
MatteoPologruto authored Dec 19, 2022
2 parents 60a5883 + cf396e3 commit 609f7e9
Show file tree
Hide file tree
Showing 5 changed files with 370 additions and 44 deletions.
63 changes: 63 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/general/.editorconfig
# See: https://editorconfig.org/
# The formatting style defined in this file is the official standardized style to be used in all Arduino Tooling
# projects and should not be modified.
# Note: indent style for each file type is defined even when it matches the universal config in order to make it clear
# that this type has an official style.

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[*.{adoc,asc,asciidoc}]
indent_size = 2
indent_style = space

[*.{bash,sh}]
indent_size = 2
indent_style = space

[*.{c,cc,cp,cpp,cxx,h,hh,hpp,hxx,ii,inl,ino,ixx,pde,tpl,tpp,txx}]
indent_size = 2
indent_style = space

[*.{go,mod}]
indent_style = tab

[*.java]
indent_size = 2
indent_style = space

[*.{js,jsx,json,jsonc,json5,ts,tsx}]
indent_size = 2
indent_style = space

[*.{md,mdx,mkdn,mdown,markdown}]
indent_size = unset
indent_style = space

[*.proto]
indent_size = 2
indent_style = space

[*.py]
indent_size = 4
indent_style = space

[*.svg]
indent_size = 2
indent_style = space

[*.{yaml,yml}]
indent_size = 2
indent_style = space

[{.gitconfig,.gitmodules}]
indent_style = tab

[deps/*/*]
ignore = true
177 changes: 177 additions & 0 deletions .github/workflows/check-shell-task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/check-shell-task.md
name: Check Shell Scripts

# See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows
on:
create:
push:
paths:
- ".github/workflows/check-shell-task.ya?ml"
- "Taskfile.ya?ml"
- "**/.editorconfig"
- "**.bash"
- "**.sh"
pull_request:
paths:
- ".github/workflows/check-shell-task.ya?ml"
- "Taskfile.ya?ml"
- "**/.editorconfig"
- "**.bash"
- "**.sh"
schedule:
# Run every Tuesday at 8 AM UTC to catch breakage caused by tool changes.
- cron: "0 8 * * TUE"
workflow_dispatch:
repository_dispatch:

jobs:
run-determination:
runs-on: ubuntu-latest
outputs:
result: ${{ steps.determination.outputs.result }}
steps:
- name: Determine if the rest of the workflow should run
id: determination
run: |
RELEASE_BRANCH_REGEX="refs/heads/[0-9]+.[0-9]+.x"
# The `create` event trigger doesn't support `branches` filters, so it's necessary to use Bash instead.
if [[
"${{ github.event_name }}" != "create" ||
"${{ github.ref }}" =~ $RELEASE_BRANCH_REGEX
]]; then
# Run the other jobs.
RESULT="true"
else
# There is no need to run the other jobs.
RESULT="false"
fi
echo "result=$RESULT" >> $GITHUB_OUTPUT
lint:
name: ${{ matrix.configuration.name }}
needs: run-determination
if: needs.run-determination.outputs.result == 'true'
runs-on: ubuntu-latest

env:
# See: https://github.com/koalaman/shellcheck/releases/latest
SHELLCHECK_RELEASE_ASSET_SUFFIX: .linux.x86_64.tar.xz

strategy:
fail-fast: false

matrix:
configuration:
- name: Generate problem matcher output
# ShellCheck's "gcc" output format is required for annotated diffs, but inferior for humans reading the log.
format: gcc
# The other matrix job is used to set the result, so this job is configured to always pass.
continue-on-error: true
- name: ShellCheck
# ShellCheck's "tty" output format is most suitable for humans reading the log.
format: tty
continue-on-error: false

steps:
- name: Set environment variables
run: |
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
echo "INSTALL_PATH=${{ runner.temp }}/shellcheck" >> "$GITHUB_ENV"
- name: Checkout repository
uses: actions/checkout@v3

- name: Install Task
uses: arduino/setup-task@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Download latest ShellCheck release binary package
id: download
uses: MrOctopus/download-asset-action@1.0
with:
repository: koalaman/shellcheck
excludes: prerelease, draft
asset: ${{ env.SHELLCHECK_RELEASE_ASSET_SUFFIX }}
target: ${{ env.INSTALL_PATH }}

- name: Install ShellCheck
run: |
cd "${{ env.INSTALL_PATH }}"
tar --extract --file="${{ steps.download.outputs.name }}"
EXTRACTION_FOLDER="$(basename "${{ steps.download.outputs.name }}" "${{ env.SHELLCHECK_RELEASE_ASSET_SUFFIX }}")"
# Add installation to PATH:
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
echo "${{ env.INSTALL_PATH }}/$EXTRACTION_FOLDER" >> "$GITHUB_PATH"
- name: Run ShellCheck
uses: liskin/gh-problem-matcher-wrap@v2
continue-on-error: ${{ matrix.configuration.continue-on-error }}
with:
linters: gcc
run: task --silent shell:check SHELLCHECK_FORMAT=${{ matrix.configuration.format }}

formatting:
needs: run-determination
if: needs.run-determination.outputs.result == 'true'
runs-on: ubuntu-latest

steps:
- name: Set environment variables
run: |
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable
echo "SHFMT_INSTALL_PATH=${{ runner.temp }}/shfmt" >> "$GITHUB_ENV"
- name: Checkout repository
uses: actions/checkout@v3

- name: Install Task
uses: arduino/setup-task@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Download shfmt
id: download
uses: MrOctopus/download-asset-action@1.0
with:
repository: mvdan/sh
excludes: prerelease, draft
asset: _linux_amd64
target: ${{ env.SHFMT_INSTALL_PATH }}

- name: Install shfmt
run: |
# Executable permissions of release assets are lost
chmod +x "${{ env.SHFMT_INSTALL_PATH }}/${{ steps.download.outputs.name }}"
# Standardize binary name
mv "${{ env.SHFMT_INSTALL_PATH }}/${{ steps.download.outputs.name }}" "${{ env.SHFMT_INSTALL_PATH }}/shfmt"
# Add installation to PATH:
# See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-system-path
echo "${{ env.SHFMT_INSTALL_PATH }}" >> "$GITHUB_PATH"
- name: Format shell scripts
run: task --silent shell:format

- name: Check formatting
run: git diff --color --exit-code

executable:
needs: run-determination
if: needs.run-determination.outputs.result == 'true'
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Install Task
uses: arduino/setup-task@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
version: 3.x

- name: Check for non-executable scripts
run: task --silent shell:check-mode
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
[![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)
[![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)
[![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)
[![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)

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.

Expand Down
77 changes: 77 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,80 @@ tasks:
desc: Install dependencies managed by npm
cmds:
- npm install

# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
shell:check:
desc: Check for problems with shell scripts
cmds:
- |
if ! which shellcheck &>/dev/null; then
echo "shellcheck not installed or not in PATH. Please install: https://github.com/koalaman/shellcheck#installing"
exit 1
fi
- |
# There is something odd about shellcheck that causes the task to always exit on the first fail, despite any
# measures that would prevent this with any other command. So it's necessary to call shellcheck only once with
# the list of script paths as an argument. This could lead to exceeding the maximum command length on Windows if
# the repository contained a large number of scripts, but it's unlikely to happen in reality.
shellcheck \
--format={{default "tty" .SHELLCHECK_FORMAT}} \
$(
# The odd method for escaping . in the regex is required for windows compatibility because mvdan.cc/sh gives
# \ characters special treatment on Windows in an attempt to support them as path separators.
find . \
-type d -name '.git' -prune -or \
-type d -name '.licenses' -prune -or \
-type d -name '__pycache__' -prune -or \
-type d -name 'node_modules' -prune -or \
-type d -path './deps/*' -prune -or \
\( \
-regextype posix-extended \
-regex '.*[.](bash|sh)' -and \
-type f \
\) \
-print
)
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
shell:check-mode:
desc: Check for non-executable shell scripts
cmds:
- |
EXIT_STATUS=0
while read -r nonExecutableScriptPath; do
# The while loop always runs once, even if no file was found
if [[ "$nonExecutableScriptPath" == "" ]]; then
continue
fi
echo "::error file=${nonExecutableScriptPath}::non-executable script file: $nonExecutableScriptPath";
EXIT_STATUS=1
done <<<"$(
# The odd approach to escaping `.` in the regex is required for windows compatibility because mvdan.cc/sh
# gives `\` characters special treatment on Windows in an attempt to support them as path separators.
find . \
-type d -name '.git' -prune -or \
-type d -name '.licenses' -prune -or \
-type d -name '__pycache__' -prune -or \
-type d -name 'node_modules' -prune -or \
-type d -path './deps/*' -prune -or \
\( \
-regextype posix-extended \
-regex '.*[.](bash|sh)' -and \
-type f -and \
-not -executable \
-print \
\)
)"
exit $EXIT_STATUS
# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/check-shell-task/Taskfile.yml
shell:format:
desc: Format shell script files
cmds:
- |
if ! which shfmt &>/dev/null; then
echo "shfmt not installed or not in PATH. Please install: https://github.com/mvdan/sh#shfmt"
exit 1
fi
- shfmt -w .
Loading

0 comments on commit 609f7e9

Please sign in to comment.