feat: add schema rs

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2026-02-03 23:32:51 +01:00
parent 41d7b76685
commit 091c09450e
9 changed files with 378 additions and 21 deletions

View File

@@ -21,3 +21,5 @@ regex = "1.12.3"
toml = "0.9.11"
nodrift = "0.3.5"
octocrab = "0.49.5"
schemars = "1.2.1"
serde_json = "1.0.149"

View File

@@ -1,6 +1,10 @@
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use crate::{Config, State, cli::serve::ServeCommand};
use clap::{Parser, Subcommand};
use schemars::schema_for;
use tokio::io::AsyncWriteExt;
use crate::{Config, State, cli::serve::ServeCommand, forge_config::ForgeConfig};
mod serve;
@@ -17,6 +21,10 @@ struct Command {
#[derive(Subcommand)]
enum Commands {
Serve(ServeCommand),
Schema {
#[arg(long, env = "FE_SCHEMA_FILE")]
schema_file: PathBuf,
},
}
pub async fn execute() -> anyhow::Result<()> {
@@ -26,5 +34,17 @@ pub async fn execute() -> anyhow::Result<()> {
match cli.command.expect("a subcommand") {
Commands::Serve(cmd) => cmd.execute(&state).await,
Commands::Schema { schema_file } => {
let schema = schema_for!(ForgeConfig);
let output = serde_json::to_string_pretty(&schema)?;
let mut file = tokio::fs::File::create(&schema_file).await?;
file.write_all(output.as_bytes()).await?;
file.flush().await?;
Ok(())
}
}
}

View File

@@ -1,32 +1,67 @@
use regex::Regex;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ForgeConfig {
#[serde(flatten)]
pub forge_type: ForgeConfigType,
#[serde(default = "Filter::default")]
pub filter: Filter,
#[serde(default = "ForgeSchedule::default")]
pub schedule: ForgeSchedule,
#[serde(default = "allow_all")]
pub allow: Vec<ForgeRegex>,
#[serde(default = "Vec::new")]
pub deny: Vec<ForgeRegex>,
#[serde(default)]
pub policies: Policies,
}
#[derive(Clone, Debug, Default, Deserialize)]
/// # Filter
#[derive(Clone, Debug, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Filter {
/// # Allow
/// Allowed repositories, uses regex patterns
///
/// ```toml
/// allow = [".*", "^something-.*$"]
/// ````
#[serde(default = "allow_all")]
pub allow: Vec<ForgeRegex>,
/// # Deny
/// Denied repositories, uses regex patterns
///
/// ```toml
/// deny = [".*", "^something-.*$"]
/// ````
#[serde(default = "deny_default")]
pub deny: Vec<ForgeRegex>,
}
impl Default for Filter {
fn default() -> Self {
Self {
allow: allow_all(),
deny: deny_default(),
}
}
}
fn deny_default() -> Vec<ForgeRegex> {
vec![]
}
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Policies {
#[serde(default)]
pub squash_merge_only: PolicyOption,
}
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PolicyOption {
#[serde(default)]
@@ -39,7 +74,7 @@ fn allow_all() -> Vec<ForgeRegex> {
}]
}
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize, JsonSchema)]
pub enum ForgeSchedule {
#[serde(rename = "cron")]
Cron(String),
@@ -60,6 +95,16 @@ pub struct ForgeRegex {
regex: Regex,
}
impl JsonSchema for ForgeRegex {
fn schema_name() -> std::borrow::Cow<'static, str> {
"regex".into()
}
fn json_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::schema_for!(String)
}
}
impl From<ForgeRegex> for Regex {
fn from(value: ForgeRegex) -> Self {
value.regex
@@ -99,20 +144,28 @@ impl<'de> Deserialize<'de> for ForgeRegex {
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
// #[serde(tag = "type")]
pub enum ForgeConfigType {
/// GitHub forge
#[serde(rename = "github")]
GitHub {
/// Credentials
credentials: GitHubCredentials,
/// Organisation
organisation: String,
},
}
#[derive(Clone, Debug, Serialize, Deserialize)]
/// GitHubCredentials
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub enum GitHubCredentials {
/// Token
#[serde(rename = "token")]
Token(String),
/// Get token from env key
#[serde(rename = "token_env")]
TokenEnv(String),
}

View File

@@ -81,8 +81,8 @@ impl nodrift::Drifter for ForgeService {
client
.get_repositories(
organisation,
self.config.allow.iter().map(|a| a.into()).collect(),
self.config.deny.iter().map(|a| a.into()).collect(),
self.config.filter.allow.iter().map(|a| a.into()).collect(),
self.config.filter.deny.iter().map(|a| a.into()).collect(),
)
.await?;
@@ -95,7 +95,7 @@ impl nodrift::Drifter for ForgeService {
}
}
pub async fn load(path: &Path, state: &State) -> anyhow::Result<Vec<ForgeService>> {
pub async fn load(path: &Path, _state: &State) -> anyhow::Result<Vec<ForgeService>> {
if !path.exists() {
anyhow::bail!("config path does not exist: {}", path.display());
}