From 7d4a3986a9ea135d96e38df9cd3ebaa61fcc0614 Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Sun, 25 Feb 2024 00:01:03 +0100 Subject: [PATCH] in THEORY maybe we could release if we ask nicely --- .autocrate.yaml | 2 + Cargo.toml | 1 + src/config/mod.rs | 2 + src/git/mod.rs | 15 +++++++ src/lib.rs | 1 + src/main.rs | 6 ++- src/release/mod.rs | 52 +++++++++++++++++----- src/serverapi/forgejo.rs | 7 +-- src/serverapi/gitea.rs | 7 +-- src/serverapi/github.rs | 7 +-- src/serverapi/gitlab.rs | 7 +-- src/serverapi/mod.rs | 95 +++++++++++++++++++++++++++++----------- 12 files changed, 151 insertions(+), 51 deletions(-) create mode 100644 src/git/mod.rs diff --git a/.autocrate.yaml b/.autocrate.yaml index 52b1079..bc33cd6 100644 --- a/.autocrate.yaml +++ b/.autocrate.yaml @@ -15,6 +15,7 @@ api: github: type: github endpoint: https://github.com + repository: autocrate auth: user: PlexSheep pass: @@ -22,6 +23,7 @@ api: cscherr: type: forgejo endpoint: https://git.cscherr.de + repository: autocrate auth: user: PlexSheep pass: diff --git a/Cargo.toml b/Cargo.toml index 3218b7f..33087cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ async-trait = "0.1.77" # cargo = "0.76.0" clap = { version = "4.4.18", features = ["derive", "help"] } clap-verbosity-flag = "2.1.2" +futures = "0.3.30" git2 = "0.18.1" libpt = { version = "0.3.11", features = ["log"] } reqwest = "0.11.24" diff --git a/src/config/mod.rs b/src/config/mod.rs index c2fbeb3..b02f7c6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -107,6 +107,8 @@ pub struct Api { /// May be left empty if the Api does not need auth or the auth is part of the /// [endpoint](Api::endpoint) [Url]. pub auth: Option, + /// Name of the repository on the Git server, as git itself has no concept of repository name + pub repository: String } impl YamlConfigSection for Api { fn check(&self) -> Result<()> { diff --git a/src/git/mod.rs b/src/git/mod.rs new file mode 100644 index 0000000..b7cb3e2 --- /dev/null +++ b/src/git/mod.rs @@ -0,0 +1,15 @@ +use git2; + +use crate::{config::Config, error::Result}; + +pub async fn tag(_cfg: &Config) -> Result { + todo!() +} + +pub async fn push(_cfg: &Config) -> Result<()> { + todo!() +} + +pub async fn get_commit_sig(_cfg: &Config) -> Result { + todo!() +} diff --git a/src/lib.rs b/src/lib.rs index 2209b7f..fe7db2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,3 +4,4 @@ pub mod error; pub mod publish; pub mod release; pub mod serverapi; +pub mod git; diff --git a/src/main.rs b/src/main.rs index 81770d0..346544a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use autocrate::{ error::*, publish::publish, release::release, - serverapi::init_servers, + serverapi::ApiCollection, }; #[tokio::main] @@ -20,10 +20,12 @@ async fn main() -> Result<()> { println!("{}", Changelog::build(&cfg)?); } Commands::Release { .. } => { - let mut apis = init_servers(&cfg).await?; + // TODO: check if repo is dirty and create a commit with a given option + let mut apis = ApiCollection::build(&cfg).await?; release(&cfg, &mut apis).await?; } Commands::Publish { .. } => { + // TODO: check if repo is dirty and create a commit with a given option publish(&cfg).await?; } Commands::Version {} => { diff --git a/src/release/mod.rs b/src/release/mod.rs index 26af656..266c849 100644 --- a/src/release/mod.rs +++ b/src/release/mod.rs @@ -1,5 +1,13 @@ -use crate::{config::Config, error::*, serverapi::ApiCollection}; +use crate::{ + config::Config, + error::*, + git::{get_commit_sig, push, tag}, + serverapi::ApiCollection, +}; +use futures::{self, stream::FuturesUnordered, StreamExt}; + +#[derive(Debug, Clone, PartialEq, Hash)] pub struct ReleaseContext { pub draft: bool, pub prerelease: bool, @@ -10,13 +18,39 @@ pub struct ReleaseContext { pub commit_sig: String, } -pub async fn release(cfg: &Config, _apis: &mut ApiCollection) -> Result<()> { - // TODO: git tag - // TODO: push to each server +pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> { + let tag: String = tag(cfg).await?; + let commit_sig = get_commit_sig(cfg).await?; + 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 + // TODO: push to multiple remotes? - // TODO: release to each server - tag(cfg).await?; - todo!(); + let mut results = FuturesUnordered::new(); + for api in apis.iter_mut() { + // TODO: check that auth exists + let specific_rc = ReleaseContext { + draft: true, + prerelease: true, + username: api + .get_cfg() + .clone() + .auth + .expect("no auth but trying to publish") + .user, + repository: api.get_cfg().repository.clone(), + text: String::from("TODO: ADD TEXT VARIABLE SOMEHOW"), + tag: tag.clone(), + commit_sig: commit_sig.clone(), + }; + results.push(api.push_release(specific_rc)); + } + + // wait for the release requests to finish + while let Some(result) = results.next().await { + if result.is_err() { + return Err(result.unwrap_err()); + } + } // TODO: check that the release is made // TODO: generate artifacts @@ -25,7 +59,3 @@ pub async fn release(cfg: &Config, _apis: &mut ApiCollection) -> Result<()> { Ok(()) } - -async fn tag(_cfg: &Config) -> Result<()> { - todo!() -} diff --git a/src/serverapi/forgejo.rs b/src/serverapi/forgejo.rs index b6eab8a..576179c 100644 --- a/src/serverapi/forgejo.rs +++ b/src/serverapi/forgejo.rs @@ -41,7 +41,7 @@ impl ServerApi for Forgejo { 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<()> { let raw_url = format!( "{}/api/v1/repos/{}/{}/releases", self.cfg.endpoint, rc.username, rc.repository @@ -74,10 +74,11 @@ impl ServerApi for Forgejo { .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!() } - async fn push_pkg(&mut self, _pc: &PublishContext) -> Result<()> { + async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { todo!() } + fn get_cfg(&self) -> &Api {&self.cfg} } diff --git a/src/serverapi/gitea.rs b/src/serverapi/gitea.rs index 3cad481..7d6279d 100644 --- a/src/serverapi/gitea.rs +++ b/src/serverapi/gitea.rs @@ -14,15 +14,16 @@ 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!() } - async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { + async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> { todo!() } - async fn push_pkg(&mut self, _pc: &PublishContext) -> Result<()> { + async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { todo!() } + fn get_cfg(&self) -> &Api {&self.cfg} } impl Gitea { diff --git a/src/serverapi/github.rs b/src/serverapi/github.rs index 49c2807..30b43bd 100644 --- a/src/serverapi/github.rs +++ b/src/serverapi/github.rs @@ -14,15 +14,16 @@ impl ServerApi for Github { 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!() } - async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { + async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> { todo!() } - async fn push_pkg(&mut self, _pc: &PublishContext) -> Result<()> { + async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { todo!() } + fn get_cfg(&self) -> &Api {&self.cfg} } impl Github { diff --git a/src/serverapi/gitlab.rs b/src/serverapi/gitlab.rs index 4151222..644114b 100644 --- a/src/serverapi/gitlab.rs +++ b/src/serverapi/gitlab.rs @@ -14,15 +14,16 @@ 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!() } - async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { + async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> { todo!() } - async fn push_pkg(&mut self, _pc: &PublishContext) -> Result<()> { + async fn push_pkg(&mut self, _pc: PublishContext) -> Result<()> { todo!() } + fn get_cfg(&self) -> &Api {&self.cfg} } impl Gitlab { diff --git a/src/serverapi/mod.rs b/src/serverapi/mod.rs index 0128855..9354c51 100644 --- a/src/serverapi/mod.rs +++ b/src/serverapi/mod.rs @@ -1,8 +1,13 @@ +use std::{ + ops::{Deref, DerefMut}, + slice::Iter, +}; + use async_trait::async_trait; use reqwest::ClientBuilder; use crate::{ - config::{ApiType, Config}, + config::{ApiType, Config, self}, error::*, publish::PublishContext, release::ReleaseContext, @@ -18,7 +23,6 @@ use github::*; use gitlab::*; pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); -pub type ApiCollection = Vec>; // NOTE: in stable rust, traits can normally not contain async methods, // see [here](https://stackoverflow.com/questions/65921581/how-can-i-define-an-async-method-in-a-trait). @@ -26,35 +30,74 @@ pub type ApiCollection = Vec>; #[async_trait] pub trait ServerApi { async fn init(&mut self, cfg: &Config) -> Result<()>; - async fn push_release(&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_release(&mut self, rc: ReleaseContext) -> Result<()>; + async fn push_release_artifact(&mut self, rc: ReleaseContext) -> Result<()>; + async fn push_pkg(&mut self, pc: PublishContext) -> Result<()>; + fn get_cfg(&self) -> &config::Api; +} + +pub(crate) type ApiCollectionInner = Vec>; + +pub struct ApiCollection { + collection: ApiCollectionInner, +} + +impl ApiCollection { + pub async fn build(cfg: &Config) -> Result { + let mut collection: ApiCollectionInner = ApiCollectionInner::new(); + for api in &cfg.yaml.api { + match api.1.server_type { + ApiType::Gitea => { + collection.push(Box::new(Gitea::build(api.1).await?)); + } + ApiType::Gitlab => { + collection.push(Box::new(Gitlab::build(api.1).await?)); + } + ApiType::Github => { + collection.push(Box::new(Github::build(api.1).await?)); + } + ApiType::Forgejo => { + collection.push(Box::new(Forgejo::build(api.1).await?)); + } + } + } + for api in collection.iter_mut() { + api.init(cfg).await?; + } + Ok(ApiCollection { collection }) + } + + pub fn collection(&self) -> &ApiCollectionInner { + self.collection.as_ref() + } + + pub fn collection_mut(&mut self) -> &mut ApiCollectionInner { + self.collection.as_mut() + } } pub fn client_builder() -> ClientBuilder { ClientBuilder::new().user_agent(USER_AGENT) } -pub async fn init_servers(cfg: &Config) -> Result { - let mut collection: ApiCollection = ApiCollection::new(); - for api in &cfg.yaml.api { - match api.1.server_type { - ApiType::Gitea => { - collection.push(Box::new(Gitea::build(api.1).await?)); - } - ApiType::Gitlab => { - collection.push(Box::new(Gitlab::build(api.1).await?)); - } - ApiType::Github => { - collection.push(Box::new(Github::build(api.1).await?)); - } - ApiType::Forgejo => { - collection.push(Box::new(Forgejo::build(api.1).await?)); - } - } +// trait iimplementations for easy use of ApiCollection follow +impl IntoIterator for ApiCollection { + fn into_iter(self) -> Self::IntoIter { + self.collection.into_iter() + } + type Item = Box; + type IntoIter = std::vec::IntoIter; +} + +impl Deref for ApiCollection { + type Target = [Box]; + fn deref(&self) -> &Self::Target { + &self.collection[..] + } +} + +impl DerefMut for ApiCollection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.collection[..] } - for api in collection.iter_mut() { - api.init(cfg).await?; - } - Ok(collection) }