in THEORY maybe we could release if we ask nicely
cargo devel CI / cargo CI (push) Successful in 2m3s Details

This commit is contained in:
Christoph J. Scherr 2024-02-25 00:01:03 +01:00
parent 2c26a65a65
commit 7d4a3986a9
Signed by: PlexSheep
GPG Key ID: 7CDD0B14851A08EF
12 changed files with 151 additions and 51 deletions

View File

@ -15,6 +15,7 @@ api:
github: github:
type: github type: github
endpoint: https://github.com endpoint: https://github.com
repository: autocrate
auth: auth:
user: PlexSheep user: PlexSheep
pass: pass:
@ -22,6 +23,7 @@ api:
cscherr: cscherr:
type: forgejo type: forgejo
endpoint: https://git.cscherr.de endpoint: https://git.cscherr.de
repository: autocrate
auth: auth:
user: PlexSheep user: PlexSheep
pass: pass:

View File

@ -25,6 +25,7 @@ async-trait = "0.1.77"
# cargo = "0.76.0" # cargo = "0.76.0"
clap = { version = "4.4.18", features = ["derive", "help"] } clap = { version = "4.4.18", features = ["derive", "help"] }
clap-verbosity-flag = "2.1.2" clap-verbosity-flag = "2.1.2"
futures = "0.3.30"
git2 = "0.18.1" git2 = "0.18.1"
libpt = { version = "0.3.11", features = ["log"] } libpt = { version = "0.3.11", features = ["log"] }
reqwest = "0.11.24" reqwest = "0.11.24"

View File

@ -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 /// May be left empty if the Api does not need auth or the auth is part of the
/// [endpoint](Api::endpoint) [Url]. /// [endpoint](Api::endpoint) [Url].
pub auth: Option<ApiAuth>, pub auth: Option<ApiAuth>,
/// Name of the repository on the Git server, as git itself has no concept of repository name
pub repository: String
} }
impl YamlConfigSection for Api { impl YamlConfigSection for Api {
fn check(&self) -> Result<()> { fn check(&self) -> Result<()> {

15
src/git/mod.rs Normal file
View File

@ -0,0 +1,15 @@
use git2;
use crate::{config::Config, error::Result};
pub async fn tag(_cfg: &Config) -> Result<String> {
todo!()
}
pub async fn push(_cfg: &Config) -> Result<()> {
todo!()
}
pub async fn get_commit_sig(_cfg: &Config) -> Result<String> {
todo!()
}

View File

@ -4,3 +4,4 @@ pub mod error;
pub mod publish; pub mod publish;
pub mod release; pub mod release;
pub mod serverapi; pub mod serverapi;
pub mod git;

View File

@ -7,7 +7,7 @@ use autocrate::{
error::*, error::*,
publish::publish, publish::publish,
release::release, release::release,
serverapi::init_servers, serverapi::ApiCollection,
}; };
#[tokio::main] #[tokio::main]
@ -20,10 +20,12 @@ async fn main() -> Result<()> {
println!("{}", Changelog::build(&cfg)?); println!("{}", Changelog::build(&cfg)?);
} }
Commands::Release { .. } => { 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?; release(&cfg, &mut apis).await?;
} }
Commands::Publish { .. } => { Commands::Publish { .. } => {
// TODO: check if repo is dirty and create a commit with a given option
publish(&cfg).await?; publish(&cfg).await?;
} }
Commands::Version {} => { Commands::Version {} => {

View File

@ -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 struct ReleaseContext {
pub draft: bool, pub draft: bool,
pub prerelease: bool, pub prerelease: bool,
@ -10,13 +18,39 @@ pub struct ReleaseContext {
pub commit_sig: String, pub commit_sig: String,
} }
pub async fn release(cfg: &Config, _apis: &mut ApiCollection) -> Result<()> { pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> {
// TODO: git tag let tag: String = tag(cfg).await?;
// TODO: push to each server 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 let mut results = FuturesUnordered::new();
tag(cfg).await?; for api in apis.iter_mut() {
todo!(); // 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: check that the release is made
// TODO: generate artifacts // TODO: generate artifacts
@ -25,7 +59,3 @@ pub async fn release(cfg: &Config, _apis: &mut ApiCollection) -> Result<()> {
Ok(()) Ok(())
} }
async fn tag(_cfg: &Config) -> Result<()> {
todo!()
}

View File

@ -41,7 +41,7 @@ impl ServerApi for Forgejo {
async fn init(&mut self, _cfg: &Config) -> Result<()> { async fn init(&mut self, _cfg: &Config) -> Result<()> {
todo!() todo!()
} }
async fn push_release(&mut self, rc: &ReleaseContext) -> Result<()> { async fn push_release(&mut self, rc: ReleaseContext) -> Result<()> {
let raw_url = format!( let raw_url = format!(
"{}/api/v1/repos/{}/{}/releases", "{}/api/v1/repos/{}/{}/releases",
self.cfg.endpoint, rc.username, rc.repository self.cfg.endpoint, rc.username, rc.repository
@ -74,10 +74,11 @@ impl ServerApi for Forgejo {
.map_err(ServerApiError::from)?; .map_err(ServerApiError::from)?;
Ok(()) Ok(())
} }
async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
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 {&self.cfg}
} }

View File

@ -14,15 +14,16 @@ impl ServerApi for Gitea {
async fn init(&mut self, _cfg: &Config) -> Result<()> { async fn init(&mut self, _cfg: &Config) -> Result<()> {
todo!() todo!()
} }
async fn push_release(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
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 {&self.cfg}
} }
impl Gitea { impl Gitea {

View File

@ -14,15 +14,16 @@ impl ServerApi for Github {
async fn init(&mut self, _cfg: &Config) -> Result<()> { async fn init(&mut self, _cfg: &Config) -> Result<()> {
todo!() todo!()
} }
async fn push_release(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
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 {&self.cfg}
} }
impl Github { impl Github {

View File

@ -14,15 +14,16 @@ impl ServerApi for Gitlab {
async fn init(&mut self, _cfg: &Config) -> Result<()> { async fn init(&mut self, _cfg: &Config) -> Result<()> {
todo!() todo!()
} }
async fn push_release(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
async fn push_release_artifact(&mut self, _rc: &ReleaseContext) -> Result<()> { async fn push_release_artifact(&mut self, _rc: ReleaseContext) -> Result<()> {
todo!() todo!()
} }
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 {&self.cfg}
} }
impl Gitlab { impl Gitlab {

View File

@ -1,8 +1,13 @@
use std::{
ops::{Deref, DerefMut},
slice::Iter,
};
use async_trait::async_trait; use async_trait::async_trait;
use reqwest::ClientBuilder; use reqwest::ClientBuilder;
use crate::{ use crate::{
config::{ApiType, Config}, config::{ApiType, Config, self},
error::*, error::*,
publish::PublishContext, publish::PublishContext,
release::ReleaseContext, release::ReleaseContext,
@ -18,7 +23,6 @@ use github::*;
use gitlab::*; use gitlab::*;
pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),); pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);
pub type ApiCollection = Vec<Box<dyn ServerApi>>;
// NOTE: in stable rust, traits can normally not contain async methods, // 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). // 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<Box<dyn ServerApi>>;
#[async_trait] #[async_trait]
pub trait ServerApi { pub trait ServerApi {
async fn init(&mut self, cfg: &Config) -> Result<()>; 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;
}
pub(crate) type ApiCollectionInner = Vec<Box<dyn ServerApi>>;
pub struct ApiCollection {
collection: ApiCollectionInner,
}
impl ApiCollection {
pub async fn build(cfg: &Config) -> Result<Self> {
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 { pub fn client_builder() -> ClientBuilder {
ClientBuilder::new().user_agent(USER_AGENT) ClientBuilder::new().user_agent(USER_AGENT)
} }
pub async fn init_servers(cfg: &Config) -> Result<ApiCollection> { // trait iimplementations for easy use of ApiCollection follow
let mut collection: ApiCollection = ApiCollection::new(); impl IntoIterator for ApiCollection {
for api in &cfg.yaml.api { fn into_iter(self) -> Self::IntoIter {
match api.1.server_type { self.collection.into_iter()
ApiType::Gitea => { }
collection.push(Box::new(Gitea::build(api.1).await?)); type Item = Box<dyn ServerApi>;
} type IntoIter = std::vec::IntoIter<Self::Item>;
ApiType::Gitlab => { }
collection.push(Box::new(Gitlab::build(api.1).await?));
} impl Deref for ApiCollection {
ApiType::Github => { type Target = [Box<dyn ServerApi>];
collection.push(Box::new(Github::build(api.1).await?)); fn deref(&self) -> &Self::Target {
} &self.collection[..]
ApiType::Forgejo => { }
collection.push(Box::new(Forgejo::build(api.1).await?)); }
}
} 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)
} }