refactor: move gitea out of the way
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
39db4b8d1c
commit
e51454088e
@ -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(¤t_dir)
|
.with_config_file(¤t_dir)
|
||||||
|
.with_source(¤t_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()),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
mod command;
|
mod command;
|
||||||
|
mod release_command;
|
||||||
|
|
||||||
pub use command::Command as PleaseCommand;
|
pub use command::Command as PleaseCommand;
|
||||||
|
159
crates/cuddle-please-commands/src/release_command.rs
Normal file
159
crates/cuddle-please-commands/src/release_command.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
13
crates/cuddle-please-frontend/src/gatheres/source.rs
Normal file
13
crates/cuddle-please-frontend/src/gatheres/source.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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};
|
||||||
|
66
crates/cuddle-please-misc/src/local_git_client.rs
Normal file
66
crates/cuddle-please-misc/src/local_git_client.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user