Merge branch 'main' into renovate/all
This commit is contained in:
commit
9e5f5f3b87
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
target/
|
target/
|
||||||
.cuddle/
|
.cuddle/
|
||||||
|
.env
|
||||||
|
60
CHANGELOG.md
Normal file
60
CHANGELOG.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.2.0] - 2023-08-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- add docker setup
|
||||||
|
- refactor frontend configuration
|
||||||
|
- with all the way through
|
||||||
|
- with create pull request and release
|
||||||
|
- with gitea
|
||||||
|
- with prepend as well
|
||||||
|
- add cliff
|
||||||
|
- remove tokio
|
||||||
|
- with doctor
|
||||||
|
- with git client
|
||||||
|
- with fixes
|
||||||
|
- with conventional parse
|
||||||
|
- with tags command
|
||||||
|
- add semver
|
||||||
|
- can get commit chain
|
||||||
|
- with start of environment engine
|
||||||
|
- with gitea client
|
||||||
|
- fmt
|
||||||
|
- add gitea client stub
|
||||||
|
- add tests for git setup
|
||||||
|
- split headings into local and global
|
||||||
|
- rename to cuddle_please
|
||||||
|
- add config parsing
|
||||||
|
- with basic get dir
|
||||||
|
- add mkdocs
|
||||||
|
- add base
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- release command
|
||||||
|
- add cuddle.release to this repository
|
||||||
|
- add granular docker setup
|
||||||
|
- fix checks
|
||||||
|
- chck refactor commands
|
||||||
|
- move doctor command
|
||||||
|
- fmt
|
||||||
|
- rename release command
|
||||||
|
- move gitea command into its own file
|
||||||
|
- move config list
|
||||||
|
- move gitea out of the way
|
||||||
|
- move config building out of main execution loop
|
||||||
|
- move commands and misc out of main binary package
|
||||||
|
- fmt
|
||||||
|
- check hide commands
|
||||||
|
- move cuddle-please to cuddle-please release
|
||||||
|
- remove no-vcs option (moved to a later stage if github is someday adopted
|
||||||
|
- fix clippy warnings
|
||||||
|
- clippy fix
|
||||||
|
- fix
|
||||||
|
- cleanup
|
@ -90,11 +90,11 @@ 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 git_client = self.get_git(config.get_source())?;
|
||||||
let gitea_client = self.get_gitea_client();
|
let gitea_client = self.get_gitea_client(&config);
|
||||||
|
|
||||||
match &self.commands {
|
match &self.commands {
|
||||||
Some(Commands::Release {}) => {
|
Some(Commands::Release {}) => {
|
||||||
ReleaseCommandHandler::new(config, git_client, gitea_client)
|
ReleaseCommandHandler::new(self.ui, config, git_client, gitea_client)
|
||||||
.execute(self.global.dry_run)?;
|
.execute(self.global.dry_run)?;
|
||||||
}
|
}
|
||||||
Some(Commands::Config { command }) => {
|
Some(Commands::Config { command }) => {
|
||||||
@ -139,11 +139,11 @@ impl Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_gitea_client(&self) -> DynRemoteGitClient {
|
fn get_gitea_client(&self, config: &PleaseConfig) -> DynRemoteGitClient {
|
||||||
match self.global.engine {
|
match self.global.engine {
|
||||||
cuddle_please_misc::RemoteEngine::Local => Box::new(LocalGitClient::new()),
|
cuddle_please_misc::RemoteEngine::Local => Box::new(LocalGitClient::new()),
|
||||||
cuddle_please_misc::RemoteEngine::Gitea => Box::new(GiteaClient::new(
|
cuddle_please_misc::RemoteEngine::Gitea => Box::new(GiteaClient::new(
|
||||||
&self.config.api_url.clone().expect("api_url to be set"),
|
config.get_api_url(),
|
||||||
self.global.token.as_deref(),
|
self.global.token.as_deref(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,12 @@ use ::semver::Version;
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
use cuddle_please_misc::{
|
use cuddle_please_misc::{
|
||||||
changelog_parser, get_most_significant_version, ChangeLogBuilder, DynRemoteGitClient,
|
changelog_parser, get_most_significant_version, ChangeLogBuilder, Commit, DynRemoteGitClient,
|
||||||
NextVersion, VcsClient,
|
DynUi, NextVersion, Tag, VcsClient,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ReleaseCommandHandler {
|
pub struct ReleaseCommandHandler {
|
||||||
|
ui: DynUi,
|
||||||
config: PleaseConfig,
|
config: PleaseConfig,
|
||||||
git_client: VcsClient,
|
git_client: VcsClient,
|
||||||
gitea_client: DynRemoteGitClient,
|
gitea_client: DynRemoteGitClient,
|
||||||
@ -16,11 +17,13 @@ pub struct ReleaseCommandHandler {
|
|||||||
|
|
||||||
impl ReleaseCommandHandler {
|
impl ReleaseCommandHandler {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
ui: DynUi,
|
||||||
config: PleaseConfig,
|
config: PleaseConfig,
|
||||||
git_client: VcsClient,
|
git_client: VcsClient,
|
||||||
gitea_client: DynRemoteGitClient,
|
gitea_client: DynRemoteGitClient,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
ui,
|
||||||
config,
|
config,
|
||||||
git_client,
|
git_client,
|
||||||
gitea_client,
|
gitea_client,
|
||||||
@ -34,87 +37,90 @@ impl ReleaseCommandHandler {
|
|||||||
let branch = self.config.get_branch();
|
let branch = self.config.get_branch();
|
||||||
let source = self.config.get_source();
|
let source = self.config.get_source();
|
||||||
|
|
||||||
// 2. Parse the cuddle.please.yaml let cuddle.please.yaml take precedence
|
self.check_git_remote_connection(owner, repository)?;
|
||||||
// 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
|
let significant_tag = self.get_most_significant_tag(owner, repository)?;
|
||||||
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());
|
let commits =
|
||||||
|
self.fetch_commits_since_last_tag(owner, repository, &significant_tag, branch)?;
|
||||||
|
let current_version = get_current_version(significant_tag);
|
||||||
|
|
||||||
// 5. Fetch git commits since last git tag
|
let conventional_commit_results = parse_conventional_commits(current_version, commits)?;
|
||||||
let commits = self.gitea_client.get_commits_since(
|
if conventional_commit_results.is_none() {
|
||||||
owner,
|
tracing::debug!("found no new commits, aborting early");
|
||||||
repository,
|
self.ui
|
||||||
significant_tag.map(|st| st.commit.sha.as_str()),
|
.write_str_ln("no new commits found, no release required");
|
||||||
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(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
let (commit_strs, next_version) = conventional_commit_results.unwrap();
|
||||||
|
|
||||||
let next_version = current_version.next(&commit_strs);
|
let (changelog_placement, changelog, changelog_last_changes) =
|
||||||
|
compose_changelog(&commit_strs, &next_version, source)?;
|
||||||
|
|
||||||
// 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 let Some(first_commit) = commit_strs.first() {
|
||||||
if first_commit.contains("chore(release): ") {
|
if first_commit.contains("chore(release): ") {
|
||||||
if !dry_run {
|
self.create_release(
|
||||||
self.gitea_client.create_release(
|
dry_run,
|
||||||
owner,
|
owner,
|
||||||
repository,
|
repository,
|
||||||
&next_version.to_string(),
|
&next_version,
|
||||||
&changelog_last_changes.unwrap(),
|
changelog_last_changes,
|
||||||
!next_version.pre.is_empty(),
|
)?;
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
tracing::debug!("creating release (dry_run)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9a. Create / Update Pr
|
self.create_pull_request(
|
||||||
// Create or update branch
|
changelog_placement,
|
||||||
|
changelog,
|
||||||
|
next_version,
|
||||||
|
dry_run,
|
||||||
|
owner,
|
||||||
|
repository,
|
||||||
|
changelog_last_changes,
|
||||||
|
branch,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_release(
|
||||||
|
&self,
|
||||||
|
dry_run: bool,
|
||||||
|
owner: &str,
|
||||||
|
repository: &str,
|
||||||
|
next_version: &Version,
|
||||||
|
changelog_last_changes: Option<String>,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
Ok(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)");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_pull_request(
|
||||||
|
&self,
|
||||||
|
changelog_placement: std::path::PathBuf,
|
||||||
|
changelog: String,
|
||||||
|
next_version: Version,
|
||||||
|
dry_run: bool,
|
||||||
|
owner: &str,
|
||||||
|
repository: &str,
|
||||||
|
changelog_last_changes: Option<String>,
|
||||||
|
branch: &str,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
self.git_client.checkout_branch()?;
|
self.git_client.checkout_branch()?;
|
||||||
|
|
||||||
std::fs::write(changelog_placement, changelog.as_bytes())?;
|
std::fs::write(changelog_placement, changelog.as_bytes())?;
|
||||||
|
|
||||||
self.git_client
|
self.git_client
|
||||||
.commit_and_push(next_version.to_string(), dry_run)?;
|
.commit_and_push(next_version.to_string(), dry_run)?;
|
||||||
|
|
||||||
let _pr_number = match self.gitea_client.get_pull_request(owner, repository)? {
|
let _pr_number = match self.gitea_client.get_pull_request(owner, repository)? {
|
||||||
Some(existing_pr) => {
|
Some(existing_pr) => {
|
||||||
if !dry_run {
|
if !dry_run {
|
||||||
@ -145,7 +151,84 @@ impl ReleaseCommandHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch_commits_since_last_tag(
|
||||||
|
&self,
|
||||||
|
owner: &str,
|
||||||
|
repository: &str,
|
||||||
|
significant_tag: &Option<Tag>,
|
||||||
|
branch: &str,
|
||||||
|
) -> Result<Vec<Commit>, anyhow::Error> {
|
||||||
|
let commits = self.gitea_client.get_commits_since(
|
||||||
|
owner,
|
||||||
|
repository,
|
||||||
|
significant_tag.as_ref().map(|st| st.commit.sha.as_str()),
|
||||||
|
branch,
|
||||||
|
)?;
|
||||||
|
Ok(commits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_most_significant_tag(
|
||||||
|
&self,
|
||||||
|
owner: &str,
|
||||||
|
repository: &str,
|
||||||
|
) -> Result<Option<Tag>, anyhow::Error> {
|
||||||
|
let tags = self.gitea_client.get_tags(owner, repository)?;
|
||||||
|
let significant_tag = get_most_significant_version(tags.iter().collect());
|
||||||
|
Ok(significant_tag.map(|t| t.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_git_remote_connection(
|
||||||
|
&self,
|
||||||
|
owner: &str,
|
||||||
|
repository: &str,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
self.gitea_client
|
||||||
|
.connect(owner, repository)
|
||||||
|
.context("failed to connect to gitea repository")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compose_changelog(
|
||||||
|
commit_strs: &Vec<String>,
|
||||||
|
next_version: &Version,
|
||||||
|
source: &std::path::PathBuf,
|
||||||
|
) -> Result<(std::path::PathBuf, String, Option<String>), anyhow::Error> {
|
||||||
|
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)?;
|
||||||
|
Ok((changelog_placement, changelog, changelog_last_changes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_conventional_commits(
|
||||||
|
current_version: Version,
|
||||||
|
commits: Vec<Commit>,
|
||||||
|
) -> anyhow::Result<Option<(Vec<String>, Version)>> {
|
||||||
|
let commit_strs = commits
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.commit.message.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if commit_strs.is_empty() {
|
||||||
|
tracing::info!("no commits to base release on");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let next_version = current_version.next(&commit_strs);
|
||||||
|
|
||||||
|
Ok(Some((commit_strs, next_version)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_version(significant_tag: Option<Tag>) -> Version {
|
||||||
|
let current_version = significant_tag
|
||||||
|
.map(|st| Version::try_from(st).unwrap())
|
||||||
|
.unwrap_or(Version::new(0, 0, 0));
|
||||||
|
current_version
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ mod versioning;
|
|||||||
pub use args::{GlobalArgs, RemoteEngine, 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::{DynRemoteGitClient, GiteaClient, RemoteGitEngine};
|
pub use gitea_client::{Commit, DynRemoteGitClient, GiteaClient, RemoteGitEngine, Tag};
|
||||||
pub use local_git_client::LocalGitClient;
|
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};
|
||||||
|
@ -7,6 +7,14 @@ vars:
|
|||||||
registry: kasperhermansen
|
registry: kasperhermansen
|
||||||
mkdocs_image: "squidfunk/mkdocs-material:9.1"
|
mkdocs_image: "squidfunk/mkdocs-material:9.1"
|
||||||
|
|
||||||
|
please:
|
||||||
|
project:
|
||||||
|
owner: kjuulh
|
||||||
|
repository: cuddle-please
|
||||||
|
branch: main
|
||||||
|
settings:
|
||||||
|
api_url: https://git.front.kjuulh.io
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
"mkdocs:new":
|
"mkdocs:new":
|
||||||
type: shell
|
type: shell
|
||||||
|
Loading…
Reference in New Issue
Block a user