generated from PlexSheep/rs-base
Compare commits
No commits in common. "1bc34011c1a27632efb6c741d366177d01f34c9d" and "6e5582cbe66e377f47d513e38a98603f2d1e5ab1" have entirely different histories.
1bc34011c1
...
6e5582cbe6
20 changed files with 130 additions and 380 deletions
|
@ -1,5 +1,4 @@
|
||||||
version:
|
---
|
||||||
!cargo
|
|
||||||
changelog:
|
changelog:
|
||||||
enable: true
|
enable: true
|
||||||
git-log: true
|
git-log: true
|
||||||
|
@ -13,18 +12,17 @@ uses:
|
||||||
- cscherr
|
- cscherr
|
||||||
|
|
||||||
api:
|
api:
|
||||||
# github:
|
github:
|
||||||
# type: github
|
type: github
|
||||||
# repository: autocrate
|
endpoint: https://github.com
|
||||||
# auth:
|
|
||||||
# user: PlexSheep
|
|
||||||
# pass:
|
|
||||||
# !env TOKEN_GH
|
|
||||||
cscherr:
|
|
||||||
type: forgejo
|
|
||||||
endpoint: https://git.cscherr.de
|
|
||||||
repository: autocrate
|
|
||||||
auth:
|
auth:
|
||||||
user: PlexSheep
|
user: PlexSheep
|
||||||
pass:
|
pass:
|
||||||
!env TOKEN_CSCHERR
|
env: TOKEN_GH
|
||||||
|
cscherr:
|
||||||
|
type: forgejo
|
||||||
|
endpoint: https://git.cscherr.de
|
||||||
|
auth:
|
||||||
|
user: PlexSheep
|
||||||
|
pass:
|
||||||
|
env: TOKEN_CSCHERR
|
||||||
|
|
2
.env
2
.env
|
@ -1,2 +0,0 @@
|
||||||
TOKEN_CSCHERR=test
|
|
||||||
TOKEN_GH=test
|
|
|
@ -31,13 +31,13 @@ jobs:
|
||||||
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
||||||
cat ~/.cargo/config.toml
|
cat ~/.cargo/config.toml
|
||||||
- name: cargo clippy check
|
- name: cargo clippy check
|
||||||
run: cargo clippy --all-features --all-targets --workspace
|
run: cargo clippy --all-features --all-targets
|
||||||
- name: cargo clippy fix
|
- name: cargo clippy fix
|
||||||
run: cargo clippy --fix --all-features --all-targets --workspace
|
run: cargo clippy --fix --all-features --all-targets
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all
|
run: cargo fmt --all
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
run: cargo test --all-features --all-targets --workspace
|
run: cargo test --all-features --all-targets
|
||||||
- name: commit back to repository
|
- name: commit back to repository
|
||||||
uses: https://github.com/stefanzweifel/git-auto-commit-action@v5
|
uses: https://github.com/stefanzweifel/git-auto-commit-action@v5
|
||||||
with:
|
with:
|
||||||
|
|
6
.github/workflows/cargo.yaml
vendored
6
.github/workflows/cargo.yaml
vendored
|
@ -32,13 +32,13 @@ jobs:
|
||||||
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
|
||||||
cat ~/.cargo/config.toml
|
cat ~/.cargo/config.toml
|
||||||
- name: cargo clippy check
|
- name: cargo clippy check
|
||||||
run: cargo clippy --all-features --all-targets --workspace
|
run: cargo clippy --all-features --all-targets
|
||||||
- name: cargo clippy fix
|
- name: cargo clippy fix
|
||||||
run: cargo clippy --fix --all-features --all-targets --workspace
|
run: cargo clippy --fix --all-features --all-targets
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all
|
run: cargo fmt --all
|
||||||
- name: cargo test
|
- name: cargo test
|
||||||
run: cargo test --all-features --all-targets --workspace
|
run: cargo test --all-features --all-targets
|
||||||
- name: commit back to repository
|
- name: commit back to repository
|
||||||
uses: stefanzweifel/git-auto-commit-action@v5
|
uses: stefanzweifel/git-auto-commit-action@v5
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "autocrate"
|
name = "autocrate"
|
||||||
version = "0.1.0-prealpha.4"
|
version = "0.1.0-prealpha.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = true
|
publish = true
|
||||||
authors = ["Christoph J. Scherr <software@cscherr.de>"]
|
authors = ["Christoph J. Scherr <software@cscherr.de>"]
|
||||||
|
@ -25,13 +25,10 @@ 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"
|
||||||
forgejo-api = "0.1.0"
|
|
||||||
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"
|
||||||
serde = { version = "1.0.195", features = ["derive"] }
|
serde = { version = "1.0.195", features = ["derive"] }
|
||||||
serde_json = "1.0.116"
|
|
||||||
serde_yaml = "0.9.30"
|
serde_yaml = "0.9.30"
|
||||||
tempfile = "3.9.0"
|
tempfile = "3.9.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
|
|
11
README.md
11
README.md
|
@ -21,7 +21,6 @@ The software is built in Rust, and offers integration for Rust Projects with Car
|
||||||
In the future, using other tools and specifying custom scripts will become possible.
|
In the future, using other tools and specifying custom scripts will become possible.
|
||||||
|
|
||||||
* [Original Repository](https://git.cscherr.de/PlexSheep/Autocrate)
|
* [Original Repository](https://git.cscherr.de/PlexSheep/Autocrate)
|
||||||
* [Codeberg Mirror](https://codeberg.org/PlexSheep/autocrate)
|
|
||||||
* [GitHub Mirror](https://github.com/PlexSheep/Autocrate)
|
* [GitHub Mirror](https://github.com/PlexSheep/Autocrate)
|
||||||
* [crates.io](https://crates.io/crates/autocrate)
|
* [crates.io](https://crates.io/crates/autocrate)
|
||||||
* [docs.rs](https://docs.rs/crate/autocrate/)
|
* [docs.rs](https://docs.rs/crate/autocrate/)
|
||||||
|
@ -161,17 +160,9 @@ are still missing or experimental
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
I'd be very happy to get contributions! Although the master repository is on
|
I'd be very happy to get contributions! Although the master repository is on
|
||||||
my self hosted git server, you're free to create issues, PRs and so on
|
my self hosted git server, you're free to create issues, PRs and so on on
|
||||||
GitHub. If enough activity comes around, moving to GitHub Codeberg might be a
|
GitHub. If enough activity comes around, moving to GitHub Codeberg might be a
|
||||||
good idea.
|
good idea.
|
||||||
|
|
||||||
If you have any questions, use issues and discussions tabs or write me an email
|
If you have any questions, use issues and discussions tabs or write me an email
|
||||||
to [software@cscherr.de](mailto:software@cscherr.de)
|
to [software@cscherr.de](mailto:software@cscherr.de)
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you find a security issue with this repository, it would be best if you sent
|
|
||||||
me a mail to [software@cscherr.de](mailto:software@cscherr.de) or reported it on
|
|
||||||
GitHub.
|
|
||||||
|
|
||||||
See [`SECURITY`](SECURITY.md).
|
|
||||||
|
|
14
SECURITY.md
14
SECURITY.md
|
@ -1,14 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Only the latest release is currently supported.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
It would be best if you reported any found security vulnerabilities on the GitHub mirror.
|
|
||||||
|
|
||||||
If you want to send something encrypted, use [this](https://static.cscherr.de/keys/software@cscherr.de.asc) key.
|
|
||||||
You can always reach me on [software@cscherr.de](mailto:software@cscherr.de), but sadly, I'm struggeling with PGP
|
|
||||||
encryption in my mail client. I use protonmail to host my EMail services, so if you send me something from protonmail,
|
|
||||||
it will be end to end encrypted.
|
|
|
@ -17,7 +17,6 @@ impl Changelog {
|
||||||
Ok(Changelog { git_log })
|
Ok(Changelog { git_log })
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use libgit2 instead of the cli interface
|
|
||||||
fn make_git_log(cfg: &Config) -> Result<Option<String>> {
|
fn make_git_log(cfg: &Config) -> Result<Option<String>> {
|
||||||
if !cfg.yaml.changelog.enable {
|
if !cfg.yaml.changelog.enable {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
@ -36,6 +35,7 @@ impl Changelog {
|
||||||
return Err(ChangelogError::GitBadStatus(out.status, buf).into());
|
return Err(ChangelogError::GitBadStatus(out.status, buf).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dbg!(&buf);
|
||||||
Ok(Some(buf))
|
Ok(Some(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,11 +62,9 @@ pub enum Commands {
|
||||||
message: Option<Vec<String>>,
|
message: Option<Vec<String>>,
|
||||||
|
|
||||||
/// generate and add a changelog
|
/// generate and add a changelog
|
||||||
#[arg(short, long)]
|
|
||||||
changelog: bool,
|
changelog: bool,
|
||||||
|
|
||||||
/// publish after releasing
|
/// publish after releasing
|
||||||
#[arg(short, long)]
|
|
||||||
publish: bool,
|
publish: bool,
|
||||||
},
|
},
|
||||||
/// Publish to a package registry
|
/// Publish to a package registry
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use std::str::FromStr;
|
use std::{collections::HashMap, fmt::Debug, fs::File, io::BufReader, path::PathBuf};
|
||||||
use std::{
|
|
||||||
collections::HashMap, fmt::Debug, fs::File, io::BufReader, path::PathBuf, process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
use git2;
|
use git2;
|
||||||
use libpt::log::{debug, error, trace};
|
use libpt::log::{debug, error, trace};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::error::*;
|
use crate::error::*;
|
||||||
|
@ -18,7 +15,7 @@ pub trait YamlConfigSection: Debug + Clone + for<'a> Deserialize<'a> {
|
||||||
fn check(&self) -> Result<()>;
|
fn check(&self) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Changelog {
|
pub struct Changelog {
|
||||||
pub enable: bool,
|
pub enable: bool,
|
||||||
#[serde(alias = "git-log")]
|
#[serde(alias = "git-log")]
|
||||||
|
@ -30,7 +27,7 @@ impl YamlConfigSection for Changelog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct UseCargo {
|
pub struct UseCargo {
|
||||||
pub publish: bool,
|
pub publish: bool,
|
||||||
pub registries: Vec<String>,
|
pub registries: Vec<String>,
|
||||||
|
@ -41,7 +38,7 @@ impl YamlConfigSection for UseCargo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Uses {
|
pub struct Uses {
|
||||||
cargo: UseCargo,
|
cargo: UseCargo,
|
||||||
}
|
}
|
||||||
|
@ -52,17 +49,13 @@ impl YamlConfigSection for Uses {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Pass {
|
pub enum Pass {
|
||||||
/// pass specified as plainext
|
/// pass specified as plainext
|
||||||
#[serde(alias = "pass_text")]
|
|
||||||
Text(String),
|
Text(String),
|
||||||
/// pass to be loaded from an env var
|
/// pass to be loaded from an env var
|
||||||
#[serde(alias = "pass_env")]
|
|
||||||
Env(String),
|
Env(String),
|
||||||
/// pass to be loaded from a file
|
/// pass to be loaded from a file
|
||||||
#[serde(alias = "pass_file")]
|
|
||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
}
|
}
|
||||||
impl Pass {
|
impl Pass {
|
||||||
|
@ -95,7 +88,7 @@ impl YamlConfigSection for Pass {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct ApiAuth {
|
pub struct ApiAuth {
|
||||||
pub user: String,
|
pub user: String,
|
||||||
pub pass: Pass,
|
pub pass: Pass,
|
||||||
|
@ -107,31 +100,20 @@ impl YamlConfigSection for ApiAuth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct Api {
|
pub struct Api {
|
||||||
#[serde(alias = "type")]
|
|
||||||
pub server_type: ApiType,
|
pub server_type: ApiType,
|
||||||
/// May be left empty if the [ApiType] is [Github](ApiType::Github).
|
pub endpoint: Url,
|
||||||
pub endpoint: Option<Url>,
|
|
||||||
/// 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<()> {
|
||||||
self.server_type.check()?;
|
self.server_type.check()?;
|
||||||
if self.server_type != ApiType::Github {
|
match self.endpoint.socket_addrs(|| None) {
|
||||||
if self.auth.is_none() {
|
Ok(_) => (),
|
||||||
return Err(ConfigError::NoEndpointSet.into());
|
Err(err) => return Err(err.into()),
|
||||||
}
|
|
||||||
match self.endpoint.clone().unwrap().socket_addrs(|| None) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(err) => return Err(err.into()),
|
|
||||||
}
|
|
||||||
} else if let Some(_url) = &self.endpoint {
|
|
||||||
return Err(ConfigError::EndpointSetButNotNeeded.into());
|
|
||||||
}
|
}
|
||||||
if self.auth.is_some() {
|
if self.auth.is_some() {
|
||||||
self.auth.clone().unwrap().check()?;
|
self.auth.clone().unwrap().check()?;
|
||||||
|
@ -140,7 +122,7 @@ impl YamlConfigSection for Api {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub enum ApiType {
|
pub enum ApiType {
|
||||||
#[serde(alias = "gitea")]
|
#[serde(alias = "gitea")]
|
||||||
Gitea,
|
Gitea,
|
||||||
|
@ -151,98 +133,22 @@ pub enum ApiType {
|
||||||
#[serde(alias = "forgejo")]
|
#[serde(alias = "forgejo")]
|
||||||
Forgejo,
|
Forgejo,
|
||||||
}
|
}
|
||||||
impl ApiType {
|
|
||||||
pub fn default_endpoint(&self) -> Option<Url> {
|
|
||||||
match self {
|
|
||||||
Self::Github => Some(Url::from_str("https://github.com").unwrap()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YamlConfigSection for ApiType {
|
impl YamlConfigSection for ApiType {
|
||||||
fn check(&self) -> Result<()> {
|
fn check(&self) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum Version {
|
|
||||||
Text(String),
|
|
||||||
Cmd(String),
|
|
||||||
Cargo,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version {
|
|
||||||
pub fn get_version(&self) -> String {
|
|
||||||
// TODO: Error handling
|
|
||||||
match self {
|
|
||||||
Self::Text(ver) => ver.clone(),
|
|
||||||
Self::Cmd(shell_command) => {
|
|
||||||
match Command::new("/bin/bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(shell_command)
|
|
||||||
.output()
|
|
||||||
{
|
|
||||||
Ok(output) => {
|
|
||||||
// TODO: check status
|
|
||||||
String::from_utf8(output.stdout).unwrap()
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Cargo => {
|
|
||||||
match Command::new("/bin/bash")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(r#"cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1'"#)
|
|
||||||
.output()
|
|
||||||
{
|
|
||||||
Ok(output) => {
|
|
||||||
// TODO: check status
|
|
||||||
String::from_utf8(output.stdout).unwrap().trim().to_owned()
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
panic!("{err:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl YamlConfigSection for Version {
|
|
||||||
fn check(&self) -> Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::Text(_) => (),
|
|
||||||
Self::Cmd(_cmd) => {
|
|
||||||
// TODO: get the version with a command
|
|
||||||
todo!("verion from cmd not implemented")
|
|
||||||
}
|
|
||||||
Self::Cargo => {
|
|
||||||
// TODO: get the version as specified in a Cargo.toml
|
|
||||||
todo!("verion from cargo not implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct YamlConfig {
|
pub struct YamlConfig {
|
||||||
pub changelog: Changelog,
|
pub changelog: Changelog,
|
||||||
pub uses: Uses,
|
pub uses: Uses,
|
||||||
pub api: HashMap<String, Api>,
|
pub api: HashMap<String, Api>,
|
||||||
pub version: Version,
|
|
||||||
}
|
}
|
||||||
impl YamlConfigSection for YamlConfig {
|
impl YamlConfigSection for YamlConfig {
|
||||||
fn check(&self) -> Result<()> {
|
fn check(&self) -> Result<()> {
|
||||||
self.changelog.check()?;
|
self.changelog.check()?;
|
||||||
self.uses.check()?;
|
self.uses.check()?;
|
||||||
self.version.check()?;
|
|
||||||
for api in self.api.values() {
|
for api in self.api.values() {
|
||||||
api.check()?;
|
api.check()?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,6 @@ pub enum ServerApiError {
|
||||||
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
|
InvalidHeaderValue(#[from] reqwest::header::InvalidHeaderValue),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ReqwestErr(#[from] reqwest::Error),
|
ReqwestErr(#[from] reqwest::Error),
|
||||||
#[error(transparent)]
|
|
||||||
ForgejoApiError(#[from] forgejo_api::ForgejoError)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -63,8 +61,4 @@ pub enum ConfigError {
|
||||||
EnvNotSet(String),
|
EnvNotSet(String),
|
||||||
#[error("Bad value for environment variable: {0}")]
|
#[error("Bad value for environment variable: {0}")]
|
||||||
BadEnv(#[from] VarError),
|
BadEnv(#[from] VarError),
|
||||||
#[error("An endpoint was set for an ApiType that does not require one")]
|
|
||||||
EndpointSetButNotNeeded,
|
|
||||||
#[error("No endpoint was set for an ApiType that requires one")]
|
|
||||||
NoEndpointSet
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use git2;
|
|
||||||
|
|
||||||
use crate::{config::Config, error::Result};
|
|
||||||
|
|
||||||
pub async fn tag(cfg: &Config) -> Result<git2::Tag> {
|
|
||||||
// TODO: error handling
|
|
||||||
// TODO: allow force
|
|
||||||
// TODO: allow setting a message
|
|
||||||
// TODO: maybe using git as cmd is fancier?
|
|
||||||
let target = cfg
|
|
||||||
.repo
|
|
||||||
.find_object(
|
|
||||||
cfg.repo.head().unwrap().target().unwrap(),
|
|
||||||
Some(git2::ObjectType::Commit),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let tagger = cfg.repo.signature().expect("could not get signature");
|
|
||||||
let message = String::new();
|
|
||||||
let force = true;
|
|
||||||
let tag = cfg
|
|
||||||
.repo
|
|
||||||
.tag(
|
|
||||||
&cfg.yaml.version.get_version(),
|
|
||||||
// "importantversion",
|
|
||||||
&target,
|
|
||||||
&tagger,
|
|
||||||
&message,
|
|
||||||
force,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let tag: git2::Tag = cfg.repo.find_tag(tag).unwrap();
|
|
||||||
Ok(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn push(_cfg: &Config) -> Result<()> {
|
|
||||||
// TODO: error handling
|
|
||||||
// TODO: maybe using git as lib is fancier?
|
|
||||||
Command::new("git").arg("push").status().unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_commit_sig(cfg: &Config) -> Result<String> {
|
|
||||||
// TODO: error handling
|
|
||||||
// TODO: maybe using git as cmd is fancier?
|
|
||||||
let target = cfg
|
|
||||||
.repo
|
|
||||||
.find_commit(cfg.repo.head().unwrap().target().unwrap())
|
|
||||||
.unwrap();
|
|
||||||
Ok(target.id().to_string())
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
pub mod changelog;
|
pub mod changelog;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod git;
|
|
||||||
pub mod publish;
|
pub mod publish;
|
||||||
pub mod release;
|
pub mod release;
|
||||||
pub mod serverapi;
|
pub mod serverapi;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use autocrate::{
|
||||||
error::*,
|
error::*,
|
||||||
publish::publish,
|
publish::publish,
|
||||||
release::release,
|
release::release,
|
||||||
serverapi::ApiCollection,
|
serverapi::init_servers,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -20,12 +20,10 @@ async fn main() -> Result<()> {
|
||||||
println!("{}", Changelog::build(&cfg)?);
|
println!("{}", Changelog::build(&cfg)?);
|
||||||
}
|
}
|
||||||
Commands::Release { .. } => {
|
Commands::Release { .. } => {
|
||||||
// TODO: check if repo is dirty and create a commit with a given option
|
let mut apis = init_servers(&cfg).await?;
|
||||||
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 {} => {
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
use crate::{
|
use crate::{config::Config, error::*, serverapi::ApiCollection};
|
||||||
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,
|
||||||
|
@ -18,40 +10,13 @@ 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: Error handling
|
// TODO: git tag
|
||||||
let tag = tag(cfg).await?.name().unwrap().to_string();
|
// 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?
|
|
||||||
|
|
||||||
let mut results = FuturesUnordered::new();
|
// TODO: release to each server
|
||||||
for api in apis.iter_mut() {
|
tag(cfg).await?;
|
||||||
// TODO: check that auth exists
|
todo!();
|
||||||
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: crate::changelog::Changelog::build(cfg)?.to_string(),
|
|
||||||
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
|
||||||
|
@ -60,3 +25,7 @@ pub async fn release(cfg: &Config, apis: &mut ApiCollection) -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn tag(_cfg: &Config) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +1,37 @@
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Api, Config},
|
config::{Api, Config},
|
||||||
error::*,
|
error::*,
|
||||||
serverapi::{PublishContext, ReleaseContext, ServerApi},
|
serverapi::{PublishContext, ReleaseContext, ServerApi},
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use forgejo_api;
|
|
||||||
use libpt::log::debug;
|
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{HeaderMap, HeaderValue},
|
header::{HeaderMap, HeaderValue},
|
||||||
Client, Url,
|
Client, Url,
|
||||||
};
|
};
|
||||||
use serde_json;
|
|
||||||
|
|
||||||
pub struct Forgejo {
|
pub struct Forgejo {
|
||||||
cfg: Api,
|
cfg: Api,
|
||||||
api_wrapper: forgejo_api::Forgejo,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Forgejo {
|
impl Forgejo {
|
||||||
pub async fn build(api: &Api) -> Result<Self> {
|
pub async fn build(api: &Api) -> Result<Self> {
|
||||||
let api_wrapper: forgejo_api::Forgejo = forgejo_api::Forgejo::new(
|
let mut headers: HeaderMap = HeaderMap::new();
|
||||||
forgejo_api::Auth::Token(&api.auth.clone().unwrap().pass.get_pass()?),
|
// may be left empty if we only do reads from publically accessible urls
|
||||||
api.endpoint.clone().unwrap(),
|
if api.auth.is_some() {
|
||||||
)
|
let _ = headers.insert(
|
||||||
.map_err(ServerApiError::from)?;
|
"Authorization",
|
||||||
|
HeaderValue::from_str(api.auth.clone().unwrap().pass.get_pass()?.as_str())
|
||||||
|
.map_err(ServerApiError::from)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let client = super::client_builder()
|
||||||
|
.default_headers(headers)
|
||||||
|
.build()
|
||||||
|
.map_err(ServerApiError::from)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
cfg: api.clone(),
|
cfg: api.clone(),
|
||||||
api_wrapper,
|
client,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,31 +39,45 @@ impl Forgejo {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ServerApi for Forgejo {
|
impl ServerApi for Forgejo {
|
||||||
async fn init(&mut self, _cfg: &Config) -> Result<()> {
|
async fn init(&mut self, _cfg: &Config) -> Result<()> {
|
||||||
Ok(())
|
todo!()
|
||||||
}
|
}
|
||||||
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 raw_url = format!(
|
||||||
forgejo_api::structs::CreateReleaseOption {
|
"{}/api/v1/repos/{}/{}/releases",
|
||||||
body: Some(rc.text),
|
self.cfg.endpoint, rc.username, rc.repository
|
||||||
draft: Some(rc.draft),
|
);
|
||||||
name: Some(rc.tag.clone()),
|
let url = Url::parse(&raw_url).map_err(ServerApiError::from)?;
|
||||||
prerelease: Some(rc.prerelease),
|
let body = format!(
|
||||||
tag_name: rc.tag,
|
r#"
|
||||||
target_commitish: Some(rc.commit_sig),
|
{{
|
||||||
};
|
"body": "{}",
|
||||||
self.api_wrapper
|
"draft": {},
|
||||||
.repo_create_release(&rc.username, &rc.repository, body)
|
"name": "{}",
|
||||||
|
"prerelease": {},
|
||||||
|
"tag_name": "{}",
|
||||||
|
"target_commitish": "{}"
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
rc.text, rc.draft, rc.tag, rc.prerelease, rc.tag, rc.commit_sig
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = self
|
||||||
|
.client
|
||||||
|
.post(url)
|
||||||
|
.body(body)
|
||||||
|
.build()
|
||||||
|
.map_err(ServerApiError::from)?;
|
||||||
|
let _response = self
|
||||||
|
.client
|
||||||
|
.execute(request)
|
||||||
.await
|
.await
|
||||||
.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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,18 +14,15 @@ 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 {
|
||||||
|
|
|
@ -14,18 +14,15 @@ 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 {
|
||||||
|
|
|
@ -14,18 +14,15 @@ 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 {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use reqwest::ClientBuilder;
|
use reqwest::ClientBuilder;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{self, ApiType, Config},
|
config::{ApiType, Config},
|
||||||
error::*,
|
error::*,
|
||||||
publish::PublishContext,
|
publish::PublishContext,
|
||||||
release::ReleaseContext,
|
release::ReleaseContext,
|
||||||
|
@ -20,6 +18,7 @@ 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).
|
||||||
|
@ -27,74 +26,35 @@ pub static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_P
|
||||||
#[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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// trait iimplementations for easy use of ApiCollection follow
|
pub async fn init_servers(cfg: &Config) -> Result<ApiCollection> {
|
||||||
impl IntoIterator for ApiCollection {
|
let mut collection: ApiCollection = ApiCollection::new();
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
for api in &cfg.yaml.api {
|
||||||
self.collection.into_iter()
|
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?));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
type Item = Box<dyn ServerApi>;
|
for api in collection.iter_mut() {
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
api.init(cfg).await?;
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for ApiCollection {
|
|
||||||
type Target = [Box<dyn ServerApi>];
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.collection[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for ApiCollection {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.collection[..]
|
|
||||||
}
|
}
|
||||||
|
Ok(collection)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue