name: "ShellCheck" author: "Ludeeus " description: "GitHub action for ShellCheck." inputs: additional_files: description: "A space seperated list of additional filename to check" required: false default: "" ignore: description: "Paths to ignore when running ShellCheck" required: false default: "" severity: description: "Minimum severity of errors to consider. Options: [error, warning, info, style]" required: false default: "" check_together: description: "Run shellcheck on _all_ files at once, instead of one at a time" required: false default: "" scandir: description: "Directory to be searched for files. Defaults to ." required: false default: "." disable_matcher: description: "Set to true to skip using problem-matcher" required: false default: "false" outputs: files: description: A list of files with issues value: ${{ steps.filepaths.outputs.filepaths }} options: description: The options used value: ${{ steps.options.outputs.options }} branding: icon: "terminal" color: "gray-dark" runs: using: "composite" steps: - name: Enable problem-matcher shell: bash run: | if [[ ${{ inputs.disable_matcher }} != "true" ]]; then echo "::add-matcher::${{ github.action_path }}/.github/problem-matcher.json" fi - name: Download shellcheck shell: bash run: | if [[ "${{ runner.os }}" == "macOS" ]]; then osvariant="darwin" else osvariant="linux" fi scversion="stable" baseurl="https://github.com/koalaman/shellcheck/releases/download" curl -Lso "${{ github.action_path }}/sc.tar.xz" \ "${baseurl}/${scversion}/shellcheck-${scversion}.${osvariant}.x86_64.tar.xz" tar -xf "${{ github.action_path }}/sc.tar.xz" -C "${{ github.action_path }}" mv "${{ github.action_path }}/shellcheck-${scversion}/shellcheck" \ "${{ github.action_path }}/shellcheck" - name: Set options shell: bash id: options run: | declare -a options if [[ -n "${{ inputs.severity }}" ]]; then options+=("-S ${{ inputs.severity }}") fi echo "::set-output name=options::${options[@]}" - name: Gather excluded paths shell: bash id: exclude run: | declare -a excludes excludes+=("! -path \"*./.git/*\"") excludes+=("! -path \"*.go\"") excludes+=("! -path \"*/mvnw\"") for path in ${{ inputs.ignore }}; do echo "::debug:: Adding "$path" to excludes" excludes+=("! -path \"*./$path/*\"") excludes+=("! -path \"*/$path/*\"") done echo "::set-output name=excludes::${excludes[@]}" - name: Gather additional files shell: bash id: additional run: | declare -a files for file in ${{ inputs.additional_files }}; do echo "::debug:: Adding "$file" to excludes" files+=("-o -name \"*$file\"") done echo "::set-output name=files::${files[@]}" - name: Gather base file paths shell: bash id: filepaths run: | declare -a filepaths shebangregex="^#! */[^ ]*/(env *)?[abkz]*sh" for path in $(find "${{ inputs.scandir }}" \ -type f -type f ${{ steps.exclude.outputs.excludes }} \ '(' \ -name '*.bash' \ -o -name '.bashrc' \ -o -name 'bashrc' \ -o -name '.bash_aliases' \ -o -name '.bash_completion' \ -o -name '.bash_login' \ -o -name '.bash_logout' \ -o -name '.bash_profile' \ -o -name 'bash_profile' \ -o -name '*.ksh' \ -o -name 'suid_profile' \ -o -name '*.zsh' \ -o -name '.zlogin' \ -o -name 'zlogin' \ -o -name '.zlogout' \ -o -name 'zlogout' \ -o -name '.zprofile' \ -o -name 'zprofile' \ -o -name '.zsenv' \ -o -name 'zsenv' \ -o -name '.zshrc' \ -o -name 'zshrc' \ -o -name '*.sh' \ -o -path '*/.profile' \ -o -path '*/profile' \ -o -name '*.shlib' \ ${{ steps.additional.outputs.files }} \ ')'\ -print); do filepaths+=("$path"); done for file in $(find "${{ inputs.scandir }}" ${{ steps.exclude.outputs.excludes }} -type f ! -name '*.*' -perm /111 -print); do head -n1 "$file" | grep -Eqs "$shebangregex" || continue filepaths+=("$file"); done echo "::set-output name=filepaths::${filepaths[@]}" - name: Check bin subdirs shell: bash run: | if find "${{ inputs.scandir }}" ${{ steps.exclude.outputs.excludes }} -path '*bin/*/*' -type f -perm /111 -print | grep . then echo "::warning:: subdirectories of bin directories are not usable via PATH" fi - name: Check no suffix in PATH shell: bash run: | if find "${{ inputs.scandir }}" ${{ steps.exclude.outputs.excludes }} -path '*bin/*' -name '*.*' -type f -perm /111 -perm /444 -print | grep . then echo "::warning:: programs in PATH should not have a filename suffix" fi - name: Run the file check id: check shell: bash run: | statuscode=0 if [[ -n "${{ inputs.check_together }}" ]]; then "${{ github.action_path }}/shellcheck" \ ${{ steps.options.outputs.options }} \ ${{ steps.filepaths.outputs.filepaths }} || statuscode=$? else for file in ${{ steps.filepaths.outputs.filepaths }}; do echo "::debug::Checking $file" "${{ github.action_path }}/shellcheck" \ ${{ steps.options.outputs.options }} \ "$file" || statuscode=$?; done fi echo "::set-output name=statuscode::$statuscode" - name: Print information shell: bash run: | echo "Files: ${{steps.filepaths.outputs.filepaths}}" echo "Excluded: ${{ steps.exclude.outputs.excludes }}" echo "Options: ${{ steps.options.outputs.options }}" echo "Status code: ${{steps.check.outputs.statuscode}}" exit ${{steps.check.outputs.statuscode}}