Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package forgejo-cli for openSUSE:Factory checked in at 2024-08-10 19:08:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/forgejo-cli (Old) and /work/SRC/openSUSE:Factory/.forgejo-cli.new.7232 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "forgejo-cli" Sat Aug 10 19:08:07 2024 rev:2 rq:1193063 version:0.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/forgejo-cli/forgejo-cli.changes 2024-08-08 10:59:09.173481168 +0200 +++ /work/SRC/openSUSE:Factory/.forgejo-cli.new.7232/forgejo-cli.changes 2024-08-10 19:13:54.192987992 +0200 @@ -1,0 +2,6 @@ +Fri Aug 9 21:17:00 UTC 2024 - Richard Rahl <[email protected]> + +- update to 0.1.1: + * Suggest correct command when OAuth login isn't supported on an instance + +------------------------------------------------------------------- Old: ---- forgejo-cli-0.1.0.obscpio New: ---- forgejo-cli-0.1.1.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ forgejo-cli.spec ++++++ --- /var/tmp/diff_new_pack.yR7Df7/_old 2024-08-10 19:13:55.313034505 +0200 +++ /var/tmp/diff_new_pack.yR7Df7/_new 2024-08-10 19:13:55.313034505 +0200 @@ -17,7 +17,7 @@ Name: forgejo-cli -Version: 0.1.0 +Version: 0.1.1 Release: 0 Summary: CLI application for interacting with Forgejo License: Apache-2.0 OR MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.yR7Df7/_old 2024-08-10 19:13:55.349036001 +0200 +++ /var/tmp/diff_new_pack.yR7Df7/_new 2024-08-10 19:13:55.353036167 +0200 @@ -2,7 +2,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://codeberg.org/Cyborus/forgejo-cli.git</param> <param name="scm">git</param> - <param name="revision">refs/tags/v0.1.0</param> + <param name="revision">refs/tags/v0.1.1</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> </service> ++++++ forgejo-cli-0.1.0.obscpio -> forgejo-cli-0.1.1.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-cli-0.1.0/Cargo.lock new/forgejo-cli-0.1.1/Cargo.lock --- old/forgejo-cli-0.1.0/Cargo.lock 2024-08-08 03:08:13.000000000 +0200 +++ new/forgejo-cli-0.1.1/Cargo.lock 2024-08-09 20:59:26.000000000 +0200 @@ -542,35 +542,6 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] -name = "fj" -version = "0.1.0" -dependencies = [ - "auth-git2", - "base64ct", - "clap", - "comrak", - "crossterm", - "directories", - "eyre", - "forgejo-api", - "futures", - "git2", - "hyper 1.4.1", - "hyper-util", - "open", - "rand", - "semver", - "serde", - "serde_json", - "sha256", - "soft_assert", - "time", - "tokio", - "url", - "uuid", -] - -[[package]] name = "flate2" version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -621,6 +592,35 @@ ] [[package]] +name = "forgejo-cli" +version = "0.1.1" +dependencies = [ + "auth-git2", + "base64ct", + "clap", + "comrak", + "crossterm", + "directories", + "eyre", + "forgejo-api", + "futures", + "git2", + "hyper 1.4.1", + "hyper-util", + "open", + "rand", + "semver", + "serde", + "serde_json", + "sha256", + "soft_assert", + "time", + "tokio", + "url", + "uuid", +] + +[[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-cli-0.1.0/Cargo.toml new/forgejo-cli-0.1.1/Cargo.toml --- old/forgejo-cli-0.1.0/Cargo.toml 2024-08-08 03:08:13.000000000 +0200 +++ new/forgejo-cli-0.1.1/Cargo.toml 2024-08-09 20:59:26.000000000 +0200 @@ -1,10 +1,19 @@ [package] -name = "fj" -version = "0.1.0" +name = "forgejo-cli" +version = "0.1.1" edition = "2021" +license = "Apache-2.0 OR MIT" +repository = "https://codeberg.org/Cyborus/forgejo-cli/" +description = "CLI tool for Forgejo" +keywords = ["cli", "forgejo"] +categories = ["command-line-utilities", "development-tools"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "fj" +path = "src/main.rs" + [features] update-check = ["dep:semver"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-cli-0.1.0/README.md new/forgejo-cli-0.1.1/README.md --- old/forgejo-cli-0.1.0/README.md 2024-08-08 03:08:13.000000000 +0200 +++ new/forgejo-cli-0.1.1/README.md 2024-08-09 20:59:26.000000000 +0200 @@ -17,7 +17,7 @@ ``` # Latest version -cargo install --git https://codeberg.org/Cyborus/forgejo-cli.git --tag v0.0.4 +cargo install forgejo-cli # From `main` cargo install --git https://codeberg.org/Cyborus/forgejo-cli.git --branch main ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-cli-0.1.0/src/auth.rs new/forgejo-cli-0.1.1/src/auth.rs --- old/forgejo-cli-0.1.0/src/auth.rs 2024-08-08 03:08:13.000000000 +0200 +++ new/forgejo-cli-0.1.1/src/auth.rs 2024-08-09 20:59:26.000000000 +0200 @@ -45,7 +45,7 @@ println!("{host_domain}{host_path} doesn't support easy login"); println!(); println!("Please visit {applications_url}"); - println!("to create a token, and use it to log in with `fj auth add-token`"); + println!("to create a token, and use it to log in with `fj auth add-key`"); } } AuthCommand::Logout { host } => { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/forgejo-cli-0.1.0/src/repo.rs new/forgejo-cli-0.1.1/src/repo.rs --- old/forgejo-cli-0.1.0/src/repo.rs 2024-08-08 03:08:13.000000000 +0200 +++ new/forgejo-cli-0.1.1/src/repo.rs 2024-08-09 20:59:26.000000000 +0200 @@ -2,7 +2,7 @@ use clap::Subcommand; use eyre::{eyre, OptionExt}; -use forgejo_api::structs::CreateRepoOption; +use forgejo_api::{structs::CreateRepoOption, Forgejo}; use url::Url; use crate::SpecialRender; @@ -364,107 +364,34 @@ remote, 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 api = keys.get_api(host.host_url()).await?; - 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}")))?; - } - } + create_repo(&api, repo, description, private, remote, push).await?; } 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) { (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) { 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 api = keys.get_api(&repo_info.host_url()).await?; let repo = repo_info .name() .ok_or_eyre("couldn't get repo name, please specify")?; - 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 - ); + fork_repo(&api, repo, name).await? } RepoCommand::View { name, remote } => { let repo = RepoInfo::get_current(host_name, name.as_ref(), remote.as_deref())?; @@ -472,143 +399,13 @@ let repo = repo .name() .ok_or_eyre("couldn't get repo name, please specify")?; - 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}"); - } + view_repo(&api, &repo).await? } RepoCommand::Clone { repo, path } => { let repo = RepoInfo::get_current(host_name, Some(&repo), None)?; let api = keys.get_api(&repo.host_url()).await?; let name = repo.name().unwrap(); - - 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())?; - } + cmd_clone_repo(&api, &name, path).await?; } RepoCommand::Star { repo } => { let repo = RepoInfo::get_current(host_name, Some(&repo), None)?; @@ -629,19 +426,7 @@ let repo = RepoInfo::get_current(host_name, Some(&repo), None)?; let api = keys.get_api(&repo.host_url()).await?; let name = repo.name().unwrap(); - 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"); - } + delete_repo(&api, &name).await?; } RepoCommand::Browse { name, remote } => { let repo = RepoInfo::get_current(host_name, name.as_ref(), remote.as_deref())?; @@ -660,6 +445,242 @@ } } +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( repo_name: &str, url: &url::Url, @@ -726,3 +747,20 @@ println!("Cloned {} into {}", repo_name, path.display()); 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(()) +} ++++++ forgejo-cli.obsinfo ++++++ --- /var/tmp/diff_new_pack.yR7Df7/_old 2024-08-10 19:13:55.497042147 +0200 +++ /var/tmp/diff_new_pack.yR7Df7/_new 2024-08-10 19:13:55.501042313 +0200 @@ -1,5 +1,5 @@ name: forgejo-cli -version: 0.1.0 -mtime: 1723079293 -commit: 2b25f0a292a7f4acc542a8248c073e770d633e86 +version: 0.1.1 +mtime: 1723229966 +commit: b3c242d8c5e57959f28fa084619027bc1715bdc0 ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/forgejo-cli/vendor.tar.zst /work/SRC/openSUSE:Factory/.forgejo-cli.new.7232/vendor.tar.zst differ: char 698932, line 2890
