refactor: move gitea out of the way

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2023-08-01 16:38:30 +02:00
parent 39db4b8d1c
commit e51454088e
Signed by: kjuulh
GPG Key ID: 9AA7BC13CE474394
11 changed files with 481 additions and 277 deletions

View File

@ -1,19 +1,24 @@
use std::{ use std::{
env::current_dir,
io::Read, io::Read,
ops::Deref, ops::Deref,
path::{Path, PathBuf}, path::{Path, PathBuf},
rc::Rc,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use ::semver::Version; use ::semver::Version;
use anyhow::Context; use anyhow::Context;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use cuddle_please_frontend::{gatheres::ConfigArgs, PleaseConfigBuilder}; use cuddle_please_frontend::{gatheres::ConfigArgs, PleaseConfig, PleaseConfigBuilder};
use cuddle_please_misc::{ use cuddle_please_misc::{
changelog_parser, get_most_significant_version, ChangeLogBuilder, ConsoleUi, DynUi, changelog_parser, get_most_significant_version, ChangeLogBuilder, ConsoleUi,
GiteaClient, GlobalArgs, NextVersion, StdinFn, VcsClient, DynRemoteGitClient, DynUi, GiteaClient, GlobalArgs, LocalGitClient, NextVersion,
RemoteGitEngine, StdinFn, VcsClient,
}; };
use crate::release_command::ReleaseCommand;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Command { pub struct Command {
@ -84,124 +89,13 @@ impl Command {
pub fn execute(self, current_dir: Option<&Path>) -> anyhow::Result<()> { pub fn execute(self, current_dir: Option<&Path>) -> anyhow::Result<()> {
let config = self.build_config(current_dir)?; let config = self.build_config(current_dir)?;
let git_client = self.get_git(config.get_source())?;
let gitea_client = self.get_gitea_client();
match &self.commands { match &self.commands {
Some(Commands::Release {}) => { Some(Commands::Release {}) => {
tracing::debug!("running bare command"); ReleaseCommand::new(config, git_client, gitea_client)
// 2. Parse the cuddle.please.yaml let cuddle.please.yaml take precedence .execute(self.global.dry_run)?;
// 2a. if not existing use default.
// 2b. if not in a git repo abort. (unless --no-vcs is turned added)
let git_client = self.get_git(config.get_source())?;
// 3. Create gitea client and do a health check
let gitea_client = self.get_gitea_client();
gitea_client
.connect(config.get_owner(), config.get_repository())
.context("failed to connect to gitea repository")?;
// 4. Fetch git tags for the current repository
let tags = gitea_client.get_tags(config.get_owner(), config.get_repository())?;
let significant_tag = get_most_significant_version(tags.iter().collect());
// 5. Fetch git commits since last git tag
let commits = gitea_client.get_commits_since(
config.get_owner(),
config.get_repository(),
significant_tag.map(|st| st.commit.sha.clone()),
config.get_branch(),
)?;
// 7. Create a versioning client
let current_version = significant_tag
.map(|st| Version::try_from(st).unwrap())
.unwrap_or(Version::new(0, 1, 0));
// 8. Parse conventional commits and determine next version
let commit_strs = commits
.iter()
.map(|c| c.commit.message.as_str())
.collect::<Vec<&str>>();
if commit_strs.is_empty() {
tracing::info!("no commits to base release on");
return Ok(());
}
let next_version = current_version.next(&commit_strs);
// Compose changelog
let builder = ChangeLogBuilder::new(&commit_strs, next_version.to_string()).build();
let changelog_placement = config.get_source().join("CHANGELOG.md");
let changelog = match std::fs::read_to_string(&changelog_placement).ok() {
Some(existing_changelog) => builder.prepend(existing_changelog)?,
None => builder.generate()?,
};
let changelog_last_changes = changelog_parser::last_changes(&changelog)?;
// 9b. check for release commit and release, if release exists continue
// 10b. create release
if let Some(first_commit) = commit_strs.first() {
if first_commit.contains("chore(release): ") {
if !self.global.dry_run {
gitea_client.create_release(
config.get_owner(),
config.get_repository(),
next_version.to_string(),
changelog_last_changes.unwrap(),
!next_version.pre.is_empty(),
)?;
} else {
tracing::debug!("creating release (dry_run)");
}
return Ok(());
}
}
// 9a. Create / Update Pr
// Create or update branch
git_client.checkout_branch()?;
std::fs::write(changelog_placement, changelog.as_bytes())?;
git_client.commit_and_push(next_version.to_string(), self.global.dry_run)?;
let _pr_number = match gitea_client
.get_pull_request(config.get_owner(), config.get_repository())?
{
Some(existing_pr) => {
if !self.global.dry_run {
gitea_client.update_pull_request(
config.get_owner(),
config.get_repository(),
next_version.to_string(),
changelog_last_changes.unwrap(),
existing_pr,
)?
} else {
tracing::debug!("updating pull request (dry_run)");
1
}
}
None => {
if !self.global.dry_run {
gitea_client.create_pull_request(
config.get_owner(),
config.get_repository(),
next_version.to_string(),
changelog,
config.get_branch(),
)?
} else {
tracing::debug!("creating pull request (dry_run)");
1
}
}
};
} }
Some(Commands::Config { command }) => match command { Some(Commands::Config { command }) => match command {
@ -222,7 +116,7 @@ impl Command {
url.push_str(format!(":{port}").as_str()); url.push_str(format!(":{port}").as_str());
} }
let client = GiteaClient::new(url, self.global.token); let client = GiteaClient::new(&url, self.global.token.as_ref().map(|t| t.as_str()));
match command { match command {
GiteaCommand::Connect {} => { GiteaCommand::Connect {} => {
client.connect(config.get_owner(), config.get_repository())?; client.connect(config.get_owner(), config.get_repository())?;
@ -302,10 +196,7 @@ impl Command {
Ok(()) Ok(())
} }
fn build_config( fn build_config(&self, current_dir: Option<&Path>) -> Result<PleaseConfig, anyhow::Error> {
&self,
current_dir: Option<&Path>,
) -> Result<cuddle_please_frontend::PleaseConfig, anyhow::Error> {
let mut builder = &mut PleaseConfigBuilder::new(); let mut builder = &mut PleaseConfigBuilder::new();
if self.global.config_stdin { if self.global.config_stdin {
if let Some(stdin_fn) = self.stdin.clone() { if let Some(stdin_fn) = self.stdin.clone() {
@ -316,6 +207,7 @@ impl Command {
let current_dir = get_current_path(current_dir, self.config.source.clone())?; let current_dir = get_current_path(current_dir, self.config.source.clone())?;
let config = builder let config = builder
.with_config_file(&current_dir) .with_config_file(&current_dir)
.with_source(&current_dir)
.with_execution_env(std::env::vars()) .with_execution_env(std::env::vars())
.with_cli(self.config.clone()) .with_cli(self.config.clone())
.build()?; .build()?;
@ -323,14 +215,21 @@ impl Command {
} }
fn get_git(&self, current_dir: &Path) -> anyhow::Result<VcsClient> { fn get_git(&self, current_dir: &Path) -> anyhow::Result<VcsClient> {
if self.global.no_vcs {
Ok(VcsClient::new_noop())
} else {
VcsClient::new_git(current_dir) VcsClient::new_git(current_dir)
} }
}
fn get_gitea_client(&self) -> GiteaClient { fn get_gitea_client(&self) -> DynRemoteGitClient {
GiteaClient::new( match self.global.engine {
self.config.api_url.clone().expect("api_url to be set"), cuddle_please_misc::RemoteEngine::Local => Box::new(LocalGitClient::new()),
self.global.token.clone(), cuddle_please_misc::RemoteEngine::Gitea => Box::new(GiteaClient::new(
) &self.config.api_url.clone().expect("api_url to be set"),
self.global.token.as_ref().map(|t| t.as_str()),
)),
}
} }
} }

View File

@ -1,3 +1,4 @@
mod command; mod command;
mod release_command;
pub use command::Command as PleaseCommand; pub use command::Command as PleaseCommand;

View File

@ -0,0 +1,159 @@
use cuddle_please_frontend::PleaseConfig;
use std::{
io::Read,
ops::Deref,
path::{Path, PathBuf},
sync::{Arc, Mutex},
};
use ::semver::Version;
use anyhow::Context;
use clap::{Parser, Subcommand};
use cuddle_please_frontend::{gatheres::ConfigArgs, PleaseConfigBuilder};
use cuddle_please_misc::{
changelog_parser, get_most_significant_version, ChangeLogBuilder, ConsoleUi,
DynRemoteGitClient, DynUi, GiteaClient, GlobalArgs, NextVersion, StdinFn, VcsClient,
};
pub struct ReleaseCommand {
config: PleaseConfig,
git_client: VcsClient,
gitea_client: DynRemoteGitClient,
}
impl ReleaseCommand {
pub fn new(
config: PleaseConfig,
git_client: VcsClient,
gitea_client: DynRemoteGitClient,
) -> Self {
Self {
config,
git_client,
gitea_client,
}
}
pub fn execute(&self, dry_run: bool) -> anyhow::Result<()> {
tracing::debug!("running bare command");
let owner = self.config.get_owner();
let repository = self.config.get_repository();
let branch = self.config.get_branch();
let source = self.config.get_source();
// 2. Parse the cuddle.please.yaml let cuddle.please.yaml take precedence
// 2a. if not existing use default.
// 2b. if not in a git repo abort. (unless --no-vcs is turned added)
// 3. Create gitea client and do a health check
self.gitea_client
.connect(owner, repository)
.context("failed to connect to gitea repository")?;
// 4. Fetch git tags for the current repository
let tags = self.gitea_client.get_tags(owner, repository)?;
let significant_tag = get_most_significant_version(tags.iter().collect());
// 5. Fetch git commits since last git tag
let commits = self.gitea_client.get_commits_since(
owner,
repository,
significant_tag.map(|st| st.commit.sha.as_str()),
branch,
)?;
// 7. Create a versioning client
let current_version = significant_tag
.map(|st| Version::try_from(st).unwrap())
.unwrap_or(Version::new(0, 1, 0));
// 8. Parse conventional commits and determine next version
let commit_strs = commits
.iter()
.map(|c| c.commit.message.as_str())
.collect::<Vec<&str>>();
if commit_strs.is_empty() {
tracing::info!("no commits to base release on");
return Ok(());
}
let next_version = current_version.next(&commit_strs);
// Compose changelog
let builder = ChangeLogBuilder::new(&commit_strs, next_version.to_string()).build();
let changelog_placement = source.join("CHANGELOG.md");
let changelog = match std::fs::read_to_string(&changelog_placement).ok() {
Some(existing_changelog) => builder.prepend(existing_changelog)?,
None => builder.generate()?,
};
let changelog_last_changes = changelog_parser::last_changes(&changelog)?;
// 9b. check for release commit and release, if release exists continue
// 10b. create release
if let Some(first_commit) = commit_strs.first() {
if first_commit.contains("chore(release): ") {
if !dry_run {
self.gitea_client.create_release(
owner,
repository,
&next_version.to_string(),
&changelog_last_changes.unwrap(),
!next_version.pre.is_empty(),
)?;
} else {
tracing::debug!("creating release (dry_run)");
}
return Ok(());
}
}
// 9a. Create / Update Pr
// Create or update branch
self.git_client.checkout_branch()?;
std::fs::write(changelog_placement, changelog.as_bytes())?;
self.git_client
.commit_and_push(next_version.to_string(), dry_run)?;
let _pr_number = match self.gitea_client.get_pull_request(owner, repository)? {
Some(existing_pr) => {
if !dry_run {
self.gitea_client.update_pull_request(
owner,
repository,
&next_version.to_string(),
&changelog_last_changes.unwrap(),
existing_pr,
)?
} else {
tracing::debug!("updating pull request (dry_run)");
1
}
}
None => {
if !dry_run {
self.gitea_client.create_pull_request(
owner,
repository,
&next_version.to_string(),
&changelog,
branch,
)?
} else {
tracing::debug!("creating pull request (dry_run)");
1
}
}
};
Ok(())
}
}

View File

@ -1,9 +1,11 @@
mod cli; mod cli;
mod config_file; mod config_file;
mod execution_env; mod execution_env;
mod source;
mod stdin; mod stdin;
pub use cli::ConfigArgs; pub use cli::ConfigArgs;
pub(crate) use config_file::get_config_from_config_file; pub(crate) use config_file::get_config_from_config_file;
pub(crate) use execution_env::get_from_environment; pub(crate) use execution_env::get_from_environment;
pub(crate) use source::get_source;
pub(crate) use stdin::get_config_from_stdin; pub(crate) use stdin::get_config_from_stdin;

View File

@ -0,0 +1,13 @@
use std::path::Path;
use crate::stage0_config;
pub fn get_source(source: &Path) -> stage0_config::PleaseConfigBuilder {
stage0_config::PleaseConfigBuilder {
project: Some(stage0_config::PleaseProjectConfigBuilder {
source: Some(source.to_path_buf()),
..Default::default()
}),
..Default::default()
}
}

View File

@ -48,6 +48,7 @@ pub struct PleaseConfigBuilder {
execution_env: Option<stage0_config::PleaseConfigBuilder>, execution_env: Option<stage0_config::PleaseConfigBuilder>,
cli: Option<stage0_config::PleaseConfigBuilder>, cli: Option<stage0_config::PleaseConfigBuilder>,
config: Option<stage0_config::PleaseConfigBuilder>, config: Option<stage0_config::PleaseConfigBuilder>,
source: Option<stage0_config::PleaseConfigBuilder>,
} }
impl PleaseConfigBuilder { impl PleaseConfigBuilder {
@ -80,8 +81,20 @@ impl PleaseConfigBuilder {
self self
} }
pub fn with_source(&mut self, source: &Path) -> &mut Self {
self.source = Some(gatheres::get_source(source));
self
}
pub fn build(&mut self) -> anyhow::Result<PleaseConfig> { pub fn build(&mut self) -> anyhow::Result<PleaseConfig> {
let gathered = vec![&self.execution_env, &self.config, &self.stdin, &self.cli]; let gathered = vec![
&self.execution_env,
&self.source,
&self.config,
&self.stdin,
&self.cli,
];
let final_config = gathered let final_config = gathered
.into_iter() .into_iter()

View File

@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use clap::Args; use clap::{Args, ValueEnum};
pub type StdinFn = Option<Arc<Mutex<dyn Fn() -> anyhow::Result<String> + Send + Sync + 'static>>>; pub type StdinFn = Option<Arc<Mutex<dyn Fn() -> anyhow::Result<String> + Send + Sync + 'static>>>;
@ -35,4 +35,27 @@ EOF
config-stdin will consume stdin until the channel is closed via. EOF" config-stdin will consume stdin until the channel is closed via. EOF"
)] )]
pub config_stdin: bool, pub config_stdin: bool,
#[arg(
env = "CUDDLE_PLEASE_NO_VCS",
long,
global = true,
help_heading = "Global"
)]
pub no_vcs: bool,
#[arg(
env = "CUDDLE_PLEASE_ENGINE",
long,
global = true,
help_heading = "Global",
default_value = "gitea"
)]
pub engine: RemoteEngine,
}
#[derive(ValueEnum, Clone, Debug)]
pub enum RemoteEngine {
Local,
Gitea,
} }

View File

@ -3,6 +3,51 @@ use reqwest::header::{HeaderMap, HeaderValue};
use semver::Version; use semver::Version;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub trait RemoteGitEngine {
fn connect(&self, owner: &str, repo: &str) -> anyhow::Result<()>;
fn get_tags(&self, owner: &str, repo: &str) -> anyhow::Result<Vec<Tag>>;
fn get_commits_since(
&self,
owner: &str,
repo: &str,
since_sha: Option<&str>,
branch: &str,
) -> anyhow::Result<Vec<Commit>>;
fn get_pull_request(&self, owner: &str, repo: &str) -> anyhow::Result<Option<usize>>;
fn create_pull_request(
&self,
owner: &str,
repo: &str,
version: &str,
body: &str,
base: &str,
) -> anyhow::Result<usize>;
fn update_pull_request(
&self,
owner: &str,
repo: &str,
version: &str,
body: &str,
index: usize,
) -> anyhow::Result<usize>;
fn create_release(
&self,
owner: &str,
repo: &str,
version: &str,
body: &str,
prerelease: bool,
) -> anyhow::Result<Release>;
}
pub type DynRemoteGitClient = Box<dyn RemoteGitEngine>;
#[allow(dead_code)] #[allow(dead_code)]
pub struct GiteaClient { pub struct GiteaClient {
url: String, url: String,
@ -13,7 +58,7 @@ pub struct GiteaClient {
const APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); const APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
impl GiteaClient { impl GiteaClient {
pub fn new(url: impl Into<String>, token: Option<impl Into<String>>) -> Self { pub fn new(url: &str, token: Option<&str>) -> Self {
Self { Self {
url: url.into(), url: url.into(),
token: token.map(|t| t.into()), token: token.map(|t| t.into()),
@ -40,11 +85,92 @@ impl GiteaClient {
Ok(client) Ok(client)
} }
pub fn connect(&self, owner: impl Into<String>, repo: impl Into<String>) -> anyhow::Result<()> { fn get_commits_since_inner<F>(
let client = self.create_client()?; &self,
owner: &str,
repo: &str,
since_sha: Option<&str>,
branch: &str,
get_commits: F,
) -> anyhow::Result<Vec<Commit>>
where
F: Fn(&str, &str, &str, usize) -> anyhow::Result<(Vec<Commit>, bool)>,
{
let mut commits = Vec::new();
let mut page = 1;
let owner = owner.into(); let owner: String = owner.into();
let repo = repo.into(); let repo: String = repo.into();
let since_sha: Option<String> = since_sha.map(|ss| ss.into());
let branch: String = branch.into();
let mut found_commit = false;
loop {
let (new_commits, has_more) = get_commits(&owner, &repo, &branch, page)?;
for commit in new_commits {
if let Some(since_sha) = &since_sha {
if commit.sha.contains(since_sha) {
found_commit = true;
} else if !found_commit {
commits.push(commit);
}
} else {
commits.push(commit);
}
}
if !has_more {
break;
}
page += 1;
}
if !found_commit && since_sha.is_some() {
return Err(anyhow::anyhow!(
"sha was not found in commit chain: {} on branch: {}",
since_sha.unwrap_or("".into()),
branch
));
}
Ok(commits)
}
fn get_pull_request_inner<F>(
&self,
owner: &str,
repo: &str,
request_pull_request: F,
) -> anyhow::Result<Option<usize>>
where
F: Fn(&str, &str, usize) -> anyhow::Result<(Vec<PullRequest>, bool)>,
{
let mut page = 1;
let owner: String = owner.into();
let repo: String = repo.into();
loop {
let (pull_requests, has_more) = request_pull_request(&owner, &repo, page)?;
for pull_request in pull_requests {
if pull_request.head.r#ref.contains("cuddle-please/release") {
return Ok(Some(pull_request.number));
}
}
if !has_more {
break;
}
page += 1;
}
Ok(None)
}
}
impl RemoteGitEngine for GiteaClient {
fn connect(&self, owner: &str, repo: &str) -> anyhow::Result<()> {
let client = self.create_client()?;
tracing::trace!(owner = &owner, repo = &repo, "gitea connect"); tracing::trace!(owner = &owner, repo = &repo, "gitea connect");
@ -67,19 +193,15 @@ impl GiteaClient {
Ok(()) Ok(())
} }
pub fn get_tags( fn get_tags(&self, owner: &str, repo: &str) -> anyhow::Result<Vec<Tag>> {
&self,
owner: impl Into<String>,
repo: impl Into<String>,
) -> anyhow::Result<Vec<Tag>> {
let client = self.create_client()?; let client = self.create_client()?;
let request = client let request = client
.get(format!( .get(format!(
"{}/api/v1/repos/{}/{}/tags", "{}/api/v1/repos/{}/{}/tags",
&self.url.trim_end_matches('/'), &self.url.trim_end_matches('/'),
owner.into(), owner,
repo.into() repo
)) ))
.build()?; .build()?;
@ -93,12 +215,12 @@ impl GiteaClient {
Ok(tags) Ok(tags)
} }
pub fn get_commits_since( fn get_commits_since(
&self, &self,
owner: impl Into<String>, owner: &str,
repo: impl Into<String>, repo: &str,
since_sha: Option<impl Into<String>>, since_sha: Option<&str>,
branch: impl Into<String>, branch: &str,
) -> anyhow::Result<Vec<Commit>> { ) -> anyhow::Result<Vec<Commit>> {
let get_commits_since_page = |owner: &str, let get_commits_since_page = |owner: &str,
repo: &str, repo: &str,
@ -149,62 +271,7 @@ impl GiteaClient {
Ok(commits) Ok(commits)
} }
fn get_commits_since_inner<F>( fn get_pull_request(&self, owner: &str, repo: &str) -> anyhow::Result<Option<usize>> {
&self,
owner: impl Into<String>,
repo: impl Into<String>,
since_sha: Option<impl Into<String>>,
branch: impl Into<String>,
get_commits: F,
) -> anyhow::Result<Vec<Commit>>
where
F: Fn(&str, &str, &str, usize) -> anyhow::Result<(Vec<Commit>, bool)>,
{
let mut commits = Vec::new();
let mut page = 1;
let owner: String = owner.into();
let repo: String = repo.into();
let since_sha: Option<String> = since_sha.map(|ss| ss.into());
let branch: String = branch.into();
let mut found_commit = false;
loop {
let (new_commits, has_more) = get_commits(&owner, &repo, &branch, page)?;
for commit in new_commits {
if let Some(since_sha) = &since_sha {
if commit.sha.contains(since_sha) {
found_commit = true;
} else if !found_commit {
commits.push(commit);
}
} else {
commits.push(commit);
}
}
if !has_more {
break;
}
page += 1;
}
if !found_commit && since_sha.is_some() {
return Err(anyhow::anyhow!(
"sha was not found in commit chain: {} on branch: {}",
since_sha.unwrap_or("".into()),
branch
));
}
Ok(commits)
}
pub fn get_pull_request(
&self,
owner: impl Into<String>,
repo: impl Into<String>,
) -> anyhow::Result<Option<usize>> {
let request_pull_request = let request_pull_request =
|owner: &str, repo: &str, page: usize| -> anyhow::Result<(Vec<PullRequest>, bool)> { |owner: &str, repo: &str, page: usize| -> anyhow::Result<(Vec<PullRequest>, bool)> {
let client = self.create_client()?; let client = self.create_client()?;
@ -241,44 +308,13 @@ impl GiteaClient {
self.get_pull_request_inner(owner, repo, request_pull_request) self.get_pull_request_inner(owner, repo, request_pull_request)
} }
fn get_pull_request_inner<F>( fn create_pull_request(
&self, &self,
owner: impl Into<String>, owner: &str,
repo: impl Into<String>, repo: &str,
request_pull_request: F, version: &str,
) -> anyhow::Result<Option<usize>> body: &str,
where base: &str,
F: Fn(&str, &str, usize) -> anyhow::Result<(Vec<PullRequest>, bool)>,
{
let mut page = 1;
let owner: String = owner.into();
let repo: String = repo.into();
loop {
let (pull_requests, has_more) = request_pull_request(&owner, &repo, page)?;
for pull_request in pull_requests {
if pull_request.head.r#ref.contains("cuddle-please/release") {
return Ok(Some(pull_request.number));
}
}
if !has_more {
break;
}
page += 1;
}
Ok(None)
}
pub fn create_pull_request(
&self,
owner: impl Into<String>,
repo: impl Into<String>,
version: impl Into<String>,
body: impl Into<String>,
base: impl Into<String>,
) -> anyhow::Result<usize> { ) -> anyhow::Result<usize> {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
struct CreatePullRequestOption { struct CreatePullRequestOption {
@ -290,15 +326,9 @@ impl GiteaClient {
let client = self.create_client()?; let client = self.create_client()?;
let owner = owner.into();
let repo = repo.into();
let version = version.into();
let body = body.into();
let base = base.into();
let request = CreatePullRequestOption { let request = CreatePullRequestOption {
base: base.clone(), base: base.into(),
body: body.clone(), body: body.into(),
head: "cuddle-please/release".into(), head: "cuddle-please/release".into(),
title: format!("chore(release): {}", version), title: format!("chore(release): {}", version),
}; };
@ -329,12 +359,12 @@ impl GiteaClient {
Ok(commits.number) Ok(commits.number)
} }
pub fn update_pull_request( fn update_pull_request(
&self, &self,
owner: impl Into<String>, owner: &str,
repo: impl Into<String>, repo: &str,
version: impl Into<String>, version: &str,
body: impl Into<String>, body: &str,
index: usize, index: usize,
) -> anyhow::Result<usize> { ) -> anyhow::Result<usize> {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -345,13 +375,8 @@ impl GiteaClient {
let client = self.create_client()?; let client = self.create_client()?;
let owner = owner.into();
let repo = repo.into();
let version = version.into();
let body = body.into();
let request = CreatePullRequestOption { let request = CreatePullRequestOption {
body: body.clone(), body: body.into(),
title: format!("chore(release): {}", version), title: format!("chore(release): {}", version),
}; };
@ -381,12 +406,12 @@ impl GiteaClient {
Ok(commits.number) Ok(commits.number)
} }
pub fn create_release( fn create_release(
&self, &self,
owner: impl Into<String>, owner: &str,
repo: impl Into<String>, repo: &str,
version: impl Into<String>, version: &str,
body: impl Into<String>, body: &str,
prerelease: bool, prerelease: bool,
) -> anyhow::Result<Release> { ) -> anyhow::Result<Release> {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -401,17 +426,12 @@ impl GiteaClient {
let client = self.create_client()?; let client = self.create_client()?;
let owner = owner.into();
let repo = repo.into();
let version = version.into();
let body = body.into();
let request = CreateReleaseOption { let request = CreateReleaseOption {
body, body: body.into(),
draft: false, draft: false,
name: version.clone(), name: version.into(),
prerelease, prerelease,
tag_name: version.clone(), tag_name: version.into(),
}; };
tracing::trace!( tracing::trace!(
@ -563,7 +583,7 @@ mod test {
let commits = client.get_commits_since_inner( let commits = client.get_commits_since_inner(
"owner", "owner",
"repo", "repo",
Some(sha), Some(&sha),
"some-branch", "some-branch",
|_, _, _, page| -> anyhow::Result<(Vec<Commit>, bool)> { |_, _, _, page| -> anyhow::Result<(Vec<Commit>, bool)> {
let commit_page = api_res.get(page - 1).unwrap(); let commit_page = api_res.get(page - 1).unwrap();

View File

@ -2,12 +2,14 @@ mod args;
mod cliff; mod cliff;
mod git_client; mod git_client;
mod gitea_client; mod gitea_client;
mod local_git_client;
mod ui; mod ui;
mod versioning; mod versioning;
pub use args::{GlobalArgs, StdinFn}; pub use args::{GlobalArgs, RemoteEngine, StdinFn};
pub use cliff::{changelog_parser, ChangeLogBuilder}; pub use cliff::{changelog_parser, ChangeLogBuilder};
pub use git_client::VcsClient; pub use git_client::VcsClient;
pub use gitea_client::GiteaClient; pub use gitea_client::{DynRemoteGitClient, GiteaClient, RemoteGitEngine};
pub use local_git_client::LocalGitClient;
pub use ui::{ConsoleUi, DynUi, Ui}; pub use ui::{ConsoleUi, DynUi, Ui};
pub use versioning::{next_version::NextVersion, semver::get_most_significant_version}; pub use versioning::{next_version::NextVersion, semver::get_most_significant_version};

View File

@ -0,0 +1,66 @@
use crate::RemoteGitEngine;
pub struct LocalGitClient {}
impl LocalGitClient {
pub fn new() -> Self {
Self {}
}
}
impl RemoteGitEngine for LocalGitClient {
fn connect(&self, owner: &str, repo: &str) -> anyhow::Result<()> {
todo!()
}
fn get_tags(&self, owner: &str, repo: &str) -> anyhow::Result<Vec<crate::gitea_client::Tag>> {
todo!()
}
fn get_commits_since(
&self,
owner: &str,
repo: &str,
since_sha: Option<&str>,
branch: &str,
) -> anyhow::Result<Vec<crate::gitea_client::Commit>> {
todo!()
}
fn get_pull_request(&self, owner: &str, repo: &str) -> anyhow::Result<Option<usize>> {
todo!()
}
fn create_pull_request(
&self,
owner: &str,
repo: &str,
version: &str,
body: &str,
base: &str,
) -> anyhow::Result<usize> {
todo!()
}
fn update_pull_request(
&self,
owner: &str,
repo: &str,
version: &str,
body: &str,
index: usize,
) -> anyhow::Result<usize> {
todo!()
}
fn create_release(
&self,
owner: &str,
repo: &str,
version: &str,
body: &str,
prerelease: bool,
) -> anyhow::Result<crate::gitea_client::Release> {
todo!()
}
}

View File

@ -7,7 +7,13 @@ use tracing_test::traced_test;
use crate::common::{assert_output, get_test_data_path}; use crate::common::{assert_output, get_test_data_path};
fn get_base_args<'a>() -> Vec<&'a str> { fn get_base_args<'a>() -> Vec<&'a str> {
vec!["cuddle-please", "config", "list"] vec![
"cuddle-please",
"config",
"list",
"--no-vcs",
"--engine=local",
]
} }
#[test] #[test]