mirror of
https://codeberg.org/Cyborus/forgejo-cli.git
synced 2024-11-10 12:09:33 +01:00
refactor: move repo commands into individual functions
This commit is contained in:
parent
5f2f3ceb95
commit
ed86e74d1b
1 changed files with 268 additions and 230 deletions
498
src/repo.rs
498
src/repo.rs
|
@ -2,7 +2,7 @@ use std::{io::Write, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use eyre::{eyre, OptionExt};
|
use eyre::{eyre, OptionExt};
|
||||||
use forgejo_api::structs::CreateRepoOption;
|
use forgejo_api::{structs::CreateRepoOption, Forgejo};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::SpecialRender;
|
use crate::SpecialRender;
|
||||||
|
@ -364,107 +364,34 @@ impl RepoCommand {
|
||||||
remote,
|
remote,
|
||||||
push,
|
push,
|
||||||
} => {
|
} => {
|
||||||
if remote.is_some() || push {
|
|
||||||
let repo = git2::Repository::open(".")?;
|
|
||||||
|
|
||||||
let upstream = remote.as_deref().unwrap_or("origin");
|
|
||||||
if repo.find_remote(upstream).is_ok() {
|
|
||||||
eyre::bail!("A remote named \"{upstream}\" already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let host = RepoInfo::get_current(host_name, None, None)?;
|
let host = RepoInfo::get_current(host_name, None, None)?;
|
||||||
let api = keys.get_api(host.host_url()).await?;
|
let api = keys.get_api(host.host_url()).await?;
|
||||||
let repo_spec = CreateRepoOption {
|
create_repo(&api, repo, description, private, remote, push).await?;
|
||||||
auto_init: Some(false),
|
|
||||||
default_branch: Some("main".into()),
|
|
||||||
description,
|
|
||||||
gitignores: None,
|
|
||||||
issue_labels: None,
|
|
||||||
license: None,
|
|
||||||
name: repo,
|
|
||||||
object_format_name: None,
|
|
||||||
private: Some(private),
|
|
||||||
readme: Some(String::new()),
|
|
||||||
template: Some(false),
|
|
||||||
trust_model: Some(forgejo_api::structs::CreateRepoOptionTrustModel::Default),
|
|
||||||
};
|
|
||||||
let new_repo = api.create_current_user_repo(repo_spec).await?;
|
|
||||||
let html_url = new_repo
|
|
||||||
.html_url
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| eyre::eyre!("new_repo does not have html_url"))?;
|
|
||||||
println!("created new repo at {}", html_url);
|
|
||||||
|
|
||||||
if remote.is_some() || push {
|
|
||||||
let repo = git2::Repository::open(".")?;
|
|
||||||
|
|
||||||
let upstream = remote.as_deref().unwrap_or("origin");
|
|
||||||
let clone_url = new_repo
|
|
||||||
.clone_url
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| eyre::eyre!("new_repo does not have clone_url"))?;
|
|
||||||
let mut remote = repo.remote(upstream, clone_url.as_str())?;
|
|
||||||
|
|
||||||
if push {
|
|
||||||
let head = repo.head()?;
|
|
||||||
if !head.is_branch() {
|
|
||||||
eyre::bail!("HEAD is not on a branch; cannot push to remote");
|
|
||||||
}
|
|
||||||
let branch_shorthand = head
|
|
||||||
.shorthand()
|
|
||||||
.ok_or_else(|| eyre!("branch name invalid utf-8"))?
|
|
||||||
.to_owned();
|
|
||||||
let branch_name = std::str::from_utf8(head.name_bytes())?.to_owned();
|
|
||||||
|
|
||||||
let auth = auth_git2::GitAuthenticator::new();
|
|
||||||
auth.push(&repo, &mut remote, &[&branch_name])?;
|
|
||||||
|
|
||||||
remote.fetch(&[&branch_shorthand], None, None)?;
|
|
||||||
|
|
||||||
let mut current_branch = git2::Branch::wrap(head);
|
|
||||||
current_branch
|
|
||||||
.set_upstream(Some(&format!("{upstream}/{branch_shorthand}")))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RepoCommand::Fork { repo, name, remote } => {
|
RepoCommand::Fork { repo, name, remote } => {
|
||||||
|
fn strip(s: &str) -> &str {
|
||||||
|
let no_scheme = s
|
||||||
|
.strip_prefix("https://")
|
||||||
|
.or_else(|| s.strip_prefix("http://"))
|
||||||
|
.unwrap_or(s);
|
||||||
|
let no_trailing_slash = no_scheme.strip_suffix("/").unwrap_or(no_scheme);
|
||||||
|
no_trailing_slash
|
||||||
|
}
|
||||||
match (repo.host.as_deref(), host_name) {
|
match (repo.host.as_deref(), host_name) {
|
||||||
(Some(a), Some(b)) => {
|
(Some(a), Some(b)) => {
|
||||||
fn strip(s: &str) -> &str {
|
|
||||||
let no_scheme = s
|
|
||||||
.strip_prefix("https://")
|
|
||||||
.or_else(|| s.strip_prefix("http://"))
|
|
||||||
.unwrap_or(s);
|
|
||||||
let no_trailing_slash =
|
|
||||||
no_scheme.strip_suffix("/").unwrap_or(no_scheme);
|
|
||||||
no_trailing_slash
|
|
||||||
}
|
|
||||||
if strip(a) != strip(b) {
|
if strip(a) != strip(b) {
|
||||||
eyre::bail!("conflicting hosts {a} and {b}. please only specify one");
|
eyre::bail!("conflicting hosts {a} and {b}. please only specify one");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo_info = RepoInfo::get_current(host_name, Some(&repo), remote.as_deref())?;
|
let repo_info = RepoInfo::get_current(host_name, Some(&repo), remote.as_deref())?;
|
||||||
let api = keys.get_api(&repo_info.host_url()).await?;
|
let api = keys.get_api(&repo_info.host_url()).await?;
|
||||||
let repo = repo_info
|
let repo = repo_info
|
||||||
.name()
|
.name()
|
||||||
.ok_or_eyre("couldn't get repo name, please specify")?;
|
.ok_or_eyre("couldn't get repo name, please specify")?;
|
||||||
let opt = forgejo_api::structs::CreateForkOption {
|
fork_repo(&api, repo, name).await?
|
||||||
name,
|
|
||||||
organization: None,
|
|
||||||
};
|
|
||||||
let new_fork = api.create_fork(repo.owner(), repo.name(), opt).await?;
|
|
||||||
let fork_full_name = new_fork
|
|
||||||
.full_name
|
|
||||||
.as_deref()
|
|
||||||
.ok_or_eyre("fork does not have name")?;
|
|
||||||
println!(
|
|
||||||
"Forked {}/{} into {}",
|
|
||||||
repo.owner(),
|
|
||||||
repo.name(),
|
|
||||||
fork_full_name
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
RepoCommand::View { name, remote } => {
|
RepoCommand::View { name, remote } => {
|
||||||
let repo = RepoInfo::get_current(host_name, name.as_ref(), remote.as_deref())?;
|
let repo = RepoInfo::get_current(host_name, name.as_ref(), remote.as_deref())?;
|
||||||
|
@ -472,143 +399,13 @@ impl RepoCommand {
|
||||||
let repo = repo
|
let repo = repo
|
||||||
.name()
|
.name()
|
||||||
.ok_or_eyre("couldn't get repo name, please specify")?;
|
.ok_or_eyre("couldn't get repo name, please specify")?;
|
||||||
let repo = api.repo_get(repo.owner(), repo.name()).await?;
|
view_repo(&api, &repo).await?
|
||||||
|
|
||||||
let SpecialRender {
|
|
||||||
dash,
|
|
||||||
body_prefix,
|
|
||||||
dark_grey,
|
|
||||||
reset,
|
|
||||||
..
|
|
||||||
} = crate::special_render();
|
|
||||||
|
|
||||||
println!("{}", repo.full_name.ok_or_eyre("no full name")?);
|
|
||||||
|
|
||||||
if let Some(parent) = &repo.parent {
|
|
||||||
println!(
|
|
||||||
"Fork of {}",
|
|
||||||
parent.full_name.as_ref().ok_or_eyre("no full name")?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if repo.mirror == Some(true) {
|
|
||||||
if let Some(original) = &repo.original_url {
|
|
||||||
println!("Mirror of {original}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let desc = repo.description.as_deref().unwrap_or_default();
|
|
||||||
// Don't use body::markdown, this is plain text.
|
|
||||||
if !desc.is_empty() {
|
|
||||||
if desc.lines().count() > 1 {
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
for line in desc.lines() {
|
|
||||||
println!("{dark_grey}{body_prefix}{reset} {line}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let lang = repo.language.as_deref().unwrap_or_default();
|
|
||||||
if !lang.is_empty() {
|
|
||||||
println!("Primary language is {lang}");
|
|
||||||
}
|
|
||||||
|
|
||||||
let stars = repo.stars_count.unwrap_or_default();
|
|
||||||
if stars == 1 {
|
|
||||||
print!("{stars} star {dash} ");
|
|
||||||
} else {
|
|
||||||
print!("{stars} stars {dash} ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let watchers = repo.watchers_count.unwrap_or_default();
|
|
||||||
print!("{watchers} watching {dash} ");
|
|
||||||
|
|
||||||
let forks = repo.forks_count.unwrap_or_default();
|
|
||||||
if forks == 1 {
|
|
||||||
print!("{forks} fork");
|
|
||||||
} else {
|
|
||||||
print!("{forks} forks");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let mut first = true;
|
|
||||||
if repo.has_issues.unwrap_or_default() && repo.external_tracker.is_none() {
|
|
||||||
let issues = repo.open_issues_count.unwrap_or_default();
|
|
||||||
if issues == 1 {
|
|
||||||
print!("{issues} issue");
|
|
||||||
} else {
|
|
||||||
print!("{issues} issues");
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
if repo.has_pull_requests.unwrap_or_default() {
|
|
||||||
if !first {
|
|
||||||
print!(" {dash} ");
|
|
||||||
}
|
|
||||||
let pulls = repo.open_pr_counter.unwrap_or_default();
|
|
||||||
if pulls == 1 {
|
|
||||||
print!("{pulls} PR");
|
|
||||||
} else {
|
|
||||||
print!("{pulls} PRs");
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
if repo.has_releases.unwrap_or_default() {
|
|
||||||
if !first {
|
|
||||||
print!(" {dash} ");
|
|
||||||
}
|
|
||||||
let releases = repo.release_counter.unwrap_or_default();
|
|
||||||
if releases == 1 {
|
|
||||||
print!("{releases} release");
|
|
||||||
} else {
|
|
||||||
print!("{releases} releases");
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
if !first {
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
if let Some(external_tracker) = &repo.external_tracker {
|
|
||||||
if let Some(tracker_url) = &external_tracker.external_tracker_url {
|
|
||||||
println!("Issue tracker is at {tracker_url}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(html_url) = &repo.html_url {
|
|
||||||
println!();
|
|
||||||
println!("View online at {html_url}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RepoCommand::Clone { repo, path } => {
|
RepoCommand::Clone { repo, path } => {
|
||||||
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
|
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
|
||||||
let api = keys.get_api(&repo.host_url()).await?;
|
let api = keys.get_api(&repo.host_url()).await?;
|
||||||
let name = repo.name().unwrap();
|
let name = repo.name().unwrap();
|
||||||
|
cmd_clone_repo(&api, &name, path).await?;
|
||||||
let repo_data = api.repo_get(name.owner(), name.name()).await?;
|
|
||||||
let clone_url = repo_data
|
|
||||||
.clone_url
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_eyre("repo does not have clone url")?;
|
|
||||||
|
|
||||||
let repo_name = repo_data
|
|
||||||
.name
|
|
||||||
.as_deref()
|
|
||||||
.ok_or_eyre("repo does not have name")?;
|
|
||||||
let repo_full_name = repo_data
|
|
||||||
.full_name
|
|
||||||
.as_deref()
|
|
||||||
.ok_or_eyre("repo does not have full name")?;
|
|
||||||
|
|
||||||
let path = path.unwrap_or_else(|| PathBuf::from(format!("./{repo_name}")));
|
|
||||||
|
|
||||||
let local_repo = clone_repo(&repo_full_name, &clone_url, &path)?;
|
|
||||||
|
|
||||||
if let Some(parent) = repo_data.parent.as_deref() {
|
|
||||||
let parent_clone_url = parent
|
|
||||||
.clone_url
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_eyre("parent repo does not have clone url")?;
|
|
||||||
local_repo.remote("upstream", parent_clone_url.as_str())?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RepoCommand::Star { repo } => {
|
RepoCommand::Star { repo } => {
|
||||||
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
|
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
|
||||||
|
@ -629,19 +426,7 @@ impl RepoCommand {
|
||||||
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
|
let repo = RepoInfo::get_current(host_name, Some(&repo), None)?;
|
||||||
let api = keys.get_api(&repo.host_url()).await?;
|
let api = keys.get_api(&repo.host_url()).await?;
|
||||||
let name = repo.name().unwrap();
|
let name = repo.name().unwrap();
|
||||||
print!(
|
delete_repo(&api, &name).await?;
|
||||||
"Are you sure you want to delete {}/{}? (y/N) ",
|
|
||||||
name.owner(),
|
|
||||||
name.name()
|
|
||||||
);
|
|
||||||
let user_response = crate::readline("").await?;
|
|
||||||
let yes = matches!(user_response.trim(), "y" | "Y" | "yes" | "Yes");
|
|
||||||
if yes {
|
|
||||||
api.repo_delete(name.owner(), name.name()).await?;
|
|
||||||
println!("Deleted {}/{}", name.owner(), name.name());
|
|
||||||
} else {
|
|
||||||
println!("Did not delete");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RepoCommand::Browse { name, remote } => {
|
RepoCommand::Browse { name, remote } => {
|
||||||
let repo = RepoInfo::get_current(host_name, name.as_ref(), remote.as_deref())?;
|
let repo = RepoInfo::get_current(host_name, name.as_ref(), remote.as_deref())?;
|
||||||
|
@ -660,6 +445,242 @@ impl RepoCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_repo(
|
||||||
|
api: &Forgejo,
|
||||||
|
repo: String,
|
||||||
|
description: Option<String>,
|
||||||
|
private: bool,
|
||||||
|
remote: Option<String>,
|
||||||
|
push: bool,
|
||||||
|
) -> eyre::Result<()> {
|
||||||
|
if remote.is_some() || push {
|
||||||
|
let repo = git2::Repository::open(".")?;
|
||||||
|
|
||||||
|
let upstream = remote.as_deref().unwrap_or("origin");
|
||||||
|
if repo.find_remote(upstream).is_ok() {
|
||||||
|
eyre::bail!("A remote named \"{upstream}\" already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let repo_spec = CreateRepoOption {
|
||||||
|
auto_init: Some(false),
|
||||||
|
default_branch: Some("main".into()),
|
||||||
|
description,
|
||||||
|
gitignores: None,
|
||||||
|
issue_labels: None,
|
||||||
|
license: None,
|
||||||
|
name: repo,
|
||||||
|
object_format_name: None,
|
||||||
|
private: Some(private),
|
||||||
|
readme: Some(String::new()),
|
||||||
|
template: Some(false),
|
||||||
|
trust_model: Some(forgejo_api::structs::CreateRepoOptionTrustModel::Default),
|
||||||
|
};
|
||||||
|
let new_repo = api.create_current_user_repo(repo_spec).await?;
|
||||||
|
let html_url = new_repo
|
||||||
|
.html_url
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| eyre::eyre!("new_repo does not have html_url"))?;
|
||||||
|
println!("created new repo at {}", html_url);
|
||||||
|
|
||||||
|
if remote.is_some() || push {
|
||||||
|
let repo = git2::Repository::open(".")?;
|
||||||
|
|
||||||
|
let upstream = remote.as_deref().unwrap_or("origin");
|
||||||
|
let clone_url = new_repo
|
||||||
|
.clone_url
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| eyre::eyre!("new_repo does not have clone_url"))?;
|
||||||
|
let mut remote = repo.remote(upstream, clone_url.as_str())?;
|
||||||
|
|
||||||
|
if push {
|
||||||
|
let head = repo.head()?;
|
||||||
|
if !head.is_branch() {
|
||||||
|
eyre::bail!("HEAD is not on a branch; cannot push to remote");
|
||||||
|
}
|
||||||
|
let branch_shorthand = head
|
||||||
|
.shorthand()
|
||||||
|
.ok_or_else(|| eyre!("branch name invalid utf-8"))?
|
||||||
|
.to_owned();
|
||||||
|
let branch_name = std::str::from_utf8(head.name_bytes())?.to_owned();
|
||||||
|
|
||||||
|
let auth = auth_git2::GitAuthenticator::new();
|
||||||
|
auth.push(&repo, &mut remote, &[&branch_name])?;
|
||||||
|
|
||||||
|
remote.fetch(&[&branch_shorthand], None, None)?;
|
||||||
|
|
||||||
|
let mut current_branch = git2::Branch::wrap(head);
|
||||||
|
current_branch.set_upstream(Some(&format!("{upstream}/{branch_shorthand}")))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fork_repo(api: &Forgejo, repo: &RepoName, name: Option<String>) -> eyre::Result<()> {
|
||||||
|
let opt = forgejo_api::structs::CreateForkOption {
|
||||||
|
name,
|
||||||
|
organization: None,
|
||||||
|
};
|
||||||
|
let new_fork = api.create_fork(repo.owner(), repo.name(), opt).await?;
|
||||||
|
let fork_full_name = new_fork
|
||||||
|
.full_name
|
||||||
|
.as_deref()
|
||||||
|
.ok_or_eyre("fork does not have name")?;
|
||||||
|
println!(
|
||||||
|
"Forked {}/{} into {}",
|
||||||
|
repo.owner(),
|
||||||
|
repo.name(),
|
||||||
|
fork_full_name
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn view_repo(api: &Forgejo, repo: &RepoName) -> eyre::Result<()> {
|
||||||
|
let repo = api.repo_get(repo.owner(), repo.name()).await?;
|
||||||
|
|
||||||
|
let SpecialRender {
|
||||||
|
dash,
|
||||||
|
body_prefix,
|
||||||
|
dark_grey,
|
||||||
|
reset,
|
||||||
|
..
|
||||||
|
} = crate::special_render();
|
||||||
|
|
||||||
|
println!("{}", repo.full_name.ok_or_eyre("no full name")?);
|
||||||
|
|
||||||
|
if let Some(parent) = &repo.parent {
|
||||||
|
println!(
|
||||||
|
"Fork of {}",
|
||||||
|
parent.full_name.as_ref().ok_or_eyre("no full name")?
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if repo.mirror == Some(true) {
|
||||||
|
if let Some(original) = &repo.original_url {
|
||||||
|
println!("Mirror of {original}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let desc = repo.description.as_deref().unwrap_or_default();
|
||||||
|
// Don't use body::markdown, this is plain text.
|
||||||
|
if !desc.is_empty() {
|
||||||
|
if desc.lines().count() > 1 {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
for line in desc.lines() {
|
||||||
|
println!("{dark_grey}{body_prefix}{reset} {line}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let lang = repo.language.as_deref().unwrap_or_default();
|
||||||
|
if !lang.is_empty() {
|
||||||
|
println!("Primary language is {lang}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let stars = repo.stars_count.unwrap_or_default();
|
||||||
|
if stars == 1 {
|
||||||
|
print!("{stars} star {dash} ");
|
||||||
|
} else {
|
||||||
|
print!("{stars} stars {dash} ");
|
||||||
|
}
|
||||||
|
|
||||||
|
let watchers = repo.watchers_count.unwrap_or_default();
|
||||||
|
print!("{watchers} watching {dash} ");
|
||||||
|
|
||||||
|
let forks = repo.forks_count.unwrap_or_default();
|
||||||
|
if forks == 1 {
|
||||||
|
print!("{forks} fork");
|
||||||
|
} else {
|
||||||
|
print!("{forks} forks");
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
if repo.has_issues.unwrap_or_default() && repo.external_tracker.is_none() {
|
||||||
|
let issues = repo.open_issues_count.unwrap_or_default();
|
||||||
|
if issues == 1 {
|
||||||
|
print!("{issues} issue");
|
||||||
|
} else {
|
||||||
|
print!("{issues} issues");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if repo.has_pull_requests.unwrap_or_default() {
|
||||||
|
if !first {
|
||||||
|
print!(" {dash} ");
|
||||||
|
}
|
||||||
|
let pulls = repo.open_pr_counter.unwrap_or_default();
|
||||||
|
if pulls == 1 {
|
||||||
|
print!("{pulls} PR");
|
||||||
|
} else {
|
||||||
|
print!("{pulls} PRs");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if repo.has_releases.unwrap_or_default() {
|
||||||
|
if !first {
|
||||||
|
print!(" {dash} ");
|
||||||
|
}
|
||||||
|
let releases = repo.release_counter.unwrap_or_default();
|
||||||
|
if releases == 1 {
|
||||||
|
print!("{releases} release");
|
||||||
|
} else {
|
||||||
|
print!("{releases} releases");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if !first {
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
if let Some(external_tracker) = &repo.external_tracker {
|
||||||
|
if let Some(tracker_url) = &external_tracker.external_tracker_url {
|
||||||
|
println!("Issue tracker is at {tracker_url}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(html_url) = &repo.html_url {
|
||||||
|
println!();
|
||||||
|
println!("View online at {html_url}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cmd_clone_repo(
|
||||||
|
api: &Forgejo,
|
||||||
|
name: &RepoName,
|
||||||
|
path: Option<std::path::PathBuf>,
|
||||||
|
) -> eyre::Result<()> {
|
||||||
|
let repo_data = api.repo_get(name.owner(), name.name()).await?;
|
||||||
|
let clone_url = repo_data
|
||||||
|
.clone_url
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_eyre("repo does not have clone url")?;
|
||||||
|
|
||||||
|
let repo_name = repo_data
|
||||||
|
.name
|
||||||
|
.as_deref()
|
||||||
|
.ok_or_eyre("repo does not have name")?;
|
||||||
|
let repo_full_name = repo_data
|
||||||
|
.full_name
|
||||||
|
.as_deref()
|
||||||
|
.ok_or_eyre("repo does not have full name")?;
|
||||||
|
|
||||||
|
let path = path.unwrap_or_else(|| PathBuf::from(format!("./{repo_name}")));
|
||||||
|
|
||||||
|
let local_repo = clone_repo(&repo_full_name, &clone_url, &path)?;
|
||||||
|
|
||||||
|
if let Some(parent) = repo_data.parent.as_deref() {
|
||||||
|
let parent_clone_url = parent
|
||||||
|
.clone_url
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_eyre("parent repo does not have clone url")?;
|
||||||
|
local_repo.remote("upstream", parent_clone_url.as_str())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clone_repo(
|
pub fn clone_repo(
|
||||||
repo_name: &str,
|
repo_name: &str,
|
||||||
url: &url::Url,
|
url: &url::Url,
|
||||||
|
@ -726,3 +747,20 @@ pub fn clone_repo(
|
||||||
println!("Cloned {} into {}", repo_name, path.display());
|
println!("Cloned {} into {}", repo_name, path.display());
|
||||||
Ok(local_repo)
|
Ok(local_repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn delete_repo(api: &Forgejo, name: &RepoName) -> eyre::Result<()> {
|
||||||
|
print!(
|
||||||
|
"Are you sure you want to delete {}/{}? (y/N) ",
|
||||||
|
name.owner(),
|
||||||
|
name.name()
|
||||||
|
);
|
||||||
|
let user_response = crate::readline("").await?;
|
||||||
|
let yes = matches!(user_response.trim(), "y" | "Y" | "yes" | "Yes");
|
||||||
|
if yes {
|
||||||
|
api.repo_delete(name.owner(), name.name()).await?;
|
||||||
|
println!("Deleted {}/{}", name.owner(), name.name());
|
||||||
|
} else {
|
||||||
|
println!("Did not delete");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue