From 70f7013166169cf2467868e847f112fad6f67043 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Thu, 2 May 2024 20:05:26 +0200 Subject: [PATCH 1/4] add only_changed input to run rubocop only against changed files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to bash for process substitution and working with arrays. Ignore if there are more than 100. Protecting from possibly hitting the command line length limit, it can be much higher and can be calculated or also extracted as one more input if needed. The effect from limiting number of files processed by rubocop is also smaller. Fix ci workflow to fetch all commits for pr branch plus head commit of base branch to be able to find changed files. Co-authored-by: Oliver Günther --- .github/workflows/ci.yml | 2 ++ README.md | 5 +++++ action.yml | 6 ++++++ script.sh | 39 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61280dd..faa10f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,8 @@ jobs: BUNDLE_GEMFILE: ${{ github.workspace }}/test/using_bundler/Gemfile steps: - uses: actions/checkout@v4 + - name: Fetch head commit of base branch + run: git fetch --depth 1 origin ${{ github.event.pull_request.base.sha }} - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 diff --git a/README.md b/README.md index 551328c..7817571 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ Default is `added`. Optional. Report level for reviewdog [`info`, `warning`, `error`]. It's same as `-level` flag of reviewdog. +### `only_changed` + +Optional. Run Rubocop only on changed (and added) files, for speedup [`true`, `false`]. +Default: `false`. + ### `reporter` Optional. Reporter of reviewdog command [`github-pr-check`, `github-check`, `github-pr-review`]. diff --git a/action.yml b/action.yml index bb27bcc..1377019 100644 --- a/action.yml +++ b/action.yml @@ -19,6 +19,9 @@ inputs: level: description: 'Report level for reviewdog [info,warning,error]' default: 'error' + only_changed: + description: "Run Rubocop only on changed (and added) files, for speedup [`true`, `false`]" + default: 'false' reporter: description: | Reporter of reviewdog command [github-pr-check,github-check,github-pr-review]. @@ -61,6 +64,7 @@ runs: INPUT_FILTER_MODE: ${{ inputs.filter_mode }} INPUT_GITHUB_TOKEN: ${{ inputs.github_token }} INPUT_LEVEL: ${{ inputs.level }} + INPUT_ONLY_CHANGED: ${{ inputs.only_changed }} INPUT_REPORTER: ${{ inputs.reporter }} INPUT_REVIEWDOG_FLAGS: ${{ inputs.reviewdog_flags }} INPUT_RUBOCOP_EXTENSIONS: ${{ inputs.rubocop_extensions }} @@ -70,6 +74,8 @@ runs: INPUT_TOOL_NAME: ${{ inputs.tool_name }} INPUT_USE_BUNDLER: ${{ inputs.use_bundler }} INPUT_WORKDIR: ${{ inputs.workdir }} + BASE_REF: ${{ github.event.pull_request.base.sha }} + HEAD_REF: ${{ github.sha }} branding: icon: 'check-circle' color: 'red' diff --git a/script.sh b/script.sh index 81a06bc..4dfda14 100755 --- a/script.sh +++ b/script.sh @@ -1,4 +1,7 @@ -#!/bin/sh -e +#!/usr/bin/env bash + +set -e +set -o pipefail cd "${GITHUB_WORKSPACE}/${INPUT_WORKDIR}" || exit export REVIEWDOG_GITHUB_API_TOKEN="${INPUT_GITHUB_TOKEN}" @@ -85,9 +88,41 @@ else BUNDLE_EXEC="bundle exec " fi +if [ "${INPUT_ONLY_CHANGED}" = "true" ]; then + echo '::group:: Getting changed files list' + + # get intersection of changed files (excluding deleted) with target files for + # rubocop as an array + # shellcheck disable=SC2086 + readarray -t CHANGED_FILES < <( + comm -12 \ + <(git diff --diff-filter=d --name-only "${BASE_REF}..${HEAD_REF}" | sort || kill $$) \ + <(${BUNDLE_EXEC}rubocop --list-target-files | sort || kill $$) + ) + + if (( ${#CHANGED_FILES[@]} == 0 )); then + echo "No relevant files for rubocop, skipping" + exit 0 + fi + + printf '%s\n' "${CHANGED_FILES[@]}" + + if (( ${#CHANGED_FILES[@]} > 100 )); then + echo "More than 100 changed files (${#CHANGED_FILES[@]}), running rubocop on all files" + unset CHANGED_FILES + fi + + echo '::endgroup::' +fi + echo '::group:: Running rubocop with reviewdog 🐶 ...' # shellcheck disable=SC2086 -${BUNDLE_EXEC}rubocop ${INPUT_RUBOCOP_FLAGS} --require ${GITHUB_ACTION_PATH}/rdjson_formatter/rdjson_formatter.rb --format RdjsonFormatter \ +${BUNDLE_EXEC}rubocop \ + ${INPUT_RUBOCOP_FLAGS} \ + --require ${GITHUB_ACTION_PATH}/rdjson_formatter/rdjson_formatter.rb \ + --format RdjsonFormatter \ + --fail-level error \ + "${CHANGED_FILES[@]}" \ | reviewdog -f=rdjson \ -name="${INPUT_TOOL_NAME}" \ -reporter="${INPUT_REPORTER}" \ From 50c80c5b2924a2602d7f2bcfb0531087f63ac99a Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Wed, 26 Jun 2024 14:20:10 +0200 Subject: [PATCH 2/4] automatically fetch the tip of the base branch if it is not available --- .github/workflows/ci.yml | 2 -- README.md | 3 +++ action.yml | 5 ++++- script.sh | 5 +++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index faa10f5..61280dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,6 @@ jobs: BUNDLE_GEMFILE: ${{ github.workspace }}/test/using_bundler/Gemfile steps: - uses: actions/checkout@v4 - - name: Fetch head commit of base branch - run: git fetch --depth 1 origin ${{ github.event.pull_request.base.sha }} - uses: ruby/setup-ruby@v1 with: ruby-version: 2.6 diff --git a/README.md b/README.md index 7817571..7fc2719 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ It's same as `-level` flag of reviewdog. Optional. Run Rubocop only on changed (and added) files, for speedup [`true`, `false`]. Default: `false`. +Will fetch the tip of the base branch with depth 1 from remote origin if it is not available. +If you use different remote name or customize the checkout otherwise, make the tip of the base branch available before this action + ### `reporter` Optional. Reporter of reviewdog command [`github-pr-check`, `github-check`, `github-pr-review`]. diff --git a/action.yml b/action.yml index 1377019..c5295aa 100644 --- a/action.yml +++ b/action.yml @@ -20,7 +20,10 @@ inputs: description: 'Report level for reviewdog [info,warning,error]' default: 'error' only_changed: - description: "Run Rubocop only on changed (and added) files, for speedup [`true`, `false`]" + description: | + Run Rubocop only on changed (and added) files, for speedup [`true`, `false`]. + Will fetch the tip of the base branch with depth 1 from remote origin if it is not available. + If you use different remote name or customize the checkout otherwise, make the tip of the base branch available before this action. default: 'false' reporter: description: | diff --git a/script.sh b/script.sh index 4dfda14..b34f6b6 100755 --- a/script.sh +++ b/script.sh @@ -91,6 +91,11 @@ fi if [ "${INPUT_ONLY_CHANGED}" = "true" ]; then echo '::group:: Getting changed files list' + # check if commit is present in repository, otherwise fetch it + if ! git cat-file -e "${BASE_REF}"; then + git fetch --depth 1 origin "${BASE_REF}" + fi + # get intersection of changed files (excluding deleted) with target files for # rubocop as an array # shellcheck disable=SC2086 From e50a873137e5ef59d42cfbcccdbc7637ec0916b4 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Tue, 9 Jul 2024 19:46:46 +0200 Subject: [PATCH 3/4] don't hide the name of the CI job --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61280dd..83a5fd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,6 @@ name: CI on: [pull_request] jobs: test-skip-install-and-use-bundler: - name: runner / rubocop runs-on: ubuntu-latest defaults: run: From 2f1682ebcd8593d40a496da53729ba3891aafc61 Mon Sep 17 00:00:00 2001 From: Ivan Kuchin Date: Fri, 12 Jul 2024 19:02:26 +0200 Subject: [PATCH 4/4] test only_changed --- .github/workflows/ci.yml | 52 +++++++++++++++++++ test/only_changed/few_relevant/files/a.rb | 1 + test/only_changed/few_relevant/files/a.txt | 1 + .../few_relevant/mock_bins/rubocop | 10 ++++ .../only_changed/nothing_relevant/files/a.txt | 1 + .../nothing_relevant/mock_bins/rubocop | 8 +++ test/only_changed/shared_mock_bins/bundle | 9 ++++ test/only_changed/shared_mock_bins/curl | 8 +++ test/only_changed/shared_mock_bins/reviewdog | 10 ++++ .../too_many_relevant/mock_bins/rubocop | 10 ++++ 10 files changed, 110 insertions(+) create mode 100644 test/only_changed/few_relevant/files/a.rb create mode 100644 test/only_changed/few_relevant/files/a.txt create mode 100755 test/only_changed/few_relevant/mock_bins/rubocop create mode 100644 test/only_changed/nothing_relevant/files/a.txt create mode 100755 test/only_changed/nothing_relevant/mock_bins/rubocop create mode 100755 test/only_changed/shared_mock_bins/bundle create mode 100755 test/only_changed/shared_mock_bins/curl create mode 100755 test/only_changed/shared_mock_bins/reviewdog create mode 100755 test/only_changed/too_many_relevant/mock_bins/rubocop diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83a5fd2..6a26232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,3 +21,55 @@ jobs: skip_install: 'true' use_bundler: 'true' - run: test "$(bundle exec rubocop --version)" == "1.18.1" + test-only_changed: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + env: + INPUT_ONLY_CHANGED: 'true' + steps: + - uses: actions/checkout@v4 + - name: setup + run: | + git config user.email "workflow@github.com" + git config user.name "I am an automated workflow" + - name: Check when there are relevant files + run: | + git checkout ${{ github.sha }} + rm -f test/only_changed/reviewdog-was-called + + cp test/only_changed/few_relevant/files/* . + git add * + git commit -m auto + + export PATH=test/only_changed/few_relevant/mock_bins:test/only_changed/shared_mock_bins:$PATH + BASE_REF=$(git rev-parse HEAD~) HEAD_REF=$(git rev-parse HEAD) ./script.sh + + [ -f test/only_changed/reviewdog-was-called ] + - name: Check when there are no relevant files + run: | + git checkout ${{ github.sha }} + rm -f test/only_changed/reviewdog-was-called + + cp test/only_changed/nothing_relevant/files/* . + git add * + git commit -m auto + + export PATH=test/only_changed/nothing_relevant/mock_bins:test/only_changed/shared_mock_bins:$PATH + BASE_REF=$(git rev-parse HEAD~) HEAD_REF=$(git rev-parse HEAD) ./script.sh + + [ ! -f test/only_changed/reviewdog-was-called ] + - name: Check when there are too many relevant files + run: | + git checkout ${{ github.sha }} + rm -f test/only_changed/reviewdog-was-called + + touch a{00..100}.rb + git add * + git commit -m auto + + export PATH=test/only_changed/too_many_relevant/mock_bins:test/only_changed/shared_mock_bins:$PATH + BASE_REF=$(git rev-parse HEAD~) HEAD_REF=$(git rev-parse HEAD) ./script.sh + + [ -f test/only_changed/reviewdog-was-called ] diff --git a/test/only_changed/few_relevant/files/a.rb b/test/only_changed/few_relevant/files/a.rb new file mode 100644 index 0000000..5460224 --- /dev/null +++ b/test/only_changed/few_relevant/files/a.rb @@ -0,0 +1 @@ +puts "Hello, " + "world!" diff --git a/test/only_changed/few_relevant/files/a.txt b/test/only_changed/few_relevant/files/a.txt new file mode 100644 index 0000000..5460224 --- /dev/null +++ b/test/only_changed/few_relevant/files/a.txt @@ -0,0 +1 @@ +puts "Hello, " + "world!" diff --git a/test/only_changed/few_relevant/mock_bins/rubocop b/test/only_changed/few_relevant/mock_bins/rubocop new file mode 100755 index 0000000..40c1a97 --- /dev/null +++ b/test/only_changed/few_relevant/mock_bins/rubocop @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +case ARGV +when %w[--list-target-files] + puts Dir['**/*.rb'] +when %W[--require #{ENV['GITHUB_ACTION_PATH']}/rdjson_formatter/rdjson_formatter.rb --format RdjsonFormatter --fail-level error a.rb] + puts 'Mock message for reviewdog' +else + abort "rubocop mock called with unexpected arguments:\n#{ARGV.join("\n")}" +end diff --git a/test/only_changed/nothing_relevant/files/a.txt b/test/only_changed/nothing_relevant/files/a.txt new file mode 100644 index 0000000..5460224 --- /dev/null +++ b/test/only_changed/nothing_relevant/files/a.txt @@ -0,0 +1 @@ +puts "Hello, " + "world!" diff --git a/test/only_changed/nothing_relevant/mock_bins/rubocop b/test/only_changed/nothing_relevant/mock_bins/rubocop new file mode 100755 index 0000000..36ab0d3 --- /dev/null +++ b/test/only_changed/nothing_relevant/mock_bins/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby + +case ARGV +when %w[--list-target-files] + puts Dir['**/*.rb'] +else + abort "rubocop mock called with unexpected arguments:\n#{ARGV.join("\n")}" +end diff --git a/test/only_changed/shared_mock_bins/bundle b/test/only_changed/shared_mock_bins/bundle new file mode 100755 index 0000000..0a28632 --- /dev/null +++ b/test/only_changed/shared_mock_bins/bundle @@ -0,0 +1,9 @@ +#!/bin/bash + +if [ "$1" = "exec" ]; then + shift + eval "$@" +else + echo "Only 'exec' command is supported" + exit 1 +fi diff --git a/test/only_changed/shared_mock_bins/curl b/test/only_changed/shared_mock_bins/curl new file mode 100755 index 0000000..3c5bc23 --- /dev/null +++ b/test/only_changed/shared_mock_bins/curl @@ -0,0 +1,8 @@ +#!/bin/bash + +arguments="$*" + +if [ "$arguments" != "-sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh" ]; then + echo "curl mock got unexpected arguments: $arguments" + exit 1 +fi diff --git a/test/only_changed/shared_mock_bins/reviewdog b/test/only_changed/shared_mock_bins/reviewdog new file mode 100755 index 0000000..5334ef7 --- /dev/null +++ b/test/only_changed/shared_mock_bins/reviewdog @@ -0,0 +1,10 @@ +#!/bin/bash + +touch test/only_changed/reviewdog-was-called + +read -r input + +if [ "$input" != "Mock message for reviewdog" ]; then + echo "reviewdog mock got unexpected input: $input" + exit 1 +fi diff --git a/test/only_changed/too_many_relevant/mock_bins/rubocop b/test/only_changed/too_many_relevant/mock_bins/rubocop new file mode 100755 index 0000000..cfab6d4 --- /dev/null +++ b/test/only_changed/too_many_relevant/mock_bins/rubocop @@ -0,0 +1,10 @@ +#!/usr/bin/env ruby + +case ARGV +when %w[--list-target-files] + puts Dir['**/*.rb'] +when %W[--require #{ENV['GITHUB_ACTION_PATH']}/rdjson_formatter/rdjson_formatter.rb --format RdjsonFormatter --fail-level error] + puts 'Mock message for reviewdog' +else + abort "rubocop mock called with unexpected arguments:\n#{ARGV.join("\n")}" +end