Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement review processing code #5

Merged
merged 41 commits into from
Oct 21, 2023
Merged

Implement review processing code #5

merged 41 commits into from
Oct 21, 2023

Conversation

tapishr
Copy link
Member

@tapishr tapishr commented Sep 17, 2023

This code implements the following scenario -
User pushes to pull request
-> next server webhook callback receives event
-> dpu receives pubsub message
-> dpu does a git pull to get latest code
->git stat command is run and big files are eliminated
-> git diff command is run and output parsed
-> git blame command is run and parsed
-> hunk authors are obtained from bb api
-> Packaged and sent to next server by calling api

I would recommend starting in pubsub/listener.rs and then following the code

Summary by CodeRabbit

  • New Feature: Enhanced Git operations with new functions for checking commit existence, pulling Git repositories, generating diffs, and generating blame information.
  • New Feature: Added functionality to process review messages, check if a review already exists, process review changes, and send the resulting hunk map.
  • New Feature: Introduced new data structures for managing repositories, reviews, code hunks, and user information.
  • New Feature: Added functionality to retrieve and save user, repository, and workspace information from Bitbucket.
  • New Feature: Improved Bitbucket authentication with new functions for retrieving and refreshing access tokens.
  • New Feature: Added functionality to manage webhooks in Bitbucket repositories.
  • New Feature: Enhanced database operations with new functions for saving and retrieving review, user, repository, and webhook information.
  • New Feature: Improved message processing from PubSub subscriptions with asynchronous operations.
  • New Feature: Added functionality to clone Git repositories and manage Git URLs.
  • New Feature: Added functionality to save and retrieve authentication information from the database.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 17, 2023

Walkthrough

The changes primarily focus on enhancing the functionality of the vibi-dpu project by introducing new modules and functions related to Bitbucket API interactions, Git operations, and database operations. The updates include handling Bitbucket authentication, retrieving and storing user, workspace, and repository data, processing code reviews, and managing Git operations such as generating diffs and blame information. The changes also involve improvements in error handling, data serialization, and asynchronous task handling.

Changes

File(s) Summary
vibi-dpu/src/bitbucket/*.rs Added and updated functions for Bitbucket API interactions, including authentication, user, workspace, repository, and webhook management.
vibi-dpu/src/core/*.rs Introduced a new "review" module and added functions for processing review messages and managing review data.
vibi-dpu/src/db/*.rs Added new modules and functions for database operations, including saving and retrieving auth info, users, workspaces, repositories, reviews, and hunks.
vibi-dpu/src/pubsub/listener.rs Updated the message processing function to handle webhook callback messages and spawn asynchronous tasks for review processing.
vibi-dpu/src/utils/*.rs Introduced new modules and functions for Git operations, handling review and hunk data, and managing repository and auth info.

Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • If you reply to a review comment from CodeRabbit, the bot will automatically respond.
  • To engage with CodeRabbit bot directly around the specific lines of code in the PR, mention @coderabbitai in your review comment
  • Note: Review comments are made on code diffs or files, not on the PR overview.

CodeRabbit Commands

  • Enter @coderabbitai pause as a PR comment to pause the reviews on a PR.
  • Enter @coderabbitai resume as a PR comment to resume the paused reviews.
  • For on-demand reviews, enter @coderabbitai review as a PR comment to trigger a review. This is useful when reviews are automatic disabled at the repository level.
  • Enter @coderabbitai: ignore in the PR description to stop a specific PR from being reviewed.

Note these commands work only in the PR comment. For conversations with the bot, please use the review comments on code diffs or files.

@tapishr tapishr changed the base branch from tr/setup to main September 17, 2023 12:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 33

Commits Files that changed from the base of the PR and between 419cdde and eda4929 commits.
Files selected for processing (13)
  • vibi-dpu/src/bitbucket/auth.rs (1 hunks)
  • vibi-dpu/src/bitbucket/user.rs (2 hunks)
  • vibi-dpu/src/core/mod.rs (1 hunks)
  • vibi-dpu/src/core/review.rs (1 hunks)
  • vibi-dpu/src/db/hunk.rs (1 hunks)
  • vibi-dpu/src/db/mod.rs (1 hunks)
  • vibi-dpu/src/db/review.rs (1 hunks)
  • vibi-dpu/src/pubsub/listener.rs (2 hunks)
  • vibi-dpu/src/utils/gitops.rs (1 hunks)
  • vibi-dpu/src/utils/hunk.rs (1 hunks)
  • vibi-dpu/src/utils/lineitem.rs (1 hunks)
  • vibi-dpu/src/utils/mod.rs (1 hunks)
  • vibi-dpu/src/utils/review.rs (1 hunks)
Files skipped from review due to trivial changes (4)
  • vibi-dpu/src/core/mod.rs
  • vibi-dpu/src/db/mod.rs
  • vibi-dpu/src/utils/lineitem.rs
  • vibi-dpu/src/utils/mod.rs
Additional comments (Suppressed): 2
vibi-dpu/src/utils/review.rs (1)
  • 87-126: Setter methods are provided for all fields of the Review struct. This could potentially lead to misuse where an object's state is changed unexpectedly. If some fields are not meant to be changed after the object is created, consider removing their setter methods.
vibi-dpu/src/pubsub/listener.rs (1)
  • 15-15: The import statement for process_review is commented out in the old hunk but is active in the new hunk. Ensure that the function process_review is implemented and available in the review module before this change is merged.

vibi-dpu/src/db/review.rs Outdated Show resolved Hide resolved
vibi-dpu/src/db/hunk.rs Outdated Show resolved Hide resolved
vibi-dpu/src/db/hunk.rs Outdated Show resolved Hide resolved
vibi-dpu/src/utils/hunk.rs Show resolved Hide resolved
vibi-dpu/src/utils/hunk.rs Show resolved Hide resolved
vibi-dpu/src/bitbucket/auth.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/auth.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/auth.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/auth.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/auth.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 67

Commits Files that changed from the base of the PR and between eda4929 and 4537683 commits.
Files ignored due to filter (1)
  • vibi-dpu/Cargo.toml
Files selected for processing (15)
  • vibi-dpu/src/bitbucket/auth.rs (1 hunks)
  • vibi-dpu/src/bitbucket/config.rs (1 hunks)
  • vibi-dpu/src/bitbucket/mod.rs (1 hunks)
  • vibi-dpu/src/bitbucket/user.rs (1 hunks)
  • vibi-dpu/src/bitbucket/webhook.rs (1 hunks)
  • vibi-dpu/src/core/review.rs (1 hunks)
  • vibi-dpu/src/core/setup.rs (1 hunks)
  • vibi-dpu/src/db/auth.rs (1 hunks)
  • vibi-dpu/src/db/hunk.rs (1 hunks)
  • vibi-dpu/src/db/owner.rs (1 hunks)
  • vibi-dpu/src/db/review.rs (1 hunks)
  • vibi-dpu/src/db/user.rs (1 hunks)
  • vibi-dpu/src/db/webhook.rs (1 hunks)
  • vibi-dpu/src/utils/gitops.rs (1 hunks)
  • vibi-dpu/src/utils/hunk.rs (1 hunks)
Files skipped from review due to trivial changes (2)
  • vibi-dpu/src/bitbucket/mod.rs
  • vibi-dpu/src/utils/hunk.rs
Additional comments (Suppressed): 2
vibi-dpu/src/db/owner.rs (1)
  • 14-14: The key for the database entry is being formatted as "owners:{uuid}". Ensure that this format is consistent with other parts of the application where this key might be used or read from.
vibi-dpu/src/db/webhook.rs (1)
  • 4-5: The get_db function and Webhook struct are imported but there is no context provided about their implementation. Ensure that these components are correctly implemented and tested.

vibi-dpu/src/bitbucket/webhook.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/webhook.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/webhook.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/webhook.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/user.rs Outdated Show resolved Hide resolved
vibi-dpu/src/core/setup.rs Outdated Show resolved Hide resolved
vibi-dpu/src/core/setup.rs Outdated Show resolved Hide resolved
vibi-dpu/src/core/setup.rs Outdated Show resolved Hide resolved
vibi-dpu/src/core/setup.rs Outdated Show resolved Hide resolved
vibi-dpu/src/core/setup.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 68

Commits Files that changed from the base of the PR and between 4537683 and 0893cdc commits.
Files selected for processing (17)
  • vibi-dpu/src/bitbucket/auth.rs (1 hunks)
  • vibi-dpu/src/bitbucket/config.rs (1 hunks)
  • vibi-dpu/src/bitbucket/repo.rs (1 hunks)
  • vibi-dpu/src/bitbucket/user.rs (1 hunks)
  • vibi-dpu/src/bitbucket/webhook.rs (1 hunks)
  • vibi-dpu/src/bitbucket/workspace.rs (1 hunks)
  • vibi-dpu/src/core/review.rs (1 hunks)
  • vibi-dpu/src/core/setup.rs (1 hunks)
  • vibi-dpu/src/db/auth.rs (1 hunks)
  • vibi-dpu/src/db/hunk.rs (1 hunks)
  • vibi-dpu/src/db/owner.rs (1 hunks)
  • vibi-dpu/src/db/repo.rs (1 hunks)
  • vibi-dpu/src/db/review.rs (1 hunks)
  • vibi-dpu/src/db/user.rs (1 hunks)
  • vibi-dpu/src/db/webhook.rs (1 hunks)
  • vibi-dpu/src/utils/gitops.rs (1 hunks)
  • vibi-dpu/src/utils/repo.rs (1 hunks)
Files skipped from review due to trivial changes (1)
  • vibi-dpu/src/db/repo.rs
Additional comments (Suppressed): 3
vibi-dpu/src/utils/repo.rs (3)
  • 1-15: The Repository struct is well defined with all necessary fields. It's good to see that you've implemented the Clone, Serialize, and Deserialize traits which will be useful for cloning instances, and serializing/deserializing the struct.

  • 19-41: The constructor function new is correctly implemented and takes in all the necessary parameters to create a new instance of Repository.

  • 44-78: The getter methods are correctly implemented and return references to the struct fields. This is a good practice as it allows read access to the fields without giving the ability to modify them directly.

vibi-dpu/src/bitbucket/webhook.rs Outdated Show resolved Hide resolved
vibi-dpu/src/bitbucket/webhook.rs Outdated Show resolved Hide resolved
Comment on lines 61 to 89
async fn process_add_webhook_response(response: Result<Response, Error>){
if response.is_err() {
let err = response.expect_err("No error in response");
eprintln!("Error in api call: {:?}", err);
return;
}
let res = response.expect("Uncaught error in response");
if !res.status().is_success() {
eprintln!("Failed to add webhook. Status code: {}, Text: {:?}",
res.status(), res.text().await);
return;
}
let webhook_res = res.json::<WebhookResponse>().await;
if webhook_res.is_err() {
let err = webhook_res.expect_err("No error in webhook response");
eprintln!("Failed to parse webhook_res: {:?}", err);
return;
}
let webhook = webhook_res.expect("Uncaught error in webhook response");
let webhook_data = Webhook::new(
webhook.uuid().to_string(),
webhook.active(),
webhook.created_at().to_string(),
webhook.events().to_owned(),
webhook.links()["self"]["href"].clone(),
webhook.url().to_string(),
);
save_webhook_to_db(&webhook_data);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the previous comment, the process_add_webhook_response function could benefit from returning a Result type. This way, you can propagate errors up to the caller instead of just printing them and continuing execution. This would allow the caller to decide how to handle these errors.

vibi-dpu/src/db/review.rs Show resolved Hide resolved
let insert_res = db.insert(IVec::from(review_key.as_bytes()), json);
if insert_res.is_err() {
let e = insert_res.expect_err("No error in insert_res");
eprintln!("Failed to upsert review into sled DB: {e}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a typo in the error print statement. The variable e should be wrapped in curly braces to be correctly interpolated into the string.

- eprintln!("Failed to upsert review into sled DB: {e}");
+ eprintln!("Failed to upsert review into sled DB: {}", e);

Comment on lines 87 to 106
async fn get_all_pages(next_url: Option<String>, access_token: &str, params: &Option<HashMap<&str, &str>>) -> Vec<Value>{
let mut values_vec = Vec::new();
let mut next_url_mut = next_url;
while next_url_mut.is_some() {
let url_opt = next_url_mut.as_ref();
if url_opt.is_none() {
eprintln!("next_url is none");
break;
}
let url = url_opt.expect("Empty next url_opt").trim_matches('"');
if url == "null" {
break;
}
let response_opt = get_api(url, access_token, params).await;
let (mut response_values, url_opt) = deserialize_response(response_opt).await;
next_url_mut = url_opt.clone();
values_vec.append(&mut response_values);
}
return values_vec;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the get_all_pages function, you're using expect which will panic if its condition is not met. Instead, consider handling this error gracefully and return an appropriate error message to the caller. This will prevent your application from crashing unexpectedly.

Comment on lines 108 to 120
pub fn prepare_auth_headers(access_token: &str) -> Option<HeaderMap>{
let mut headers_map = HeaderMap::new();
let auth_header = format!("Bearer {}", access_token);
let headervalres = HeaderValue::from_str(&auth_header);
if headervalres.is_err() {
let e = headervalres.expect_err("No error found in headervalres");
eprintln!("Could not parse header value: {}", e);
return None;
}
let headerval = headervalres.expect("Empty headervalres");
headers_map.insert("Authorization", headerval);
return Some(headers_map);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the prepare_auth_headers function, you're using expect_err and expect which will panic if their conditions are not met. Instead, consider handling these errors gracefully and return an appropriate error message to the caller. This will prevent your application from crashing unexpectedly.

Comment on lines 22 to 44
pub fn commit_exists(commit: &str, directory: &str) -> bool {
let output_res = Command::new("git")
.arg("rev-list")
.arg(commit)
.current_dir(directory)
.output();
if output_res.is_err() {
let e = output_res.expect_err("No error in output_res");
eprintln!("Failed to start git rev-list: {:?}", e);
return false;
}
let output = output_res.expect("Uncaught error in output_res");
println!("Execute git rev-list, exit code: {:?}", output.status.code());
match str::from_utf8(&output.stderr) {
Ok(v) => println!("git rev-list stderr = {:?}", v),
Err(e) => {/* error handling */ println!("git rev-list stderr error {}", e)},
};
match str::from_utf8(&output.stdout) {
Ok(v) => println!("git rev-list stdout = {:?}", v),
Err(e) => {/* error handling */ println!("git rev-list stdout error {}", e)},
};
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function commit_exists always returns true, regardless of whether the commit exists or not. The function should return false if the command fails or if the commit does not exist in the repository. Consider checking the output of the git command to determine if the commit exists.

Comment on lines 46 to 74
pub async fn git_pull(review: &Review) {
let directory = review.clone_dir();
println!("directory = {}", &directory);
let access_token_opt = refresh_git_auth(review.clone_url(), review.clone_dir()).await;
if access_token_opt.is_none() {
eprintln!("Unable to get access_token from refresh_git_auth");
return;
}
let access_token = access_token_opt.expect("Empty access_token");
set_git_url(review.clone_url(), directory, &access_token);
let output_res = Command::new("git")
.arg("pull")
.current_dir(directory)
.output();
if output_res.is_err() {
let e = output_res.expect_err("No error in output_res");
eprintln!("failed to execute git pull: {:?}", e);
return;
}
let output = output_res.expect("Uncaught error in output_res");
match str::from_utf8(&output.stderr) {
Ok(v) => println!("git pull stderr = {:?}", v),
Err(e) => {/* error handling */ println!("git pull stderr error {}", e)},
};
match str::from_utf8(&output.stdout) {
Ok(v) => println!("git pull stdout = {:?}", v),
Err(e) => {/* error handling */ println!("git pull stdout error {}", e)},
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the git_pull function, if the refresh_git_auth function fails to get an access token, the function simply prints an error message and returns. This could lead to unexpected behavior if the caller expects the repository to be up-to-date after this function is called. Consider propagating the error up to the caller so they can handle it appropriately.

Comment on lines +234 to +284
fn process_diff(filepath: &str, diff: &str, linemap: &mut HashMap<String, Vec<String>> ) -> HashMap<String, Vec<String>> {
let mut limiterpos = Vec::new();
let delimitter = "@@";
for (idx, _) in diff.match_indices(delimitter) {
limiterpos.push(idx);
}
let mut idx: usize = 0;
let len = limiterpos.len();
while (idx + 1) < len {
let line_res = diff.get(
(limiterpos[idx]+delimitter.len())..limiterpos[idx+1]
);
if line_res.is_none() {
eprintln!("Unable to format diff line");
continue;
}
let line = line_res.expect("Empty line_res");
let sublines: Vec<&str> = line.split(" ").collect();
if line.contains("\n") || sublines.len() != 4 {
idx += 1;
continue;
}
let mut deletionstr = sublines[1].to_owned();
// let additionstr = sublines[1];
if deletionstr.contains("-") {
deletionstr = deletionstr.replace("-", "");
if deletionstr.contains(",") {
let delsplit: Vec<&str> = deletionstr.split(",").collect();
let delidx = delsplit[0].parse::<i32>().unwrap();
let deldiff = delsplit[1].parse::<i32>().unwrap();
deletionstr = format!("{delidx},{}", delidx+deldiff);
}
else {
let delidx = deletionstr.parse::<i32>().unwrap();
deletionstr.push_str(format!(",{}", delidx).as_str());
}
}
else {
idx += 1;
continue;
}
if linemap.contains_key(filepath) {
linemap.get_mut(filepath).unwrap().push(deletionstr);
}
else {
linemap.insert(filepath.to_string(), vec!(deletionstr));
}
idx += 1;
}
return linemap.to_owned();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The process_diff function assumes that the diff output is in a specific format. If the diff output does not match this format, the function may not work as expected. Consider adding checks to ensure that the diff output is in the expected format before attempting to process it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 18

Commits Files that changed from the base of the PR and between 0893cdc and 9d4f5b5 commits.
Files selected for processing (4)
  • vibi-dpu/src/bitbucket/auth.rs (1 hunks)
  • vibi-dpu/src/pubsub/listener.rs (1 hunks)
  • vibi-dpu/src/utils/auth.rs (1 hunks)
  • vibi-dpu/src/utils/gitops.rs (1 hunks)

Comment on lines 1 to 44
use serde::Deserialize;
use serde::Serialize;

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct AuthInfo {
access_token: String,
refresh_token: String,
expires_in: u64,
timestamp: Option<u64>,
}

impl AuthInfo {
// Constructor
pub fn new(access_token: String, refresh_token: String, expires_in: u64, timestamp: Option<u64>) -> Self {
Self {
access_token,
refresh_token,
expires_in,
timestamp,
}
}

// Public getter methods
pub fn access_token(&self) -> &String {
&self.access_token
}

pub fn refresh_token(&self) -> &String {
&self.refresh_token
}

pub fn expires_in(&self) -> u64 {
self.expires_in
}

pub fn timestamp(&self) -> &Option<u64> {
&self.timestamp
}

// Public setters
pub fn set_timestamp(&mut self, timestamp: u64) {
self.timestamp = Some(timestamp);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AuthInfo struct and its methods look good. The constructor, getters, and setters are correctly implemented. However, consider adding a method to check if the access token is expired or not. This could be useful in other parts of your code where you need to check the validity of the token before making API calls.

impl AuthInfo {
    // ...

    pub fn is_expired(&self) -> bool {
        match self.timestamp {
            Some(timestamp) => {
                let current_time = std::time::SystemTime::now()
                    .duration_since(std::time::UNIX_EPOCH)
                    .expect("Time went backwards")
                    .as_secs();
                current_time > timestamp + self.expires_in
            }
            None => true,
        }
    }
}

Comment on lines 24 to 57
async fn process_message(attributes: &HashMap<String, String>, data_bytes: &Vec<u8>) {
let msgtype_opt = attributes.get("msgtype");
if msgtype_opt.is_none() {
eprintln!("msgtype attribute not found in message : {:?}", attributes);
return;
}
let msgtype = msgtype_opt.expect("Empty msgtype");
match msgtype.as_str() {
"install_callback" => {
println!("Processing install callback message");
let msg_data_res = serde_json::from_slice::<InstallCallback>(data_bytes);
if msg_data_res.is_err() {
eprintln!("Error deserializing install callback: {:?}", msg_data_res);
return;
}
let data = msg_data_res.expect("msg_data not found");
let code_async = data.installation_code.clone();
task::spawn(async move {
handle_install_bitbucket(&code_async).await;
println!("Processed install callback message");
});
},
"webhook_callback" => {
let data_bytes_async = data_bytes.to_owned();
task::spawn(async move {
process_review(&data_bytes_async).await;
println!("Processed webhook callback message");
});
}
_ => {
eprintln!("Message type not found for message : {:?}", attributes);
}
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The process_message function is handling two types of messages: "install_callback" and "webhook_callback". The processing of these messages is done asynchronously using tokio::task::spawn. However, the error handling for these spawned tasks is missing. If an error occurs during the execution of these tasks, it will be silently ignored. Consider adding error handling to these tasks.

task::spawn(async move {
    match handle_install_bitbucket(&code_async).await {
        Ok(_) => println!("Processed install callback message"),
        Err(e) => eprintln!("Error processing install callback message: {:?}", e),
    }
});

task::spawn(async move {
    match process_review(&data_bytes_async).await {
        Ok(_) => println!("Processed webhook callback message"),
        Err(e) => eprintln!("Error processing webhook callback message: {:?}", e),
    }
});

Comment on lines 68 to 101
async fn setup_subscription(keypath: &str, topicname: &str) -> Subscription{
let config = get_pubsub_client_config(keypath).await;
let client = Client::new(config).await
.expect("Unable to create pubsub client to listen to messages");
let topic = client.topic(topicname);
let topic_res = topic.exists(None).await;
if topic_res.is_err() {
let e = topic_res.expect_err("No error found in topic_res");
if e.code() == Code::NotFound {
client.create_topic(topicname, None, None).await
.expect("Unable to create topic");
}
else {
eprintln!("Error getting topic: {:?}", e);
}
}
let sub_config = SubscriptionConfig {
enable_message_ordering: true,
..Default::default()
};
let subscription_name = format!("{topicname}-sub");
let subscription = client.subscription(&subscription_name);
let subconfig = SubscriptionConfig {
enable_message_ordering: true,
..Default::default()
};
if !subscription.exists(None).await.expect("Unable to get subscription information") {
subscription.create(
topic.fully_qualified_name(), subconfig, None)
.await.expect("Unable to create subscription for listening to messages");
}
println!("sub = {:?}", &subscription);
subscription
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup_subscription function creates a new topic if it does not exist (lines 77-78). However, there is no error handling for the case when the topic creation fails. This could lead to a situation where the function continues to execute even though the topic was not created successfully, which would likely cause errors later on. Consider adding error handling for this scenario.

if e.code() == Code::NotFound {
    match client.create_topic(topicname, None, None).await {
        Ok(_) => (),
        Err(e) => {
            eprintln!("Unable to create topic: {:?}", e);
            return;
        },
    }
}

Comment on lines 103 to 126
pub async fn listen_messages(keypath: &str, topicname: &str) {
let queue_cap = 100;
let mut message_hashes = VecDeque::with_capacity(queue_cap);
let subscription = setup_subscription(keypath, topicname).await;
let mut stream = subscription.subscribe(None).await
.expect("Unable to subscribe to messages");
while let Some(message) = stream.next().await {
println!("Listening for messages...");
let attrmap: HashMap<String, String> = message.message.attributes.clone().into_iter().collect();
let message_hash = digest(&*message.message.data);
if !message_hashes.contains(&message_hash) {
message_hashes.push_back(message_hash);
if message_hashes.len() > queue_cap {
while message_hashes.len() > queue_cap {
message_hashes.pop_front();
}
}
let msg_bytes = message.message.data.clone();
process_message(&attrmap, &msg_bytes).await;
}
// Ack or Nack message.
let _ = message.ack().await;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the listen_messages function, you are maintaining a queue of message hashes to avoid processing duplicate messages. However, this approach has a limitation in that it only prevents duplicates within the most recent queue_cap messages. If a duplicate message arrives after more than queue_cap messages have been processed, it will be processed again. If this is a concern, you might want to consider a different approach for preventing duplicate message processing.

Comment on lines 12 to 13
let bitbucket_client_id = env::var("BITBUCKET_CLIENT_ID").unwrap();
let bitbucket_client_secret = env::var("BITBUCKET_CLIENT_SECRET").unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environment variables are directly unwrapped without checking if they exist or not. This could lead to a panic at runtime if the environment variables are not set. It would be better to handle this gracefully.

-    let bitbucket_client_id = env::var("BITBUCKET_CLIENT_ID").unwrap();
-    let bitbucket_client_secret = env::var("BITBUCKET_CLIENT_SECRET").unwrap();
+    let bitbucket_client_id = match env::var("BITBUCKET_CLIENT_ID") {
+        Ok(val) => val,
+        Err(_) => {
+            eprintln!("BITBUCKET_CLIENT_ID is not set");
+            return None;
+        }
+    };
+    let bitbucket_client_secret = match env::var("BITBUCKET_CLIENT_SECRET") {
+        Ok(val) => val,
+        Err(_) => {
+            eprintln!("BITBUCKET_CLIENT_SECRET is not set");
+            return None;
+        }
+    };

Comment on lines +152 to +169
fn process_statoutput(statstr: &str) -> Option<(Vec<StatItem>, Vec<StatItem>)>{
let statvec = process_statitems(statstr);
let mut bigfiles = Vec::<StatItem>::new();
let mut smallfiles = Vec::<StatItem>::new();
let line_threshold = 500;
for item in statvec {
// logic for exclusion
if (item.additions > line_threshold) ||
(item.deletions > line_threshold) ||
(item.additions + item.deletions > line_threshold) {
bigfiles.push(item);
}
else {
smallfiles.push(item);
}
}
return Some((bigfiles, smallfiles));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the process_statoutput function, you are using a hardcoded threshold value of 500 to separate big files from small files based on the number of additions and deletions. It would be better to make this threshold configurable so that it can be easily adjusted based on the needs of your application.

- let line_threshold = 500;
+ let line_threshold = env::var("LINE_THRESHOLD").unwrap_or(500);

Comment on lines +171 to +191
fn generate_statitem(statitems: &Vec<&str>) -> StatItem {
let mut additions = 0;
let statitem = StatItem {
filepath: statitems[2].to_string(),
additions: match statitems[0].to_string().parse() {
Ok(adds) => {adds}
Err(e) => {
eprintln!("Unable to parse additions: {:?}", e);
0 // default value
}
},
deletions: match statitems[1].to_string().parse() {
Ok(dels) => {dels}
Err(e) => {
eprintln!("Unable to parse deletions: {:?}", e);
0 // default value
}
},
};
return statitem;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generate_statitem function attempts to parse the number of additions and deletions as integers. If parsing fails, it defaults to 0 and logs an error message. However, it might be more appropriate to return an error in this case, as a failure to parse these values likely indicates a problem with the input data.

Comment on lines +216 to +250
pub fn generate_diff(review: &Review, smallfiles: &Vec<StatItem>) -> HashMap<String, String> {
let mut diffmap = HashMap::<String, String>::new();
let prev_commit = review.base_head_commit();
let curr_commit = review.pr_head_commit();
let clone_dir = review.clone_dir();
for item in smallfiles {
let filepath = item.filepath.as_str();
let params = vec![
"diff".to_string(),
format!("{prev_commit}:{filepath}"),
format!("{curr_commit}:{filepath}"),
"-U0".to_string(),
];
let output_res = Command::new("git").args(&params)
.current_dir(&clone_dir)
.output();
if output_res.is_err() {
let commanderr = output_res.expect_err("No error in output_res");
eprintln!("git diff command failed to start : {:?}", commanderr);
continue;
}
let result = output_res.expect("Uncaught error in output_res");
let diff = result.stdout;
let diffstr_res = str::from_utf8(&diff);
if diffstr_res.is_err() {
let e = diffstr_res.expect_err("No error in diffstr_res");
eprintln!("Unable to deserialize diff: {:?}", e);
continue;
}
let diffstr = diffstr_res.expect("Uncaught error in diffstr_res");
println!("diffstr = {}", &diffstr);
diffmap.insert(filepath.to_string(), diffstr.to_string());
}
return diffmap;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generate_diff function generates diffs for each file in the smallfiles vector. However, if the git diff command fails for any reason (e.g., the git command is not found), the function simply logs an error message and continues processing the remaining files. This could result in incomplete or incorrect results. Consider handling this error more robustly, for example by returning an error or stopping execution.

Comment on lines +304 to +310
pub fn process_diffmap(diffmap: &HashMap<String, String>) -> HashMap<String, Vec<String>> {
let mut linemap: HashMap<String, Vec<String>> = HashMap::new();
for (filepath, diff) in diffmap {
linemap = process_diff(filepath, diff, &mut linemap);
}
return linemap;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The process_diffmap function processes a map of diffs. However, if processing a particular diff fails, the function simply logs an error message and continues processing the remaining diffs. This could result in incomplete or incorrect results. Consider handling this error more robustly, for example by returning an error or stopping execution.

Comment on lines 312 to 363
pub async fn generate_blame(review: &Review, linemap: &HashMap<String, Vec<String>>) -> Vec<BlameItem>{
let mut blamevec = Vec::<BlameItem>::new();
let commit = review.pr_head_commit();
let clone_dir = review.clone_dir();
for (path, linevec) in linemap {
for line in linevec {
let linenumvec: Vec<&str> = line.split(",").collect();
let linenum = linenumvec[0];
let paramvec: Vec<&str> = vec!(
"blame",
commit,
"-L",
line.as_str(),
"-e",
"--date=unix",
path.as_str(),
);
let blame_res = Command::new("git").args(paramvec)
.current_dir(clone_dir)
.output();
if blame_res.is_err() {
let e = blame_res.expect_err("No error in blame_res");
eprintln!("git blame command failed to start : {e}");
continue;
}
let blame_output = blame_res.expect("Uncaught error in blame_res");
if !blame_output.status.success() {
eprintln!("git blame command failed with exit code {:?} and error: {:?}",
blame_output.status.code(), String::from_utf8_lossy(&blame_output.stderr));
continue;
}
let blame = blame_output.stdout;
let parse_res = str::from_utf8(&blame);
if parse_res.is_err() {
let e = parse_res.expect_err("No error in parse_res");
eprintln!("Unable to deserialize blame: {e}");
}
let blamestr = parse_res.expect("Uncaught error in parse_res");
println!("blamestr = {}", blamestr);
let blamelines: Vec<&str> = blamestr.lines().collect();
if blamelines.len() == 0 {
continue;
}
let blamitems_opt = process_blameitem(path, commit, linenum, blamelines, review).await;
if blamitems_opt.is_some() {
let blameitems = blamitems_opt.expect("blameitem not found in blameitem_opt");
blamevec.extend(blameitems);
}
}
}
return blamevec;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generate_blame function generates blame information for each line in each file in the linemap. However, if the git blame command fails for any reason (e.g., the git command is not found), the function simply logs an error message and continues processing the remaining lines/files. This could result in incomplete or incorrect results. Consider handling this error more robustly, for example by returning an error or stopping execution.

@tapishr tapishr closed this Oct 13, 2023
@tapishr tapishr reopened this Oct 13, 2023
Copy link
Member

@avikalpg avikalpg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have reviewed the core/review.rs file and added my comments accordingly.

@@ -12,8 +12,7 @@ use tokio::task;
use std::collections::VecDeque;
use sha256::digest;
use tonic::Code;
use crate::core::setup::handle_install_bitbucket;
// use crate::core::{setup::handle_install_bitbucket, review::process_review}; // To be added in future PR
use crate::core::{setup::handle_install_bitbucket, review::process_review}; // To be added in future PR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
use crate::core::{setup::handle_install_bitbucket, review::process_review}; // To be added in future PR
use crate::core::{setup::handle_install_bitbucket, review::process_review};

Comment on lines 145 to 149
println!("Hunkmap published successfully!");
},
Err(e) => {
eprintln!("Failed to publish hunkmap: {}", e);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are running asynchronously. We will not be able to associate which log message is related to which URL. Please add the URL to t he log messages.

Comment on lines +270 to +273
if line.contains("\n") || sublines.len() != 4 {
idx += 1;
continue;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment mentioning which edge case are you handling by this.

let ln = blamelines[lnum];
let wordvec: Vec<&str> = ln.split(" ").collect();
let commit = wordvec[0];
let lineitem_opt = get_commit_bb(commit, repo_name, repo_owner).await;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a repo-provider-specific function call, so there should have been a conditional based on the repo provider before calling this function.

This will create confusion going forward when we implement this for other providers and treat the functions in gitops as black boxes (as we should).

review.author().to_string(),
blamevec,
);
prvec.push(hmapitem);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only pushing once in this vector. You could have directly defined the variable here instead of creating a mutable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has too many cascading effects, will need to change how tmw parses hunks as well

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 7

Commits Files that changed from the base of the PR and between 9d4f5b5 and b468237.
Files selected for processing (2)
  • vibi-dpu/src/core/review.rs (1 hunks)
  • vibi-dpu/src/pubsub/listener.rs (2 hunks)
Additional comments (Suppressed): 2
vibi-dpu/src/pubsub/listener.rs (2)
  • 12-15: The import of process_review function from review module in core is added. This function is used to process the review tasks. Ensure that the function is implemented correctly and handles all the necessary tasks as expected.

  • 44-52: The commented out code for processing the "webhook_callback" message type is now uncommented and updated. The process_review function is now called with data_bytes_async as an argument. Ensure that the process_review function handles the data correctly and that the data is in the expected format.

Comment on lines 132 to 153
fn publish_hunkmap(hunkmap: &HunkMap) {
let client = get_client();
let hunkmap_json = serde_json::to_string(&hunkmap).expect("Unable to serialize hunkmap");
tokio::spawn(async move {
let url = format!("{}/api/hunks",
env::var("SERVER_URL").expect("SERVER_URL must be set"));
let url_clone = url.clone();
println!("url for hunkmap publishing {}", &url);
match client
.post(url)
.json(&hunkmap_json)
.send()
.await {
Ok(_) => {
println!("[publish_hunkmap] Hunkmap published successfully for: {} !", &url_clone);
},
Err(e) => {
eprintln!("[publish_hunkmap] Failed to publish hunkmap: {} for: {}", e, &url_clone);
}
};
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function publish_hunkmap sends a POST request to publish the hunkmap. However, it does not handle any errors that might occur during the request. Consider adding error handling to this function to improve the robustness of the code.

@sonarcloud
Copy link

sonarcloud bot commented Oct 21, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
No Duplication information No Duplication information

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 9

Commits Files that changed from the base of the PR and between b468237 and ed7b280.
Files selected for processing (2)
  • vibi-dpu/src/core/review.rs (1 hunks)
  • vibi-dpu/src/utils/gitops.rs (1 hunks)
Additional comments (Suppressed): 1
vibi-dpu/src/utils/gitops.rs (1)
  • 94-122: The function set_git_url sets the URL of the remote repository. However, it doesn't handle the case where the git command fails due to reasons other

Comment on lines +19 to +33
pub async fn process_review(message_data: &Vec<u8>) {
let review_opt = parse_review(message_data);
if review_opt.is_none() {
eprintln!("Unable to deserialize review message");
return;
}
let review = review_opt.expect("review_opt is empty");
if hunk_already_exists(&review) {
return;
}
println!("Processing PR : {}", &review.id());
commit_check(&review).await;
let hunkmap_opt = process_review_changes(&review).await;
send_hunkmap(&hunkmap_opt, &review);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function process_review does not handle errors effectively. If parse_review returns None, it simply prints an error message and continues execution. This could lead to unexpected behavior if review_opt is None. Consider returning an error or exiting the function early in this case. Also, the function commit_check does not return any value or error, which makes it difficult to handle any potential errors that might occur during its execution. Consider modifying this function to return a Result type.

pub async fn process_review(message_data: &Vec<u8>) {
	let review_opt = parse_review(message_data);
	if review_opt.is_none() {
		eprintln!("Unable to deserialize review message");
		return;
	}
	let review = review_opt.expect("review_opt is empty");
	if hunk_already_exists(&review) {
		return;
	}
	println!("Processing PR : {}", &review.id());
- 30: 	commit_check(&review).await;
+ 30: 	if let Err(e) = commit_check(&review).await {
+ 31: 		eprintln!("Failed to check commit: {}", e);
+ 32: 		return;
+ 33: 	}
	let hunkmap_opt = process_review_changes(&review).await;
	send_hunkmap(&hunkmap_opt, &review);
}

Comment on lines +35 to +46
fn send_hunkmap(hunkmap_opt: &Option<HunkMap>, review: &Review) {
if hunkmap_opt.is_none() {
eprintln!("Empty hunkmap in send_hunkmap");
return;
}
let hunkmap = hunkmap_opt.to_owned().expect("empty hunkmap_opt");
println!("HunkMap = {:?}", &hunkmap);
store_hunkmap_to_db(&hunkmap, review);
publish_hunkmap(&hunkmap);
// let hunkmap_async = hunkmap.clone();
// process_coverage(&hunkmap_async).await; TODO - include in future PR
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function send_hunkmap does not handle the case where hunkmap_opt is None effectively. It simply prints an error message and continues execution. This could lead to unexpected behavior if hunkmap_opt is None. Consider returning an error or exiting the function early in this case.

fn send_hunkmap(hunkmap_opt: &Option<HunkMap>, review: &Review) {
	if hunkmap_opt.is_none() {
		eprintln!("Empty hunkmap in send_hunkmap");
		return;
	}
	let hunkmap = hunkmap_opt.to_owned().expect("empty hunkmap_opt");
	println!("HunkMap = {:?}", &hunkmap);
	store_hunkmap_to_db(&hunkmap, review);
	publish_hunkmap(&hunkmap);
	// let hunkmap_async = hunkmap.clone();
	// process_coverage(&hunkmap_async).await; TODO - include in future PR
}

Comment on lines +48 to +58
fn hunk_already_exists(review: &Review) -> bool {
let hunk_opt = get_hunk_from_db(&review);
if hunk_opt.is_none() {
eprintln!("No hunk from get_hunk_from_db");
return false;
}
let hunkmap = hunk_opt.expect("empty hunk from get_hunk_from_db");
publish_hunkmap(&hunkmap);
println!("Hunk already in db!");
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function hunk_already_exists returns true if a hunk already exists in the database, but it also publishes the hunkmap. This could lead to unnecessary network traffic if the hunkmap is published multiple times. Consider separating the concerns of checking if a hunk exists and publishing a hunkmap into separate functions.

Comment on lines +59 to +85
async fn process_review_changes(review: &Review) -> Option<HunkMap>{
let mut prvec = Vec::<PrHunkItem>::new();
let fileopt = get_excluded_files(&review);
println!("fileopt = {:?}", &fileopt);
if fileopt.is_none() {
eprintln!("No files to review for PR {}", review.id());
return None;
}
let (_, smallfiles) = fileopt.expect("fileopt is empty");
let diffmap = generate_diff(&review, &smallfiles);
println!("diffmap = {:?}", &diffmap);
let linemap = process_diffmap(&diffmap);
let blamevec = generate_blame(&review, &linemap).await;
let hmapitem = PrHunkItem::new(
review.id().to_string(),
review.author().to_string(),
blamevec,
);
prvec.push(hmapitem);
let hunkmap = HunkMap::new(review.provider().to_string(),
review.repo_owner().to_string(),
review.repo_name().to_string(),
prvec,
format!("{}/hunkmap", review.db_key()),
);
return Some(hunkmap);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function process_review_changes is doing too many things: it's generating a diff, processing the diffmap, generating blame, and creating a hunkmap. Consider breaking this function into smaller, more manageable functions to improve readability and maintainability.

Comment on lines +87 to +93
async fn commit_check(review: &Review) {
if !commit_exists(&review.base_head_commit(), &review.clone_dir())
|| !commit_exists(&review.pr_head_commit(), &review.clone_dir()) {
println!("Pulling repository {} for commit history", &review.repo_name());
git_pull(review).await;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function commit_check does not return any value or error, which makes it difficult to handle any potential errors that might occur during its execution. Consider modifying this function to return a Result type.

Comment on lines +95 to +130
fn parse_review(message_data: &Vec<u8>) -> Option<Review>{
let data_res = serde_json::from_slice::<Value>(&message_data);
if data_res.is_err() {
let e = data_res.expect_err("No error in data_res");
eprintln!("Incoming message does not contain valid reviews: {:?}", e);
return None;
}
let data = data_res.expect("Uncaught error in deserializing message_data");
println!("data == {:?}", &data["eventPayload"]["repository"]);
let repo_provider = data["repositoryProvider"].to_string().trim_matches('"').to_string();
let repo_name = data["eventPayload"]["repository"]["name"].to_string().trim_matches('"').to_string();
println!("repo NAME == {}", &repo_name);
let workspace_name = data["eventPayload"]["repository"]["workspace"]["slug"].to_string().trim_matches('"').to_string();
let clone_opt = get_clone_url_clone_dir(&repo_provider, &workspace_name, &repo_name);
if clone_opt.is_none() {
eprintln!("Unable to get clone url and directory");
return None;
}
let (clone_url, clone_dir) = clone_opt.expect("Empty clone_opt");
let pr_id = data["eventPayload"]["pullrequest"]["id"].to_string().trim_matches('"').to_string();
let review = Review::new(
data["eventPayload"]["pullrequest"]["destination"]["commit"]["hash"].to_string().replace("\"", ""),
data["eventPayload"]["pullrequest"]["source"]["commit"]["hash"].to_string().replace("\"", ""),
pr_id.clone(),
repo_name.clone(),
workspace_name.clone(),
repo_provider.clone(),
format!("bitbucket/{}/{}/{}", &workspace_name, &repo_name, &pr_id),
clone_dir,
clone_url,
data["eventPayload"]["pullrequest"]["author"]["account_id"].to_string().replace("\"", ""),
);
println!("review = {:?}", &review);
save_review_to_db(&review);
return Some(review);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function parse_review is doing too many things: it's deserializing the message data, extracting various pieces of information, getting the clone URL and directory, and creating a review. Consider breaking this function into smaller, more manageable functions to improve readability and maintainability.

Comment on lines +132 to +153
fn publish_hunkmap(hunkmap: &HunkMap) {
let client = get_client();
let hunkmap_json = serde_json::to_string(&hunkmap).expect("Unable to serialize hunkmap");
let key_clone = hunkmap.db_key().to_string();
tokio::spawn(async move {
let url = format!("{}/api/hunks",
env::var("SERVER_URL").expect("SERVER_URL must be set"));
println!("url for hunkmap publishing {}", &url);
match client
.post(url)
.json(&hunkmap_json)
.send()
.await {
Ok(_) => {
println!("[publish_hunkmap] Hunkmap published successfully for: {} !", &key_clone);
},
Err(e) => {
eprintln!("[publish_hunkmap] Failed to publish hunkmap: {} for: {}", e, &key_clone);
}
};
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function publish_hunkmap is doing too many things: it's serializing the hunkmap, creating a URL, and sending a POST request. Consider breaking this function into smaller, more manageable functions to improve readability and maintainability.

Comment on lines +22 to +62
pub fn commit_exists(commit: &str, directory: &str) -> bool {
let output_res = Command::new("git")
.arg("rev-list")
.arg(commit)
.current_dir(directory)
.output();
if output_res.is_err() {
let e = output_res.expect_err("No error in output_res");
eprintln!("Failed to start git rev-list: {:?}", e);
return false;
}
let output = output_res.expect("Uncaught error in output_res");
if !output.status.success() {
eprintln!("git rev-list, exit code: {:?}",
output.status.code());
// for debugging
match str::from_utf8(&output.stderr) {
Ok(v) => println!("git rev-list stderr = {:?}", v),
Err(e) => {/* error handling */ println!("git rev-list stderr error {}", e)},
};
return false;
}
println!("Execute git rev-list, exit code: {:?}", output.status.code());
if output.status.code() == Some(128) {
// for debugging
match str::from_utf8(&output.stderr) {
Ok(v) => eprintln!("git rev-list stderr = {:?}", v),
Err(e) => {/* error handling */ eprintln!("git rev-list stderr error {}", e)},
};
return false;
}
// for debugging
match str::from_utf8(&output.stderr) {
Ok(v) => eprintln!("git rev-list stderr = {:?}", v),
Err(e) => {/* error handling */ eprintln!("git rev-list stderr error {}", e)},
};
match str::from_utf8(&output.stdout) {
Ok(v) => println!("git rev-list stdout = {:?}", v),
Err(e) => {/* error handling */ eprintln!("git rev-list stdout error {}", e)},
};
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function commit_exists checks if a commit exists in a given directory. However, it doesn't handle the case where the git command fails due to reasons other than the commit not existing (e.g., the git command is not found). This could lead to unexpected behavior. Consider checking the error message or exit code to distinguish between different failure cases. Also, the function prints a lot of debug information to the console. Consider using a logging library to control the verbosity of the output.

pub fn commit_exists(commit: &str, directory: &str) -> bool {
	let output_res = Command::new("git")
		.arg("rev-list")
		.arg(commit)
		.current_dir(directory)
		.output();
	if output_res.is_err() {
		let e = output_res.expect_err("No error in output_res");
		eprintln!("Failed to start git rev-list: {:?}", e);
		return false;
	}
	let output = output_res.expect("Uncaught error in output_res");
	if !output.status.success() {
		eprintln!("git rev-list, exit code: {:?}",
			output.status.code());
		// for debugging
		match str::from_utf8(&output.stderr) {
			Ok(v) => println!("git rev-list stderr = {:?}", v),
			Err(e) => {/* error handling */ println!("git rev-list stderr error {}", e)}, 
		};
		return false;
	}
	println!("Execute git rev-list, exit code: {:?}", output.status.code());
	if output.status.code() == Some(128) {
		// for debugging
		match str::from_utf8(&output.stderr) {
			Ok(v) => eprintln!("git rev-list stderr = {:?}", v),
			Err(e) => {/* error handling */ eprintln!("git rev-list stderr error {}", e)}, 
		};
		return false;
	}
	// for debugging
	match str::from_utf8(&output.stderr) {
		Ok(v) => eprintln!("git rev-list stderr = {:?}", v),
		Err(e) => {/* error handling */ eprintln!("git rev-list stderr error {}", e)}, 
	};
	match str::from_utf8(&output.stdout) {
		Ok(v) => println!("git rev-list stdout = {:?}", v),
		Err(e) => {/* error handling */ eprintln!("git rev-list stdout error {}", e)}, 
	};
	return true;
}

Comment on lines +65 to +92
pub async fn git_pull(review: &Review) {
let directory = review.clone_dir();
println!("directory = {}", &directory);
let access_token_opt = refresh_git_auth(review.clone_url(), review.clone_dir()).await;
if access_token_opt.is_none() {
eprintln!("no refresh token acquired");
}
let access_token = access_token_opt.expect("Empty access_token");
set_git_url(review.clone_url(), directory, &access_token);
let output_res = Command::new("git")
.arg("pull")
.current_dir(directory)
.output();
if output_res.is_err() {
let e = output_res.expect_err("No error in output_res");
eprintln!("failed to execute git pull: {:?}", e);
return;
}
let output = output_res.expect("Uncaught error in output_res");
match str::from_utf8(&output.stderr) {
Ok(v) => println!("git pull stderr = {:?}", v),
Err(e) => {/* error handling */ println!("git pull stderr error {}", e)},
};
match str::from_utf8(&output.stdout) {
Ok(v) => println!("git pull stdout = {:?}", v),
Err(e) => {/* error handling */ println!("git pull stdout error {}", e)},
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function git_pull pulls the latest changes from a git repository. However, it doesn't handle the case where the git command fails due to reasons other than the commit not existing (e.g., the git command is not found). This could lead to unexpected behavior. Consider checking the error message or exit code to distinguish between different failure cases. Also, the function prints a lot of debug information to the console. Consider using a logging library to control the verbosity of the output.

pub async fn git_pull(review: &Review) {
	let directory = review.clone_dir();
	println!("directory = {}", &directory);
	let access_token_opt = refresh_git_auth(review.clone_url(), review.clone_dir()).await;
	if access_token_opt.is_none() {
		eprintln!("no refresh token acquired");
	}
	let access_token = access_token_opt.expect("Empty access_token");
    set_git_url(review.clone_url(), directory, &access_token);
	let output_res = Command::new("git")
		.arg("pull")
		.current_dir(directory)
		.output();
	if output_res.is_err() {
		let e = output_res.expect_err("No error in output_res");
		eprintln!("failed to execute git pull: {:?}", e);
		return;
	}
	let output = output_res.expect("Uncaught error in output_res");
	match str::from_utf8(&output.stderr) {
		Ok(v) => println!("git pull stderr = {:?}", v),
		Err(e) => {/* error handling */ println!("git pull stderr error {}", e)}, 
	};
	match str::from_utf8(&output.stdout) {
		Ok(v) => println!("git pull stdout = {:?}", v),
		Err(e) => {/* error handling */ println!("git pull stdout error {}", e)}, 
	};
}

@tapishr tapishr merged commit a9a807d into main Oct 21, 2023
3 checks passed
@tapishr tapishr deleted the tr/review branch October 21, 2023 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants