feat: refactor frontend configuration
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
e235483783
commit
8cd68d569b
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -344,6 +344,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"conventional_commit_parser",
|
||||
"cuddle-please-frontend",
|
||||
"dotenv",
|
||||
"git-cliff-core",
|
||||
"lazy_static",
|
||||
@ -361,6 +362,23 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cuddle-please-frontend"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"clap",
|
||||
"dotenv",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"tempdir",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deunicode"
|
||||
version = "0.4.3"
|
||||
|
@ -1,9 +1,10 @@
|
||||
[workspace]
|
||||
members = ["crates/cuddle-please"]
|
||||
members = ["crates/cuddle-please", "crates/cuddle-please-frontend"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.dependencies]
|
||||
cuddle-please = { path = "crates/cuddle-please" }
|
||||
cuddle-please-frontend = { path = "crates/cuddle-please-frontend" }
|
||||
|
||||
anyhow = { version = "1.0.71" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
|
@ -34,7 +34,7 @@ See docs for more information about installation and some such
|
||||
### 0.1 Milestone
|
||||
|
||||
- [x] Hide unneccessary commands
|
||||
- [ ] Redo configuration frontend
|
||||
- [x] Redo configuration frontend
|
||||
- [ ] Refactor command.rs into smaller bits so that bits are easier to test
|
||||
- [ ] Add reporter for PR and Repositories
|
||||
- [ ] Setup temporary git name and email to use for git committing
|
||||
|
19
crates/cuddle-please-frontend/Cargo.toml
Normal file
19
crates/cuddle-please-frontend/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "cuddle-please-frontend"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
clap.workspace = true
|
||||
dotenv.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
serde.workspace = true
|
||||
chrono.workspace = true
|
||||
tempdir.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tracing-test = { workspace = true, features = ["no-env-filter"] }
|
||||
pretty_assertions.workspace = true
|
71
crates/cuddle-please-frontend/src/gatheres/cli.rs
Normal file
71
crates/cuddle-please-frontend/src/gatheres/cli.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Args;
|
||||
|
||||
use crate::stage0_config::{
|
||||
PleaseConfigBuilder, PleaseProjectConfigBuilder, PleaseSettingsConfigBuilder,
|
||||
};
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct ConfigArgs {
|
||||
/// Which repository to publish against. If not supplied remote url will be inferred from environment or fail if not present.
|
||||
#[arg(
|
||||
env = "CUDDLE_PLEASE_API_URL",
|
||||
long,
|
||||
global = true,
|
||||
help_heading = "Config"
|
||||
)]
|
||||
pub api_url: Option<String>,
|
||||
|
||||
/// repo is the name of repository you want to release for
|
||||
#[arg(
|
||||
env = "CUDDLE_PLEASE_REPO",
|
||||
long,
|
||||
global = true,
|
||||
help_heading = "Config"
|
||||
)]
|
||||
pub repo: Option<String>,
|
||||
|
||||
/// owner is the name of user from which the repository belongs <user>/<repo>
|
||||
#[arg(
|
||||
env = "CUDDLE_PLEASE_OWNER",
|
||||
long,
|
||||
global = true,
|
||||
help_heading = "Config"
|
||||
)]
|
||||
pub owner: Option<String>,
|
||||
|
||||
/// which source directory to use, if not set `std::env::current_dir` is used instead.
|
||||
#[arg(
|
||||
env = "CUDDLE_PLEASE_SOURCE",
|
||||
long,
|
||||
global = true,
|
||||
help_heading = "Config"
|
||||
)]
|
||||
pub source: Option<PathBuf>,
|
||||
|
||||
/// which branch is being run from
|
||||
#[arg(
|
||||
env = "CUDDLE_PLEASE_BRANCH",
|
||||
long,
|
||||
global = true,
|
||||
help_heading = "Config"
|
||||
)]
|
||||
pub branch: Option<String>,
|
||||
}
|
||||
|
||||
impl From<ConfigArgs> for PleaseConfigBuilder {
|
||||
fn from(value: ConfigArgs) -> Self {
|
||||
Self {
|
||||
project: Some(PleaseProjectConfigBuilder {
|
||||
owner: value.owner,
|
||||
repository: value.repo,
|
||||
source: value.source,
|
||||
branch: value.branch,
|
||||
}),
|
||||
settings: Some(PleaseSettingsConfigBuilder {
|
||||
api_url: value.api_url,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
80
crates/cuddle-please-frontend/src/gatheres/config_file.rs
Normal file
80
crates/cuddle-please-frontend/src/gatheres/config_file.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
use crate::stage0_config::PleaseConfigBuilder;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct CuddleEmbeddedPleaseConfig {
|
||||
please: PleaseConfigBuilder,
|
||||
}
|
||||
|
||||
impl From<CuddleEmbeddedPleaseConfig> for PleaseConfigBuilder {
|
||||
fn from(value: CuddleEmbeddedPleaseConfig) -> Self {
|
||||
value.please
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct CuddlePleaseConfig {
|
||||
#[serde(flatten)]
|
||||
please: PleaseConfigBuilder,
|
||||
}
|
||||
impl From<CuddlePleaseConfig> for PleaseConfigBuilder {
|
||||
fn from(value: CuddlePleaseConfig) -> Self {
|
||||
value.please
|
||||
}
|
||||
}
|
||||
|
||||
const CUDDLE_FILE_NAME: &str = "cuddle";
|
||||
const CUDDLE_CONFIG_FILE_NAME: &str = "cuddle.please";
|
||||
const YAML_EXTENSION: &str = "yaml";
|
||||
|
||||
pub fn get_config_from_config_file(current_dir: &Path) -> PleaseConfigBuilder {
|
||||
let current_cuddle_path = current_dir
|
||||
.clone()
|
||||
.join(format!("{CUDDLE_FILE_NAME}.{YAML_EXTENSION}"));
|
||||
let current_cuddle_config_path = current_dir
|
||||
.clone()
|
||||
.join(format!("{CUDDLE_CONFIG_FILE_NAME}.{YAML_EXTENSION}"));
|
||||
let mut please_config = PleaseConfigBuilder::default();
|
||||
|
||||
if let Some(config) = get_config_from_file::<CuddleEmbeddedPleaseConfig>(current_cuddle_path) {
|
||||
please_config = please_config.merge(&config).clone();
|
||||
}
|
||||
|
||||
if let Some(config) = get_config_from_file::<CuddlePleaseConfig>(current_cuddle_config_path) {
|
||||
please_config = please_config.merge(&config).clone();
|
||||
}
|
||||
|
||||
please_config
|
||||
}
|
||||
|
||||
pub fn get_config_from_file<T>(current_cuddle_path: PathBuf) -> Option<PleaseConfigBuilder>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
T: Into<PleaseConfigBuilder>,
|
||||
{
|
||||
match std::fs::File::open(¤t_cuddle_path) {
|
||||
Ok(file) => match serde_yaml::from_reader::<_, T>(file) {
|
||||
Ok(config) => {
|
||||
return Some(config.into());
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
"{} doesn't contain a valid please config: {}",
|
||||
¤t_cuddle_path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
"did not find or was not allowed to read {}, error: {}",
|
||||
¤t_cuddle_path.display(),
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
53
crates/cuddle-please-frontend/src/gatheres/execution_env.rs
Normal file
53
crates/cuddle-please-frontend/src/gatheres/execution_env.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use crate::stage0_config::{PleaseConfigBuilder, PleaseProjectConfigBuilder};
|
||||
|
||||
pub fn get_from_environment(vars: std::env::Vars) -> PleaseConfigBuilder {
|
||||
let vars: HashMap<String, String> = vars.collect();
|
||||
|
||||
let env = detect_environment(&vars);
|
||||
|
||||
match env {
|
||||
ExecutionEnvironment::Local => PleaseConfigBuilder {
|
||||
project: Some(PleaseProjectConfigBuilder {
|
||||
source: Some(PathBuf::from(".")),
|
||||
..Default::default()
|
||||
}),
|
||||
settings: None,
|
||||
},
|
||||
ExecutionEnvironment::Drone => PleaseConfigBuilder {
|
||||
project: Some(PleaseProjectConfigBuilder {
|
||||
owner: Some(
|
||||
vars.get("DRONE_REPO_OWNER")
|
||||
.expect("DRONE_REPO_OWNER to be present")
|
||||
.clone(),
|
||||
),
|
||||
repository: Some(
|
||||
vars.get("DRONE_REPO_NAME")
|
||||
.expect("DRONE_REPO_NAME to be present")
|
||||
.clone(),
|
||||
),
|
||||
source: Some(PathBuf::from(".")),
|
||||
branch: Some(
|
||||
vars.get("DRONE_REPO_BRANCH")
|
||||
.expect("DRONE_REPO_BRANCH to be present")
|
||||
.clone(),
|
||||
),
|
||||
}),
|
||||
settings: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_environment(vars: &HashMap<String, String>) -> ExecutionEnvironment {
|
||||
if let Some(_) = vars.get("DRONE".into()) {
|
||||
return ExecutionEnvironment::Drone;
|
||||
}
|
||||
|
||||
ExecutionEnvironment::Local
|
||||
}
|
||||
|
||||
pub enum ExecutionEnvironment {
|
||||
Local,
|
||||
Drone,
|
||||
}
|
9
crates/cuddle-please-frontend/src/gatheres/mod.rs
Normal file
9
crates/cuddle-please-frontend/src/gatheres/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod cli;
|
||||
mod config_file;
|
||||
mod execution_env;
|
||||
mod stdin;
|
||||
|
||||
pub use cli::ConfigArgs;
|
||||
pub(crate) use config_file::get_config_from_config_file;
|
||||
pub(crate) use execution_env::get_from_environment;
|
||||
pub(crate) use stdin::get_config_from_stdin;
|
19
crates/cuddle-please-frontend/src/gatheres/stdin.rs
Normal file
19
crates/cuddle-please-frontend/src/gatheres/stdin.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::stage0_config::PleaseConfigBuilder;
|
||||
|
||||
pub fn get_config_from_stdin<'d, T>(stdin: &'d str) -> PleaseConfigBuilder
|
||||
where
|
||||
T: Deserialize<'d>,
|
||||
T: Into<PleaseConfigBuilder>,
|
||||
{
|
||||
match serde_yaml::from_str::<'d, T>(stdin) {
|
||||
Ok(config) => {
|
||||
return config.into();
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::debug!("stdin doesn't contain a valid please config: {}", e);
|
||||
}
|
||||
}
|
||||
PleaseConfigBuilder::default()
|
||||
}
|
95
crates/cuddle-please-frontend/src/lib.rs
Normal file
95
crates/cuddle-please-frontend/src/lib.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub mod gatheres;
|
||||
mod stage0_config;
|
||||
|
||||
pub use gatheres::ConfigArgs;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PleaseProjectConfig {
|
||||
pub owner: String,
|
||||
pub repository: String,
|
||||
pub source: PathBuf,
|
||||
pub branch: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PleaseSettingsConfig {
|
||||
pub api_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PleaseConfig {
|
||||
pub project: PleaseProjectConfig,
|
||||
pub settings: PleaseSettingsConfig,
|
||||
}
|
||||
|
||||
impl PleaseConfig {
|
||||
pub fn get_owner<'a>(&'a self) -> &'a str {
|
||||
&self.project.owner
|
||||
}
|
||||
pub fn get_repository<'a>(&'a self) -> &'a str {
|
||||
&self.project.repository
|
||||
}
|
||||
pub fn get_source<'a>(&'a self) -> &'a PathBuf {
|
||||
&self.project.source
|
||||
}
|
||||
pub fn get_branch<'a>(&'a self) -> &'a str {
|
||||
&self.project.branch
|
||||
}
|
||||
pub fn get_api_url<'a>(&'a self) -> &'a str {
|
||||
&self.settings.api_url
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PleaseConfigBuilder {
|
||||
stdin: Option<stage0_config::PleaseConfigBuilder>,
|
||||
execution_env: Option<stage0_config::PleaseConfigBuilder>,
|
||||
cli: Option<stage0_config::PleaseConfigBuilder>,
|
||||
config: Option<stage0_config::PleaseConfigBuilder>,
|
||||
}
|
||||
|
||||
impl PleaseConfigBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_stdin(&mut self, stdin: String) -> &mut Self {
|
||||
self.stdin = Some(gatheres::get_config_from_stdin::<
|
||||
stage0_config::PleaseConfigBuilder,
|
||||
>(stdin.as_str()));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_config_file(&mut self, current_dir: &Path) -> &mut Self {
|
||||
self.config = Some(gatheres::get_config_from_config_file(current_dir));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_execution_env(&mut self, env_bag: std::env::Vars) -> &mut Self {
|
||||
self.execution_env = Some(gatheres::get_from_environment(env_bag));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cli(&mut self, cli: gatheres::ConfigArgs) -> &mut Self {
|
||||
self.cli = Some(cli.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> anyhow::Result<PleaseConfig> {
|
||||
let gathered = vec![&self.execution_env, &self.config, &self.stdin, &self.cli];
|
||||
|
||||
let final_config = gathered
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.fold(stage0_config::PleaseConfigBuilder::default(), |mut a, x| {
|
||||
a.merge(x).clone()
|
||||
});
|
||||
|
||||
Ok(final_config.try_into()?)
|
||||
}
|
||||
}
|
111
crates/cuddle-please-frontend/src/stage0_config.rs
Normal file
111
crates/cuddle-please-frontend/src/stage0_config.rs
Normal file
@ -0,0 +1,111 @@
|
||||
use std::{default, path::PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{PleaseConfig, PleaseProjectConfig, PleaseSettingsConfig};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PleaseProjectConfigBuilder {
|
||||
pub owner: Option<String>,
|
||||
pub repository: Option<String>,
|
||||
pub source: Option<PathBuf>,
|
||||
pub branch: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PleaseSettingsConfigBuilder {
|
||||
pub api_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PleaseConfigBuilder {
|
||||
pub project: Option<PleaseProjectConfigBuilder>,
|
||||
pub settings: Option<PleaseSettingsConfigBuilder>,
|
||||
}
|
||||
|
||||
impl PleaseConfigBuilder {
|
||||
pub fn merge(&mut self, config: &PleaseConfigBuilder) -> &Self {
|
||||
let config = config.clone();
|
||||
let mut fproject = match self.project.clone() {
|
||||
None => PleaseProjectConfigBuilder::default(),
|
||||
Some(project) => project,
|
||||
};
|
||||
let mut fsettings = match self.settings.clone() {
|
||||
None => PleaseSettingsConfigBuilder::default(),
|
||||
Some(settings) => settings,
|
||||
};
|
||||
|
||||
if let Some(mut project) = config.project {
|
||||
if let Some(owner) = project.owner {
|
||||
fproject.owner = Some(owner);
|
||||
}
|
||||
if let Some(repository) = project.repository {
|
||||
fproject.repository = Some(repository);
|
||||
}
|
||||
if let Some(source) = project.source {
|
||||
fproject.source = Some(source);
|
||||
}
|
||||
if let Some(branch) = project.branch {
|
||||
fproject.branch = Some(branch);
|
||||
}
|
||||
self.project = Some(fproject);
|
||||
}
|
||||
|
||||
if let Some(mut settings) = config.settings {
|
||||
if let Some(api_url) = settings.api_url {
|
||||
fsettings.api_url = Some(api_url);
|
||||
}
|
||||
|
||||
self.settings = Some(fsettings);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PleaseConfigBuilder> for PleaseConfig {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: PleaseConfigBuilder) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
project: value
|
||||
.project
|
||||
.ok_or(value_is_missing("project"))?
|
||||
.try_into()?,
|
||||
settings: value
|
||||
.settings
|
||||
.ok_or(value_is_missing("settings"))?
|
||||
.try_into()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PleaseProjectConfigBuilder> for PleaseProjectConfig {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: PleaseProjectConfigBuilder) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
owner: value.owner.ok_or(value_is_missing("owner"))?,
|
||||
repository: value.repository.ok_or(value_is_missing("repository"))?,
|
||||
source: value.source.ok_or(value_is_missing("source"))?,
|
||||
branch: value.branch.ok_or(value_is_missing("branch"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PleaseSettingsConfigBuilder> for PleaseSettingsConfig {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(value: PleaseSettingsConfigBuilder) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
api_url: value.api_url.ok_or(value_is_missing("api_url"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn value_is_missing(message: &str) -> anyhow::Error {
|
||||
anyhow::anyhow!(
|
||||
"{} is required, pass via. cli, env or config file, see --help",
|
||||
message.to_string()
|
||||
)
|
||||
}
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cuddle-please-frontend.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
@ -8,11 +8,10 @@ use std::{
|
||||
use ::semver::Version;
|
||||
use anyhow::Context;
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use cuddle_please_frontend::{gatheres::ConfigArgs, PleaseConfig, PleaseConfigBuilder};
|
||||
|
||||
use crate::{
|
||||
cliff::{self, changelog_parser},
|
||||
environment::get_from_environment,
|
||||
git_client::VcsClient,
|
||||
gitea_client::GiteaClient,
|
||||
ui::{ConsoleUi, DynUi},
|
||||
@ -27,6 +26,9 @@ pub struct Command {
|
||||
#[command(flatten)]
|
||||
global: GlobalArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
config: ConfigArgs,
|
||||
|
||||
#[command(subcommand)]
|
||||
commands: Option<Commands>,
|
||||
|
||||
@ -49,32 +51,13 @@ struct GlobalArgs {
|
||||
)]
|
||||
token: Option<String>,
|
||||
|
||||
/// Which repository to publish against. If not supplied remote url will be inferred from environment or fail if not present.
|
||||
#[arg(long, global = true, help_heading = "Global")]
|
||||
api_url: Option<String>,
|
||||
|
||||
/// repo is the name of repository you want to release for
|
||||
#[arg(long, global = true, help_heading = "Global")]
|
||||
repo: Option<String>,
|
||||
|
||||
/// owner is the name of user from which the repository belongs <user>/<repo>
|
||||
#[arg(long, global = true, help_heading = "Global")]
|
||||
owner: Option<String>,
|
||||
|
||||
/// which source directory to use, if not set `std::env::current_dir` is used instead.
|
||||
#[arg(long, global = true, help_heading = "Global")]
|
||||
source: Option<PathBuf>,
|
||||
|
||||
/// which branch is being run from
|
||||
#[arg(long, global = true, help_heading = "Global")]
|
||||
branch: Option<String>,
|
||||
|
||||
/// whether to run in dry run mode (i.e. no pushes or releases)
|
||||
#[arg(long, global = true, help_heading = "Global")]
|
||||
dry_run: bool,
|
||||
|
||||
/// Inject configuration from stdin
|
||||
#[arg(
|
||||
env = "CUDDLE_PLEASE_CONFIG_STDIN",
|
||||
long,
|
||||
global = true,
|
||||
help_heading = "Global",
|
||||
@ -138,31 +121,23 @@ impl Command {
|
||||
s
|
||||
}
|
||||
|
||||
fn get_config(
|
||||
&self,
|
||||
current_dir: &Path,
|
||||
stdin: Option<String>,
|
||||
) -> anyhow::Result<PleaseConfig> {
|
||||
let mut config = get_config(current_dir, stdin)?;
|
||||
|
||||
self.get_from_environment(&mut config)?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn execute(self, current_dir: Option<&Path>) -> anyhow::Result<()> {
|
||||
// 1. Parse the current directory
|
||||
let current_dir = get_current_path(current_dir, self.global.source.clone())?;
|
||||
let stdin = if self.global.config_stdin {
|
||||
// 0. Get config
|
||||
let mut builder = &mut PleaseConfigBuilder::new();
|
||||
|
||||
if self.global.config_stdin {
|
||||
if let Some(stdin_fn) = self.stdin.clone() {
|
||||
let output = (stdin_fn.lock().unwrap().deref())();
|
||||
Some(output.unwrap())
|
||||
} else {
|
||||
None
|
||||
builder = builder.with_stdin(output?);
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
// 1. Parse the current directory
|
||||
let current_dir = get_current_path(current_dir, self.config.source.clone())?;
|
||||
let config = builder
|
||||
.with_config_file(¤t_dir)
|
||||
.with_execution_env(std::env::vars())
|
||||
.with_cli(self.config.clone())
|
||||
.build()?;
|
||||
|
||||
match &self.commands {
|
||||
Some(Commands::Release {}) => {
|
||||
@ -170,30 +145,24 @@ impl Command {
|
||||
// 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)
|
||||
let _config = self.get_config(¤t_dir, stdin)?;
|
||||
|
||||
let owner = self.global.owner.as_ref().expect("owner to be set");
|
||||
let repo = self.global.repo.as_ref().expect("repo to be set");
|
||||
let branch = self.global.branch.as_ref().expect("branch to be set");
|
||||
|
||||
let git_client = self.get_git(¤t_dir)?;
|
||||
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(owner, repo)
|
||||
.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(owner, repo)?;
|
||||
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(
|
||||
owner,
|
||||
repo,
|
||||
config.get_owner(),
|
||||
config.get_repository(),
|
||||
significant_tag.map(|st| st.commit.sha.clone()),
|
||||
branch,
|
||||
config.get_branch(),
|
||||
)?;
|
||||
|
||||
// 7. Create a versioning client
|
||||
@ -219,12 +188,7 @@ impl Command {
|
||||
let builder =
|
||||
cliff::ChangeLogBuilder::new(&commit_strs, next_version.to_string()).build();
|
||||
|
||||
let changelog_placement = self
|
||||
.global
|
||||
.source
|
||||
.as_ref()
|
||||
.map(|s| s.join("CHANGELOG.md"))
|
||||
.unwrap_or(PathBuf::from("CHANGELOG.md"));
|
||||
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)?,
|
||||
@ -239,8 +203,8 @@ impl Command {
|
||||
if first_commit.contains("chore(release): ") {
|
||||
if !self.global.dry_run {
|
||||
gitea_client.create_release(
|
||||
owner,
|
||||
repo,
|
||||
config.get_owner(),
|
||||
config.get_repository(),
|
||||
next_version.to_string(),
|
||||
changelog_last_changes.unwrap(),
|
||||
!next_version.pre.is_empty(),
|
||||
@ -261,12 +225,14 @@ impl Command {
|
||||
|
||||
git_client.commit_and_push(next_version.to_string(), self.global.dry_run)?;
|
||||
|
||||
let _pr_number = match gitea_client.get_pull_request(owner, repo)? {
|
||||
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(
|
||||
owner,
|
||||
repo,
|
||||
config.get_owner(),
|
||||
config.get_repository(),
|
||||
next_version.to_string(),
|
||||
changelog_last_changes.unwrap(),
|
||||
existing_pr,
|
||||
@ -279,11 +245,11 @@ impl Command {
|
||||
None => {
|
||||
if !self.global.dry_run {
|
||||
gitea_client.create_pull_request(
|
||||
owner,
|
||||
repo,
|
||||
config.get_owner(),
|
||||
config.get_repository(),
|
||||
next_version.to_string(),
|
||||
changelog,
|
||||
self.global.branch.clone().unwrap(),
|
||||
config.get_branch(),
|
||||
)?
|
||||
} else {
|
||||
tracing::debug!("creating pull request (dry_run)");
|
||||
@ -296,13 +262,12 @@ impl Command {
|
||||
Some(Commands::Config { command }) => match command {
|
||||
ConfigCommand::List { .. } => {
|
||||
tracing::debug!("running command: config list");
|
||||
let _config = self.get_config(current_dir.as_path(), stdin)?;
|
||||
|
||||
self.ui.write_str_ln("cuddle-config");
|
||||
}
|
||||
},
|
||||
Some(Commands::Gitea { command }) => {
|
||||
let git_url = url::Url::parse(&self.global.api_url.unwrap())?;
|
||||
let git_url = url::Url::parse(config.get_api_url())?;
|
||||
|
||||
let mut url = String::new();
|
||||
url.push_str(git_url.scheme());
|
||||
@ -315,13 +280,13 @@ impl Command {
|
||||
let client = GiteaClient::new(url, self.global.token);
|
||||
match command {
|
||||
GiteaCommand::Connect {} => {
|
||||
client.connect(self.global.owner.unwrap(), self.global.repo.unwrap())?;
|
||||
client.connect(config.get_owner(), config.get_repository())?;
|
||||
self.ui.write_str_ln("connected succesfully go gitea");
|
||||
}
|
||||
GiteaCommand::Tags { command } => match command {
|
||||
Some(GiteaTagsCommand::MostSignificant {}) => {
|
||||
let tags = client
|
||||
.get_tags(self.global.owner.unwrap(), self.global.repo.unwrap())?;
|
||||
let tags =
|
||||
client.get_tags(config.get_owner(), config.get_repository())?;
|
||||
|
||||
match get_most_significant_version(tags.iter().collect()) {
|
||||
Some(tag) => {
|
||||
@ -336,8 +301,8 @@ impl Command {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let tags = client
|
||||
.get_tags(self.global.owner.unwrap(), self.global.repo.unwrap())?;
|
||||
let tags =
|
||||
client.get_tags(config.get_owner(), config.get_repository())?;
|
||||
self.ui.write_str_ln("got tags from gitea");
|
||||
for tag in tags {
|
||||
self.ui.write_str_ln(&format!("- {}", tag.name))
|
||||
@ -346,8 +311,8 @@ impl Command {
|
||||
},
|
||||
GiteaCommand::SinceCommit { sha, branch } => {
|
||||
let commits = client.get_commits_since(
|
||||
self.global.owner.unwrap(),
|
||||
self.global.repo.unwrap(),
|
||||
config.get_owner(),
|
||||
config.get_repository(),
|
||||
Some(sha),
|
||||
branch,
|
||||
)?;
|
||||
@ -357,10 +322,8 @@ impl Command {
|
||||
}
|
||||
}
|
||||
GiteaCommand::CheckPr {} => {
|
||||
let pr = client.get_pull_request(
|
||||
self.global.owner.unwrap(),
|
||||
self.global.repo.unwrap(),
|
||||
)?;
|
||||
let pr =
|
||||
client.get_pull_request(config.get_owner(), config.get_repository())?;
|
||||
|
||||
match pr {
|
||||
Some(index) => {
|
||||
@ -398,16 +361,9 @@ impl Command {
|
||||
VcsClient::new_git(current_dir)
|
||||
}
|
||||
|
||||
fn get_from_environment(&self, config: &mut PleaseConfig) -> anyhow::Result<()> {
|
||||
let input_config = get_from_environment();
|
||||
config.merge_mut(input_config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_gitea_client(&self) -> GiteaClient {
|
||||
GiteaClient::new(
|
||||
self.global.api_url.clone().expect("api_url to be set"),
|
||||
self.config.api_url.clone().expect("api_url to be set"),
|
||||
self.global.token.clone(),
|
||||
)
|
||||
}
|
||||
@ -469,7 +425,8 @@ fn get_current_path(
|
||||
.or_else(|| optional_current_dir.map(|p| p.to_path_buf())) // fall back on current env from environment
|
||||
.filter(|v| v.to_string_lossy() != "") // make sure we don't get empty values
|
||||
//.and_then(|p| p.canonicalize().ok()) // Make sure we get the absolute path
|
||||
.context("could not find current dir, pass --source as a replacement")?;
|
||||
//.context("could not find current dir, pass --source as a replacement")?;
|
||||
.unwrap_or(PathBuf::from("."));
|
||||
|
||||
if !path.exists() {
|
||||
anyhow::bail!("path doesn't exist {}", path.display());
|
||||
@ -477,131 +434,3 @@ fn get_current_path(
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PleaseProjectConfig {
|
||||
pub owner: Option<String>,
|
||||
pub repository: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PleaseSettingsConfig {
|
||||
pub api_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct PleaseConfig {
|
||||
pub project: Option<PleaseProjectConfig>,
|
||||
pub settings: Option<PleaseSettingsConfig>,
|
||||
}
|
||||
|
||||
impl PleaseConfig {
|
||||
fn merge(self, _config: PleaseConfig) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn merge_mut(&mut self, _config: PleaseConfig) -> &mut Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct CuddleEmbeddedPleaseConfig {
|
||||
please: PleaseConfig,
|
||||
}
|
||||
|
||||
impl From<CuddleEmbeddedPleaseConfig> for PleaseConfig {
|
||||
fn from(value: CuddleEmbeddedPleaseConfig) -> Self {
|
||||
value.please
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct CuddlePleaseConfig {
|
||||
#[serde(flatten)]
|
||||
please: PleaseConfig,
|
||||
}
|
||||
impl From<CuddlePleaseConfig> for PleaseConfig {
|
||||
fn from(value: CuddlePleaseConfig) -> Self {
|
||||
value.please
|
||||
}
|
||||
}
|
||||
|
||||
const CUDDLE_FILE_NAME: &str = "cuddle";
|
||||
const CUDDLE_CONFIG_FILE_NAME: &str = "cuddle.please";
|
||||
const YAML_EXTENSION: &str = "yaml";
|
||||
|
||||
fn get_config(current_dir: &Path, stdin: Option<String>) -> anyhow::Result<PleaseConfig> {
|
||||
let current_cuddle_path = current_dir
|
||||
.clone()
|
||||
.join(format!("{CUDDLE_FILE_NAME}.{YAML_EXTENSION}"));
|
||||
let current_cuddle_config_path = current_dir
|
||||
.clone()
|
||||
.join(format!("{CUDDLE_CONFIG_FILE_NAME}.{YAML_EXTENSION}"));
|
||||
let mut please_config = PleaseConfig::default();
|
||||
|
||||
if let Some(config) = get_config_from_file::<CuddleEmbeddedPleaseConfig>(current_cuddle_path) {
|
||||
please_config = please_config.merge(config);
|
||||
}
|
||||
|
||||
if let Some(config) = get_config_from_file::<CuddlePleaseConfig>(current_cuddle_config_path) {
|
||||
please_config = please_config.merge(config);
|
||||
}
|
||||
|
||||
if let Some(input_config) = get_config_from_stdin::<CuddlePleaseConfig>(stdin.as_ref()) {
|
||||
please_config = please_config.merge(input_config);
|
||||
}
|
||||
|
||||
Ok(please_config)
|
||||
}
|
||||
|
||||
fn get_config_from_file<T>(current_cuddle_path: PathBuf) -> Option<PleaseConfig>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
T: Into<PleaseConfig>,
|
||||
{
|
||||
match std::fs::File::open(¤t_cuddle_path) {
|
||||
Ok(file) => match serde_yaml::from_reader::<_, T>(file) {
|
||||
Ok(config) => {
|
||||
return Some(config.into());
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
"{} doesn't contain a valid please config: {}",
|
||||
¤t_cuddle_path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::debug!(
|
||||
"did not find or was not allowed to read {}, error: {}",
|
||||
¤t_cuddle_path.display(),
|
||||
e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn get_config_from_stdin<'d, T>(stdin: Option<&'d String>) -> Option<PleaseConfig>
|
||||
where
|
||||
T: Deserialize<'d>,
|
||||
T: Into<PleaseConfig>,
|
||||
{
|
||||
match stdin {
|
||||
Some(content) => match serde_yaml::from_str::<'d, T>(content) {
|
||||
Ok(config) => {
|
||||
return Some(config.into());
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::debug!("stdin doesn't contain a valid please config: {}", e);
|
||||
}
|
||||
},
|
||||
None => {
|
||||
tracing::trace!("Stdin was not set continueing",);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1,38 +0,0 @@
|
||||
use crate::command::{PleaseConfig, PleaseProjectConfig};
|
||||
|
||||
pub mod drone;
|
||||
|
||||
pub fn get_from_environment() -> PleaseConfig {
|
||||
let env = detect_environment();
|
||||
|
||||
match env {
|
||||
ExecutionEnvironment::Local => PleaseConfig {
|
||||
project: None,
|
||||
settings: None,
|
||||
},
|
||||
ExecutionEnvironment::Drone => PleaseConfig {
|
||||
project: Some(PleaseProjectConfig {
|
||||
owner: Some(
|
||||
std::env::var("DRONE_REPO_OWNER").expect("DRONE_REPO_OWNER to be present"),
|
||||
),
|
||||
repository: Some(
|
||||
std::env::var("DRONE_REPO_NAME").expect("DRONE_REPO_NAME to be present"),
|
||||
),
|
||||
}),
|
||||
settings: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect_environment() -> ExecutionEnvironment {
|
||||
if std::env::var("DRONE").is_ok() {
|
||||
return ExecutionEnvironment::Drone;
|
||||
}
|
||||
|
||||
ExecutionEnvironment::Local
|
||||
}
|
||||
|
||||
pub enum ExecutionEnvironment {
|
||||
Local,
|
||||
Drone,
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
pub mod cliff;
|
||||
pub mod command;
|
||||
pub mod environment;
|
||||
pub mod git_client;
|
||||
pub mod gitea_client;
|
||||
pub mod ui;
|
||||
|
@ -1,6 +1,5 @@
|
||||
pub mod cliff;
|
||||
pub mod command;
|
||||
pub mod environment;
|
||||
pub mod git_client;
|
||||
pub mod gitea_client;
|
||||
pub mod ui;
|
||||
|
6
crates/cuddle-please/testdata/cuddle-embed/cuddle.please.yaml
vendored
Normal file
6
crates/cuddle-please/testdata/cuddle-embed/cuddle.please.yaml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
project:
|
||||
owner: kjuulh
|
||||
repository: cuddle-please
|
||||
branch: main
|
||||
settings:
|
||||
api_url: https://some-example.gitea-instance
|
@ -0,0 +1,7 @@
|
||||
please:
|
||||
project:
|
||||
owner: kjuulh
|
||||
repository: cuddle-please
|
||||
branch: main
|
||||
settings:
|
||||
api_url: https://some-example.gitea-instance
|
@ -0,0 +1,6 @@
|
||||
project:
|
||||
owner: kjuulh
|
||||
repository: cuddle-please
|
||||
branch: main
|
||||
settings:
|
||||
api_url: https://some-example.gitea-instance
|
@ -45,15 +45,19 @@ fn test_config_from_source_dir() {
|
||||
fn test_config_from_stdin() {
|
||||
let mut args = get_base_args();
|
||||
let ui = &BufferUi::default();
|
||||
let current_dir = get_test_data_path("cuddle-embed");
|
||||
args.push("--source");
|
||||
args.push(current_dir.to_str().unwrap());
|
||||
args.push("--config-stdin");
|
||||
let config = r#"
|
||||
project:
|
||||
owner: kjuulh
|
||||
repository: cuddle-please
|
||||
branch: main
|
||||
settings:
|
||||
api_url: https://some-example.gitea-instance
|
||||
"#;
|
||||
|
||||
Command::new_from_args_with_stdin(Some(ui), args, || Ok("please".into()))
|
||||
args.push("--config-stdin");
|
||||
Command::new_from_args_with_stdin(Some(ui), args, || Ok(config.into()))
|
||||
.execute(None)
|
||||
.unwrap();
|
||||
|
||||
assert_output(ui, "cuddle-config\n", "");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user