cuddle-please/crates/cuddle-please-commands/src/release_command.rs

152 lines
4.9 KiB
Rust
Raw Normal View History

use cuddle_please_frontend::PleaseConfig;
use ::semver::Version;
use anyhow::Context;
use cuddle_please_misc::{
changelog_parser, get_most_significant_version, ChangeLogBuilder, DynRemoteGitClient,
NextVersion, VcsClient,
};
pub struct ReleaseCommandHandler {
config: PleaseConfig,
git_client: VcsClient,
gitea_client: DynRemoteGitClient,
}
impl ReleaseCommandHandler {
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 command: release");
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(())
}
}