Compare commits

..

17 Commits

Author SHA1 Message Date
Christoph J. Scherr 872338c83f Merge branch 'master' of https://git.cscherr.de/PlexSheep/autocrate
cargo devel CI / cargo CI (push) Successful in 2m28s Details
2024-04-26 14:24:29 +02:00
Christoph J. Scherr 411ded1395 more print error 2024-04-26 14:24:23 +02:00
PlexSheep 97b36966d5 automatic cargo CI changes 2024-04-26 08:16:07 +00:00
Christoph J. Scherr e7ab89f050 better error display
cargo devel CI / cargo CI (push) Successful in 2m23s Details
2024-04-26 10:13:47 +02:00
Christoph J. Scherr ca3d1b7e82 Merge branch 'devel'
cargo devel CI / cargo CI (push) Successful in 2m13s Details
2024-04-26 10:07:30 +02:00
PlexSheep fcd6b6a4d4 automatic cargo CI changes 2024-04-26 08:07:04 +00:00
Christoph J. Scherr 56d6a50142 Merge branch 'devel'
cargo devel CI / cargo CI (push) Has been cancelled Details
2024-04-26 10:05:58 +02:00
Christoph J. Scherr ff9306047e Merge branch 'devel' of https://git.cscherr.de/PlexSheep/autocrate into devel
cargo devel CI / cargo CI (push) Successful in 2m38s Details
2024-04-26 10:04:33 +02:00
Christoph J. Scherr 181954cbce remodeling our structure with git a bit 2024-04-26 10:04:30 +02:00
Christoph J. Scherr 5bb4072e24 remodeling our structure with git a bit 2024-04-26 10:03:52 +02:00
PlexSheep 9aa9c8e07b automatic cargo CI changes 2024-04-26 06:58:04 +00:00
PlexSheep e767752338 automatic cargo CI changes 2024-04-26 06:57:17 +00:00
Christoph J. Scherr e80b68f1c4 Merge branch 'devel'
cargo devel CI / cargo CI (push) Successful in 3m4s Details
2024-04-26 08:55:01 +02:00
Christoph J. Scherr e66550f2ef github should maybe work
cargo devel CI / cargo CI (push) Successful in 3m4s Details
2024-04-26 08:54:23 +02:00
PlexSheep abe2e25071 automatic cargo CI changes 2024-04-26 06:36:24 +00:00
Christoph J. Scherr bf2b9c6d08 bump libpt
cargo devel CI / cargo CI (push) Successful in 2m15s Details
2024-04-26 08:34:13 +02:00
Christoph J. Scherr 1bc34011c1 release to forgejo works in an early state
cargo devel CI / cargo CI (push) Failing after 1m53s Details
2024-04-25 15:44:55 +02:00
15 changed files with 140 additions and 98 deletions

View File

@ -13,13 +13,13 @@ uses:
- cscherr - cscherr
api: api:
# github: github:
# type: github type: github
# repository: autocrate repository: autocrate
# auth: auth:
# user: PlexSheep user: PlexSheep
# pass: pass:
# !env TOKEN_GH !env TOKEN_GH
cscherr: cscherr:
type: forgejo type: forgejo
endpoint: https://git.cscherr.de endpoint: https://git.cscherr.de

2
.env
View File

@ -1,2 +0,0 @@
TOKEN_CSCHERR=test
TOKEN_GH=test

1
.gitignore vendored
View File

@ -19,3 +19,4 @@ Cargo.lock
# Added by cargo # Added by cargo
/target /target
.env

View File

@ -1,6 +1,6 @@
[package] [package]
name = "autocrate" name = "autocrate"
version = "0.1.0-prealpha.4" version = "0.1.0-prealpha.5"
edition = "2021" edition = "2021"
publish = true publish = true
authors = ["Christoph J. Scherr <software@cscherr.de>"] authors = ["Christoph J. Scherr <software@cscherr.de>"]
@ -28,7 +28,8 @@ clap-verbosity-flag = "2.1.2"
forgejo-api = "0.1.0" forgejo-api = "0.1.0"
futures = "0.3.30" futures = "0.3.30"
git2 = "0.18.1" git2 = "0.18.1"
libpt = { version = "0.3.11", features = ["log"] } libpt = { version = "0.4.2", features = ["log"] }
octocrab = "0.38.0"
reqwest = "0.11.24" reqwest = "0.11.24"
serde = { version = "1.0.195", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.116" serde_json = "1.0.116"

View File

@ -110,10 +110,10 @@ impl Cli {
} }
}; };
if cli.meta { if cli.meta {
Logger::init(None, Some(ll), true).expect("could not initialize Logger"); Logger::build(None, Some(ll), true).expect("could not initialize Logger");
} else { } else {
// less verbose version // less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger"); Logger::build_mini(Some(ll)).expect("could not initialize Logger");
} }
cli cli
} }

View File

@ -119,6 +119,20 @@ pub struct Api {
/// Name of the repository on the Git server, as git itself has no concept of repository name /// Name of the repository on the Git server, as git itself has no concept of repository name
pub repository: String, pub repository: String,
} }
impl std::fmt::Display for Api {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.server_type {
ApiType::Github => write!(
f,
"{}",
self.server_type
.default_endpoint()
.expect("no default endpoint set for github")
),
_ => write!(f, "{}", self.endpoint.clone().expect("no endpoint set")),
}
}
}
impl YamlConfigSection for Api { impl YamlConfigSection for Api {
fn check(&self) -> Result<()> { fn check(&self) -> Result<()> {
self.server_type.check()?; self.server_type.check()?;
@ -208,8 +222,7 @@ impl Version {
panic!("{err:?}"); panic!("{err:?}");
} }
} }
}
},
} }
} }
} }
@ -260,10 +273,10 @@ impl YamlConfig {
} }
} }
#[derive(Clone)]
pub struct Config { pub struct Config {
pub yaml: YamlConfig, pub yaml: YamlConfig,
pub cli: Cli, pub cli: Cli,
pub repo: git2::Repository,
pub path: PathBuf, pub path: PathBuf,
} }
@ -320,7 +333,6 @@ impl Config {
Ok(Config { Ok(Config {
yaml, yaml,
repo,
path, path,
cli: cli.clone(), cli: cli.clone(),
}) })

View File

@ -33,7 +33,9 @@ pub enum ServerApiError {
#[error(transparent)] #[error(transparent)]
ReqwestErr(#[from] reqwest::Error), ReqwestErr(#[from] reqwest::Error),
#[error(transparent)] #[error(transparent)]
ForgejoApiError(#[from] forgejo_api::ForgejoError) ForgejoApiError(#[from] forgejo_api::ForgejoError),
#[error(transparent)]
GithubApiError(#[from] octocrab::Error),
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -66,5 +68,5 @@ pub enum ConfigError {
#[error("An endpoint was set for an ApiType that does not require one")] #[error("An endpoint was set for an ApiType that does not require one")]
EndpointSetButNotNeeded, EndpointSetButNotNeeded,
#[error("No endpoint was set for an ApiType that requires one")] #[error("No endpoint was set for an ApiType that requires one")]
NoEndpointSet NoEndpointSet,
} }

View File

@ -1,26 +1,41 @@
use std::process::Command; use std::process::Command;
use git2; use git2;
use libpt::log::error;
use crate::error::ConfigError;
use crate::{config::Config, error::Result}; use crate::{config::Config, error::Result};
pub async fn tag(cfg: &Config) -> Result<git2::Tag> { pub(crate) fn get_repo() -> Result<git2::Repository> {
let repo = match git2::Repository::open_from_env() {
Ok(repo) => repo,
Err(_err) => {
let err = ConfigError::GitRepoNotFound.into();
error!("{err}");
return Err(err);
}
};
Ok(repo)
}
pub async fn tag<'repo>(
repo: &'repo mut git2::Repository,
cfg: &Config,
) -> Result<git2::Tag<'repo>> {
// TODO: error handling // TODO: error handling
// TODO: allow force // TODO: allow force
// TODO: allow setting a message // TODO: allow setting a message
// TODO: maybe using git as cmd is fancier? // TODO: maybe using git as cmd is fancier?
let target = cfg let target = repo
.repo
.find_object( .find_object(
cfg.repo.head().unwrap().target().unwrap(), repo.head().unwrap().target().unwrap(),
Some(git2::ObjectType::Commit), Some(git2::ObjectType::Commit),
) )
.unwrap(); .unwrap();
let tagger = cfg.repo.signature().expect("could not get signature"); let tagger = repo.signature().expect("could not get signature");
let message = String::new(); let message = String::new();
let force = true; let force = true;
let tag = cfg let tag = repo
.repo
.tag( .tag(
&cfg.yaml.version.get_version(), &cfg.yaml.version.get_version(),
// "importantversion", // "importantversion",
@ -30,7 +45,7 @@ pub async fn tag(cfg: &Config) -> Result<git2::Tag> {
force, force,
) )
.unwrap(); .unwrap();
let tag: git2::Tag = cfg.repo.find_tag(tag).unwrap(); let tag: git2::Tag = repo.find_tag(tag).unwrap();
Ok(tag) Ok(tag)
} }
@ -41,12 +56,11 @@ pub async fn push(_cfg: &Config) -> Result<()> {
Ok(()) Ok(())
} }
pub async fn get_commit_sig(cfg: &Config) -> Result<String> { pub async fn get_commit_sig<'repo>(repo: &'repo git2::Repository) -> Result<String> {
// TODO: error handling // TODO: error handling
// TODO: maybe using git as cmd is fancier? // TODO: maybe using git as cmd is fancier?
let target = cfg let target = repo
.repo .find_commit(repo.head().unwrap().target().unwrap())
.find_commit(cfg.repo.head().unwrap().target().unwrap())
.unwrap(); .unwrap();
Ok(target.id().to_string()) Ok(target.id().to_string())
} }

View File

@ -1,3 +1,5 @@
use std::error::Error as _;
use autocrate::{ use autocrate::{
changelog::*, changelog::*,
config::{ config::{
@ -9,24 +11,37 @@ use autocrate::{
release::release, release::release,
serverapi::ApiCollection, serverapi::ApiCollection,
}; };
use libpt::log::{debug, error};
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let cli = Cli::cli_parse(); let cli = Cli::cli_parse();
let cfg = Config::load(&cli)?; let cfg = Config::load(&cli)?;
match cli.command { let status: Option<Error> = match cli.command {
Commands::Changelog { .. } => { Commands::Changelog { .. } => {
println!("{}", Changelog::build(&cfg)?); let chlog = Changelog::build(&cfg);
if chlog.is_ok() {
println!("{}", chlog.unwrap());
None
} else {
Some(chlog.unwrap_err())
}
} }
Commands::Release { .. } => { Commands::Release { .. } => {
// TODO: check if repo is dirty and create a commit with a given option // TODO: check if repo is dirty and create a commit with a given option
let mut apis = ApiCollection::build(&cfg).await?; let mut apis = ApiCollection::build(&cfg).await?;
release(&cfg, &mut apis).await?; match release(&cfg, &mut apis).await {
Ok(_) => None,
Err(err) => Some(err),
}
} }
Commands::Publish { .. } => { Commands::Publish { .. } => {
// TODO: check if repo is dirty and create a commit with a given option // TODO: check if repo is dirty and create a commit with a given option
publish(&cfg).await?; match publish(&cfg).await {
Ok(_) => None,
Err(err) => Some(err),
}
} }
Commands::Version {} => { Commands::Version {} => {
// TODO: version bump // TODO: version bump
@ -39,5 +54,9 @@ async fn main() -> Result<()> {
todo!() todo!()
} }
}; };
if let Some(err) = status {
error!("{err}");
debug!("{:#?}", err.source());
}
Ok(()) Ok(())
} }

View File

@ -1,12 +1,12 @@
use crate::{ use crate::{
changelog,
config::Config, config::Config,
error::*, error::*,
git::{get_commit_sig, push, tag}, git::{self, get_commit_sig, push, tag},
serverapi::ApiCollection, serverapi::ApiCollection,
}; };
use futures::{self, stream::FuturesUnordered, StreamExt}; use futures::{self, stream::FuturesUnordered, StreamExt};
use libpt::log::info;
#[derive(Debug, Clone, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct ReleaseContext { pub struct ReleaseContext {
@ -21,9 +21,10 @@ pub struct ReleaseContext {
pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> { pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> {
// TODO: Error handling // TODO: Error handling
let changelog = crate::changelog::Changelog::build(cfg)?.to_string(); let _changelog = crate::changelog::Changelog::build(cfg)?.to_string();
let tag = tag(cfg).await?.name().unwrap().to_string(); let mut repo = git::get_repo()?;
let commit_sig = get_commit_sig(cfg).await?; let tag = tag(&mut repo, cfg).await?.name().unwrap().to_string();
let commit_sig = get_commit_sig(&repo).await?;
push(cfg).await?; // we assume that we only need to push the current branch to the singular push(cfg).await?; // we assume that we only need to push the current branch to the singular
// remote, expecting that the repositories are somehow mirrored // remote, expecting that the repositories are somehow mirrored
// TODO: push to multiple remotes? // TODO: push to multiple remotes?
@ -35,16 +36,17 @@ pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> {
draft: true, draft: true,
prerelease: true, prerelease: true,
username: api username: api
.get_cfg() .get_inner()
.clone() .clone()
.auth .auth
.expect("no auth but trying to publish") .expect("no auth but trying to publish")
.user, .user,
repository: api.get_cfg().repository.clone(), repository: api.get_inner().repository.clone(),
text: changelog.clone(), text: crate::changelog::Changelog::build(cfg)?.to_string(),
tag: tag.clone(), tag: tag.clone(),
commit_sig: commit_sig.clone(), commit_sig: commit_sig.clone(),
}; };
info!("pushing release for {}", api.get_inner());
results.push(api.push_release(specific_rc)); results.push(api.push_release(specific_rc));
} }

View File

@ -1,5 +1,3 @@
use std::str::FromStr;
use crate::{ use crate::{
config::{Api, Config}, config::{Api, Config},
error::*, error::*,
@ -7,27 +5,23 @@ use crate::{
}; };
use async_trait::async_trait; use async_trait::async_trait;
use forgejo_api; use forgejo_api;
use libpt::log::debug;
use reqwest::{
header::{HeaderMap, HeaderValue},
Client, Url,
};
use serde_json;
pub struct Forgejo { pub struct Forgejo {
cfg: Api, api: Api,
cfg: Config,
api_wrapper: forgejo_api::Forgejo, api_wrapper: forgejo_api::Forgejo,
} }
impl Forgejo { impl Forgejo {
pub async fn build(api: &Api) -> Result<Self> { pub async fn build(api: &Api, cfg: &Config) -> Result<Self> {
let api_wrapper: forgejo_api::Forgejo = forgejo_api::Forgejo::new( let api_wrapper: forgejo_api::Forgejo = forgejo_api::Forgejo::new(
forgejo_api::Auth::Token(&api.auth.clone().unwrap().pass.get_pass()?), forgejo_api::Auth::Token(&api.auth.clone().unwrap().pass.get_pass()?),
api.endpoint.clone().unwrap(), api.endpoint.clone().unwrap(),
) )
.map_err(ServerApiError::from)?; .map_err(ServerApiError::from)?;
Ok(Self { Ok(Self {
cfg: api.clone(), api: api.clone(),
cfg: cfg.clone(),
api_wrapper, api_wrapper,
}) })
} }
@ -35,9 +29,6 @@ impl Forgejo {
#[async_trait] #[async_trait]
impl ServerApi for Forgejo { impl ServerApi for Forgejo {
async fn init(&mut self, _cfg: &Config) -> Result<()> {
Ok(())
}
async fn push_release(&mut self, rc: ReleaseContext) -> Result<()> { async fn push_release(&mut self, rc: ReleaseContext) -> Result<()> {
let body: forgejo_api::structs::CreateReleaseOption = let body: forgejo_api::structs::CreateReleaseOption =
forgejo_api::structs::CreateReleaseOption { forgejo_api::structs::CreateReleaseOption {
@ -60,7 +51,7 @@ impl ServerApi for Forgejo {
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
todo!() todo!()
} }
fn get_cfg(&self) -> &Api { fn get_inner(&self) -> &Api {
&self.cfg &self.api
} }
} }

View File

@ -6,14 +6,11 @@ use crate::{
error::*, error::*,
}; };
pub struct Gitea { pub struct Gitea {
cfg: Api, api: Api,
} }
#[async_trait] #[async_trait]
impl ServerApi for Gitea { impl ServerApi for Gitea {
async fn init(&mut self, _cfg: &Config) -> Result<()> {
todo!()
}
async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> { async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
@ -23,13 +20,13 @@ impl ServerApi for Gitea {
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
todo!() todo!()
} }
fn get_cfg(&self) -> &Api { fn get_inner(&self) -> &Api {
&self.cfg &self.api
} }
} }
impl Gitea { impl Gitea {
pub async fn build(api: &Api) -> Result<Self> { pub async fn build(api: &Api, _cfg: &Config) -> Result<Self> {
Ok(Self { cfg: api.clone() }) Ok(Self { api: api.clone() })
} }
} }

View File

@ -1,4 +1,5 @@
use async_trait::async_trait; use async_trait::async_trait;
use octocrab;
use super::{PublishContext, ReleaseContext, ServerApi}; use super::{PublishContext, ReleaseContext, ServerApi};
use crate::{ use crate::{
@ -6,16 +7,33 @@ use crate::{
error::*, error::*,
}; };
pub struct Github { pub struct Github {
cfg: Api, api: Api,
}
impl Github {
pub async fn build(api: &Api, _cfg: &Config) -> Result<Self> {
Ok(Self {
api: api.to_owned(),
})
}
} }
#[async_trait] #[async_trait]
impl ServerApi for Github { impl ServerApi for Github {
async fn init(&mut self, _cfg: &Config) -> Result<()> { async fn push_release(&mut self, rc: ReleaseContext) -> Result<()> {
todo!() let _response = octocrab::instance()
} .repos(rc.username, rc.repository)
async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> { .releases()
todo!() .create(&rc.tag)
.target_commitish(&rc.commit_sig)
.name(&rc.tag)
.body(&rc.text)
.draft(rc.draft)
.prerelease(rc.prerelease)
.send()
.await
.map_err(ServerApiError::from)?;
Ok(())
} }
async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> { async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
@ -23,13 +41,7 @@ impl ServerApi for Github {
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
todo!() todo!()
} }
fn get_cfg(&self) -> &Api { fn get_inner(&self) -> &Api {
&self.cfg &self.api
}
}
impl Github {
pub async fn build(api: &Api) -> Result<Self> {
Ok(Self { cfg: api.clone() })
} }
} }

View File

@ -6,14 +6,11 @@ use crate::{
error::*, error::*,
}; };
pub struct Gitlab { pub struct Gitlab {
cfg: Api, api: Api,
} }
#[async_trait] #[async_trait]
impl ServerApi for Gitlab { impl ServerApi for Gitlab {
async fn init(&mut self, _cfg: &Config) -> Result<()> {
todo!()
}
async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> { async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
@ -23,13 +20,13 @@ impl ServerApi for Gitlab {
async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> {
todo!() todo!()
} }
fn get_cfg(&self) -> &Api { fn get_inner(&self) -> &Api {
&self.cfg &self.api
} }
} }
impl Gitlab { impl Gitlab {
pub async fn build(api: &Api) -> Result<Self> { pub async fn build(api: &Api, _cfg: &Config) -> Result<Self> {
Ok(Self { cfg: api.clone() }) Ok(Self { api: api.clone() })
} }
} }

View File

@ -26,11 +26,10 @@ pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_P
// The `async_trait` crate can be used to work around this limitation. // The `async_trait` crate can be used to work around this limitation.
#[async_trait] #[async_trait]
pub trait ServerApi { pub trait ServerApi {
async fn init(&mut self, cfg: &Config) -> Result<()>;
async fn push_release(&mut self, rc: ReleaseContext) -> Result<()>; async fn push_release(&mut self, rc: ReleaseContext) -> Result<()>;
async fn push_release_artifact(&mut self, rc: ReleaseContext) -> Result<()>; async fn push_release_artifact(&mut self, rc: ReleaseContext) -> Result<()>;
async fn push_pkg(&mut self, pc: PublishContext) -> Result<()>; async fn push_pkg(&mut self, pc: PublishContext) -> Result<()>;
fn get_cfg(&self) -> &config::Api; fn get_inner(&self) -> &config::Api;
} }
pub(crate) type ApiCollectionInner = Vec<Box<dyn ServerApi>>; pub(crate) type ApiCollectionInner = Vec<Box<dyn ServerApi>>;
@ -45,22 +44,19 @@ impl ApiCollection {
for api in &cfg.yaml.api { for api in &cfg.yaml.api {
match api.1.server_type { match api.1.server_type {
ApiType::Gitea => { ApiType::Gitea => {
collection.push(Box::new(Gitea::build(api.1).await?)); collection.push(Box::new(Gitea::build(api.1, cfg).await?));
} }
ApiType::Gitlab => { ApiType::Gitlab => {
collection.push(Box::new(Gitlab::build(api.1).await?)); collection.push(Box::new(Gitlab::build(api.1, cfg).await?));
} }
ApiType::Github => { ApiType::Github => {
collection.push(Box::new(Github::build(api.1).await?)); collection.push(Box::new(Github::build(api.1, cfg).await?));
} }
ApiType::Forgejo => { ApiType::Forgejo => {
collection.push(Box::new(Forgejo::build(api.1).await?)); collection.push(Box::new(Forgejo::build(api.1, cfg).await?));
} }
} }
} }
for api in collection.iter_mut() {
api.init(cfg).await?;
}
Ok(ApiCollection { collection }) Ok(ApiCollection { collection })
} }