From 313510dd1729b331240d9d772d84a9bf7d93624f Mon Sep 17 00:00:00 2001 From: hestonhoffman Date: Sat, 28 Jun 2025 15:34:03 -0700 Subject: [PATCH 1/3] Fetch 100 items per page --- main.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/main.py b/main.py index 7a278aa..e61fd88 100644 --- a/main.py +++ b/main.py @@ -24,9 +24,9 @@ def fetch_patch(): 'X-GitHub-Api-Version': '2022-11-28', 'Authorization':f'Bearer {TOKEN}' } - # Fetch the first page + # Fetch the first page with per_page=100 response = git_session.get( - f"{api_url}/repos/{repo}/pulls/{pr}/files", headers=headers + f"{api_url}/repos/{repo}/pulls/{pr}/files?per_page=100", headers=headers ) files = response.json() # Follow 'next' links to fetch the remaining pages @@ -47,22 +47,22 @@ def parse_patch_data(patch_data): # Some really big files don't have a patch key because GitHub # returns a message in the PR that the file is too large to display if entry['additions'] != 0 and 'patch' in entry: - patch_array = re.split('\n', entry['patch']) - # clean patch array - patch_array = [i for i in patch_array if i] + patch_array = re.split('\n', entry['patch']) + # clean patch array + patch_array = [i for i in patch_array if i] - for item in patch_array: - # Grabs hunk annotation and strips out added lines - if item.startswith('@@ -'): - if sublist: - line_array.append(sublist) - sublist = [re.sub(r'\s@@(.*)','',item.split('+')[1])] - # We don't need removed lines ('-') - elif not item.startswith('-') and not item == '\\ No newline at end of file': - sublist.append(item) - if sublist: - line_array.append(sublist) - final_dict[entry['filename']] = line_array + for item in patch_array: + # Grabs hunk annotation and strips out added lines + if item.startswith('@@ -'): + if sublist: + line_array.append(sublist) + sublist = [re.sub(r'\s@@(.*)','',item.split('+')[1])] + # We don't need removed lines ('-') + elif not item.startswith('-') and not item == '\\ No newline at end of file': + sublist.append(item) + if sublist: + line_array.append(sublist) + final_dict[entry['filename']] = line_array return final_dict def get_lines(line_dict): From 2757bc132daab044cbe3d34c04ac1a83293a3374 Mon Sep 17 00:00:00 2001 From: hestonhoffman Date: Sat, 28 Jun 2025 16:23:05 -0700 Subject: [PATCH 2/3] Improve documentation - Answers some questions posed in #18 - Documents some steps for local development Closes #18 --- .gitignore | 5 ++++- README.md | 34 ++++++++++++++++++++++++++++++++-- example.env | 7 +++++++ main.py | 1 + 4 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 example.env diff --git a/.gitignore b/.gitignore index c7f68db..12bd337 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ temp.txt .DS_Store __pycache__ -test_patch_data.json \ No newline at end of file +test_patch_data.json + +# used for local testing +.env \ No newline at end of file diff --git a/README.md b/README.md index f6b1570..4a44651 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,10 @@ ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/hestonhoffman/changed-lines) -This GitHub action returns the file names and modified lines of each file in a pull request. This is useful if you're running a custom linter against your files and you want to compare log lines against modified lines in your PR. +This GitHub action returns the file names and modified lines of each file in a pull request. This action is intended for custom linters, where you want to compare log lines against modified lines in your PR. + +The action uses the patch data returned from the [Git API PR endpoint](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files) to collect the modified file names and lines. Changes in your PR are compared against the target branch and line numbers in the output are relative to the modified file. If the entire file is new, all lines appear in the output. + > [!NOTE] > This action only works with pull requests. @@ -86,4 +89,31 @@ Use the `api_url` input if you need to use a custom GitHub API URL. This is usef run: echo ${{ steps.changed_lines.outputs.changed_files }} ``` -Thanks to Jacob Tomlinson [for a helpful tutorial](https://jacobtomlinson.dev/posts/2019/creating-github-actions-in-python/). \ No newline at end of file +Thanks to Jacob Tomlinson [for a helpful tutorial](https://jacobtomlinson.dev/posts/2019/creating-github-actions-in-python/). + +## Local development + +You can use the instructions below to load some environment variables so you can run the script locally. You'll need a GitHub personal access token with repo privileges. + +If you want to run the script locally: +1. (Optional) Set up a virtual environment using your preferred method. +1. Install the python modules: + ```shell + pip install -r requirements.txt + ``` +1. Make a copy of `example.env` and change the environment variables to point to a PR you want to test against: + + **Note**: This file (`.env`) is ignored by git, but make sure you don't accidentally commit it somehow, as it stores your git token. + + ```shell + cp example.env .env + ``` +1. Run the script: + ```shell + python main.py + ``` +1. Check the generated text file for the results. The environment `GITHUB_OUTPUT` variable sets this to `temp.txt` by default. + +## Deleted files and lines + +Deleted files and lines don't show up in the output. This is intentional because the main use case for the action is to pass changed lines to a linter for GitHub command annotations. The GitHub API doesn't allow annotations on deleted lines. \ No newline at end of file diff --git a/example.env b/example.env new file mode 100644 index 0000000..8a17b1c --- /dev/null +++ b/example.env @@ -0,0 +1,7 @@ +export INPUT_API_URL=https://api.github.com +export INPUT_TOKEN=your_token_here +export INPUT_BRANCH=some_branch +export INPUT_REPO=owner/repo +export INPUT_PR=99 +export GITHUB_OUTPUT=temp.txt +export INPUT_DELIMITER=' ' \ No newline at end of file diff --git a/main.py b/main.py index e61fd88..b0e2a9c 100644 --- a/main.py +++ b/main.py @@ -33,6 +33,7 @@ def fetch_patch(): while 'next' in response.links: response = git_session.get(response.links['next']['url'], headers=headers) files.extend(response.json()) + return files def parse_patch_data(patch_data): From a53407757f216625163d42b0d962fe49355e033f Mon Sep 17 00:00:00 2001 From: hestonhoffman Date: Sat, 28 Jun 2025 16:26:24 -0700 Subject: [PATCH 3/3] Add missing instruction --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4a44651..16cebf3 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,10 @@ If you want to run the script locally: ```shell cp example.env .env ``` +1. Source the environment variables: + ```shell + source .env + ``` 1. Run the script: ```shell python main.py