From d3204241f885a907022e7d1a3a26bd424a996e39 Mon Sep 17 00:00:00 2001 From: Nikkel Mollenhauer <57323886+NikkelM@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:43:09 +0100 Subject: [PATCH] Added a new option to ignore files using regular expressions (#43) --- .github/config.yml | 10 ++++--- .rubocop.yml | 6 +++++ CHANGELOG.md | 1 + POSTINSTALL.md | 1 - README.md | 7 ++--- app.rb | 57 ++++++++++++++++++++++++++++------------ validation/validation.js | 4 +-- version.rb | 2 +- 8 files changed, 60 insertions(+), 28 deletions(-) diff --git a/.github/config.yml b/.github/config.yml index eb25d8c..f2cc23c 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -1,9 +1,8 @@ todo-pr-checker: # When should comments be posted? post_comment: 'items_found' - # If multiline comments should also be searched for action items - # This may result in some false positives or negatives if the opening or closing lines of a comment are not in the diff - enable_multiline_comments: false + # Files that get matched by any of these regular expressions will be ignored + ignore_files: null # What action items should be looked for (setting this will the overwrite default values) action_items: ['TODO', 'FIXME', 'BUG'] # Languages/file types to add. At least either the line comment or both block comment definitions are required. @@ -14,4 +13,7 @@ todo-pr-checker: ['.py', '#'] ] # Whether or not searching for action items should be case sensitive - case_sensitive: false \ No newline at end of file + case_sensitive: false + # If multiline comments should also be searched for action items + # This may result in some false positives or negatives if the opening or closing lines of a comment are not in the diff + enable_multiline_comments: false \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 2f52802..a2cd048 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -14,6 +14,9 @@ Metrics/MethodLength: Metrics/BlockLength: Enabled: false +Metrics/BlockNesting: + Max: 4 + Metrics/AbcSize: Enabled: false @@ -25,3 +28,6 @@ Metrics/PerceivedComplexity: Style/MethodCallWithoutArgsParentheses: Enabled: false + +Style/RescueModifier: + Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index d94c295..c07b6e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - The app will now always include the results of the action item search in the check run summary. +- Added an option that allows you to ignore files that match a specific pattern. - Added an option to enable multiline block comments. This option is disabled by default, as it may cause the app to incorrectly identify action items. - If the app encounters an error while the check is running, the check will now be concluded as neutral and the error message will be included in the check run summary. - Fixed a bug where setting one of the values of the `add_languages` option to `null` would cause the app to not accept any new languages. diff --git a/POSTINSTALL.md b/POSTINSTALL.md index f8b412f..33d9af9 100644 --- a/POSTINSTALL.md +++ b/POSTINSTALL.md @@ -12,4 +12,3 @@ I hope that you'll find it useful and that it will help you and your team keep t Hosting and maintaining this app unfortunately is not free, so I appreciate your contribution to help keep it running - consider buying me a [coffee](https://ko-fi.com/nikkelm) or [sponsoring](https://github.com/sponsors/NikkelM) this project. Please also take a look at the [project readme](https://github.com/NikkelM/Todo-PR-Checker/blob/main/README.md) to learn how to configure the app to match your needs through a `.github/config.yml` file, and feel free to reach out and open an issue if you have any questions or need help. - diff --git a/README.md b/README.md index 652f50e..c8c32e8 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,11 @@ To get started, you can copy the `.github/config.yml` file from this repository | Option | Possible Values | Description | Default | | --- | --- | --- | --- | | `post_comment` | `items_found`, `always`, `never` | Controls when the app should post a comment. By default, a comment is only posted if an action item has been found. If set to `never`, the check will still fail. If set to `always`, the app will post a comment that all action items have been resolved if none are found. | `items_found` | -| `enable_multiline_comments` | `true`, `false` | Whether or not looking for action items in multiline block comments is enabled or not. When enabled, the app *may* incorrectly mark action items in your Pull Request if at least one of the opening or closing line of the block comment (e.g. `*/` and `/*` in JavaScript) are not included in the Pull Request diff, which causes them to not be found by the app. For multiline comments to always work, you must ensure that both the opening and closing characters are included in the diff. Action items located on the first line of a block comment will always be detected, even if this option is disabled. | `false` | -| `action_items` | `string[]` | A list of action items to look for in code comments. If you set this option, the default values will be overwritten, so you must include them in your list to use them. | `['TODO', 'FIXME', 'BUG']` | +| `ignore_files` | `string[]`, maximum 7 entries | A list of glob patterns to specify files that should be ignored during the check. You may specify up to 7 patterns. The pattern matching logic used is the same as for `.gitignore` files. | `null` | +| `action_items` | `string[]`, maximum 15 entries | A list of action items to look for in code comments. If you set this option, the default values will be overwritten, so you must include them in your list to use them. By default, action items are case insensitive. You may specify up to 15 items. | `['TODO', 'FIXME', 'BUG']` | +| `add_languages` | `[string[file_type, line_start, block_start, block_end]]`
Example: `[['js', '//', '/*', '*,'], ['css', null, '/*', '*/'], ['.py', '#']]`, maximum 10 entries | A list of a list of programming languages to add support for. This list will be added to the already supported languages. If you define a language that is already supported, the default values will be overwritten. `file_type` must be the extension of the file (e.g. `js`) and may start with a `.`. You may omit the block comment definitions if the file type does not support block comments. If you want to omit the definition of a line comment, you must set `line_start` to `null`. If defining `block_start`, `block_end` must also be defined. You may specify up to 10 new file types. *The file types shown in the example are already natively supported by the app.* | `null` | | `case_sensitive` | `true`, `false` | Controls whether the app should look for action items in a case-sensitive manner. | `false` | -| `add_languages` | `[string[file_type, line_start, block_start, block_end]]`
Example: `[['js', '//', '/*', '*,'], ['css', null, '/*', '*/'], ['.py', '#']]` | A list of a list of programming languages to add support for. This list will be added to the already supported languages. If you define a language that is already supported, the default values will be overwritten. `file_type` must be the extension of the file (e.g. `js`) and may start with a `.`. You may omit the block comment definitions if the file type does not support block comments. If you want to omit the definition of a line comment, you must set `line_start` to `null`. If defining `block_start`, `block_end` must also be defined. *The file types shown in the example are already natively supported by the app.* | `null` | +| `enable_multiline_comments` | `true`, `false` | Whether or not looking for action items in multiline block comments is enabled or not. When enabled, the app *may* incorrectly mark action items in your Pull Request if at least one of the opening or closing line of the block comment (e.g. `*/` and `/*` in JavaScript) are not included in the Pull Request diff, which causes them to not be found by the app. For multiline comments to always work, you must ensure that both the opening and closing characters are included in the diff. Action items located on the first line of a block comment will always be detected, even if this option is disabled. | `false` |
Expand me to see the currently supported file types: diff --git a/app.rb b/app.rb index f645dd5..4753ffd 100644 --- a/app.rb +++ b/app.rb @@ -101,21 +101,23 @@ def initiate_check_run # Get the options for the app from the `.github/config.yml` file in the repository app_options = get_app_options(full_repo_name, @payload['check_run']['head_sha']) + # logger.debug app_options # Get a list of changed lines in the Pull request, grouped by their file name and associated with a line number - changes = get_pull_request_changes(full_repo_name, pull_number) + changes = get_pull_request_changes(full_repo_name, pull_number, app_options['ignore_files']) # Filter the changed lines for only those that contain action items ("Todos"), and group them by file todo_changes = check_for_todos(changes, app_options) # If the app has previously created a comment on the Pull Request, fetch it app_comment = fetch_app_comment(full_repo_name, pull_number) + comment_footer = "\n----\nDid I do good? Let me know by [helping maintain this app](https://github.com/sponsors/NikkelM)!" # If any action items were found, create/update a comment on the Pull Request with embedded links to the relevant lines if todo_changes.any? # If the user has enabled post_comment in the options if app_options['post_comment'] != 'never' - check_run_title, comment_summary, comment_body, comment_footer = create_pr_comment_from_changes(todo_changes, full_repo_name).values_at(:title, :summary, :body, :footer) + check_run_title, comment_summary, comment_body = create_pr_comment_from_changes(todo_changes, full_repo_name).values_at(:title, :summary, :body) # Post or update the comment with the found action items if app_comment @@ -168,15 +170,18 @@ def get_app_options(full_repo_name, head_sha) 'enable_multiline_comments' => true, 'action_items' => %w[todo fixme bug], 'case_sensitive' => false, - 'add_languages' => [] + 'add_languages' => [], + 'ignore_files' => [] } accepted_option_values = { 'post_comment' => ->(value) { %w[items_found always never].include?(value) }, 'enable_multiline_comments' => ->(value) { [true, false].include?(value) }, - 'action_items' => ->(value) { value.is_a?(Array) }, + 'action_items' => ->(value) { value.is_a?(Array) && (0..15).include?(value.size) }, 'case_sensitive' => ->(value) { [true, false].include?(value) }, - 'add_languages' => ->(value) { value.is_a?(Array) && value.all? { |v| v.is_a?(Array) && (2..4).include?(v.size) && v.all? { |i| i.is_a?(String) || i.nil? } } } + 'add_languages' => ->(value) { value.is_a?(Array) && (1..10).include?(value.size) && value.all? { |v| v.is_a?(Array) && (2..4).include?(v.size) && v.all? { |i| i.is_a?(String) || i.nil? } } }, + # The regex checks if the given input is a valid .gitignore pattern + 'ignore_files' => ->(value) { value.is_a?(Array) && (1..7).include?(value.size) && value.all? { |v| v.is_a?(String) && %r{\A(/?(\*\*/)?[\w*\[\]{}?\.\/-]+(/\*\*)?/?)\Z}.match?(v) } } } file = @installation_client.contents(full_repo_name, path: '.github/config.yml', ref: head_sha) @@ -198,28 +203,47 @@ def get_app_options(full_repo_name, head_sha) end # (4) Retrieves all changes in a pull request from the GitHub API and formats them to be usable by the app - def get_pull_request_changes(full_repo_name, pull_number) + def get_pull_request_changes(full_repo_name, pull_number, ignore_files_regex) diff = @installation_client.pull_request(full_repo_name, pull_number, accept: 'application/vnd.github.diff') + ignore_files_regex.map! do |pattern| + pattern.gsub!('.', '\.') + pattern.gsub!('*', '.*') + pattern.gsub!('/', '\/') + Regexp.new(pattern) + end + current_file = '' line_number = 0 changes = {} - diff.each_line do |line| - # This is the most common case, indicating a line was added to the file - if line.start_with?('+') && !line.start_with?('+++') + diff_enum = diff.each_line + line = diff_enum.next rescue nil + loop do + break if line.nil? + + if line.start_with?('+++') + while line&.start_with?('+++') + current_file = line[6..].strip + if ignore_files_regex.any? { |pattern| pattern.match?(current_file) } + loop do + line = diff_enum.next rescue nil + break if line.nil? || line.start_with?('+++') + end + else + changes[current_file] = [] + break + end + end + break if line.nil? + elsif line.start_with?('+') changes[current_file] << { line: line_number, text: line[1..] } - # Lines that start with @@ contain the the starting line and its length for a new block of changes, for the old and new file respectively elsif line.start_with?('@@') line_number = line.split()[2].split(',')[0].to_i - 1 - # Lines that start with +++ contain the new name of the file, which is the one we want to link to in the comment - elsif line.start_with?('+++') - current_file = line[6..].strip - changes[current_file] = [] end - # We count the line numbers for lines in the new file, which means we need to exclude unchanged lines, and the special "no newline" line line_number += 1 unless line.start_with?('-') || line.chomp == '\ No newline at end of file' + line = diff_enum.next rescue nil end changes @@ -347,9 +371,8 @@ def create_pr_comment_from_changes(todo_changes, full_repo_name) end end end - comment_footer = "\n----\nDid I do good? Let me know by [helping maintain this app](https://github.com/sponsors/NikkelM)!" - { title: check_run_title, summary: comment_summary, body: comment_body, footer: comment_footer } + { title: check_run_title, summary: comment_summary, body: comment_body } end # (8) If the app has already created a comment on the Pull Request, return a reference to it, otherwise return nil diff --git a/validation/validation.js b/validation/validation.js index 9a16e58..d20d010 100644 --- a/validation/validation.js +++ b/validation/validation.js @@ -1,10 +1,10 @@ -// TODO: Alerts in this file: ? if multiline comments, 1 if standalone_items, 5 else +// TODO: Alerts in this file: ? if multiline comments, 1 if standalone_items, 6 else let a = "todo"; //TODO: Alert let b = "todo"; // TODO Alert let c = "todo"; -/* TODO: Alert */ +/* TODO: Alert */ let d = "todo"; /* TODO: Alert let alertThis = "TODO"; diff --git a/version.rb b/version.rb index b46dd90..4d74e07 100644 --- a/version.rb +++ b/version.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -VERSION = '1.2.0' +VERSION = '1.3.0'