mirror of
https://codeberg.org/Cyborus/forgejo-cli.git
synced 2024-11-10 03:59:31 +01:00
Merge pull request 'add pr status
' (#88) from pr-status into main
Reviewed-on: https://codeberg.org/Cyborus/forgejo-cli/pulls/88
This commit is contained in:
commit
43765c77d7
3 changed files with 135 additions and 1 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -1186,6 +1186,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -1868,7 +1877,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
|||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
|
|
|
@ -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"] }
|
||||
|
|
123
src/prs.rs
123
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,
|
||||
|
|
Loading…
Reference in a new issue