Support ignore deletions with "ignore_line_deletions" param (#71)

* Support ignore deletions with "ignore_file_deletions" param

Test files_to_ignore code path

Tweak the variable type

Correct bad logic in the file ignore block

Debug

More debugging

More debugging

Fingers crossed

Another try

Last try

Lets see if this one does it

Testing again

More tests

More testing

Last test

Wait, I might be onto something

Put some changes back

Fixes

Whoops

Walk back yml test

* Rename config variable to be ignore_line_deletions

* Add and update tests

* Update action.yml to provided more consistent desc.

Co-authored-by: Javier Ferrer González <javier.mailserio@gmail.com>

* Delete redundant helper function

* Fix parse error in action.yml

---------

Co-authored-by: Javier Ferrer González <javier.mailserio@gmail.com>
This commit is contained in:
John Kuhn 2024-04-16 08:35:06 -07:00 committed by GitHub
parent 1cc5389c9b
commit f2aafc4d87
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: B5690EEEBB952194
12 changed files with 134 additions and 41 deletions

View file

@ -55,22 +55,23 @@ jobs:
## 🎛️ Arguments
| Name | Required | Default Value | Description |
|-------------------|----------|----------------------|-------------------------------------------------------------------------------------------------------------------------|
| `GITHUB_TOKEN` | Yes | Automatically supplied| GitHub token needed to interact with the repository. |
| `xs_label` | No | 'size/xs' | Label for very small-sized PRs. |
| `xs_max_size` | No | '10' | Maximum number of changes allowed for XS-sized PRs. |
| `s_label` | No | 'size/s' | Label for small-sized PRs. |
| `s_max_size` | No | '100' | Maximum number of changes allowed for S-sized PRs. |
| `m_label` | No | 'size/m' | Label for medium-sized PRs. |
| `m_max_size` | No | '500' | Maximum number of changes allowed for M-sized PRs. |
| `l_label` | No | 'size/l' | Label for large-sized PRs. |
| `l_max_size` | No | '1000' | Maximum number of changes allowed for L-sized PRs. |
| `xl_label` | No | 'size/xl' | Label for extra-large-sized PRs. |
| `fail_if_xl` | No | 'false' | Whether to fail the GitHub workflow if the PR size is 'XL' (blocks the merge). |
| `message_if_xl` | No | Custom message | Message to display when a PR exceeds the 'XL' size limit. |
| `github_api_url` | No | 'https://api.github.com' | URL for the GitHub API, can be changed for GitHub Enterprise Servers. |
| `files_to_ignore` | No | '' | Files to ignore during PR size calculation. Supports newline or whitespace delimited list. |
| Name | Required | Default Value | Description |
|-------------------------|----------|----------------------|-------------------------------------------------------------------------------------------------------------------------|
| `GITHUB_TOKEN` | Yes | Automatically supplied| GitHub token needed to interact with the repository. |
| `xs_label` | No | 'size/xs' | Label for very small-sized PRs. |
| `xs_max_size` | No | '10' | Maximum number of changes allowed for XS-sized PRs. |
| `s_label` | No | 'size/s' | Label for small-sized PRs. |
| `s_max_size` | No | '100' | Maximum number of changes allowed for S-sized PRs. |
| `m_label` | No | 'size/m' | Label for medium-sized PRs. |
| `m_max_size` | No | '500' | Maximum number of changes allowed for M-sized PRs. |
| `l_label` | No | 'size/l' | Label for large-sized PRs. |
| `l_max_size` | No | '1000' | Maximum number of changes allowed for L-sized PRs. |
| `xl_label` | No | 'size/xl' | Label for extra-large-sized PRs. |
| `fail_if_xl` | No | 'false' | Whether to fail the GitHub workflow if the PR size is 'XL' (blocks the merge). |
| `message_if_xl` | No | Custom message | Message to display when a PR exceeds the 'XL' size limit. |
| `github_api_url` | No | 'https://api.github.com' | URL for the GitHub API, can be changed for GitHub Enterprise Servers. |
| `files_to_ignore` | No | '' | Files to ignore during PR size calculation. Supports newline or whitespace delimited list. |
| `ignore_line_deletions` | No | 'false' | Whether to ignore lines which are deleted when calculating the PR size. If set to 'true', deleted lines will be ignored. |
### Example for `files_to_ignore`:
```yml

View file

@ -59,6 +59,10 @@ inputs:
description: 'Whitespace separated list of files to ignore when calculating the PR size (sum of changes)'
required: false
default: ''
ignore_line_deletions:
description: 'Whether to ignore lines which are deleted when calculating the PR size. If set to "true", deleted lines will be ignored.'
required: false
default: 'false'
runs:
using: 'docker'
image: 'Dockerfile'
@ -77,6 +81,7 @@ runs:
- --fail_if_xl=${{ inputs.fail_if_xl }}
- --message_if_xl="${{ inputs.message_if_xl }}"
- --files_to_ignore=${{ inputs.files_to_ignore }}
- --ignore_line_deletions=${{ inputs.ignore_line_deletions }}
branding:
icon: 'tag'
color: 'green'

View file

@ -5,36 +5,44 @@ GITHUB_API_HEADER="Accept: application/vnd.github.v3+json"
github::calculate_total_modifications() {
local -r pr_number="${1}"
local -r files_to_ignore="${2}"
local -r ignore_line_deletions="${3}"
local additions=0
local deletions=0
if [ -z "$files_to_ignore" ]; then
local -r body=$(curl -sSL -H "Authorization: token $GITHUB_TOKEN" -H "$GITHUB_API_HEADER" "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/$pr_number")
local -r additions=$(echo "$body" | jq '.additions')
local -r deletions=$(echo "$body" | jq '.deletions')
additions=$(echo "$body" | jq '.additions')
echo $((additions + deletions))
if [ "$ignore_line_deletions" != "true" ]; then
((deletions += $(echo "$body" | jq '.deletions')))
fi
else
local -r body=$(curl -sSL -H "Authorization: token $GITHUB_TOKEN" -H "$GITHUB_API_HEADER" "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls/$pr_number/files?per_page=100")
local changes=0
for file in $(echo "$body" | jq -r '.[] | @base64'); do
local ignore_file=0
for file_to_ignore in $files_to_ignore; do
if [ -z "$file_to_ignore" ]; then
continue
fi
if [[ "$(jq::base64 '.filename')" == $file_to_ignore ]]; then
ignore_file=1
filename=$(jq::base64 '.filename')
ignore=false
for pattern in $files_to_ignore; do
if [[ $filename == $pattern ]]; then
ignore=true
break
fi
done
if [ $ignore_file -eq 0 ]; then
((changes += $(jq::base64 '.changes')))
if [ "$ignore" = false ]; then
((additions += $(jq::base64 '.additions')))
if [ "$ignore_line_deletions" != "true" ]; then
((deletions += $(jq::base64 '.deletions')))
fi
fi
done
echo $changes
fi
echo $((additions + deletions))
}
github::add_label_to_pr() {

View file

@ -1,5 +1,12 @@
#!/usr/bin/env bash
github_actions::get_pr_number() {
jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH"
local -r pull_request_number=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH")
if [[ "$pull_request_number" != "null" ]]; then
echo "$pull_request_number"
else
echo "Not a pull request event"
exit 1
fi
}

View file

@ -9,9 +9,10 @@ labeler::label() {
local -r fail_if_xl="${10}"
local -r message_if_xl="${11}"
local -r files_to_ignore="${12}"
local -r ignore_line_deletions="${13}"
local -r pr_number=$(github_actions::get_pr_number)
local -r total_modifications=$(github::calculate_total_modifications "$pr_number" "$files_to_ignore")
local -r total_modifications=$(github::calculate_total_modifications "$pr_number" "${files_to_ignore[*]}" "$ignore_line_deletions")
log::message "Total modifications (additions + deletions): $total_modifications"
log::message "Ignoring files (if present): $files_to_ignore"

View file

@ -9,7 +9,7 @@ source "$PR_SIZE_LABELER_HOME/src/misc.sh"
##? Adds a size label to a GitHub Pull Request
##?
##? Usage:
##? main.sh --github_token=<token> --xs_label=<label> --xs_max_size=<size> --s_label=<label> --s_max_size=<size> --m_label=<label> --m_max_size=<size> --l_label=<label> --l_max_size=<size> --xl_label=<label> --fail_if_xl=<false> --message_if_xl=<message> --github_api_url=<url> --files_to_ignore=<files>
##? main.sh --github_token=<token> --xs_label=<label> --xs_max_size=<size> --s_label=<label> --s_max_size=<size> --m_label=<label> --m_max_size=<size> --l_label=<label> --l_max_size=<size> --xl_label=<label> --fail_if_xl=<false> --message_if_xl=<message> --github_api_url=<url> --files_to_ignore=<files> --ignore_line_deletions=<false>
main() {
eval "$(/root/bin/docpars -h "$(grep "^##?" "$PR_SIZE_LABELER_HOME/src/main.sh" | cut -c 5-)" : "$@")"
@ -31,7 +31,8 @@ main() {
"$xl_label" \
"$fail_if_xl" \
"$message_if_xl" \
"$files_to_ignore"
"$files_to_ignore" \
"$ignore_line_deletions"
exit $?
}

38
tests/fixtures/pull_request_api vendored Normal file
View file

@ -0,0 +1,38 @@
{
"url": "https://api.github.com/repos/test/pulls/123",
"number": 123,
"state": "open",
"locked": false,
"title": "YOLO PR",
"user": {},
"body": "",
"created_at": "2024-04-12T17:08:32Z",
"updated_at": "2024-04-12T20:03:46Z",
"closed_at": null,
"merged_at": null,
"merge_commit_sha": "9ec1fc67bbc7bd9151270",
"assignee": null,
"assignees": [],
"requested_reviewers": [],
"requested_teams": [],
"labels": [],
"milestone": null,
"draft": false,
"head": {},
"base": {},
"author_association": "CONTRIBUTOR",
"auto_merge": null,
"active_lock_reason": null,
"merged": false,
"mergeable": true,
"rebaseable": false,
"mergeable_state": "blocked",
"merged_by": null,
"comments": 4,
"review_comments": 0,
"maintainer_can_modify": false,
"commits": 9,
"additions": 173,
"deletions": 1,
"changed_files": 4
}

View file

@ -5,10 +5,39 @@ function set_up() {
source ./src/github.sh
}
function test_should_ignore_files_with_glob() {
local -r pr_number=123
local -r files_to_ignore=("*.lock" ".editorconfig")
mock curl cat ./tests/fixtures/test_should_ignore_files_with_regex_response
pr_number=123
files_to_ignore=''
ignore_line_deletions='false'
assert_match_snapshot "$(github::calculate_total_modifications "$pr_number" "${files_to_ignore[*]}")"
function test_should_count_changes() {
mock curl cat ./tests/fixtures/pull_request_api
assert_match_snapshot "$(github::calculate_total_modifications "$pr_number" "${files_to_ignore[*]}" "$ignore_line_deletions")"
}
function test_should_count_changes_ignore_deletions() {
ignore_line_deletions='true'
mock curl cat ./tests/fixtures/pull_request_api
assert_match_snapshot "$(github::calculate_total_modifications "$pr_number" "${files_to_ignore[*]}" "$ignore_line_deletions")"
}
# NOTE: when `files_to_ignore` is set, we have to invoke the PR files API and iterate each file
# one at at time. This is why the mock call is diffent in the subsequent test cases
function test_should_ignore_files_with_glob() {
files_to_ignore=("*.lock" ".editorconfig")
mock curl cat ./tests/fixtures/pull_request_files_api
assert_match_snapshot "$(github::calculate_total_modifications "$pr_number" "${files_to_ignore[*]}" "$ignore_line_deletions")"
}
function test_should_ignore_files_with_glob_ignore_deletions() {
files_to_ignore=("*.lock" ".editorconfig")
ignore_line_deletions='true'
mock curl cat ./tests/fixtures/pull_request_files_api
assert_match_snapshot "$(github::calculate_total_modifications "$pr_number" "${files_to_ignore[*]}" "$ignore_line_deletions")"
}

View file

@ -0,0 +1 @@
174