This repository hosts code for running Differential ShellCheck in GitHub Actions. Idea of having something like a Differential ShellCheck was first introduced in @fedora-sysv/initscripts. Initscripts needed some way to verify incoming Pull Requests without getting warnings and errors about already merged and for years working code. Therefore, Differential ShellCheck was born.
First Differential ShellCheck gets a list of changed shell scripts based on file extensions, shebangs and script list, if provided. Then it calls @koalaman/shellcheck on those scripts where it stores ShellCheck output for later use. Then it switches from HEAD
to provided BASE
and runs ShellCheck on the same files as before and stores output to a separate file.
To evaluate results, Differential ShellCheck uses utilities csdiff
and csgrep
from @csutils/csdiff. First csdiff
is used to get a list/number of fixed and added errors. And then csgrep
is used to output the results in a nice colorized way to console and optionally into GitHub GUI as a security alert.
- Shell scripts auto-detection based on shebangs, ShellCheck directives, file extensions and more
- supported shell interpreters are:
sh
,ash
,bash
,dash
,ksh
andbats
- supported shebangs are:
#!/bin/
,#!/usr/bin/
,#!/usr/local/bin/
,#!/bin/envā£
,#!/usr/bin/envā£
and#!/usr/local/bin/envā£
; e.g.#!/bin/envā£bash
- support for ShellCheck directives ; e.g.
# shellcheck shell=bash
- support for
emacs
modes specifications ; e.g.# -*- sh -*-
- support for
vi/vim
modeline specifications ; e.g.# vi: set filetype=sh
,# vim: ft=sh
- supported shell interpreters are:
- Ability to allowlist specific error codes
- Statistics about fixed and added defects and their severity
- Colored console output with emojis
- SARIF support - warnings are visible in the
Changed files
tab of the Pull-Request and as comment alerts on Pull-Requests - Ability to run in a verbose mode when run with debug option
- Results displayed as Job Summaries - example
- Ability to configure Differential ShellCheck using
.shellcheckrc
Example of running Differential ShellCheck:
name: Differential ShellCheck
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
permissions:
# required for all workflows
security-events: write
# only required for workflows in private repositories
actions: read
contents: read
steps:
- name: Repository checkout
uses: actions/checkout@v4
with:
# Differential ShellCheck requires full git history
fetch-depth: 0
- id: ShellCheck
name: Differential ShellCheck
uses: redhat-plumbers-in-action/differential-shellcheck@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- if: always()
name: Upload artifact with ShellCheck defects in SARIF format
uses: actions/upload-artifact@v4
with:
name: Differential ShellCheck SARIF
path: ${{ steps.ShellCheck.outputs.sarif }}
Important
fetch-depth: 0
is required to run differential-shellcheck
successfully. It fetches all git history.
Example of Job Summary
Example of output in Changed files tab
Example of @github-code-scanning bot review comment
-
more examples - here
Action currently accepts following options:
# ...
- name: Differential ShellCheck
uses: redhat-plumbers-in-action/differential-shellcheck@v5
with:
triggering-event: <name of triggering event>
base: <sha1>
head: <sha1>
pull-request-base: <sha1>
pull-request-head: <sha1>
push-event-base: <sha1>
push-event-head: <sha1>
diff-scan: <true or false>
strict-check-on-push: <true or false>
external-sources: <true or false>
severity: <minimal severity level>
scan-directory: <list of paths>
exclude-path: <list of paths>
include-path: <list of paths>
token: <GitHub token>
# ...
The name of the event that triggered the workflow run. Supported values are: merge_group
, pull_request
, push
and manual
.
- default value:
${{ github.event_name }}
- requirements:
optional
SHA1
of the commit which will be used as the base when performing differential ShellCheck. Input is used only when triggering-event
is set to manual
.
- default value:
undefined
- requirements:
optional
SHA1
of the commit which refers to the HEAD
of changes. Input is used only when triggering-event
is set to manual
.
- default value:
undefined
- requirements:
optional
SHA1
of the merge group's parent commit. Input is used when triggering-event
is set to merge_group
.
- default value:
${{ github.event.merge_group.base_sha }}
- requirements:
optional
SHA1
of the merge group commit. Input is used when triggering-event
is set to merge_group
.
- default value:
${{ github.event.merge_group.head_sha }}
- requirements:
optional
SHA1
of the top commit on the base branch. Input is used when triggering-event
is set to pull_request
.
- default value:
${{ github.event.pull_request.base.sha }}
- requirements:
optional
SHA1
of the latest commit in Pull Request. Input is used when triggering-event
is set to pull_request
.
- default value:
${{ github.event.pull_request.head.sha }}
- requirements:
optional
SHA1
of the last commit before the push. Input is used when triggering-event
is set to push
.
- default value:
${{ github.event.before }}
- requirements:
optional
SHA1
of the last commit after push. Input is used when triggering-event
is set to push
.
- default value:
${{ github.event.after }}
- requirements:
optional
Input allows requesting a specific type of scan. Input is considered only if triggering-event
is set to manual
.
Default types of scans based on triggering-event
input:
triggering-event |
type of scan |
---|---|
merge_group |
differential |
pull_request |
differential |
push |
full |
manual |
based on diff-scan input |
- default value:
true
- requirements:
optional
Differential ShellCheck performs full scans when running on a push
event, but the Action fails only when new defects are added. This option allows overwriting this behavior. Hence when strict-check-on-push
is set to true
it will fail when any defect is discovered.
- default value:
false
- requirements:
optional
Enable following of source statements even when the file is not specified as input. By default, ShellCheck will only follow files specified on the command-line (plus /dev/null
). This option allows following any file the script may source. This option may also be enabled using external-sources=true
in .shellcheckrc
.
- default value:
true
- requirements:
optional
Minimal severity level of detected errors that will be reported. Valid values in order of severity are error
, warning
, info
and style
.
- default value:
style
- requirements:
optional
List of relative paths to directories that will be scanned for shell scripts. Globbing is supported. The list is a multi-line string, not a YAML list.
By default the whole repository is scanned. This feature is useful when you want to scan only a subset of the repository.
This feature is fully compatible with exclude-path and include-path options.
-
requirements:
optional
-
example:
"build/**"
-
example for multiple values:
scan-directory: | build/** testing
List of relative paths excluded from ShellCheck scanning. Globbing is supported. The list is a multi-line string, not a YAML list.
- requirements:
optional
- example:
"test/{bats,beakerlib}/**"
List of file paths that will be scanned by ShellCheck. Globbing is supported. The list is a multi-line string, not a YAML list.
- requirements:
optional
- example:
"src/**.{shell,custom}"
Tool used to display the defects and fixes in the console output. Currently supported tools are csgrep
and sarif-fmt
.
- requirements:
optional
- default value:
csgrep
The token is used to upload findings in SARIF format to GitHub.
- default value:
undefined
- requirements:
optional
The token needs to have the following permissions:
security_events: write
- required for all repositories.actions: read
andcontents: read
- required only for private repositories.
Tip
When the token
isn't passed, the SARIF file won't be uploaded (the GitHub Security Dashboard won't be updated), but the Action will work as expected. SARIF file can also be uploaded manually using sarif from outputs and github/codeql-action/upload-sarif GitHub Action.
Differential ShellCheck exposes following outputs.
Relative path to the SARIF file containing detected defects. Example of how to use sarif
output within the workflow:
- id: ShellCheck
name: Differential ShellCheck
uses: redhat-plumbers-in-action/differential-shellcheck@v5
- if: always()
name: Upload artifact with ShellCheck defects in SARIF format
uses: actions/upload-artifact@v4
with:
name: Differential ShellCheck SARIF
path: ${{ steps.ShellCheck.outputs.sarif }}
- if: always()
name: Upload SARIF to GitHub using github/codeql-action/upload-sarif
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{ steps.ShellCheck.outputs.sarif }}
Tip
sarif
output can be used together with tools like microsoft/sarif-tools
to convert SARIF to other formats like codeclimate
, csv
, docx
and more. Example of use.
Relative path to the HTML file containing detected defects. Example of how to use html
output within the workflow:
- id: ShellCheck
name: Differential ShellCheck
uses: redhat-plumbers-in-action/differential-shellcheck@v5
- if: always()
name: Upload artifact with ShellCheck defects in HTML format
uses: actions/upload-artifact@v4
with:
name: Differential ShellCheck HTML
path: ${{ steps.ShellCheck.outputs.html }}
Example of HTML output:
Differential ShellCheck GitHub Action could be used in private repositories by any user. But code scanning-related features are available only for GitHub Enterprise users, as mentioned in GitHub Documentation:
Code scanning is available for all public repositories on GitHub.com. Code scanning is also available for private repositories owned by organizations that use GitHub Enterprise Cloud and have a license for GitHub Advanced Security. For more information, see "About GitHub Advanced Security".
Differential ShellCheck doesn't have a Visual Studio Code plugin, but results can be accessed by using SARIF Viewer Visual Studio Code extension provided by Microsoft. Once installed, you have to connect your GitHub account with Visual Studio Code. Then, if you open a repository that uses Differential ShellCheck, you will see reported defects directly in your Visual Studio Code IDE.
differential-shellcheck
Action doesn't run correctly when overwriting commits using--force
and when the triggering event ispush
.
Useful documents: CHANGELOG | ARCHITECTURE | CONTRIBUTING | CODE_OF_CONDUCT | SECURITY | LICENSE