├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── issue.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── check_together.yml │ ├── ignore_names.yml │ ├── additional_files.yml │ ├── scandir.yml │ └── ignore_paths.yml ├── testfiles ├── scandir │ ├── run │ ├── discovery │ ├── test.bash │ ├── finish │ └── run me.bash ├── test ├── test.bash ├── test.sh ├── duplicate_name.bash ├── ignore │ └── ignore.bash ├── ignore_single_file.sh ├── ignore_some │ ├── ignore.bash │ ├── do_not_ignore.bash │ └── duplicate_name.bash └── bashfile.c ├── .devcontainer.json ├── LICENSE ├── README.md └── action.yaml /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /testfiles/scandir/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "hi" -------------------------------------------------------------------------------- /testfiles/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test="test" 4 | echo "$test" -------------------------------------------------------------------------------- /testfiles/test.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" -------------------------------------------------------------------------------- /testfiles/scandir/discovery: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bashio 2 | 3 | echo "hi" -------------------------------------------------------------------------------- /testfiles/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | 3 | test="test" 4 | echo "$test" -------------------------------------------------------------------------------- /testfiles/scandir/test.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" -------------------------------------------------------------------------------- /testfiles/duplicate_name.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" 4 | -------------------------------------------------------------------------------- /testfiles/ignore/ignore.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" 4 | -------------------------------------------------------------------------------- /testfiles/ignore_single_file.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | 3 | test="test" 4 | echo "$test" -------------------------------------------------------------------------------- /testfiles/ignore_some/ignore.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" 4 | -------------------------------------------------------------------------------- /testfiles/scandir/finish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bashio 2 | 3 | hi="hi" 4 | 5 | echo "$hi" -------------------------------------------------------------------------------- /testfiles/scandir/run me.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "I love spaces" 4 | -------------------------------------------------------------------------------- /testfiles/ignore_some/do_not_ignore.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" 4 | -------------------------------------------------------------------------------- /testfiles/ignore_some/duplicate_name.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | test="test" 3 | echo "$test" 4 | -------------------------------------------------------------------------------- /.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ludeeus/action-shellcheck", 3 | "image": "mcr.microsoft.com/devcontainers/base:bullseye" 4 | } -------------------------------------------------------------------------------- /testfiles/bashfile.c: -------------------------------------------------------------------------------- 1 | /* C code test file 2 | * file that should not be matched for shellcheck runs 3 | */ 4 | int main(void) { 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "06:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Issue" 3 | about: For issue reporting. 4 | labels: "bug" 5 | --- 6 | 7 | ## Describe the issue 8 | 9 | A clear and concise description of what the issue is. 10 | 11 | ### Links 12 | 13 | - **Link to action run:** 14 | - **Link to action configuration:** 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: "Feature Request" 5 | --- 6 | 7 | ## Checklist 8 | 9 | - [ ] This Feature Request only contains 1 request (if you have multiple open multiple feature requests). 10 | 11 | ## The idea 12 | 13 | A good description of what you are suggesting. 14 | 15 | ## Implementation 16 | 17 | How do you see this being implemented? 18 | 19 | ## Alternatives 20 | 21 | Are there any alternative solutions or features you've considered? 22 | 23 | ## Additional context 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ludeeus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/check_together.yml: -------------------------------------------------------------------------------- 1 | name: 'check_together' 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | check_together: 15 | name: check_together 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Run ShellCheck 27 | uses: ./ 28 | id: check 29 | with: 30 | ignore_paths: ignore 31 | check_together: true 32 | 33 | - name: Verify check 34 | run: | 35 | expect="testfiles/test.bash" 36 | notexpect="testfiles/ignore/ignore.bash" 37 | 38 | if [[ ! "${{ steps.check.outputs.files }}" =~ $expect ]];then 39 | echo "::error:: Expected file $expect not found in ${{ steps.check.outputs.files }}" 40 | exit 1 41 | elif [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then 42 | echo "::error:: Expected file $notexpect found in ${{ steps.check.outputs.files }}" 43 | exit 1 44 | fi -------------------------------------------------------------------------------- /.github/workflows/ignore_names.yml: -------------------------------------------------------------------------------- 1 | name: 'ignore_names' 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | ignore_names: 15 | name: ignore_names 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Run ShellCheck 27 | uses: ./ 28 | id: check 29 | with: 30 | ignore_paths: ignore 31 | ignore_names: ignore_single_file.sh 32 | 33 | - name: Verify check 34 | run: | 35 | expect="testfiles/test.bash" 36 | notexpect="testfiles/ignore_single_file.sh" 37 | 38 | if [[ ! "${{ steps.check.outputs.files }}" =~ $expect ]];then 39 | echo "::error:: Expected file $expect not found in ${{ steps.check.outputs.files }}" 40 | exit 1 41 | elif [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then 42 | echo "::error:: Expected file $notexpect found in ${{ steps.check.outputs.files }}" 43 | exit 1 44 | fi 45 | -------------------------------------------------------------------------------- /.github/workflows/additional_files.yml: -------------------------------------------------------------------------------- 1 | name: 'additional_files' 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | additional_files: 15 | name: additional_files 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Run ShellCheck 27 | uses: ./ 28 | id: check 29 | with: 30 | additional_files: run finish discovery 31 | ignore_paths: ignore 32 | scandir: testfiles 33 | 34 | - name: Verify check 35 | run: | 36 | expect="testfiles/scandir/run" 37 | 38 | if [[ ! "${{ steps.check.outputs.files }}" =~ testfiles/scandir/run ]];then 39 | echo "::error:: Expected file testfiles/scandir/run not found in ${{ steps.check.outputs.files }}" 40 | exit 1 41 | elif [[ ! "${{ steps.check.outputs.files }}" =~ testfiles/scandir/finish ]];then 42 | echo "::error:: Expected file testfiles/scandir/finish not found in ${{ steps.check.outputs.files }}" 43 | exit 1 44 | elif [[ ! "${{ steps.check.outputs.files }}" =~ testfiles/scandir/discovery ]];then 45 | echo "::error:: Expected file testfiles/scandir/discovery not found in ${{ steps.check.outputs.files }}" 46 | exit 1 47 | fi -------------------------------------------------------------------------------- /.github/workflows/scandir.yml: -------------------------------------------------------------------------------- 1 | name: 'scandir' 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | scandir: 15 | name: scandir 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Run ShellCheck 27 | uses: ./ 28 | id: one 29 | with: 30 | scandir: testfiles/scandir 31 | 32 | - name: Verify check 33 | run: | 34 | expect="testfiles/scandir/run[[:space:]]me.bash" 35 | notexpect="testfiles/test.bash" 36 | 37 | if [[ ! "${{ steps.one.outputs.files }}" =~ $expect ]];then 38 | echo "::error:: Expected file $expect not found in ${{ steps.one.outputs.files }}" 39 | exit 1 40 | elif [[ "${{ steps.one.outputs.files }}" =~ $notexpect ]];then 41 | echo "::error:: Expected file $notexpect found in ${{ steps.one.outputs.files }}" 42 | exit 1 43 | fi 44 | 45 | - name: Run ShellCheck 46 | uses: ./ 47 | id: two 48 | with: 49 | scandir: './testfiles/scandir' 50 | ignore_paths: ignore 51 | 52 | - name: Verify check 53 | run: | 54 | expect="testfiles/scandir/test.bash" 55 | notexpect="testfiles/test.bash" 56 | 57 | if [[ ! "${{ steps.two.outputs.files }}" =~ $expect ]];then 58 | echo "::error:: Expected file $expect not found in ${{ steps.two.outputs.files }}" 59 | exit 1 60 | elif [[ "${{ steps.two.outputs.files }}" =~ $notexpect ]];then 61 | echo "::error:: Expected file $notexpect found in ${{ steps.two.outputs.files }}" 62 | exit 1 63 | fi 64 | -------------------------------------------------------------------------------- /.github/workflows/ignore_paths.yml: -------------------------------------------------------------------------------- 1 | name: "ignore_paths" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | branches: 9 | - "master" 10 | 11 | permissions: {} 12 | 13 | jobs: 14 | ignore_paths: 15 | name: ignore_paths 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Run ShellCheck 27 | uses: ./ 28 | id: check 29 | with: 30 | ignore_paths: ignore ./testfiles/ignore_some/duplicate_name.bash **/ignore_some/ignore.bash 31 | 32 | - name: Verify check 33 | run: | 34 | fail=false 35 | 36 | # verify a non-ignored path is not excluded 37 | expect="testfiles/test.bash" 38 | if [[ ! "${{ steps.check.outputs.files }}" =~ $expect ]];then 39 | echo "::error:: Expected file $expect not found in ${{ steps.check.outputs.files }}" 40 | fail=true 41 | fi 42 | 43 | # verify a file with the same name as an ignored file but at a 44 | # different path is not excluded 45 | expect="testfiles/duplicate_name.bash" 46 | if [[ ! "${{ steps.check.outputs.files }}" =~ $expect ]];then 47 | echo "::error:: Expected file $expect not found in ${{ steps.check.outputs.files }}" 48 | fail=true 49 | fi 50 | 51 | # verify ignored full path excluded 52 | notexpect="testfiles/ignore_some/duplicate_name.bash" 53 | if [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then 54 | echo "::error:: Unexpected file $notexpect found in ${{ steps.check.outputs.files }}" 55 | fail=true 56 | fi 57 | 58 | # verify ignored directory excluded 59 | notexpect="testfiles/ignore/ignore.bash" 60 | if [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then 61 | echo "::error:: Unexpected file $notexpect found in ${{ steps.check.outputs.files }}" 62 | fail=true 63 | fi 64 | 65 | # verify ignored glob excluded 66 | notexpect="testfiles/ignore_some/ignore.bash" 67 | if [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then 68 | echo "::error:: Unexpected file $notexpect found in ${{ steps.check.outputs.files }}" 69 | fail=true 70 | fi 71 | 72 | if $fail;then 73 | exit 1 74 | fi 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShellCheck 2 | 3 | _GitHub action for [ShellCheck](https://www.shellcheck.net/)._ 4 | 5 | ## Example 6 | 7 | ```yaml 8 | on: 9 | push: 10 | branches: 11 | - master 12 | 13 | name: "Trigger: Push action" 14 | permissions: {} 15 | 16 | jobs: 17 | shellcheck: 18 | name: Shellcheck 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Run ShellCheck 23 | uses: ludeeus/action-shellcheck@master 24 | ``` 25 | 26 | ## ShellCheck options 27 | 28 | You can pass any supported ShellCheck option or flag with the `SHELLCHECK_OPTS` env key in the job definition. 29 | 30 | Some examples include: 31 | 32 | - To disable specific checks (eg: `-e SC2059 -e SC2034 -e SC1090`) 33 | - To test against different shells (eg: `-s dash` or `-s ksh`) 34 | 35 | example: 36 | 37 | ```yaml 38 | ... 39 | - name: Run ShellCheck 40 | uses: ludeeus/action-shellcheck@master 41 | env: 42 | SHELLCHECK_OPTS: -e SC2059 -e SC2034 -e SC1090 43 | ``` 44 | 45 | ## Ignore paths and names 46 | 47 | You can use the `ignore_paths` and `ignore_names` input to disable specific directories and files. 48 | These are passed as environment variables, and should evaluate to a single space-separated string. 49 | It may be convenient to use [`>-`](https://yaml.org/spec/1.2.2/#65-line-folding) for readability if you have multiple selectors. 50 | 51 | ```text 52 | sample structure: 53 | sample/directory/with/files/ignoreme/test.sh 54 | sample/directory/with/files/ignoremetoo/test.sh 55 | sample/directory/with/files/test.sh 56 | sample/directory/with/files/ignorable.sh 57 | ``` 58 | 59 | example: 60 | 61 | ```yaml 62 | ... 63 | - name: Run ShellCheck 64 | uses: ludeeus/action-shellcheck@master 65 | with: 66 | ignore_paths: >- 67 | ignoreme 68 | ignoremetoo 69 | ignore_names: ignorable.sh 70 | ``` 71 | 72 | This will skip `sample/directory/with/files/ignoreme/test.sh`, `sample/directory/with/files/ignoremetoo/test.sh` and `sample/directory/with/files/ignorable.sh`. 73 | 74 | You can also ignore specific files using full paths or glob patterns with `ignore_paths`. 75 | 76 | example: 77 | 78 | ```yaml 79 | ... 80 | - name: Run ShellCheck 81 | uses: ludeeus/action-shellcheck@master 82 | with: 83 | ignore_paths: ./sample/directory/with/files/ignorable.sh **/ignoreme/test.sh 84 | ``` 85 | 86 | This will skip `sample/directory/with/files/ignorable.sh` and `sample/directory/with/files/ignoreme/test.sh`. 87 | 88 | ## Minimum severity of errors to consider (error, warning, info, style) 89 | 90 | You can use the `severity` input to not fail until specified severity is met, for example fail only if there are errors in scripts but ignore styling, info and warnings. 91 | 92 | example: 93 | 94 | ```yaml 95 | ... 96 | - name: Run ShellCheck 97 | uses: ludeeus/action-shellcheck@master 98 | with: 99 | severity: error 100 | ``` 101 | 102 | ## Run shellcheck with all paths in a single invocation 103 | 104 | If you run into SC1090/SC1091 errors you may need to tell shellcheck to check 105 | all files at once: 106 | 107 | ```yaml 108 | ... 109 | - name: Run ShellCheck 110 | uses: ludeeus/action-shellcheck@master 111 | with: 112 | check_together: 'yes' 113 | ``` 114 | 115 | This can turn into a problem if you have enough script files to overwhelm the 116 | maximum argv length on your system. 117 | 118 | ## Run shellcheck only in a single directory 119 | 120 | If you have multiple directories with scripts, but only want to scan 121 | one of them, you can use the following configuration: 122 | 123 | ```yaml 124 | ... 125 | - name: Run ShellCheck 126 | uses: ludeeus/action-shellcheck@master 127 | with: 128 | scandir: './scripts' 129 | ``` 130 | 131 | ## Scan for additional files 132 | 133 | If you need to scan for unusual files, you can use the `additional_files` key. 134 | 135 | ```yaml 136 | ... 137 | - name: Run ShellCheck 138 | uses: ludeeus/action-shellcheck@master 139 | with: 140 | additional_files: 'run finish' 141 | ``` 142 | 143 | ## Change output format 144 | 145 | Shellcheck can print output in these formats: `checkstyle`, `diff`, `gcc`, `json`, `json1`, `quiet`, `tty`. See some examples [here](https://github.com/koalaman/shellcheck/wiki/Integration#pick-the-output-format-that-makes-your-life-easier). 146 | 147 | - `tty` has multi-line log messages 148 | - `gcc` has single-line log messages 149 | 150 | ```yaml 151 | ... 152 | - name: Run ShellCheck 153 | uses: ludeeus/action-shellcheck@master 154 | with: 155 | format: tty 156 | ``` 157 | 158 | ## Run a specific version of Shellcheck 159 | 160 | If running the latest stable version of Shellcheck is not to your liking, you can specify a concrete version of Shellcheck to be used. When specifying a custom version, please use any of the released versions listed in the [Shellcheck repository](https://github.com/koalaman/shellcheck/tags). 161 | 162 | ```yaml 163 | ... 164 | - name: Run ShellCheck 165 | uses: ludeeus/action-shellcheck@master 166 | with: 167 | version: v0.9.0 168 | ``` 169 | -------------------------------------------------------------------------------- /action.yaml: -------------------------------------------------------------------------------- 1 | name: "ShellCheck" 2 | author: "Ludeeus " 3 | description: "GitHub action for ShellCheck." 4 | inputs: 5 | additional_files: 6 | description: "A space separated list of additional filename to check" 7 | required: false 8 | default: "" 9 | ignore: 10 | description: "Paths to ignore when running ShellCheck" 11 | required: false 12 | default: "" 13 | deprecationMessage: "Use ignore_paths or ignore_names instead." 14 | ignore_paths: 15 | description: "Paths to ignore when running ShellCheck" 16 | required: false 17 | default: "" 18 | ignore_names: 19 | description: "Names to ignore when running ShellCheck" 20 | required: false 21 | default: "" 22 | severity: 23 | description: "Minimum severity of errors to consider. Options: [error, warning, info, style]" 24 | required: false 25 | default: "" 26 | check_together: 27 | description: "Run shellcheck on _all_ files at once, instead of one at a time" 28 | required: false 29 | default: "" 30 | scandir: 31 | description: "Directory to be searched for files. Defaults to ." 32 | required: false 33 | default: "." 34 | disable_matcher: 35 | description: "Set to true to skip using problem-matcher" 36 | required: false 37 | default: "false" 38 | deprecationMessage: "There are no problem-matchers, this setting does not do anything." 39 | format: 40 | description: "Output format (checkstyle, diff, gcc, json, json1, quiet, tty)" 41 | required: false 42 | default: "gcc" 43 | version: 44 | description: "Specify a concrete version of ShellCheck to use" 45 | required: false 46 | default: "stable" 47 | outputs: 48 | files: 49 | description: A list of files with issues 50 | value: ${{ steps.check.outputs.filepaths }} 51 | options: 52 | description: The options used 53 | value: ${{ steps.options.outputs.options }} 54 | branding: 55 | icon: "terminal" 56 | color: "gray-dark" 57 | runs: 58 | using: "composite" 59 | steps: 60 | - name: Download shellcheck 61 | shell: bash 62 | env: 63 | INPUT_VERSION: ${{ inputs.version }} 64 | run: | 65 | if [[ "${{ runner.os }}" == "macOS" ]]; then 66 | osvariant="darwin" 67 | else 68 | osvariant="linux" 69 | fi 70 | 71 | baseurl="https://github.com/koalaman/shellcheck/releases/download" 72 | 73 | curl -Lso "${{ github.action_path }}/sc.tar.xz" \ 74 | "${baseurl}/${INPUT_VERSION}/shellcheck-${INPUT_VERSION}.${osvariant}.x86_64.tar.xz" 75 | 76 | tar -xf "${{ github.action_path }}/sc.tar.xz" -C "${{ github.action_path }}" 77 | mv "${{ github.action_path }}/shellcheck-${INPUT_VERSION}/shellcheck" \ 78 | "${{ github.action_path }}/shellcheck" 79 | 80 | - name: Display shellcheck version 81 | shell: bash 82 | run: | 83 | "${{ github.action_path }}/shellcheck" --version 84 | 85 | - name: Set options 86 | shell: bash 87 | id: options 88 | env: 89 | INPUT_SEVERITY: ${{ inputs.severity }} 90 | INPUT_FORMAT: ${{ inputs.format }} 91 | run: | 92 | declare -a options 93 | if [[ -n "${INPUT_SEVERITY}" ]]; then 94 | options+=("-S ${INPUT_SEVERITY}") 95 | fi 96 | options+=("--format=${INPUT_FORMAT}") 97 | echo "options=${options[@]}" >> $GITHUB_OUTPUT 98 | 99 | - name: Gather excluded paths 100 | shell: bash 101 | id: exclude 102 | env: 103 | INPUT_IGNORE: ${{ inputs.ignore }} 104 | INPUT_IGNORE_PATHS: ${{ inputs.ignore_paths }} 105 | INPUT_IGNORE_NAMES: ${{ inputs.ignore_names }} 106 | run: | 107 | declare -a excludes 108 | set -f # temporarily disable globbing so that globs in input aren't expanded 109 | 110 | excludes+=("! -path *./.git/*") 111 | excludes+=("! -path *.go") 112 | excludes+=("! -path */mvnw") 113 | if [[ -n "${INPUT_IGNORE}" ]]; then 114 | for path in ${INPUT_IGNORE}; do 115 | excludes+=("! -path *./$path/*") 116 | excludes+=("! -path */$path/*") 117 | excludes+=("! -path $path") 118 | done 119 | else 120 | for path in ${INPUT_IGNORE_PATHS}; do 121 | excludes+=("! -path *./$path/*") 122 | excludes+=("! -path */$path/*") 123 | excludes+=("! -path $path") 124 | done 125 | fi 126 | 127 | for name in ${INPUT_IGNORE_NAMES}; do 128 | excludes+=("! -name $name") 129 | done 130 | echo "excludes=${excludes[@]}" >> $GITHUB_OUTPUT 131 | 132 | set +f # re-enable globbing 133 | 134 | - name: Gather additional files 135 | shell: bash 136 | id: additional 137 | env: 138 | INPUT_ADDITIONAL_FILES: ${{ inputs.additional_files }} 139 | run: | 140 | declare -a files 141 | for file in ${INPUT_ADDITIONAL_FILES}; do 142 | files+=("-o -name *$file") 143 | done 144 | echo "files=${files[@]}" >> $GITHUB_OUTPUT 145 | 146 | - name: Run the check 147 | shell: bash 148 | id: check 149 | env: 150 | INPUT_SCANDIR: ${{ inputs.scandir }} 151 | INPUT_CHECK_TOGETHER: ${{ inputs.check_together }} 152 | INPUT_EXCLUDE_ARGS: ${{ steps.exclude.outputs.excludes }} 153 | INPUT_ADDITIONAL_FILE_ARGS: ${{ steps.additional.outputs.files }} 154 | INPUT_SHELLCHECK_OPTIONS: ${{ steps.options.outputs.options }} 155 | run: | 156 | statuscode=0 157 | declare -a filepaths 158 | shebangregex="^#! */[^ ]*/(env *)?[abk]*sh" 159 | 160 | set -f # temporarily disable globbing so that globs in inputs aren't expanded 161 | 162 | while IFS= read -r -d '' file; do 163 | filepaths+=("$file") 164 | done < <(find "${INPUT_SCANDIR}" \ 165 | ${INPUT_EXCLUDE_ARGS} \ 166 | -type f \ 167 | '(' \ 168 | -name '*.bash' \ 169 | -o -name '.bashrc' \ 170 | -o -name 'bashrc' \ 171 | -o -name '.bash_aliases' \ 172 | -o -name '.bash_completion' \ 173 | -o -name '.bash_login' \ 174 | -o -name '.bash_logout' \ 175 | -o -name '.bash_profile' \ 176 | -o -name 'bash_profile' \ 177 | -o -name '*.ksh' \ 178 | -o -name 'suid_profile' \ 179 | -o -name '*.zsh' \ 180 | -o -name '.zlogin' \ 181 | -o -name 'zlogin' \ 182 | -o -name '.zlogout' \ 183 | -o -name 'zlogout' \ 184 | -o -name '.zprofile' \ 185 | -o -name 'zprofile' \ 186 | -o -name '.zsenv' \ 187 | -o -name 'zsenv' \ 188 | -o -name '.zshrc' \ 189 | -o -name 'zshrc' \ 190 | -o -name '*.sh' \ 191 | -o -path '*/.profile' \ 192 | -o -path '*/profile' \ 193 | -o -name '*.shlib' \ 194 | ${INPUT_ADDITIONAL_FILE_ARGS} \ 195 | ')' \ 196 | -print0) 197 | 198 | while IFS= read -r -d '' file; do 199 | head -n1 "$file" | grep -Eqs "$shebangregex" || continue 200 | filepaths+=("$file") 201 | done < <(find "${INPUT_SCANDIR}" \ 202 | ${INPUT_EXCLUDE_ARGS} \ 203 | -type f ! -name '*.*' -perm /111 \ 204 | -print0) 205 | 206 | if [[ -n "${INPUT_CHECK_TOGETHER}" ]]; then 207 | "${{ github.action_path }}/shellcheck" \ 208 | ${INPUT_SHELLCHECK_OPTIONS} \ 209 | "${filepaths[@]}" || statuscode=$? 210 | else 211 | for file in "${filepaths[@]}"; do 212 | "${{ github.action_path }}/shellcheck" \ 213 | ${INPUT_SHELLCHECK_OPTIONS} \ 214 | "$file" || statuscode=$? 215 | done 216 | fi 217 | 218 | echo "filepaths=${filepaths[@]}" >> $GITHUB_OUTPUT 219 | echo "statuscode=$statuscode" >> $GITHUB_OUTPUT 220 | 221 | set +f # re-enable globbing 222 | 223 | - name: Exit action 224 | shell: bash 225 | run: exit ${{steps.check.outputs.statuscode}} 226 | --------------------------------------------------------------------------------