diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..ec4bb38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..28303f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature request +about: Suggest an idea for this project +labels: "Feature Request" +--- + +## Checklist + +- [ ] This Feature Request only contains 1 request (if you have multiple open multiple feature requests). + +## The idea + +A good description of what you are suggesting. + +## Implementation + +How do you see this being implemented? + +## Alternatives + +Are there any alternative solutions or features you've considered? + +## Additional context + diff --git a/.github/ISSUE_TEMPLATE/issue.md b/.github/ISSUE_TEMPLATE/issue.md new file mode 100644 index 0000000..d84e921 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.md @@ -0,0 +1,14 @@ +--- +name: "Issue" +about: For issue reporting. +labels: "bug" +--- + +## Describe the issue + +A clear and concise description of what the issue is. + +### Links + +- **Link to action run:** +- **Link to action configuration:** diff --git a/.github/workflows/ShellCheck.yml b/.github/workflows/ShellCheck.yml deleted file mode 100644 index e115d4d..0000000 --- a/.github/workflows/ShellCheck.yml +++ /dev/null @@ -1,15 +0,0 @@ -on: [push, pull_request] -name: 'ShellCheck' -jobs: - shellcheck: - name: ShellCheck - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Run ShellCheck - uses: ./ - with: - ignore: ignore diff --git a/.github/workflows/additional_files.yml b/.github/workflows/additional_files.yml new file mode 100644 index 0000000..a03f61b --- /dev/null +++ b/.github/workflows/additional_files.yml @@ -0,0 +1,42 @@ +name: 'additional_files' + +on: + push: + branches: ["master"] + pull_request: + +jobs: + additional_files: + name: additional_files + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Run ShellCheck + uses: ./ + id: check + with: + additional_files: run finish discovery + ignore: ignore + scandir: testfiles + + - name: Verify check + run: | + expect="testfiles/scandir/run" + + if [[ ! "${{ steps.check.outputs.files }}" =~ testfiles/scandir/run ]];then + echo "::error:: Expected file testfiles/scandir/run not found in ${{ steps.check.outputs.files }}" + exit 1 + elif [[ ! "${{ steps.check.outputs.files }}" =~ testfiles/scandir/finish ]];then + echo "::error:: Expected file testfiles/scandir/finish not found in ${{ steps.check.outputs.files }}" + exit 1 + elif [[ ! "${{ steps.check.outputs.files }}" =~ testfiles/scandir/discovery ]];then + echo "::error:: Expected file testfiles/scandir/discovery not found in ${{ steps.check.outputs.files }}" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/base.yml b/.github/workflows/base.yml new file mode 100644 index 0000000..f144c4d --- /dev/null +++ b/.github/workflows/base.yml @@ -0,0 +1,38 @@ +name: 'base' + +on: + push: + branches: ["master"] + pull_request: + +jobs: + base: + name: base + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Run ShellCheck + uses: ./ + id: check + with: + ignore: ignore + + - name: Verify check + run: | + expect="testfiles/test.bash" + notexpect="testfiles/ignore/ignore.bash" + + if [[ ! "${{ steps.check.outputs.files }}" =~ $expect ]];then + echo "::error:: Expected file $expect not found in ${{ steps.check.outputs.files }}" + exit 1 + elif [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then + echo "::error:: Expected file $notexpect found in ${{ steps.check.outputs.files }}" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/check_together.yml b/.github/workflows/check_together.yml new file mode 100644 index 0000000..9b18c88 --- /dev/null +++ b/.github/workflows/check_together.yml @@ -0,0 +1,39 @@ +name: 'check_together' + +on: + push: + branches: ["master"] + pull_request: + +jobs: + check_together: + name: check_together + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Run ShellCheck + uses: ./ + id: check + with: + ignore: ignore + check_together: true + + - name: Verify check + run: | + expect="testfiles/test.bash" + notexpect="testfiles/ignore/ignore.bash" + + if [[ ! "${{ steps.check.outputs.files }}" =~ $expect ]];then + echo "::error:: Expected file $expect not found in ${{ steps.check.outputs.files }}" + exit 1 + elif [[ "${{ steps.check.outputs.files }}" =~ $notexpect ]];then + echo "::error:: Expected file $notexpect found in ${{ steps.check.outputs.files }}" + exit 1 + fi \ No newline at end of file diff --git a/.github/workflows/scandir.yml b/.github/workflows/scandir.yml new file mode 100644 index 0000000..a457b66 --- /dev/null +++ b/.github/workflows/scandir.yml @@ -0,0 +1,58 @@ +name: 'scandir' + +on: + push: + branches: ["master"] + pull_request: + +jobs: + scandir: + name: scandir + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Run ShellCheck + uses: ./ + id: one + with: + scandir: testfiles/scandir + + - name: Verify check + run: | + expect="testfiles/scandir/test.bash" + notexpect="testfiles/test.bash" + + if [[ ! "${{ steps.one.outputs.files }}" =~ $expect ]];then + echo "::error:: Expected file $expect not found in ${{ steps.one.outputs.files }}" + exit 1 + elif [[ "${{ steps.one.outputs.files }}" =~ $notexpect ]];then + echo "::error:: Expected file $notexpect found in ${{ steps.one.outputs.files }}" + exit 1 + fi + + - name: Run ShellCheck + uses: ./ + id: two + with: + scandir: './testfiles/scandir' + ignore: ignore + + - name: Verify check + run: | + expect="testfiles/scandir/test.bash" + notexpect="testfiles/test.bash" + + if [[ ! "${{ steps.two.outputs.files }}" =~ $expect ]];then + echo "::error:: Expected file $expect not found in ${{ steps.two.outputs.files }}" + exit 1 + elif [[ "${{ steps.two.outputs.files }}" =~ $notexpect ]];then + echo "::error:: Expected file $notexpect found in ${{ steps.two.outputs.files }}" + exit 1 + fi \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 4738ce6..0000000 --- a/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM alpine:3.12.0 - -RUN apk add --no-cache shellcheck bash - -COPY runaction /action/runaction -COPY .github/problem-matcher.json /problem-matcher.json - -ENTRYPOINT ["bash", "/action/runaction"] \ No newline at end of file diff --git a/LICENSE b/LICENSE index 156bc55..c1df9eb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 ludeeus +Copyright (c) 2020 ludeeus Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d57df72..9563308 100644 --- a/README.md +++ b/README.md @@ -100,3 +100,28 @@ one of them, you can use the following configuration: with: scandir: './scripts' ``` + +## Scan for additional files + +If you need to scan for unusual files, you can use the `additional_files` key. + +```yaml + ... + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + additional_files: 'run finish' +``` + +## Disable problem matcher + +If you do not want to have the problem-matcher annotate files, you can disable it +by setting `disable_matcher` to `true`. + +```yaml + ... + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + disable_matcher: true +``` \ No newline at end of file diff --git a/action.yaml b/action.yaml index 4fc804d..c033f74 100644 --- a/action.yaml +++ b/action.yaml @@ -2,25 +2,198 @@ 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' + description: "Paths to ignore when running ShellCheck" required: false - default: '' + default: "" severity: - description: 'Minimum severity of errors to consider. Options: [error, warning, info, style]' + description: "Minimum severity of errors to consider. Options: [error, warning, info, style]" required: false - default: '' + default: "" check_together: - description: 'Run shellcheck on _all_ files at once, instead of one at a time' + description: "Run shellcheck on _all_ files at once, instead of one at a time" required: false - default: '' + default: "" scandir: - description: 'Directory to be searched for files. Defaults to .' + description: "Directory to be searched for files. Defaults to ." required: false - default: '.' -runs: - using: 'docker' - image: 'Dockerfile' + 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' + 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 -print0); 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}} \ No newline at end of file diff --git a/runaction b/runaction deleted file mode 100755 index 920a9ed..0000000 --- a/runaction +++ /dev/null @@ -1,95 +0,0 @@ -#!/bin/bash - -## Enable problem matcher -cp /problem-matcher.json ./problem-matcher.json - -echo "::add-matcher::problem-matcher.json" - -## Run action -cd "$GITHUB_WORKSPACE" || exit 1 - -declare statuscode -declare -a filepaths -declare -a excludes -declare -a tmp - -INPUT_SCANDIR="${INPUT_SCANDIR:-.}" -statuscode=0 -shebangregex="^#! */[^ ]*/(env *)?[abkz]*sh" - -excludes+=( ! -path *./.git/* ) -excludes+=( ! -path *.go ) -excludes+=( ! -path */mvnw ) - -for path in ${INPUT_IGNORE}; do - echo "::debug:: Adding '${path}' to excludes" - excludes+=(! -path "*./${path}/*" ) - excludes+=(! -path "*/${path}/*" ) -done - -readarray -d '' filepaths < <(find "${INPUT_SCANDIR}" -type f "${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' \ - ')'\ - \ - -print0) - - -readarray -d '' tmp < <(find "${INPUT_SCANDIR}" "${excludes[@]}" -type f ! -name '*.*' -perm /111 -print0) -for file in "${tmp[@]}"; do - head -n1 "$file" | grep -Eqs "$shebangregex" || continue - filepaths+=("$file") -done - -if find "${INPUT_SCANDIR}" "${excludes[@]}" -path '*bin/*/*' -type f -perm /111 -print | - grep . -then - echo >&2 "::warning:: subdirectories of bin directories are not usable via PATH" -fi - -if find "${INPUT_SCANDIR}" "${excludes[@]}" -path '*bin/*' -name '*.*' -type f -perm /111 -perm /444 -print | - grep . -then - echo >&2 "::warning:: programs in PATH should not have a filename suffix" -fi - -[[ -n "${INPUT_SEVERITY}" ]] && options+=(-S "${INPUT_SEVERITY}") - -if [[ -n "$INPUT_CHECK_TOGETHER" ]]; then - echo "::debug:: shellcheck ${options[*]} ${filepaths[*]}" - shellcheck "${options[@]}" "${filepaths[@]}" || statuscode=$? -else - echo "::debug:: Shellcheck options: ${options[*]}" - for file in "${filepaths[@]}"; do - echo "::debug:: Checking $file" - shellcheck "${options[@]}" "$file" || statuscode=$? - done -fi - -exit "$statuscode" diff --git a/testfiles/scandir/discovery b/testfiles/scandir/discovery new file mode 100644 index 0000000..41c0b11 --- /dev/null +++ b/testfiles/scandir/discovery @@ -0,0 +1,3 @@ +#!/usr/bin/env bashio + +echo "hi" \ No newline at end of file diff --git a/testfiles/scandir/finish b/testfiles/scandir/finish new file mode 100644 index 0000000..216019e --- /dev/null +++ b/testfiles/scandir/finish @@ -0,0 +1,5 @@ +#!/usr/bin/env bashio + +hi="hi" + +echo "$hi" \ No newline at end of file diff --git a/testfiles/scandir/run b/testfiles/scandir/run new file mode 100644 index 0000000..9bc3184 --- /dev/null +++ b/testfiles/scandir/run @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +echo "hi" \ No newline at end of file diff --git a/testfiles/scandir/test.bash b/testfiles/scandir/test.bash new file mode 100644 index 0000000..3ea25c6 --- /dev/null +++ b/testfiles/scandir/test.bash @@ -0,0 +1,3 @@ +#!/bin/bash +test="test" +echo "$test" \ No newline at end of file