2023-08-01 16:38:30 +02:00
|
|
|
use cuddle_please_frontend::PleaseConfig;
|
|
|
|
|
|
|
|
use ::semver::Version;
|
|
|
|
use anyhow::Context;
|
2023-08-01 17:01:00 +02:00
|
|
|
|
2023-08-01 16:38:30 +02:00
|
|
|
use cuddle_please_misc::{
|
2023-08-01 17:01:00 +02:00
|
|
|
changelog_parser, get_most_significant_version, ChangeLogBuilder, DynRemoteGitClient,
|
|
|
|
NextVersion, VcsClient,
|
2023-08-01 16:38:30 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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<()> {
|
2023-08-01 17:11:59 +02:00
|
|
|
tracing::debug!("running command: release");
|
2023-08-01 16:38:30 +02:00
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|