From 41b96e262a7b6b6ffa2c4c3cdb1b7e8a9b324aa9 Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Sun, 25 Aug 2024 18:38:34 -0700 Subject: [PATCH 1/5] mock REST API calls in tests --- .github/workflows/binary-builds.yml | 2 +- .github/workflows/python-packaging.yml | 6 +- .github/workflows/run-dev-tests.yml | 16 +- .pre-commit-config.yaml | 2 +- Cargo.lock | 92 ++ cpp-linter-lib/Cargo.toml | 5 + cpp-linter-lib/examples/gh_rest_api.rs | 34 - cpp-linter-lib/src/clang_tools/clang_tidy.rs | 4 +- cpp-linter-lib/src/cli/mod.rs | 2 +- cpp-linter-lib/src/common_fs/file_filter.rs | 15 +- cpp-linter-lib/src/git.rs | 21 +- cpp-linter-lib/src/lib.rs | 3 +- cpp-linter-lib/src/main.rs | 1 + cpp-linter-lib/src/rest_api/github_api.rs | 406 ++--- cpp-linter-lib/src/rest_api/mod.rs | 210 ++- cpp-linter-lib/src/run.rs | 3 +- .../tests/comment_test_assets/pr_22.diff | 67 + .../comment_test_assets/pr_comments_pg1.json | 1322 +++++++++++++++++ .../comment_test_assets/pr_comments_pg2.json | 266 ++++ ...8756375e0483c7ac2b4d6bbbece420dbbb495.diff | 234 +++ ...8756375e0483c7ac2b4d6bbbece420dbbb495.json | 48 + cpp-linter-lib/tests/comments.rs | 356 +++++ cpp-linter-lib/tests/demo/some source.c | 1 + cpp-linter-lib/tests/demo/some source.cpp | 1 - cspell.config.yml | 4 + justfile | 5 +- 26 files changed, 2853 insertions(+), 273 deletions(-) delete mode 100644 cpp-linter-lib/examples/gh_rest_api.rs create mode 100644 cpp-linter-lib/tests/comment_test_assets/pr_22.diff create mode 100644 cpp-linter-lib/tests/comment_test_assets/pr_comments_pg1.json create mode 100644 cpp-linter-lib/tests/comment_test_assets/pr_comments_pg2.json create mode 100644 cpp-linter-lib/tests/comment_test_assets/push_8d68756375e0483c7ac2b4d6bbbece420dbbb495.diff create mode 100644 cpp-linter-lib/tests/comment_test_assets/push_comments_8d68756375e0483c7ac2b4d6bbbece420dbbb495.json create mode 100644 cpp-linter-lib/tests/comments.rs create mode 100644 cpp-linter-lib/tests/demo/some source.c delete mode 100644 cpp-linter-lib/tests/demo/some source.cpp diff --git a/.github/workflows/binary-builds.yml b/.github/workflows/binary-builds.yml index 0af6935..7c6f3f3 100644 --- a/.github/workflows/binary-builds.yml +++ b/.github/workflows/binary-builds.yml @@ -151,7 +151,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - files=$(ls dist/cpp-linter* + files=$(ls dist/cpp-linter*) gh release create ${{ github.ref_name }} --generate-notes $files - run: cargo publish working-directory: cpp-linter-lib diff --git a/.github/workflows/python-packaging.yml b/.github/workflows/python-packaging.yml index eb03448..1e912e3 100644 --- a/.github/workflows/python-packaging.yml +++ b/.github/workflows/python-packaging.yml @@ -111,9 +111,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.x' - - name: Increment version - if: startsWith(github.ref, 'refs/tags/') - run: python .github/workflows/replace_version_spec.py --new-version=${{ github.ref_name }} - name: Build wheels uses: PyO3/maturin-action@v1 with: @@ -153,7 +150,8 @@ jobs: - name: Publish to PyPI uses: PyO3/maturin-action@v1 env: - MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + MATURIN_PYPI_TOKEN: ${{ contains(github.ref_name, 'rc') && secrets.TEST_PYPI_API_TOKEN || secrets.PYPI_API_TOKEN }} + MATURIN_REPOSITORY: ${{ contains(github.ref_name, 'rc') && 'test-pypi' || 'pypi' }} with: command: upload args: --non-interactive --skip-existing dist/* diff --git a/.github/workflows/run-dev-tests.yml b/.github/workflows/run-dev-tests.yml index c3f2b3c..99f7816 100644 --- a/.github/workflows/run-dev-tests.yml +++ b/.github/workflows/run-dev-tests.yml @@ -76,16 +76,16 @@ jobs: with: python-version: 3.x - # - name: Install workflow deps - # run: python3 -m pip install meson + - name: Install workflow deps + run: python3 -m pip install meson # # https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages - # - name: Install ninja (Linux) - # if: runner.os == 'Linux' - # run: sudo apt-get install ninja-build - # - name: Install ninja (Windows) - # if: runner.os == 'Windows' - # run: choco install ninja + - name: Install ninja (Linux) + if: runner.os == 'Linux' + run: sudo apt-get install ninja-build + - name: Install ninja (Windows) + if: runner.os == 'Windows' + run: choco install ninja - name: Install Linux clang dependencies if: runner.os == 'Linux' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b70074..21aeeac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ repos: rev: v4.6.0 hooks: - id: trailing-whitespace - exclude: cpp-linter-lib/tests/capture_tools_output/cpp-linter/cpp-linter/test_git_lib.patch + exclude: cpp-linter-lib/tests/.*\.(?:patch|diff) - id: end-of-file-fixer - id: check-docstring-first - id: check-added-large-files diff --git a/Cargo.lock b/Cargo.lock index b18f6c1..41d63f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,16 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -221,7 +231,9 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", + "wasm-bindgen", "windows-targets 0.52.6", ] @@ -279,6 +291,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -299,10 +321,13 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" name = "cpp-linter-lib" version = "2.0.0" dependencies = [ + "chrono", "clap", + "futures", "git2", "lenient_semver", "log", + "mockito", "openssl", "openssl-probe", "regex", @@ -540,6 +565,21 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -556,6 +596,23 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + [[package]] name = "futures-macro" version = "0.3.30" @@ -585,10 +642,13 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -877,6 +937,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1322,6 +1383,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockito" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "log", + "rand", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -2113,6 +2198,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "similar" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" + [[package]] name = "siphasher" version = "0.3.11" @@ -2314,6 +2405,7 @@ dependencies = [ "bytes", "libc", "mio 1.0.2", + "parking_lot", "pin-project-lite", "socket2", "tokio-macros", diff --git a/cpp-linter-lib/Cargo.toml b/cpp-linter-lib/Cargo.toml index fa348eb..7127c25 100644 --- a/cpp-linter-lib/Cargo.toml +++ b/cpp-linter-lib/Cargo.toml @@ -8,7 +8,9 @@ documentation.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chrono = "0.4.38" clap = "4.5.16" +futures = "0.3.30" git2 = "0.19.0" lenient_semver = "0.4.2" log = "0.4.22" @@ -26,6 +28,7 @@ tokio-stream = "0.1.15" which = "6.0.3" [dev-dependencies] +mockito = "1.5.0" tempfile = "3.9.0" [features] @@ -34,3 +37,5 @@ openssl-vendored = ["dep:openssl", "dep:openssl-probe"] [[bin]] name = "cpp-linter" path = "src/main.rs" +test = false +bench = false diff --git a/cpp-linter-lib/examples/gh_rest_api.rs b/cpp-linter-lib/examples/gh_rest_api.rs deleted file mode 100644 index 1ec8b94..0000000 --- a/cpp-linter-lib/examples/gh_rest_api.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::env; -use std::error::Error; - -use cpp_linter_lib::common_fs::FileFilter; -use cpp_linter_lib::github_api::GithubApiClient; - -// needed to use trait implementations (ie `get_list_of_changed_files()`) -use cpp_linter_lib::rest_api::RestApiClient; - -#[tokio::main] -pub async fn main() -> Result<(), Box> { - env::set_var("GITHUB_SHA", "950ff0b690e1903797c303c5fc8d9f3b52f1d3c5"); - env::set_var("GITHUB_REPOSITORY", "cpp-linter/cpp-linter"); - let client_controller = GithubApiClient::new(); - - let file_filter = FileFilter::new( - &["target".to_string(), ".github".to_string()], - vec!["cpp".to_string(), "hpp".to_string()], - ); - - env::set_var("CI", "true"); // needed for get_list_of_changed_files() to use REST API - let files = client_controller - .get_list_of_changed_files(&file_filter) - .await; - - for file in &files { - println!("{}", file.name.to_string_lossy()); - println!("lines with additions: {:?}", file.added_lines); - println!("ranges of added lines: {:?}", file.added_ranges); - println!("ranges of diff hunks: {:?}", file.diff_chunks); - } - println!("found {} files in diff", files.len()); - Ok(()) -} diff --git a/cpp-linter-lib/src/clang_tools/clang_tidy.rs b/cpp-linter-lib/src/clang_tools/clang_tidy.rs index ca84420..8312c8c 100644 --- a/cpp-linter-lib/src/clang_tools/clang_tidy.rs +++ b/cpp-linter-lib/src/clang_tools/clang_tidy.rs @@ -77,7 +77,7 @@ impl TidyNotification { pub fn diagnostic_link(&self) -> String { let ret_val = if let Some((category, name)) = self.diagnostic.split_once('-') { format!( - "[{}](https://clang.llvm.org/extra/clang-tidy/checks/{category}/{name}).html", + "[{}](https://clang.llvm.org/extra/clang-tidy/checks/{category}/{name}.html)", self.diagnostic ) } else { @@ -153,7 +153,7 @@ fn parse_tidy_output( line: captured[2].parse::().unwrap(), cols: captured[3].parse::().unwrap(), severity: String::from(&captured[4]), - rationale: String::from(&captured[5]), + rationale: String::from(&captured[5]).trim().to_string(), diagnostic: String::from(&captured[6]), suggestion: Vec::new(), }); diff --git a/cpp-linter-lib/src/cli/mod.rs b/cpp-linter-lib/src/cli/mod.rs index 0f6e9b6..e26c3a5 100644 --- a/cpp-linter-lib/src/cli/mod.rs +++ b/cpp-linter-lib/src/cli/mod.rs @@ -226,7 +226,7 @@ cpp-linter --extra-arg="-std=c++17" --extra-arg="-Wall" Arg::new("thread-comments") .long("thread-comments") .short('g') - .value_parser(["true", "on", "1", "false", "off", "0", "updated"]) + .value_parser(["true", "on", "1", "false", "off", "0", "update"]) .default_value("false") .help_heading("feedback options") .help( diff --git a/cpp-linter-lib/src/common_fs/file_filter.rs b/cpp-linter-lib/src/common_fs/file_filter.rs index 8b568bb..17cecc8 100644 --- a/cpp-linter-lib/src/common_fs/file_filter.rs +++ b/cpp-linter-lib/src/common_fs/file_filter.rs @@ -1,4 +1,7 @@ -use std::{fs, path::Path}; +use std::{ + fs, + path::{Path, PathBuf}, +}; use super::FileObj; @@ -82,13 +85,21 @@ impl FileFilter { /// Returns a [`Some`] value of the the path/pattern that matches the given `file_name`. /// If given `file_name` is not in the specified list, then [`None`] is returned. pub fn is_file_in_list(&self, file_name: &Path, is_ignored: bool) -> Option { + let file_name = PathBuf::from(format!( + "./{}", + file_name + .as_os_str() + .to_string_lossy() + .to_string() + .replace("\\", "/") + )); let set = if is_ignored { &self.ignored } else { &self.not_ignored }; for pattern in set { - let pat = Path::new(&pattern); + let pat = PathBuf::from(&pattern); if (pat.is_file() && file_name == pat) || (pat.is_dir() && file_name.starts_with(pat)) { return Some(pattern.to_owned()); } diff --git a/cpp-linter-lib/src/git.rs b/cpp-linter-lib/src/git.rs index 683ffba..b9eaed9 100644 --- a/cpp-linter-lib/src/git.rs +++ b/cpp-linter-lib/src/git.rs @@ -253,36 +253,36 @@ mod brute_force_parse_diff { git::parse_diff_from_buf, }; - static RENAMED_DIFF: &str = r#"diff --git a/tests/demo/some source.cpp b/tests/demo/some source.cpp + static RENAMED_DIFF: &str = r#"diff --git a/tests/demo/some source.cpp b/tests/demo/some source.c similarity index 100% rename from /tests/demo/some source.cpp -rename to /tests/demo/some source.cpp +rename to /tests/demo/some source.c diff --git a/some picture.png b/some picture.png new file mode 100644 Binary files /dev/null and b/some picture.png differ "#; - static RENAMED_DIFF_WITH_CHANGES: &str = r#"diff --git a/tests/demo/some source.cpp b/tests/demo/some source.cpp + static RENAMED_DIFF_WITH_CHANGES: &str = r#"diff --git a/tests/demo/some source.cpp b/tests/demo/some source.c similarity index 99% rename from /tests/demo/some source.cpp -rename to /tests/demo/some source.cpp +rename to /tests/demo/some source.c @@ -3,7 +3,7 @@ \n \n \n-#include "iomanip" -+#include \n \n \n \n"#; ++#include \n \n \n \n"#; #[test] fn parse_renamed_diff() { let diff_buf = RENAMED_DIFF.as_bytes(); let files = parse_diff_from_buf( diff_buf, - &FileFilter::new(&["target".to_string()], vec![String::from("cpp")]), + &FileFilter::new(&["target".to_string()], vec!["c".to_string()]), ); assert!(!files.is_empty()); assert!(files .first() .unwrap() .name - .ends_with("tests/demo/some source.cpp")); + .ends_with("tests/demo/some source.c")); } #[test] @@ -290,7 +290,7 @@ rename to /tests/demo/some source.cpp let diff_buf = RENAMED_DIFF_WITH_CHANGES.as_bytes(); let files = parse_diff_from_buf( diff_buf, - &FileFilter::new(&["target".to_string()], vec![String::from("cpp")]), + &FileFilter::new(&["target".to_string()], vec!["c".to_string()]), ); assert!(!files.is_empty()); } @@ -378,7 +378,10 @@ mod test { use tempfile::{tempdir, TempDir}; - use crate::{common_fs::FileFilter, github_api::GithubApiClient, rest_api::RestApiClient}; + use crate::{ + common_fs::FileFilter, + rest_api::{github_api::GithubApiClient, RestApiClient}, + }; fn get_temp_dir() -> TempDir { let tmp = tempdir().unwrap(); diff --git a/cpp-linter-lib/src/lib.rs b/cpp-linter-lib/src/lib.rs index 6857c8a..eb29735 100644 --- a/cpp-linter-lib/src/lib.rs +++ b/cpp-linter-lib/src/lib.rs @@ -11,7 +11,6 @@ pub mod clang_tools; pub mod cli; pub mod common_fs; pub mod git; -pub mod rest_api; -pub use rest_api::github_api; pub mod logger; +pub mod rest_api; pub mod run; diff --git a/cpp-linter-lib/src/main.rs b/cpp-linter-lib/src/main.rs index c368198..5815e81 100644 --- a/cpp-linter-lib/src/main.rs +++ b/cpp-linter-lib/src/main.rs @@ -1,3 +1,4 @@ +#![cfg(not(test))] /// This crate is the binary executable's entrypoint. use std::env; diff --git a/cpp-linter-lib/src/rest_api/github_api.rs b/cpp-linter-lib/src/rest_api/github_api.rs index e145152..73c1266 100644 --- a/cpp-linter-lib/src/rest_api/github_api.rs +++ b/cpp-linter-lib/src/rest_api/github_api.rs @@ -7,9 +7,9 @@ use std::io::{Read, Write}; use std::sync::{Arc, Mutex}; // non-std crates -use reqwest::header::{HeaderMap, HeaderValue}; -use reqwest::Client; +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; use reqwest::Method; +use reqwest::{Client, Url}; use serde::Deserialize; use serde_json; @@ -20,10 +20,7 @@ use crate::cli::{FeedbackInput, ThreadComments}; use crate::common_fs::{FileFilter, FileObj}; use crate::git::{get_diff, open_repo, parse_diff, parse_diff_from_buf}; -use super::{RestApiClient, COMMENT_MARKER}; - -static USER_AGENT: &str = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0"; +use super::{RestApiClient, RestApiRateLimitHeaders, COMMENT_MARKER}; /// A structure to work with Github REST API. pub struct GithubApiClient { @@ -37,7 +34,7 @@ pub struct GithubApiClient { pub event_name: String, /// The value of the `GITHUB_API_URL` environment variable. - api_url: String, + api_url: Url, /// The value of the `GITHUB_REPOSITORY` environment variable. repo: Option, @@ -47,6 +44,8 @@ pub struct GithubApiClient { /// The value of the `ACTIONS_STEP_DEBUG` environment variable. pub debug_enabled: bool, + + rate_limit_headers: RestApiRateLimitHeaders, } impl Default for GithubApiClient { @@ -71,7 +70,7 @@ impl GithubApiClient { .read_to_string(file_buf) .unwrap(); let json = serde_json::from_str::>( - file_buf.as_str(), + file_buf, ) .unwrap(); json["number"].as_i64() @@ -79,12 +78,21 @@ impl GithubApiClient { _ => None, } }; + let api_url = Url::parse( + env::var("GITHUB_API_URL") + .unwrap_or("https://api.github.com".to_string()) + .as_str(), + ) + .expect("Failed to parse URL from GITHUB_API_URL"); GithubApiClient { - client: Client::new(), + client: Client::builder() + .default_headers(Self::make_headers()) + .build() + .expect("Failed to create a session client for REST API calls"), pull_request, event_name, - api_url: env::var("GITHUB_API_URL").unwrap_or(String::from("https://api.github.com")), + api_url, repo: match env::var("GITHUB_REPOSITORY") { Ok(val) => Some(val), Err(_) => None, @@ -97,6 +105,11 @@ impl GithubApiClient { Ok(val) => val == "true", Err(_) => false, }, + rate_limit_headers: RestApiRateLimitHeaders { + reset: "x-ratelimit-reset".to_string(), + remaining: "x-ratelimit-remaining".to_string(), + retry: "retry-after".to_string(), + }, } } } @@ -136,18 +149,16 @@ impl RestApiClient for GithubApiClient { checks_failed } - fn make_headers(&self, use_diff: Option) -> HeaderMap { + fn make_headers() -> HeaderMap { let mut headers = HeaderMap::new(); - let return_fmt = "application/vnd.github.".to_owned() - + if use_diff.is_some_and(|val| val) { - "diff" - } else { - "raw+json" - }; + let return_fmt = "application/vnd.github.raw+json".to_owned(); headers.insert("Accept", return_fmt.parse().unwrap()); - headers.insert("User-Agent", USER_AGENT.parse().unwrap()); + // headers.insert("User-Agent", USER_AGENT.parse().unwrap()); if let Ok(token) = env::var("GITHUB_TOKEN") { - headers.insert("Authorization", token.parse().unwrap()); + let mut val = HeaderValue::from_str(token.as_str()) + .expect("Failed to create a secure header value for the API token."); + val.set_sensitive(true); + headers.insert(AUTHORIZATION, val); } headers } @@ -158,29 +169,35 @@ impl RestApiClient for GithubApiClient { && self.sha.is_some() { // get diff from Github REST API - let url = format!( - "{}/repos/{}/{}", - self.api_url, - self.repo.as_ref().unwrap(), - if self.event_name == "pull_request" { - format!( - "pulls/{}", - &self.pull_request.expect("Pull request number unknown") - ) - } else { - format!("commits/{}", self.sha.as_ref().unwrap()) - } - ); - let response = self - .client - .get(url) - .headers(self.make_headers(Some(true))) - .send() - .await + let is_pr = self.event_name == "pull_request"; + let pr = self.pull_request.unwrap_or(-1).to_string(); + let sha = self.sha.clone().unwrap(); + let url = self + .api_url + .join("repos/") .unwrap() - .bytes() - .await + .join(format!("{}/", self.repo.as_ref().unwrap()).as_str()) + .unwrap() + .join(if is_pr { "pulls/" } else { "commits/" }) + .unwrap() + .join(if is_pr { pr.as_str() } else { sha.as_str() }) .unwrap(); + let mut diff_header = HeaderMap::new(); + diff_header.insert("Accept", "application/vnd.github.diff".parse().unwrap()); + let request = + Self::make_api_request(&self.client, url, Method::GET, None, Some(diff_header)); + let response = Self::send_api_request( + self.client.clone(), + request, + true, + self.rate_limit_headers.to_owned(), + 0, + ) + .await + .unwrap() + .bytes() + .await + .unwrap(); parse_diff_from_buf(&response, file_filter) } else { @@ -227,50 +244,29 @@ impl RestApiClient for GithubApiClient { } if let Some(repo) = &self.repo { let is_pr = self.event_name == "pull_request"; - let base_url = format!("{}/repos/{}/", &self.api_url, &repo); - let comments_url = if is_pr { - format!( - "{base_url}issues/{}", - &self.pull_request.expect("Pull request number unknown") - ) - } else { - format!("{base_url}/commits/{}", &self.sha.as_ref().unwrap()) - }; - - // get count of comments - let request = self - .client - .get(&comments_url) - .headers(self.make_headers(None)) - .send() - .await; - if let Ok(response) = request { - let json = response.json::().await.unwrap(); - let count = if is_pr { - json["comments"].as_u64().unwrap() - } else { - json["commit"]["comment_count"].as_u64().unwrap() - }; - self.update_comment( - &format!("{}/comments", &comments_url), - &comment.unwrap(), - count, - user_inputs.no_lgtm, - format_checks_failed + tidy_checks_failed == 0, - user_inputs.thread_comments == ThreadComments::Update, - ) - .await; - } else { - let error = request.unwrap_err(); - if let Some(status) = error.status() { - log::error!( - "Could not get comment count. Got response {:?} from {comments_url}", - status - ); - } else { - log::error!("attempt GET comment count failed"); - } - } + let pr = self.pull_request.unwrap_or(-1).to_string() + "/"; + let sha = self.sha.clone().unwrap() + "/"; + let comments_url = self + .api_url + .join("repos/") + .unwrap() + .join(format!("{}/", repo).as_str()) + .unwrap() + .join(if is_pr { "issues/" } else { "commits/" }) + .unwrap() + .join(if is_pr { pr.as_str() } else { sha.as_str() }) + .unwrap() + .join("comments/") + .unwrap(); + + self.update_comment( + comments_url, + &comment.unwrap(), + user_inputs.no_lgtm, + format_checks_failed + tidy_checks_failed == 0, + user_inputs.thread_comments == ThreadComments::Update, + ) + .await; } } format_checks_failed + tidy_checks_failed @@ -284,7 +280,7 @@ impl GithubApiClient { .append(true) .open(gh_out) .expect("GITHUB_STEP_SUMMARY file could not be opened"); - if let Err(e) = writeln!(gh_out_file, "\n{}\n", comment,) { + if let Err(e) = writeln!(gh_out_file, "\n{}\n", comment) { panic!("Could not write to GITHUB_STEP_SUMMARY file: {}", e); } } @@ -351,127 +347,173 @@ impl GithubApiClient { } /// update existing comment or remove old comment(s) and post a new comment - #[allow(clippy::too_many_arguments)] async fn update_comment( &self, - url: &String, + url: Url, comment: &String, - count: u64, no_lgtm: bool, is_lgtm: bool, update_only: bool, ) { let comment_url = self - .remove_bot_comments(url, count, !update_only || (is_lgtm && no_lgtm)) + .remove_bot_comments(&url, !update_only || (is_lgtm && no_lgtm)) .await; #[allow(clippy::nonminimal_bool)] // an inaccurate assessment if (is_lgtm && !no_lgtm) || !is_lgtm { - let payload = HashMap::from([("body", comment)]); + let payload = HashMap::from([("body", comment.to_owned())]); + #[cfg(not(test))] log::debug!("payload body:\n{:?}", payload); let req_meth = if comment_url.is_some() { Method::PATCH } else { Method::POST }; - if let Ok(response) = self - .client - .request( - req_meth.clone(), - if let Some(_url) = comment_url { - _url - } else { - url.to_string() - }, - ) - .headers(self.make_headers(None)) - .json(&payload) - .send() - .await - { - log::info!( - "Got {} response from {:?}ing comment", - response.status(), - req_meth, - ); - } + let request = Self::make_api_request( + &self.client, + if let Some(url_) = comment_url { + url_ + } else { + url + }, + req_meth, + Some(payload), + None, + ); + Self::send_api_request( + self.client.clone(), + request, + false, + self.rate_limit_headers.to_owned(), + 0, + ) + .await; } } - async fn remove_bot_comments(&self, url: &String, count: u64, delete: bool) -> Option { - let mut page = 1; + async fn remove_bot_comments(&self, url: &Url, delete: bool) -> Option { let mut comment_url = None; - let mut total = count; - while total > 0 { - let request = self.client.get(format!("{url}/?page={page}")).send().await; - if request.is_err() { - log::error!("Failed to get list of existing comments"); - return None; - } else if let Ok(response) = request { - let payload: JsonCommentsPayload = response.json().await.unwrap(); - let mut comment_count = 0; - for comment in payload.comments { - if comment.body.starts_with(COMMENT_MARKER) { - log::debug!( - "comment id {} from user {} ({})", - comment.id, - comment.user.login, - comment.user.id, - ); - #[allow(clippy::nonminimal_bool)] // an inaccurate assessment - if delete || (!delete && comment_url.is_none()) { - // if not updating: remove all outdated comments - // if updating: remove all outdated comments except the last one - - // use last saved comment_url (if not None) or current comment url - let del_url = if let Some(last_url) = &comment_url { - last_url - } else { - &comment.url - }; - if let Ok(response) = self - .client - .delete(del_url) - .headers(self.make_headers(None)) - .send() + let mut comments_url = Some( + Url::parse_with_params(url.as_str(), &[("page", "1")]) + .expect("Failed to parse invalid URL string"), + ); + let repo = format!( + "repos/{}/comments/", + self.repo.as_ref().expect("Repo name unknown.") + ); + let base_comment_url = self.api_url.join(&repo).unwrap(); + while let Some(ref endpoint) = comments_url { + let request = + Self::make_api_request(&self.client, endpoint.as_str(), Method::GET, None, None); + match Self::send_api_request( + self.client.clone(), + request, + false, + self.rate_limit_headers.to_owned(), + 0, + ) + .await + { + None => { + log::error!("Failed to get list of existing comments from {}", endpoint); + return comment_url; + } + Some(response) => { + if !response.status().is_success() { + log::error!("Failed to get list of existing comments from {}", endpoint); + return comment_url; + } + comments_url = Self::try_next_page(response.headers()); + let payload: Vec = response + .json() + .await + .expect("Unable to deserialize malformed JSON about comments"); + for comment in payload { + if comment.body.starts_with(COMMENT_MARKER) { + log::debug!( + "comment id {} from user {} ({})", + comment.id, + comment.user.login, + comment.user.id, + ); + #[allow(clippy::nonminimal_bool)] // an inaccurate assessment + if delete || (!delete && comment_url.is_none()) { + // if not updating: remove all outdated comments + // if updating: remove all outdated comments except the last one + + // use last saved comment_url (if not None) or current comment url + let del_url = if let Some(last_url) = &comment_url { + last_url + } else { + let comment_id = comment.id.to_string(); + &base_comment_url + .join(&comment_id) + .expect("Failed to parse URL from JSON comment.url") + }; + let req = Self::make_api_request( + &self.client, + del_url.clone(), + Method::DELETE, + None, + None, + ); + match Self::send_api_request( + self.client.clone(), + req, + false, + self.rate_limit_headers.to_owned(), + 0, + ) .await - { - log::info!( - "Got {} from DELETE {}", - response.status(), - del_url.strip_prefix(&self.api_url).unwrap(), + { + Some(res) => { + log::info!( + "Got {} from DELETE {}", + res.status(), + del_url.path(), + ) + } + None => { + log::error!("Unable to remove old bot comment"); + // exit early as this is most likely due to rate limit. + return comment_url; + } + } + } + if !delete { + let comment_id = comment.id.to_string(); + comment_url = Some( + base_comment_url + .join(&comment_id) + .expect("Failed to parse URL from JSON comment.url"), ) - } else { - log::error!("Unable to remove old bot comment"); - return None; // exit early as this is most likely due to rate limit. } } - if !delete { - comment_url = Some(comment.url) - } } - comment_count += 1; } - total -= comment_count; - page += 1; } } comment_url } } -#[derive(Debug, Deserialize, PartialEq)] -struct JsonCommentsPayload { - comments: Vec, -} - +/// A structure for deserializing a comment from a response's json. #[derive(Debug, Deserialize, PartialEq, Clone)] struct Comment { + /// The comment's ID number. pub id: i64, + /// The comment's url number. pub url: String, + /// The comment's body number. pub body: String, + /// The comment's user number. + /// + /// This is only used for debug output. pub user: User, } +/// A structure for deserializing a comment's author from a response's json. +/// +/// This is only used for debug output. #[derive(Debug, Deserialize, PartialEq, Clone)] struct User { pub login: String, @@ -490,7 +532,7 @@ mod test { use regex::Regex; use tempfile::{tempdir, NamedTempFile}; - use super::{GithubApiClient, USER_AGENT}; + use super::GithubApiClient; use crate::{ clang_tools::capture_clang_tools_output, cli::{ClangParams, FeedbackInput, LinesChangedOnly}, @@ -498,38 +540,6 @@ mod test { rest_api::{RestApiClient, USER_OUTREACH}, }; - // ************************** tests for GithubApiClient::make_headers() - fn assert_header(use_diff: bool, auth: Option<&str>) { - let rest_api_client = GithubApiClient::new(); - if let Some(token) = auth { - env::set_var("GITHUB_TOKEN", token); - } - let headers = rest_api_client.make_headers(Some(use_diff)); - assert!(headers.contains_key("User-Agent")); - assert_eq!(headers.get("User-Agent").unwrap(), USER_AGENT); - assert!(headers.contains_key("Accept")); - assert!(headers - .get("Accept") - .unwrap() - .to_str() - .unwrap() - .ends_with(if use_diff { "diff" } else { "raw+json" })); - if let Some(token) = auth { - assert!(headers.contains_key("Authorization")); - assert_eq!(headers.get("Authorization").unwrap(), token); - } - } - - #[test] - fn get_headers_json_token() { - assert_header(false, Some("123456")); - } - - #[test] - fn get_headers_diff() { - assert_header(true, None); - } - // ************************* tests for step-summary and output variables async fn create_comment(tidy_checks: &str, style: &str) -> (String, String) { diff --git a/cpp-linter-lib/src/rest_api/mod.rs b/cpp-linter-lib/src/rest_api/mod.rs index a7aaa21..5ccd29c 100644 --- a/cpp-linter-lib/src/rest_api/mod.rs +++ b/cpp-linter-lib/src/rest_api/mod.rs @@ -3,20 +3,38 @@ //! //! Currently, only Github is supported. +use std::collections::HashMap; +use std::future::Future; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use std::{future::Future, path::PathBuf}; +use std::time::Duration; // non-std crates +use chrono::DateTime; +use futures::future::{BoxFuture, FutureExt}; use reqwest::header::{HeaderMap, HeaderValue}; +use reqwest::{Client, IntoUrl, Method, Request, Response, Url}; // project specific modules/crates pub mod github_api; use crate::cli::FeedbackInput; use crate::common_fs::{FileFilter, FileObj}; -pub static COMMENT_MARKER: &str = ""; +pub static COMMENT_MARKER: &str = "\n"; pub static USER_OUTREACH: &str = "\n\nHave any feedback or feature suggestions? [Share it here.](https://github.com/cpp-linter/cpp-linter-action/issues)"; +/// A structure to contain the different forms of headers that +/// describe a REST API's rate limit status. +#[derive(Debug, Clone)] +pub struct RestApiRateLimitHeaders { + /// The header key of the rate limit's reset time. + pub reset: String, + /// The header key of the rate limit's remaining attempts. + pub remaining: String, + /// The header key of the rate limit's "backoff" time interval. + pub retry: String, +} + /// A custom trait that templates necessary functionality with a Git server's REST API. pub trait RestApiClient { /// A way to set output variables specific to cpp_linter executions in CI. @@ -29,9 +47,160 @@ pub trait RestApiClient { /// A convenience method to create the headers attached to all REST API calls. /// - /// If an authentication token is provided, this method shall include the relative - /// information in the returned [HeaderMap]. - fn make_headers(&self, use_diff: Option) -> HeaderMap; + /// If an authentication token is provided (via environment variable), + /// this method shall include the relative information. + fn make_headers() -> HeaderMap; + + /// Construct a HTTP request to be sent. + /// + /// The idea here is that this method is called before [`RestApiClient::send_api_request()`]. + /// ```ignore + /// let request = Self::make_api_request( + /// &self.client, + /// "https://example.com", + /// Method::GET, + /// None, + /// None + /// ); + /// let response = Self::send_api_request( + /// self.client.clone(), + /// request, + /// false, // false means don't panic + /// 0, // start recursion count at 0 + /// ); + /// match response.await { + /// Some(res) => {/* handle response */} + /// None => {/* handle failure */} + /// } + /// ``` + fn make_api_request( + client: &Client, + url: impl IntoUrl, + method: Method, + data: Option>, + headers: Option, + ) -> Request { + let mut req = client.request(method, url); + if let Some(h) = headers { + req = req.headers(h); + } + if let Some(d) = data { + req = req.json(&d); + } + req.build().expect("Failed to create a HTTP request") + } + + /// A convenience function to send HTTP requests and respect a REST API rate limits. + /// + /// This method must own all the data passed to it because asynchronous recursion is used. + /// Recursion is needed when a secondary rate limit is hit. The server tells the client that + /// it should back off and retry after a specified time interval. + /// + /// Setting the `strict` parameter to true will panic when the HTTP request fails to send or + /// the HTTP response's status code does not describe success. This should only be used for + /// requests that are vital to the app operation. + /// With `strict` as true, the returned type is guaranteed to be a [`Some`] value. + /// If `strict` is false, then a failure to send the request is returned as a [`None`] value, + /// and a [`Some`] value could also indicate if the server failed to process the request. + fn send_api_request( + client: Client, + request: Request, + strict: bool, + rate_limit_headers: RestApiRateLimitHeaders, + retries: u64, + ) -> BoxFuture<'static, Option> { + async move { + match client + .execute( + request + .try_clone() + .expect("Could not clone HTTP request object"), + ) + .await + { + Ok(response) => { + if [403u16, 429u16].contains(&response.status().as_u16()) { + // rate limit exceeded + + // check if primary rate limit was violated; panic if so. + let remaining = response + .headers() + .get(&rate_limit_headers.remaining) + .expect("Response headers do not include remaining API usage count") + .to_str() + .expect("Failed to extract remaining attempts about rate limit") + .parse::() + .expect("Failed to parse i64 from remaining attempts about rate limit"); + let reset = DateTime::from_timestamp( + response + .headers() + .get(&rate_limit_headers.reset) + .expect("response headers does not include a reset timestamp") + .to_str() + .expect("Failed to extract reset info about rate limit") + .parse::() + .expect("Failed to parse i64 from reset time about rate limit"), + 0, + ) + .expect("rate limit reset UTC timestamp is an invalid"); + if remaining <= 0 { + panic!("REST API rate limit exceeded! Resets at {}", reset); + } + + // check if secondary rate limit is violated; backoff and try again. + if retries >= 5 { + panic!("REST API secondary rate limit exceeded"); + } + if let Some(retry) = response.headers().get(&rate_limit_headers.retry) { + let interval = Duration::from_secs( + retry + .to_str() + .expect("Failed to extract retry interval about rate limit") + .parse::() + .expect( + "Failed to parse u64 from retry interval about rate limit", + ) + + retries.pow(2), + ); + tokio::time::sleep(interval).await; + return Self::send_api_request( + client, + request, + strict, + rate_limit_headers, + retries + 1, + ) + .await; + } + } + if !response.status().is_success() { + let summary = format!( + "Got {} response from {}ing to {}", + response.status().as_u16(), + request.method().as_str(), + request.url().as_str(), + ); + if strict { + panic!( + "{summary}: {}", + response.text().await.unwrap_or("".to_string()) + ); + } + log::error!("{summary}"); + } + Some(response) + } + Err(e) => { + if strict { + panic!("Failed to complete the HTTP request.\n{}", e); + } else { + None + } + } + } + } + .boxed() + } /// A way to get the list of changed files using REST API calls. It is this method's /// job to parse diff blobs and return a list of changed files. @@ -58,7 +227,7 @@ pub trait RestApiClient { tidy_checks_failed: u64, max_len: Option, ) -> String { - let mut comment = format!("{COMMENT_MARKER}\n# Cpp-Linter Report "); + let mut comment = format!("{COMMENT_MARKER}# Cpp-Linter Report "); let mut remaining_length = max_len.unwrap_or(u64::MAX) - comment.len() as u64 - USER_OUTREACH.len() as u64; @@ -104,6 +273,33 @@ pub trait RestApiClient { files: &[Arc>], user_inputs: FeedbackInput, ) -> impl Future; + + /// Gets the URL for the next page in a paginated response. + /// + /// Returns [`None`] if current response is the last page. + fn try_next_page(headers: &HeaderMap) -> Option { + if headers.contains_key("link") { + let pages = headers["link"] + .to_str() + .expect("Failed to convert header value of links to a str") + .split(", "); + for page in pages { + if page.ends_with("; rel=\"next\"") { + let url = page + .split_once(">;") + .expect("header link for pagination is malformed") + .0 + .trim_start_matches("<") + .to_string(); + return Some( + Url::parse(&url) + .expect("Failed to parse next page link from response header"), + ); + } + } + } + None + } } fn make_format_comment( @@ -164,7 +360,7 @@ fn make_tidy_comment( concerned_code = if tidy_note.suggestion.is_empty() {String::from("")} else { format!("\n ```{ext}\n {suggestion}\n ```\n", ext = file_path.extension().expect("file extension was not determined").to_string_lossy(), - suggestion = tidy_note.suggestion.join("\n "), + suggestion = tidy_note.suggestion.join("\n "), ).to_string() }, ).to_string()); diff --git a/cpp-linter-lib/src/run.rs b/cpp-linter-lib/src/run.rs index dd9b922..2e7c143 100644 --- a/cpp-linter-lib/src/run.rs +++ b/cpp-linter-lib/src/run.rs @@ -16,9 +16,8 @@ use openssl_probe; use crate::clang_tools::capture_clang_tools_output; use crate::cli::{get_arg_parser, ClangParams, Cli, FeedbackInput, LinesChangedOnly}; use crate::common_fs::FileFilter; -use crate::github_api::GithubApiClient; use crate::logger::{self, end_log_group, start_log_group}; -use crate::rest_api::RestApiClient; +use crate::rest_api::{github_api::GithubApiClient, RestApiClient}; const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/cpp-linter-lib/tests/comment_test_assets/pr_22.diff b/cpp-linter-lib/tests/comment_test_assets/pr_22.diff new file mode 100644 index 0000000..2963281 --- /dev/null +++ b/cpp-linter-lib/tests/comment_test_assets/pr_22.diff @@ -0,0 +1,67 @@ +diff --git a/.github/workflows/cpp-lint-action.yml b/.github/workflows/cpp-lint-action.yml +index 0680634..c77a971 100644 +--- a/.github/workflows/cpp-lint-action.yml ++++ b/.github/workflows/cpp-lint-action.yml +@@ -3,6 +3,7 @@ name: cpp-linter as action + on: + workflow_dispatch: + workflow_call: ++ pull_request: + + + jobs: +@@ -31,21 +32,22 @@ jobs: + run: mkdir build && cmake -Bbuild src + + - name: Run linter as action +- uses: cpp-linter/cpp-linter-action@latest ++ uses: cpp-linter/cpp-linter-action@dependabot/pip/cpp-linter-1.6.1 + id: linter + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + style: file +- files-changed-only: false ++ files-changed-only: true + # to ignore all build folder contents + ignore: build + database: build + verbosity: 9 + version: ${{ matrix.clang-version }} +- thread-comments: ${{ matrix.clang-version == '12' }} ++ thread-comments: ${{ matrix.os == 'ubuntu-latest' && matrix.clang-version == '12' && 'update' }} + file-annotations: ${{ matrix.clang-version == '12' }} + extra-args: -std=c++14 -Wall ++ no-lgtm: true + + - name: Fail fast?! + # if: steps.linter.outputs.checks-failed > 0 +diff --git a/.github/workflows/cpp-lint-package.yml b/.github/workflows/cpp-lint-package.yml +index ea23371..926e443 100644 +--- a/.github/workflows/cpp-lint-package.yml ++++ b/.github/workflows/cpp-lint-package.yml +@@ -16,7 +16,7 @@ jobs: + matrix: + clang-version: ['7', '8', '9','10', '11', '12', '13', '14'] + repo: ['cpp-linter/cpp-linter'] +- branch: ['${{ inputs.branch }}'] ++ branch: ['resolve-34'] # ['${{ inputs.branch }}'] + fail-fast: false + + steps: +@@ -59,11 +59,12 @@ jobs: + -v=9 + -i=build + -p=build +- -V=${{ runner.temp }}/llvm +- -f=false ++ -V=${{ runner.temp }}/llvm ++ -f=true + --extra-arg="-std=c++14 -Wall" +- --thread-comments=${{ matrix.clang-version == '12' }} ++ --thread-comments=${{ matrix.clang-version == '12' && 'true' || 'false' }} + -a=${{ matrix.clang-version == '12' }} ++ --no-lgtm=false + + - name: Fail fast?! + if: steps.linter.outputs.checks-failed > 0 diff --git a/cpp-linter-lib/tests/comment_test_assets/pr_comments_pg1.json b/cpp-linter-lib/tests/comment_test_assets/pr_comments_pg1.json new file mode 100644 index 0000000..39629f1 --- /dev/null +++ b/cpp-linter-lib/tests/comment_test_assets/pr_comments_pg1.json @@ -0,0 +1,1322 @@ +[ + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261434", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261434", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261434, + "node_id": "IC_kwDOFY2uzM5qOya6", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:11:49Z", + "updated_at": "2023-10-27T04:11:49Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261434/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261484", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261484", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261484, + "node_id": "IC_kwDOFY2uzM5qOybs", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:11:54Z", + "updated_at": "2023-10-27T04:11:54Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261484/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261536", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261536", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261536, + "node_id": "IC_kwDOFY2uzM5qOycg", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:11:59Z", + "updated_at": "2023-10-27T04:11:59Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261536/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261586", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261586", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261586, + "node_id": "IC_kwDOFY2uzM5qOydS", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:02Z", + "updated_at": "2023-10-27T04:12:02Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261586/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261619", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261619", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261619, + "node_id": "IC_kwDOFY2uzM5qOydz", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:06Z", + "updated_at": "2023-10-27T04:12:06Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261619/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261669", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261669", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261669, + "node_id": "IC_kwDOFY2uzM5qOyel", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:10Z", + "updated_at": "2023-10-27T04:12:10Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261669/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261697", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261697", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261697, + "node_id": "IC_kwDOFY2uzM5qOyfB", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:13Z", + "updated_at": "2023-10-27T04:12:13Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261697/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261719", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261719", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261719, + "node_id": "IC_kwDOFY2uzM5qOyfX", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:15Z", + "updated_at": "2023-10-27T04:12:15Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261719/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261734", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261734", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261734, + "node_id": "IC_kwDOFY2uzM5qOyfm", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:17Z", + "updated_at": "2023-10-27T04:12:17Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261734/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261768", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261768", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261768, + "node_id": "IC_kwDOFY2uzM5qOygI", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:20Z", + "updated_at": "2023-10-27T04:12:20Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261768/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261807", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261807", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261807, + "node_id": "IC_kwDOFY2uzM5qOygv", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:24Z", + "updated_at": "2023-10-27T04:12:24Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261807/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261835", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782261835", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782261835, + "node_id": "IC_kwDOFY2uzM5qOyhL", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:27Z", + "updated_at": "2023-10-27T04:12:27Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782261835/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262011", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262011", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262011, + "node_id": "IC_kwDOFY2uzM5qOyj7", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:43Z", + "updated_at": "2023-10-27T04:12:43Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262011/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262051", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262051", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262051, + "node_id": "IC_kwDOFY2uzM5qOykj", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:46Z", + "updated_at": "2023-10-27T04:12:46Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262051/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262068", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262068", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262068, + "node_id": "IC_kwDOFY2uzM5qOyk0", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:49Z", + "updated_at": "2023-10-27T04:12:49Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262068/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262099", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262099", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262099, + "node_id": "IC_kwDOFY2uzM5qOylT", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:52Z", + "updated_at": "2023-10-27T04:12:52Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262099/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262128", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262128", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262128, + "node_id": "IC_kwDOFY2uzM5qOylw", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:54Z", + "updated_at": "2023-10-27T04:12:54Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262128/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262146", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262146", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262146, + "node_id": "IC_kwDOFY2uzM5qOymC", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:56Z", + "updated_at": "2023-10-27T04:12:56Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262146/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262170", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262170", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262170, + "node_id": "IC_kwDOFY2uzM5qOyma", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:12:59Z", + "updated_at": "2023-10-27T04:12:59Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262170/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262199", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262199", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262199, + "node_id": "IC_kwDOFY2uzM5qOym3", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:01Z", + "updated_at": "2023-10-27T04:13:01Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262199/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262221", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262221", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262221, + "node_id": "IC_kwDOFY2uzM5qOynN", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:04Z", + "updated_at": "2023-10-27T04:13:04Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262221/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262258", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262258", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262258, + "node_id": "IC_kwDOFY2uzM5qOyny", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:06Z", + "updated_at": "2023-10-27T04:13:06Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262258/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262292", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262292", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262292, + "node_id": "IC_kwDOFY2uzM5qOyoU", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:09Z", + "updated_at": "2023-10-27T04:13:09Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262292/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262313", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262313", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262313, + "node_id": "IC_kwDOFY2uzM5qOyop", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:11Z", + "updated_at": "2023-10-27T04:13:11Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262313/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262343", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262343", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262343, + "node_id": "IC_kwDOFY2uzM5qOypH", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:14Z", + "updated_at": "2023-10-27T04:13:14Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262343/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262374", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262374", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262374, + "node_id": "IC_kwDOFY2uzM5qOypm", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:16Z", + "updated_at": "2023-10-27T04:13:16Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262374/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262410", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262410", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262410, + "node_id": "IC_kwDOFY2uzM5qOyqK", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:18Z", + "updated_at": "2023-10-27T04:13:18Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262410/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262424", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262424", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262424, + "node_id": "IC_kwDOFY2uzM5qOyqY", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:21Z", + "updated_at": "2023-10-27T04:13:21Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262424/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262443", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262443", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262443, + "node_id": "IC_kwDOFY2uzM5qOyqr", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:23Z", + "updated_at": "2023-10-27T04:13:23Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262443/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262467", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262467", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262467, + "node_id": "IC_kwDOFY2uzM5qOyrD", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:25Z", + "updated_at": "2023-10-27T04:13:25Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262467/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + } +] diff --git a/cpp-linter-lib/tests/comment_test_assets/pr_comments_pg2.json b/cpp-linter-lib/tests/comment_test_assets/pr_comments_pg2.json new file mode 100644 index 0000000..e37ec94 --- /dev/null +++ b/cpp-linter-lib/tests/comment_test_assets/pr_comments_pg2.json @@ -0,0 +1,266 @@ +[ + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262495", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262495", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262495, + "node_id": "IC_kwDOFY2uzM5qOyrf", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:27Z", + "updated_at": "2023-10-27T04:13:27Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262495/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262531", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262531", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262531, + "node_id": "IC_kwDOFY2uzM5qOysD", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:30Z", + "updated_at": "2023-10-27T04:13:30Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262531/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262562", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262562", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262562, + "node_id": "IC_kwDOFY2uzM5qOysi", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:33Z", + "updated_at": "2023-10-27T04:13:33Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262562/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262590", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262590", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262590, + "node_id": "IC_kwDOFY2uzM5qOys-", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:35Z", + "updated_at": "2023-10-27T04:13:35Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262590/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262621", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262621", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262621, + "node_id": "IC_kwDOFY2uzM5qOytd", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:38Z", + "updated_at": "2023-10-27T04:13:38Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262621/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + }, + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262659", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/pull/22#issuecomment-1782262659", + "issue_url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/22", + "id": 1782262659, + "node_id": "IC_kwDOFY2uzM5qOyuD", + "user": { + "login": "2bndy5", + "id": 14963867, + "node_id": "MDQ6VXNlcjE0OTYzODY3", + "avatar_url": "https://avatars.githubusercontent.com/u/14963867?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/2bndy5", + "html_url": "https://github.com/2bndy5", + "followers_url": "https://api.github.com/users/2bndy5/followers", + "following_url": "https://api.github.com/users/2bndy5/following{/other_user}", + "gists_url": "https://api.github.com/users/2bndy5/gists{/gist_id}", + "starred_url": "https://api.github.com/users/2bndy5/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/2bndy5/subscriptions", + "organizations_url": "https://api.github.com/users/2bndy5/orgs", + "repos_url": "https://api.github.com/users/2bndy5/repos", + "events_url": "https://api.github.com/users/2bndy5/events{/privacy}", + "received_events_url": "https://api.github.com/users/2bndy5/received_events", + "type": "User", + "site_admin": false + }, + "created_at": "2023-10-27T04:13:40Z", + "updated_at": "2023-10-27T04:13:40Z", + "author_association": "COLLABORATOR", + "body": "A useless comment to ensure traversal of more than 30 comments in a PR thread.", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/issues/comments/1782262659/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + }, + "performed_via_github_app": null + } +] diff --git a/cpp-linter-lib/tests/comment_test_assets/push_8d68756375e0483c7ac2b4d6bbbece420dbbb495.diff b/cpp-linter-lib/tests/comment_test_assets/push_8d68756375e0483c7ac2b4d6bbbece420dbbb495.diff new file mode 100644 index 0000000..c712bbf --- /dev/null +++ b/cpp-linter-lib/tests/comment_test_assets/push_8d68756375e0483c7ac2b4d6bbbece420dbbb495.diff @@ -0,0 +1,234 @@ +diff --git a/.github/workflows/cpp-lint-action.yml b/.github/workflows/cpp-lint-action.yml +new file mode 100644 +index 0000000..9b17f87 +--- /dev/null ++++ b/.github/workflows/cpp-lint-action.yml +@@ -0,0 +1,52 @@ ++name: cpp-linter as action ++ ++on: ++ push: ++ paths-ignore: "docs/**" ++ pull_request: ++ paths-ignore: "docs/**" ++ ++ ++jobs: ++ cpp-linter: ++ runs-on: ubuntu-latest ++ ++ strategy: ++ matrix: ++ clang-version: ['9','10', '11', '12', '13', '14'] ++ fail-fast: false ++ ++ steps: ++ - uses: actions/checkout@v2 ++ ++ - name: Cache the build artifacts ++ id: cache-build ++ uses: actions/cache@v3 ++ with: ++ path: build ++ key: ${{ hashFiles('src/CMakeLists.txt', 'src/demo.cpp', 'src/demo.hpp') }} ++ ++ - name: Generate compilation database ++ if: steps.cache-build.outputs.cache-hit != 'true' ++ run: mkdir build && cmake -Bbuild src ++ ++ - name: run linter as action ++ uses: shenxianpeng/cpp-linter-action@master ++ id: linter ++ env: ++ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ++ with: ++ style: file ++ files-changed-only: ${{ matrix.clang-version == '12' }} ++ # to ignore all build folder contents ++ ignore: build ++ database: build ++ verbosity: 9 ++ version: ${{ matrix.clang-version }} ++ file-annotations: ${{ matrix.clang-version == '12' }} ++ ++ - name: Fail fast?! ++ if: steps.linter.outputs.checks-failed > 0 ++ run: echo "some linter checks failed" ++ # for actual deployment ++ # run: exit 1 +diff --git a/.github/workflows/cpp-lint-package.yml b/.github/workflows/cpp-lint-package.yml +new file mode 100644 +index 0000000..42ecb20 +--- /dev/null ++++ b/.github/workflows/cpp-lint-package.yml +@@ -0,0 +1,54 @@ ++name: cpp-linter as pkg ++ ++on: ++ push: ++ paths-ignore: "docs/**" ++ pull_request: ++ paths-ignore: "docs/**" ++ ++jobs: ++ cpp-linter: ++ runs-on: windows-latest ++ ++ strategy: ++ matrix: ++ clang-version: ['9','10', '11', '12', '13', '14'] ++ repo: ['shenxianpeng/cpp-linter-action'] ++ branch: ['master'] ++ fail-fast: false ++ ++ steps: ++ ++ - uses: actions/checkout@v3 ++ - uses: actions/setup-python@v3 ++ - name: Install clang-tools ++ uses: KyleMayes/install-llvm-action@v1 ++ with: ++ version: ${{ matrix.clang-version }} ++ directory: ${{ runner.temp }}/llvm ++ ++ - name: Install linter package ++ run: python3 -m pip install git+https://github.com/${{ matrix.repo }}/@${{ matrix.branch }} ++ ++ - name: Cache the build artifacts ++ id: cache-build ++ uses: actions/cache@v3 ++ with: ++ path: build ++ key: ${{ hashFiles('src/CMakeLists.txt', 'src/demo.cpp', 'src/demo.hpp') }} ++ ++ - name: Generate compiler database ++ if: steps.cache-build.outputs.cache-hit != 'true' ++ run: mkdir build && cmake -Bbuild src ++ ++ - name: run linter as package ++ id: linter ++ env: ++ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ++ run: cpp-linter -s=file -v=9 -i=build -p=build -V=${{ runner.temp }}/llvm --thread-comments=${{ matrix.clang-version == '12' }} -a=${{ matrix.clang-version == '12' }} ++ ++ - name: Fail fast?! ++ if: steps.linter.outputs.checks-failed > 0 ++ run: echo "Some files failed the linting checks!" ++ # for actual deployment ++ # run: exit 1 +diff --git a/.github/workflows/cpp-lint.yml b/.github/workflows/cpp-lint.yml +deleted file mode 100644 +index 5194143..0000000 +--- a/.github/workflows/cpp-lint.yml ++++ /dev/null +@@ -1,29 +0,0 @@ +-name: cpp-linter +- +-on: +- push: +- paths-ignore: "docs/**" +- pull_request: +- paths-ignore: "docs/**" +- +-jobs: +- cpp-linter: +- runs-on: ubuntu-latest +- steps: +- - uses: actions/checkout@v2 +- - uses: 2bndy5/cpp-linter-action@private-repo-support +- id: linter +- env: +- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +- with: +- style: file +- files-changed-only: false +- # to ignore all demo folder contents except for demo.cpp +- # ignore: demo|!demo/demo.cpp +- +- - name: Fail fast?! +- if: steps.linter.outputs.checks-failed > 0 +- run: | +- echo "Some files failed the linting checks!" +- # for actual deployment +- # run: exit 1 +diff --git a/.gitignore b/.gitignore +new file mode 100644 +index 0000000..3dda933 +--- /dev/null ++++ b/.gitignore +@@ -0,0 +1,6 @@ ++build/ ++build.ninja ++cmake_install.cmake ++CMakeCache.txt ++compile_commands.json ++CMakeFiles/ +\ No newline at end of file +diff --git a/compile_commands.json b/compile_commands.json +deleted file mode 100644 +index 88fdc2a..0000000 +--- a/compile_commands.json ++++ /dev/null +@@ -1,12 +0,0 @@ +-[ +- { +- "directory": ".", +- "command": "/usr/bin/g++ -Wall -Werror demo.cpp", +- "file": "/demo.cpp" +- }, +- { +- "directory": ".", +- "command": "/usr/bin/g++ -Wall -Werror demo.cpp", +- "file": "/demo.hpp" +- } +-] +\ No newline at end of file +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +new file mode 100644 +index 0000000..97bf80c +--- /dev/null ++++ b/src/CMakeLists.txt +@@ -0,0 +1,12 @@ ++cmake_minimum_required(VERSION 3.15) ++ ++# Set the project name to your project name ++project(demo C CXX) ++ ++set(CMAKE_EXPORT_COMPILE_COMMANDS ON) ++ ++add_executable(demo_app ++ ${CMAKE_BINARY_SOURCE_DIR}demo.hpp ++ ${CMAKE_BINARY_SOURCE_DIR}demo.cpp ++) ++target_include_directories(demo_app PUBLIC ${CMAKE_BINARY_SOURCE_DIR}) +\ No newline at end of file +diff --git a/demo.cpp b/src/demo.cpp +similarity index 76% +rename from demo.cpp +rename to src/demo.cpp +index b7d4a0d..8a4a9ed 100644 +--- a/demo.cpp ++++ b/src/demo.cpp +@@ -1,15 +1,16 @@ + /** This is a very ugly test code (doomed to fail linting) */ + #include "demo.hpp" + #include ++#include ++ ++size_t dummyFunc(size_t i) { return i; } + + int main() + { + for (;;) + break; + +- + printf("Hello world!\n"); + + return 0; + } +- +diff --git a/demo.hpp b/src/demo.hpp +similarity index 100% +rename from demo.hpp +rename to src/demo.hpp diff --git a/cpp-linter-lib/tests/comment_test_assets/push_comments_8d68756375e0483c7ac2b4d6bbbece420dbbb495.json b/cpp-linter-lib/tests/comment_test_assets/push_comments_8d68756375e0483c7ac2b4d6bbbece420dbbb495.json new file mode 100644 index 0000000..4f5356d --- /dev/null +++ b/cpp-linter-lib/tests/comment_test_assets/push_comments_8d68756375e0483c7ac2b4d6bbbece420dbbb495.json @@ -0,0 +1,48 @@ +[ + { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/comments/76453652", + "html_url": "https://github.com/cpp-linter/test-cpp-linter-action/commit/8d68756375e0483c7ac2b4d6bbbece420dbbb495#commitcomment-76453652", + "id": 76453652, + "node_id": "CC_kwDOFY2uzM4EjpcU", + "user": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "site_admin": false + }, + "position": null, + "line": null, + "path": null, + "commit_id": "8d68756375e0483c7ac2b4d6bbbece420dbbb495", + "created_at": "2022-06-19T12:17:04Z", + "updated_at": "2022-06-19T12:17:04Z", + "author_association": "NONE", + "body": "\n## :scroll: Run `clang-format` on the following files\n- [ ] src\\demo.cpp\n- [ ] src\\demo.hpp\n\n---\n## :speech_balloon: Output from `clang-tidy`\n
src\\demo.cpp
\n
\nC:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC\\14.32.31326\\include\\yvals_core.h:599:2: error: [clang-diagnostic-error]\n\n> STL1000: Unexpected compiler version, expected Clang 13.0.0 or newer.\n

\n\n```.h\n#error STL1000: Unexpected compiler version, expected Clang 13.0.0 or newer.\n ^\n```\n

\n
\n\n
\nsrc\\demo.cpp:6:8: warning: [modernize-use-trailing-return-type]\n\n> use a trailing return type for this function\n

\n\n```.cpp\nsize_t dummyFunc(size_t i) { return i; }\n~~~~~~ ^\nauto -> size_t\n```\n

\n
\n\n
\nsrc\\demo.cpp:8:5: warning: [modernize-use-trailing-return-type]\n\n> use a trailing return type for this function\n

\n\n```.cpp\nint main()\n~~~ ^\nauto -> int\n```\n

\n
\n\n
\nsrc\\demo.cpp:10:13: warning: [readability-braces-around-statements]\n\n> statement should be inside braces\n

\n\n```.cpp\n for (;;)\n ^\n {\n```\n

\n
\n\n
\nsrc\\demo.cpp:13:5: warning: [cppcoreguidelines-pro-type-vararg]\n\n> do not call c-style vararg functions\n

\n\n```.cpp\n printf(\"Hello world!\\n\");\n ^\n```\n

\n
\n\n
src\\demo.hpp
\n
\nsrc\\demo.hpp:10:11: warning: [modernize-use-trailing-return-type]\n\n> use a trailing return type for this function\n

\n\n```.hpp\n void *not_usefull(char *str){\n ~~~~~~^\n auto -> void *\n```\n

\n
\n\n
\nsrc\\demo.hpp:12:16: warning: [modernize-use-nullptr]\n\n> use nullptr\n

\n\n```.hpp\n return 0;\n ^\n nullptr\n```\n

\n
\n\n", + "reactions": { + "url": "https://api.github.com/repos/cpp-linter/test-cpp-linter-action/comments/76453652/reactions", + "total_count": 0, + "+1": 0, + "-1": 0, + "laugh": 0, + "hooray": 0, + "confused": 0, + "heart": 0, + "rocket": 0, + "eyes": 0 + } + } +] diff --git a/cpp-linter-lib/tests/comments.rs b/cpp-linter-lib/tests/comments.rs new file mode 100644 index 0000000..99929fe --- /dev/null +++ b/cpp-linter-lib/tests/comments.rs @@ -0,0 +1,356 @@ +use chrono::Utc; +use cpp_linter_lib::run::run_main; +use mockito::{Matcher, Server, ServerGuard}; +use std::{env, fmt::Display, fs, io::Write, path::Path, process::Command}; +use tempfile::{NamedTempFile, TempDir}; + +const SHA: &str = "8d68756375e0483c7ac2b4d6bbbece420dbbb495"; +const REPO: &str = "cpp-linter/test-cpp-linter-action"; +const PR: i64 = 22; +const TOKEN: &str = "123456"; +const MOCK_ASSETS_PATH: &str = "tests/comment_test_assets/"; +const EVENT_PAYLOAD: &str = "{\"number\": 22}"; + +const RESET_RATE_LIMIT_HEADER: &str = "x-ratelimit-reset"; +const REMAINING_RATE_LIMIT_HEADER: &str = "x-ratelimit-remaining"; +// const RETRY_RATE_LIMIT_HEADER: &str = "retry-after"; + +async fn mock_server() -> ServerGuard { + Server::new_async().await +} + +#[derive(PartialEq, Clone, Copy, Debug)] +enum EventType { + Push, + PullRequest, +} + +impl Display for EventType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Push => write!(f, "push"), + Self::PullRequest => write!(f, "pull_request"), + } + } +} + +async fn setup( + event_t: EventType, + lib_root: &Path, + lines_changed_only: &str, + thread_comments: &str, + no_lgtm: &str, +) { + env::set_var("GITHUB_EVENT_NAME", event_t.to_string().as_str()); + env::set_var("GITHUB_REPOSITORY", REPO); + env::set_var("GITHUB_SHA", SHA); + env::set_var("GITHUB_TOKEN", TOKEN); + env::set_var("CI", "true"); + let mut event_payload_path = NamedTempFile::new().unwrap(); + if event_t == EventType::PullRequest { + event_payload_path + .write_all(EVENT_PAYLOAD.as_bytes()) + .expect("Failed to create mock event payload."); + env::set_var("GITHUB_EVENT_PATH", event_payload_path.path()); + } + let diff_end_point = if event_t == EventType::PullRequest { + format!("pulls/{PR}") + } else { + format!("commits/{SHA}") + }; + let diff_file = if event_t == EventType::PullRequest { + format!("pr_{PR}") + } else { + format!("push_{SHA}") + }; + let reset_timestamp = (Utc::now().timestamp() + 60).to_string(); + let is_lgtm = no_lgtm == "true" + || (("false", "update", "false") == (lines_changed_only, thread_comments, no_lgtm)); + + let asset_path = format!("{}/{MOCK_ASSETS_PATH}", lib_root.to_str().unwrap()); + let mut server = mock_server().await; + env::set_var("GITHUB_API_URL", server.url()); + server + .mock("GET", format!("/repos/{REPO}/{diff_end_point}").as_str()) + .match_header("Accept", "application/vnd.github.diff") + .match_header("Authorization", TOKEN) + .with_body_from_file(format!("{asset_path}{diff_file}.diff")) + .with_header(REMAINING_RATE_LIMIT_HEADER, "50") + .with_header(RESET_RATE_LIMIT_HEADER, reset_timestamp.as_str()) + .create(); + if event_t == EventType::Push { + server + .mock( + "GET", + format!("/repos/{REPO}/commits/{SHA}/comments/").as_str(), + ) + .match_header("Accept", "application/vnd.github.raw+json") + .match_header("Authorization", TOKEN) + .match_body(Matcher::Any) + .match_query(Matcher::UrlEncoded("page".to_string(), "1".to_string())) + .with_body_from_file(format!("{asset_path}push_comments_{SHA}.json")) + .with_header(REMAINING_RATE_LIMIT_HEADER, "50") + .with_header(RESET_RATE_LIMIT_HEADER, reset_timestamp.as_str()) + .create(); + } else { + let pr_endpoint = format!("/repos/{REPO}/issues/{PR}/comments/"); + for pg in ["1", "2"] { + let link = if pg == "1" { + format!("<{}{pr_endpoint}?page=2>; rel=\"next\"", server.url()) + } else { + "".to_string() + }; + server + .mock("GET", pr_endpoint.as_str()) + .match_header("Accept", "application/vnd.github.raw+json") + .match_header("Authorization", TOKEN) + .match_body(Matcher::Any) + .match_query(Matcher::UrlEncoded("page".to_string(), pg.to_string())) + .with_body_from_file(format!("{asset_path}pr_comments_pg{pg}.json")) + .with_header(REMAINING_RATE_LIMIT_HEADER, "50") + .with_header(RESET_RATE_LIMIT_HEADER, reset_timestamp.as_str()) + .with_header("link", link.as_str()) + .create(); + } + } + let comment_url = format!("/repos/{REPO}/comments/76453652"); + for method in ["DELETE", "PATCH"] { + server + .mock(method, comment_url.as_str()) + .match_body(Matcher::Regex(format!( + "# Cpp-Linter Report :{}:", + if is_lgtm { + "heavy_check_mark" + } else { + "warning" + } + ))) + .create(); + } + for method in ["PATCH", "POST"] { + server + .mock( + method, + format!( + "/repos/{REPO}/{}/comments/", + if event_t == EventType::PullRequest { + format!("issues/{PR}") + } else { + format!("commits/{SHA}") + } + ) + .as_str(), + ) + .match_query(Matcher::Any) + .match_body(Matcher::Any) + .create(); + } + + let mut args = vec![ + "cpp-linter".to_string(), + "-v=debug".to_string(), + format!("-V={}", env::var("CLANG_VERSION").unwrap_or("".to_string())), + format!("-l={lines_changed_only}"), + "--ignore-tidy=src/some source.c".to_string(), + "--ignore-format=src/some source.c".to_string(), + format!("--thread-comments={thread_comments}"), + format!("--no-lgtm={no_lgtm}"), + ]; + if is_lgtm { + args.push("-e=c".to_string()); + } + let result = run_main(args).await; + assert_eq!(result, 0); +} + +fn create_test_space() -> TempDir { + let tmp = TempDir::new().unwrap(); + fs::create_dir(tmp.path().join("src")).unwrap(); + let src = fs::read_dir("tests/demo").unwrap(); + for file in src { + let file = file.unwrap(); + if file.path().is_file() { + let new_file = tmp.path().join("src").join(file.file_name()); + fs::copy(file.path(), new_file.to_str().unwrap()).unwrap(); + } + } + + // generate compilation database with meson (& ninja) + let test_dir = tmp.path().to_str().unwrap(); + let mut cmd = Command::new("meson"); + cmd.args(["init", "-C", test_dir]); + let output = cmd.output().expect("Failed to run 'meson init'"); + println!( + "meson init stdout:\n{}", + String::from_utf8(output.stdout.to_vec()).unwrap() + ); + let meson_build_dir = tmp.path().join("build"); + let mut cmd = Command::new("meson"); + cmd.args([ + "setup", + "--backend=ninja", + meson_build_dir.as_path().to_str().unwrap(), + test_dir, + ]); + let output = cmd + .output() + .expect("Failed to generate build assets with 'meson setup'"); + println!( + "meson setup stdout:\n{}", + String::from_utf8(output.stdout.to_vec()).unwrap() + ); + tmp +} + +async fn test_comment( + event_t: EventType, + lines_changed_only: &str, + thread_comments: &str, + no_lgtm: &str, +) { + let tmp_dir = create_test_space(); + let lib_root = env::current_dir().unwrap(); + env::set_current_dir(tmp_dir.path()).unwrap(); + setup( + event_t, + &lib_root, + lines_changed_only, + thread_comments, + no_lgtm, + ) + .await; + env::set_current_dir(lib_root.as_path()).unwrap(); + drop(tmp_dir); +} + +#[tokio::test] +async fn new_push_all_lines() { + test_comment( + EventType::Push, // event_t + "false", // lines_changed_only + "true", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn new_push_changed_lines() { + test_comment( + EventType::Push, // event_t + "true", // lines_changed_only + "true", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn new_pr_all_lines() { + test_comment( + EventType::PullRequest, // event_t + "false", // lines_changed_only + "true", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn new_pr_changed_lines() { + test_comment( + EventType::PullRequest, // event_t + "true", // lines_changed_only + "true", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn update_push_all_lines() { + test_comment( + EventType::Push, // event_t + "false", // lines_changed_only + "update", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn update_push_changed_lines() { + test_comment( + EventType::Push, // event_t + "true", // lines_changed_only + "update", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn update_pr_all_lines() { + test_comment( + EventType::PullRequest, // event_t + "false", // lines_changed_only + "update", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn update_pr_changed_lines() { + test_comment( + EventType::PullRequest, // event_t + "true", // lines_changed_only + "update", // thread_comments + "false", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn new_push_no_lgtm() { + test_comment( + EventType::Push, // event_t + "false", // lines_changed_only + "true", // thread_comments + "true", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn update_push_no_lgtm() { + test_comment( + EventType::Push, // event_t + "false", // lines_changed_only + "update", // thread_comments + "true", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn new_pr_no_lgtm() { + test_comment( + EventType::PullRequest, // event_t + "false", // lines_changed_only + "true", // thread_comments + "true", // no_lgtm + ) + .await; +} + +#[tokio::test] +async fn update_pr_no_lgtm() { + test_comment( + EventType::PullRequest, // event_t + "false", // lines_changed_only + "update", // thread_comments + "true", // no_lgtm + ) + .await; +} diff --git a/cpp-linter-lib/tests/demo/some source.c b/cpp-linter-lib/tests/demo/some source.c new file mode 100644 index 0000000..ca8ed3e --- /dev/null +++ b/cpp-linter-lib/tests/demo/some source.c @@ -0,0 +1 @@ +#include diff --git a/cpp-linter-lib/tests/demo/some source.cpp b/cpp-linter-lib/tests/demo/some source.cpp deleted file mode 100644 index 0ae13fb..0000000 --- a/cpp-linter-lib/tests/demo/some source.cpp +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/cspell.config.yml b/cspell.config.yml index d933cee..2dd5bd3 100644 --- a/cspell.config.yml +++ b/cspell.config.yml @@ -3,9 +3,11 @@ language: en words: - Boolish - bugprone + - chrono - codecov - consts - cppcoreguidelines + - cstdio - endgroup - Falsey - gitmodules @@ -20,10 +22,12 @@ words: - pybind - pyfunction - pymodule + - ratelimit - reqwest - revparse - tempdir - tempfile + - vararg - venv ignorePaths: - .env/** diff --git a/justfile b/justfile index 9cb67f9..85fbb0d 100644 --- a/justfile +++ b/justfile @@ -20,7 +20,10 @@ py-dev: # run the test suite [group("code coverage")] test: - cargo llvm-cov --no-report nextest --manifest-path cpp-linter-lib/Cargo.toml --lib + cargo llvm-cov --no-report --tests \ + nextest --manifest-path cpp-linter-lib/Cargo.toml \ + #--success-output=final \ + --lib --color always # generate and open pretty coverage report [group("code coverage")] From d3e4e15739f666a59f4931280afa7c994c0bc6ed Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Mon, 26 Aug 2024 10:04:56 -0700 Subject: [PATCH 2/5] fix parsing of compilation database; add rate limit tests --- cpp-linter-lib/src/clang_tools/clang_tidy.rs | 75 ++++++++++---------- cpp-linter-lib/src/clang_tools/mod.rs | 8 +-- cpp-linter-lib/src/cli/structs.rs | 4 +- cpp-linter-lib/src/rest_api/github_api.rs | 49 ++++++++++++- cpp-linter-lib/src/rest_api/mod.rs | 26 +++---- cpp-linter-lib/tests/comments.rs | 30 ++++++-- justfile | 7 +- 7 files changed, 133 insertions(+), 66 deletions(-) diff --git a/cpp-linter-lib/src/clang_tools/clang_tidy.rs b/cpp-linter-lib/src/clang_tools/clang_tidy.rs index 8312c8c..9f891a2 100644 --- a/cpp-linter-lib/src/clang_tools/clang_tidy.rs +++ b/cpp-linter-lib/src/clang_tools/clang_tidy.rs @@ -18,19 +18,19 @@ use crate::{ common_fs::{normalize_path, FileObj}, }; -/// Used to deserialize a JSON compilation database -#[derive(Deserialize, Debug, Clone)] -pub struct CompilationDatabase { - /// A list of [`CompilationUnit`] - units: Vec, -} +// /// Used to deserialize a JSON compilation database +// #[derive(Deserialize, Debug, Clone)] +// pub struct CompilationDatabase { +// /// A list of [`CompilationUnit`] +// units: Vec, +// } /// Used to deserialize a json compilation database's translation unit. /// /// The only purpose this serves is to normalize relative paths for build systems that /// use/need relative paths (ie ninja). #[derive(Deserialize, Debug, Clone)] -struct CompilationUnit { +pub struct CompilationUnit { /// The directory of the build environment directory: String, @@ -75,15 +75,14 @@ pub struct TidyNotification { impl TidyNotification { pub fn diagnostic_link(&self) -> String { - let ret_val = if let Some((category, name)) = self.diagnostic.split_once('-') { - format!( - "[{}](https://clang.llvm.org/extra/clang-tidy/checks/{category}/{name}.html)", - self.diagnostic - ) - } else { - self.diagnostic.clone() - }; - ret_val + if self.diagnostic.starts_with("clang-diagnostic") { + return self.diagnostic.clone(); + } + let (category, name) = self.diagnostic.split_once('-').unwrap(); + format!( + "[{}](https://clang.llvm.org/extra/clang-tidy/checks/{category}/{name}.html)", + self.diagnostic + ) } } @@ -100,7 +99,7 @@ pub struct TidyAdvice { /// in the notifications. fn parse_tidy_output( tidy_stdout: &[u8], - database_json: &Option, + database_json: &Option>, ) -> Option { let note_header = Regex::new(r"^(.+):(\d+):(\d+):\s(\w+):(.*)\[([a-zA-Z\d\-\.]+)\]$").unwrap(); let mut notification = None; @@ -114,29 +113,29 @@ fn parse_tidy_output( // normalize the filename path and try to make it relative to the repo root let mut filename = PathBuf::from(&captured[1]); - if filename.is_relative() { - // if database was given try to use that first - if let Some(db_json) = &database_json { - let mut found_unit = false; - for unit in &db_json.units { - if unit.file == captured[0] { - filename = - normalize_path(&PathBuf::from_iter([&unit.directory, &unit.file])); - found_unit = true; - break; - } + // if database was given try to use that first + if let Some(db_json) = &database_json { + let mut found_unit = false; + for unit in db_json { + let unit_path = + PathBuf::from_iter([unit.directory.as_str(), unit.file.as_str()]); + if unit_path == filename { + filename = + normalize_path(&PathBuf::from_iter([&unit.directory, &unit.file])); + found_unit = true; + break; } - if !found_unit { - // file was not a named unit in the database; - // try to normalize path as if relative to working directory. - // NOTE: This shouldn't happen with a properly formed JSON database - filename = normalize_path(&PathBuf::from_iter([&cur_dir, &filename])); - } - } else { - // still need to normalize the relative path despite missing database info. - // let's assume the file is relative to current working directory. + } + if !found_unit { + // file was not a named unit in the database; + // try to normalize path as if relative to working directory. + // NOTE: This shouldn't happen with a properly formed JSON database filename = normalize_path(&PathBuf::from_iter([&cur_dir, &filename])); } + } else { + // still need to normalize the relative path despite missing database info. + // let's assume the file is relative to current working directory. + filename = normalize_path(&PathBuf::from_iter([&cur_dir, &filename])); } assert!(filename.is_absolute()); if filename.is_absolute() && filename.starts_with(&cur_dir) { @@ -198,7 +197,7 @@ pub fn run_clang_tidy( lines_changed_only: &LinesChangedOnly, database: &Option, extra_args: &Option>, - database_json: &Option, + database_json: &Option>, ) -> Vec<(log::Level, std::string::String)> { let mut logs = vec![]; let mut file = file.lock().unwrap(); diff --git a/cpp-linter-lib/src/clang_tools/mod.rs b/cpp-linter-lib/src/clang_tools/mod.rs index 5f47cf0..6f3e546 100644 --- a/cpp-linter-lib/src/clang_tools/mod.rs +++ b/cpp-linter-lib/src/clang_tools/mod.rs @@ -24,7 +24,7 @@ use crate::{ pub mod clang_format; use clang_format::run_clang_format; pub mod clang_tidy; -use clang_tidy::{run_clang_tidy, CompilationDatabase}; +use clang_tidy::{run_clang_tidy, CompilationUnit}; /// Fetch the path to a clang tool by `name` (ie `"clang-tidy"` or `"clang-format"`) and /// `version`. @@ -182,12 +182,12 @@ pub async fn capture_clang_tools_output( // parse database (if provided) to match filenames when parsing clang-tidy's stdout if let Some(db_path) = &clang_params.database { - if let Ok(db_str) = fs::read(db_path) { + if let Ok(db_str) = fs::read(db_path.join("compile_commands.json")) { clang_params.database_json = Some( - serde_json::from_str::( + serde_json::from_str::>( String::from_utf8(db_str).unwrap().as_str(), ) - .unwrap(), + .expect("Failed to parse compile_commands.json"), ) } }; diff --git a/cpp-linter-lib/src/cli/structs.rs b/cpp-linter-lib/src/cli/structs.rs index 3ec06d3..cebd1d4 100644 --- a/cpp-linter-lib/src/cli/structs.rs +++ b/cpp-linter-lib/src/cli/structs.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use clap::ArgMatches; use super::convert_extra_arg_val; -use crate::{clang_tools::clang_tidy::CompilationDatabase, common_fs::FileFilter}; +use crate::{clang_tools::clang_tidy::CompilationUnit, common_fs::FileFilter}; /// An enum to describe `--lines-changed-only` CLI option's behavior. #[derive(PartialEq, Clone, Debug)] @@ -139,7 +139,7 @@ pub struct ClangParams { pub lines_changed_only: LinesChangedOnly, pub database: Option, pub extra_args: Option>, - pub database_json: Option, + pub database_json: Option>, pub style: String, pub clang_tidy_command: Option, pub clang_format_command: Option, diff --git a/cpp-linter-lib/src/rest_api/github_api.rs b/cpp-linter-lib/src/rest_api/github_api.rs index 73c1266..5a114d1 100644 --- a/cpp-linter-lib/src/rest_api/github_api.rs +++ b/cpp-linter-lib/src/rest_api/github_api.rs @@ -361,7 +361,6 @@ impl GithubApiClient { #[allow(clippy::nonminimal_bool)] // an inaccurate assessment if (is_lgtm && !no_lgtm) || !is_lgtm { let payload = HashMap::from([("body", comment.to_owned())]); - #[cfg(not(test))] log::debug!("payload body:\n{:?}", payload); let req_meth = if comment_url.is_some() { Method::PATCH @@ -529,7 +528,10 @@ mod test { sync::{Arc, Mutex}, }; + use chrono::Utc; + use mockito::{Matcher, Server}; use regex::Regex; + use reqwest::{Method, Url}; use tempfile::{tempdir, NamedTempFile}; use super::GithubApiClient; @@ -619,4 +621,49 @@ mod test { "checks-failed=0\nformat-checks-failed=0\ntidy-checks-failed=0\n" ); } + + async fn simulate_rate_limit(secondary: bool) { + let mut server = Server::new_async().await; + let url = Url::parse(server.url().as_str()).unwrap(); + env::set_var("GITHUB_API_URL", server.url()); + let client = GithubApiClient::default(); + let reset_timestamp = (Utc::now().timestamp() + 60).to_string(); + let mock = server + .mock("GET", "/") + .match_query(Matcher::Any) + .with_status(429) + .with_header( + &client.rate_limit_headers.remaining, + if secondary { "1" } else { "0" }, + ) + .with_header(&client.rate_limit_headers.reset, &reset_timestamp); + if secondary { + mock.with_header(&client.rate_limit_headers.retry, "0") + .create(); + } else { + mock.create(); + } + let request = + GithubApiClient::make_api_request(&client.client, url, Method::GET, None, None); + let _response = GithubApiClient::send_api_request( + client.client.clone(), + request, + true, + client.rate_limit_headers.clone(), + 0, + ) + .await; + } + + #[tokio::test] + #[should_panic(expected = "REST API secondary rate limit exceeded")] + async fn secondary_rate_limit() { + simulate_rate_limit(true).await; + } + + #[tokio::test] + #[should_panic(expected = "REST API rate limit exceeded!")] + async fn primary_rate_limit() { + simulate_rate_limit(false).await; + } } diff --git a/cpp-linter-lib/src/rest_api/mod.rs b/cpp-linter-lib/src/rest_api/mod.rs index 5ccd29c..2eaa683 100644 --- a/cpp-linter-lib/src/rest_api/mod.rs +++ b/cpp-linter-lib/src/rest_api/mod.rs @@ -131,24 +131,24 @@ pub trait RestApiClient { .expect("Failed to extract remaining attempts about rate limit") .parse::() .expect("Failed to parse i64 from remaining attempts about rate limit"); - let reset = DateTime::from_timestamp( - response - .headers() - .get(&rate_limit_headers.reset) - .expect("response headers does not include a reset timestamp") - .to_str() - .expect("Failed to extract reset info about rate limit") - .parse::() - .expect("Failed to parse i64 from reset time about rate limit"), - 0, - ) - .expect("rate limit reset UTC timestamp is an invalid"); if remaining <= 0 { + let reset = DateTime::from_timestamp( + response + .headers() + .get(&rate_limit_headers.reset) + .expect("response headers does not include a reset timestamp") + .to_str() + .expect("Failed to extract reset info about rate limit") + .parse::() + .expect("Failed to parse i64 from reset time about rate limit"), + 0, + ) + .expect("rate limit reset UTC timestamp is an invalid"); panic!("REST API rate limit exceeded! Resets at {}", reset); } // check if secondary rate limit is violated; backoff and try again. - if retries >= 5 { + if retries > 4 { panic!("REST API secondary rate limit exceeded"); } if let Some(retry) = response.headers().get(&rate_limit_headers.retry) { diff --git a/cpp-linter-lib/tests/comments.rs b/cpp-linter-lib/tests/comments.rs index 99929fe..62912d0 100644 --- a/cpp-linter-lib/tests/comments.rs +++ b/cpp-linter-lib/tests/comments.rs @@ -1,7 +1,14 @@ use chrono::Utc; use cpp_linter_lib::run::run_main; use mockito::{Matcher, Server, ServerGuard}; -use std::{env, fmt::Display, fs, io::Write, path::Path, process::Command}; +use std::{ + env, + fmt::Display, + fs, + io::{self, BufRead, Write}, + path::Path, + process::Command, +}; use tempfile::{NamedTempFile, TempDir}; const SHA: &str = "8d68756375e0483c7ac2b4d6bbbece420dbbb495"; @@ -155,6 +162,8 @@ async fn setup( "--ignore-format=src/some source.c".to_string(), format!("--thread-comments={thread_comments}"), format!("--no-lgtm={no_lgtm}"), + "-p=build".to_string(), + "-i=build".to_string(), ]; if is_lgtm { args.push("-e=c".to_string()); @@ -176,9 +185,17 @@ fn create_test_space() -> TempDir { } // generate compilation database with meson (& ninja) - let test_dir = tmp.path().to_str().unwrap(); + let test_dir = tmp.path().join("src"); let mut cmd = Command::new("meson"); - cmd.args(["init", "-C", test_dir]); + cmd.args([ + "init", + "-C", + test_dir.to_str().unwrap(), + "--name", + "demo", + "demo.cpp", + "demo.hpp", + ]); let output = cmd.output().expect("Failed to run 'meson init'"); println!( "meson init stdout:\n{}", @@ -190,7 +207,7 @@ fn create_test_space() -> TempDir { "setup", "--backend=ninja", meson_build_dir.as_path().to_str().unwrap(), - test_dir, + test_dir.to_str().unwrap(), ]); let output = cmd .output() @@ -199,6 +216,11 @@ fn create_test_space() -> TempDir { "meson setup stdout:\n{}", String::from_utf8(output.stdout.to_vec()).unwrap() ); + let db = fs::File::open(meson_build_dir.join("compile_commands.json")) + .expect("Failed to open compilation database"); + for line in io::BufReader::new(db).lines().map_while(Result::ok) { + println!("{line}"); + } tmp } diff --git a/justfile b/justfile index 85fbb0d..a108ceb 100644 --- a/justfile +++ b/justfile @@ -20,21 +20,20 @@ py-dev: # run the test suite [group("code coverage")] test: - cargo llvm-cov --no-report --tests \ + cargo llvm-cov --no-report \ nextest --manifest-path cpp-linter-lib/Cargo.toml \ - #--success-output=final \ --lib --color always # generate and open pretty coverage report [group("code coverage")] pretty-cov *args='': - cargo llvm-cov report --json --output-path coverage.json + cargo llvm-cov report --json --output-path coverage.json --ignore-filename-regex main llvm-cov-pretty coverage.json {{ args }} # generate and open detailed coverage report [group("code coverage")] llvm-cov *args='': - cargo llvm-cov report --html {{ args }} + cargo llvm-cov report --html --ignore-filename-regex main {{ args }} # This is useful for IDE gutter indicators of line coverage. # See Coverage Gutters ext in VSCode. From fd1cd1928ab2eab303245111a1c3243c31680584 Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Mon, 26 Aug 2024 10:32:56 -0700 Subject: [PATCH 3/5] measure coverage from integration tests --- cpp-linter-lib/src/clang_tools/clang_tidy.rs | 7 ------- justfile | 16 +++++++--------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/cpp-linter-lib/src/clang_tools/clang_tidy.rs b/cpp-linter-lib/src/clang_tools/clang_tidy.rs index 9f891a2..edcce22 100644 --- a/cpp-linter-lib/src/clang_tools/clang_tidy.rs +++ b/cpp-linter-lib/src/clang_tools/clang_tidy.rs @@ -18,13 +18,6 @@ use crate::{ common_fs::{normalize_path, FileObj}, }; -// /// Used to deserialize a JSON compilation database -// #[derive(Deserialize, Debug, Clone)] -// pub struct CompilationDatabase { -// /// A list of [`CompilationUnit`] -// units: Vec, -// } - /// Used to deserialize a json compilation database's translation unit. /// /// The only purpose this serves is to normalize relative paths for build systems that diff --git a/justfile b/justfile index a108ceb..17fd021 100644 --- a/justfile +++ b/justfile @@ -22,7 +22,7 @@ py-dev: test: cargo llvm-cov --no-report \ nextest --manifest-path cpp-linter-lib/Cargo.toml \ - --lib --color always + --lib --tests --color always # generate and open pretty coverage report [group("code coverage")] @@ -40,30 +40,28 @@ llvm-cov *args='': # generate lcov.info [group("code coverage")] lcov: - @cargo llvm-cov report --lcov --output-path lcov.info + cargo llvm-cov report --lcov --output-path lcov.info --ignore-filename-regex main # serve docs [group("docs")] docs open='': - @mdbook serve docs {{ open }} + mdbook serve docs {{ open }} # build docs [group("docs")] -docs-build: - mdbook build docs --open +docs-build open='': + mdbook build docs {{ open }} # rust docs [group("docs")] -docs-rs: - cargo doc --no-deps --lib --manifest-path cpp-linter-lib/Cargo.toml --open +docs-rs open='': + cargo doc --no-deps --lib --manifest-path cpp-linter-lib/Cargo.toml {{ open }} # run cpp-linter native binary [group("bin")] run *args: cargo run --bin cpp-linter --manifest-path cpp-linter-lib/Cargo.toml -- {{ args }} -# The tool parameter can be set to 'cross' when cross compiling. - # build the native binary [group("bin")] build *args='': From 5adf65a30b48fbc69b88b88c2c68af55d05de380 Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Mon, 26 Aug 2024 13:26:41 -0700 Subject: [PATCH 4/5] fix CI conflicting cache keys fix test CI cache-deps jobs' runner OS --- .github/workflows/build-docs.yml | 6 +++--- .github/workflows/run-dev-tests.yml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 189c912..1e036f9 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -16,7 +16,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.cargo - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-docs-cargo-${{ hashFiles('Cargo.lock') }} - run: cargo fetch build-mdbook: @@ -30,7 +30,7 @@ jobs: uses: actions/cache/restore@v4 with: path: ~/.cargo - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-docs-cargo-${{ hashFiles('Cargo.lock') }} - name: Install mdbook uses: taiki-e/install-action@v2 with: @@ -62,7 +62,7 @@ jobs: uses: actions/cache/restore@v4 with: path: ~/.cargo - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-docs-cargo-${{ hashFiles('Cargo.lock') }} - run: cargo doc --no-deps --manifest-path cpp-linter-lib/Cargo.toml - name: upload rustdoc build as artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/run-dev-tests.yml b/.github/workflows/run-dev-tests.yml index 99f7816..fcb5464 100644 --- a/.github/workflows/run-dev-tests.yml +++ b/.github/workflows/run-dev-tests.yml @@ -23,11 +23,11 @@ env: jobs: cache-deps: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: ['windows-latest', ubuntu-latest] + os: [windows-latest, ubuntu-latest] steps: - run: rustup update --no-self-update - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.cargo - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-test-cargo-${{ hashFiles('Cargo.lock') }} - run: cargo fetch test: @@ -111,7 +111,7 @@ jobs: uses: actions/cache/restore@v4 with: path: ~/.cargo - key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} + key: ${{ runner.os }}-test-cargo-${{ hashFiles('Cargo.lock') }} - name: Collect Coverage working-directory: cpp-linter-lib From 8d2593d7eb1dc6413b3f8a5d2d8cc81c5412a260 Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Mon, 26 Aug 2024 13:45:30 -0700 Subject: [PATCH 5/5] fix title in permissions.md doc --- docs/src/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/permissions.md b/docs/src/permissions.md index 31e7050..d2e15a0 100644 --- a/docs/src/permissions.md +++ b/docs/src/permissions.md @@ -67,7 +67,7 @@ For [`push` events][push-events]. * The `contents` permission is needed to post or update a commit comment. This also allows us to delete an outdated comment if needed. -### Pull_request +### Pull Request For [`pull_request` events][pr-events].