summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCyborus <cyborus@cyborus.xyz>2024-07-09 22:47:00 +0200
committerCyborus <cyborus@cyborus.xyz>2024-07-09 22:49:27 +0200
commit6c6c07b50c856167335b9d60e3fee737e944e436 (patch)
tree3377a6542043789d3165ef8697f162760af30db7
parentMerge pull request '`repo fork` command' (#83) from fork into main (diff)
downloadforgejo-cli-6c6c07b50c856167335b9d60e3fee737e944e436.tar.xz
forgejo-cli-6c6c07b50c856167335b9d60e3fee737e944e436.zip
feat(pr): status command
-rw-r--r--Cargo.lock11
-rw-r--r--Cargo.toml2
-rw-r--r--src/prs.rs123
3 files changed, 135 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 77cbdcd..8fe1892 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1187,6 +1187,15 @@ dependencies = [
]
[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1868,7 +1877,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa",
+ "libc",
"num-conv",
+ "num_threads",
"powerfmt",
"serde",
"time-core",
diff --git a/Cargo.toml b/Cargo.toml
index 6a96148..0ff53c8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ serde = { version = "1.0.170", features = ["derive"] }
serde_json = "1.0.100"
sha256 = "1.5.0"
soft_assert = "0.1.1"
-time = { version = "0.3.30", features = ["formatting", "macros"] }
+time = { version = "0.3.30", features = ["formatting", "local-offset", "macros"] }
tokio = { version = "1.29.1", features = ["full"] }
url = "2.4.0"
uuid = { version = "1.5.0", features = ["v4"] }
diff --git a/src/prs.rs b/src/prs.rs
index 0a46296..ed4fe2d 100644
--- a/src/prs.rs
+++ b/src/prs.rs
@@ -68,6 +68,11 @@ pub enum PrSubcommand {
#[clap(subcommand)]
command: Option<ViewCommand>,
},
+ /// View the mergability and CI status of a pull request
+ Status {
+ /// The pull request to view.
+ id: Option<IssueId>,
+ },
/// Checkout a pull request in a new branch
Checkout {
/// The pull request to check out.
@@ -300,6 +305,7 @@ impl PrCommand {
}
}
}
+ Status { id } => view_pr_status(&repo, &api, id.map(|id| id.number)).await?,
Search {
query,
labels,
@@ -351,6 +357,7 @@ impl PrCommand {
Search { repo, .. } | Create { repo, .. } => repo.as_ref(),
Checkout { .. } => None,
View { id: pr, .. }
+ | Status { id: pr, .. }
| Comment { pr, .. }
| Edit { pr, .. }
| Close { pr, .. }
@@ -373,6 +380,7 @@ impl PrCommand {
}
}
View { id: pr, .. }
+ | Status { id: pr, .. }
| Comment { pr, .. }
| Edit { pr, .. }
| Close { pr, .. }
@@ -578,6 +586,121 @@ fn darken(r: u8, g: u8, b: u8) -> (u8, u8, u8) {
)
}
+async fn view_pr_status(repo: &RepoName, api: &Forgejo, id: Option<u64>) -> eyre::Result<()> {
+ let pr = try_get_pr(repo, api, id).await?;
+
+ let SpecialRender {
+ bright_magenta,
+ bright_red,
+ bright_green,
+ yellow,
+ light_grey,
+ dash,
+ bullet,
+ reset,
+ ..
+ } = *crate::special_render();
+
+ if pr.merged.ok_or_eyre("pr merge status unknown")? {
+ let merged_by = pr.merged_by.ok_or_eyre("pr not merged by anyone")?;
+ let merged_by = merged_by
+ .login
+ .as_deref()
+ .ok_or_eyre("pr merger does not have login")?;
+ let merged_at = pr.merged_at.ok_or_eyre("pr does not have merge date")?;
+ let date_format = time::macros::format_description!(
+ "on [month repr:long] [day], [year], at [hour repr:12]:[minute] [period]"
+ );
+ let tz_format = time::macros::format_description!(
+ "[offset_hour padding:zero sign:mandatory]:[offset_minute]"
+ );
+ let (merged_at, show_tz) = if let Ok(local_offset) = time::UtcOffset::current_local_offset()
+ {
+ let merged_at = merged_at.to_offset(local_offset);
+ (merged_at, false)
+ } else {
+ (merged_at, true)
+ };
+ print!(
+ "{bright_magenta}Merged{reset} by {merged_by} {}",
+ merged_at.format(date_format)?
+ );
+ if show_tz {
+ print!("{}", merged_at.format(tz_format)?);
+ }
+ println!();
+ } else {
+ let pr_number = pr.number.ok_or_eyre("pr does not have number")?;
+ let query = forgejo_api::structs::RepoGetPullRequestCommitsQuery {
+ page: None,
+ limit: Some(u32::MAX),
+ verification: Some(false),
+ files: Some(false),
+ };
+ let (_commit_headers, commits) = api
+ .repo_get_pull_request_commits(repo.owner(), repo.name(), pr_number, query)
+ .await?;
+ let latest_commit = commits
+ .iter()
+ .max_by_key(|x| x.created)
+ .ok_or_eyre("no commits in pr")?;
+ let sha = latest_commit
+ .sha
+ .as_deref()
+ .ok_or_eyre("commit does not have sha")?;
+ let query = forgejo_api::structs::RepoGetCombinedStatusByRefQuery {
+ page: None,
+ limit: Some(u32::MAX),
+ };
+ let combined_status = api
+ .repo_get_combined_status_by_ref(repo.owner(), repo.name(), sha, query)
+ .await?;
+
+ let state = pr.state.ok_or_eyre("pr does not have state")?;
+ let is_draft = pr.title.as_deref().is_some_and(|s| s.starts_with("WIP:"));
+ match state {
+ StateType::Open => {
+ if is_draft {
+ println!("{light_grey}Draft{reset} {dash} Can't merge draft PR")
+ } else {
+ print!("{bright_green}Open{reset} {dash} ");
+ let mergable = pr.mergeable.ok_or_eyre("pr does not have mergable")?;
+ if mergable {
+ println!("Can be merged");
+ } else {
+ println!("{bright_red}Merge conflicts{reset}");
+ }
+ }
+ }
+ StateType::Closed => println!("{bright_red}Closed{reset} {dash} Reopen to merge"),
+ }
+
+ let commit_statuses = combined_status
+ .statuses
+ .ok_or_eyre("combined status does not have status list")?;
+ for status in commit_statuses {
+ let state = status
+ .status
+ .as_deref()
+ .ok_or_eyre("status does not have status")?;
+ let context = status
+ .context
+ .as_deref()
+ .ok_or_eyre("status does not have context")?;
+ print!("{bullet} ");
+ match state {
+ "success" => print!("{bright_green}Success{reset}"),
+ "pending" => print!("{yellow}Pending{reset}"),
+ "failure" => print!("{bright_red}Failure{reset}"),
+ _ => eyre::bail!("invalid status"),
+ };
+ println!(" {dash} {context}");
+ }
+ }
+
+ Ok(())
+}
+
async fn edit_pr_labels(
repo: &RepoName,
api: &Forgejo,