122
Cargo.lock
generated
122
Cargo.lock
generated
@@ -2,6 +2,15 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -204,6 +213,12 @@ version = "0.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@@ -225,12 +240,16 @@ name = "forge-enforce"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"clap",
|
"clap",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"notmad",
|
"notmad",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"toml",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@@ -358,6 +377,12 @@ dependencies = [
|
|||||||
"wasip2",
|
"wasip2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -452,6 +477,16 @@ dependencies = [
|
|||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@@ -523,9 +558,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
@@ -712,6 +747,35 @@ dependencies = [
|
|||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.20"
|
version = "0.37.20"
|
||||||
@@ -788,6 +852,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -947,6 +1020,45 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.9.11+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.5+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.0.6+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.0.6+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@@ -1351,6 +1463,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.7.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.51.0"
|
version = "0.51.0"
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ serde.workspace = true
|
|||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
tower-http.workspace = true
|
tower-http.workspace = true
|
||||||
notmad.workspace = true
|
notmad.workspace = true
|
||||||
|
tokio-util = "0.7.18"
|
||||||
|
async-trait = "0.1.89"
|
||||||
|
regex = "1.12.3"
|
||||||
|
toml = "0.9.11"
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use axum::{Router, extract::MatchedPath, http::Request, routing::get};
|
use crate::{State, forge_services::load, servehttp::ServeHttp};
|
||||||
use tower_http::trace::TraceLayer;
|
|
||||||
|
|
||||||
use crate::State;
|
|
||||||
|
|
||||||
#[derive(clap::Parser)]
|
#[derive(clap::Parser)]
|
||||||
pub struct ServeCommand {
|
pub struct ServeCommand {
|
||||||
@@ -13,37 +10,20 @@ pub struct ServeCommand {
|
|||||||
|
|
||||||
impl ServeCommand {
|
impl ServeCommand {
|
||||||
pub async fn execute(&self, state: &State) -> anyhow::Result<()> {
|
pub async fn execute(&self, state: &State) -> anyhow::Result<()> {
|
||||||
let app = Router::new()
|
let svcs = load(&state.config.config_dir, state).await?;
|
||||||
.route("/", get(root))
|
|
||||||
.with_state(state.clone())
|
|
||||||
.layer(
|
|
||||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
|
||||||
// Log the matched route's path (with placeholders not filled in).
|
|
||||||
// Use request.uri() or OriginalUri if you want the real path.
|
|
||||||
let matched_path = request
|
|
||||||
.extensions()
|
|
||||||
.get::<MatchedPath>()
|
|
||||||
.map(MatchedPath::as_str);
|
|
||||||
|
|
||||||
tracing::info_span!(
|
let mut mad = notmad::Mad::builder();
|
||||||
"http_request",
|
mad.add(ServeHttp {
|
||||||
method = ?request.method(),
|
host: self.host,
|
||||||
matched_path,
|
state: state.clone(),
|
||||||
some_other_field = tracing::field::Empty,
|
});
|
||||||
)
|
|
||||||
}), // ...
|
|
||||||
);
|
|
||||||
|
|
||||||
tracing::info!("listening on {}", self.host);
|
for svc in svcs {
|
||||||
let listener = tokio::net::TcpListener::bind(self.host).await.unwrap();
|
mad.add(svc);
|
||||||
axum::serve(listener, app.into_make_service())
|
}
|
||||||
.await
|
|
||||||
.unwrap();
|
mad.run().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn root() -> &'static str {
|
|
||||||
"Hello, nostore!"
|
|
||||||
}
|
|
||||||
|
|||||||
90
crates/forge-enforce/src/forge_config.rs
Normal file
90
crates/forge-enforce/src/forge_config.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
use regex::Regex;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct ForgeConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub forge_type: ForgeConfigType,
|
||||||
|
|
||||||
|
#[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)]
|
||||||
|
pub struct Policies {
|
||||||
|
#[serde(default)]
|
||||||
|
pub squash_merge_only: PolicyOption,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
|
pub struct PolicyOption {
|
||||||
|
#[serde(default)]
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allow_all() -> Vec<ForgeRegex> {
|
||||||
|
vec![ForgeRegex {
|
||||||
|
regex: Regex::new(".*").unwrap(),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub enum ForgeSchedule {
|
||||||
|
Cron {},
|
||||||
|
Interval {},
|
||||||
|
Once {},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ForgeSchedule {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Interval {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ForgeRegex {
|
||||||
|
regex: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for ForgeRegex {
|
||||||
|
type Target = Regex;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.regex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for ForgeRegex {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("ForgeRegex")
|
||||||
|
.field("regex", &self.regex)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ForgeRegex {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
Regex::new(&s)
|
||||||
|
.map(|regex| ForgeRegex { regex })
|
||||||
|
.map_err(serde::de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum ForgeConfigType {
|
||||||
|
#[serde(rename = "github")]
|
||||||
|
GitHub {},
|
||||||
|
}
|
||||||
58
crates/forge-enforce/src/forge_services.rs
Normal file
58
crates/forge-enforce/src/forge_services.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use notmad::{Component, MadError};
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
|
use crate::{State, forge_config::ForgeConfig};
|
||||||
|
|
||||||
|
pub struct ForgeService {
|
||||||
|
name: String,
|
||||||
|
config: ForgeConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Component for ForgeService {
|
||||||
|
fn name(&self) -> Option<String> {
|
||||||
|
Some(format!("forge-enforce/{}", self.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&self, cancellation_token: CancellationToken) -> Result<(), MadError> {
|
||||||
|
cancellation_token.cancelled().await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load(path: &Path, state: &State) -> anyhow::Result<Vec<ForgeService>> {
|
||||||
|
if !path.exists() {
|
||||||
|
anyhow::bail!("config path does not exist: {}", path.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = tokio::fs::read_dir(&path).await?;
|
||||||
|
let mut files = Vec::new();
|
||||||
|
while let Ok(Some(entry)) = res.next_entry().await {
|
||||||
|
if !entry.metadata().await?.is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = tokio::fs::read(entry.path()).await?;
|
||||||
|
|
||||||
|
let config: ForgeConfig = toml::from_slice(&content)?;
|
||||||
|
|
||||||
|
files.push((
|
||||||
|
entry
|
||||||
|
.file_name()
|
||||||
|
.into_string()
|
||||||
|
.expect("to have strings that are utf"),
|
||||||
|
config,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let services = files
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, config)| ForgeService { config, name })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Ok(services)
|
||||||
|
}
|
||||||
@@ -3,6 +3,11 @@ pub use state::*;
|
|||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
|
||||||
|
pub mod servehttp;
|
||||||
|
|
||||||
|
mod forge_config;
|
||||||
|
mod forge_services;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
|
|||||||
57
crates/forge-enforce/src/servehttp.rs
Normal file
57
crates/forge-enforce/src/servehttp.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::{Router, extract::MatchedPath, http::Request, routing::get};
|
||||||
|
use notmad::{Component, MadError};
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
use tower_http::trace::TraceLayer;
|
||||||
|
|
||||||
|
use crate::State;
|
||||||
|
|
||||||
|
pub struct ServeHttp {
|
||||||
|
pub host: SocketAddr,
|
||||||
|
pub state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Component for ServeHttp {
|
||||||
|
fn name(&self) -> Option<String> {
|
||||||
|
Some("forge-enforce/http".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&self, cancellation_token: CancellationToken) -> Result<(), MadError> {
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/", get(root))
|
||||||
|
.with_state(self.state.clone())
|
||||||
|
.layer(
|
||||||
|
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||||
|
// Log the matched route's path (with placeholders not filled in).
|
||||||
|
// Use request.uri() or OriginalUri if you want the real path.
|
||||||
|
let matched_path = request
|
||||||
|
.extensions()
|
||||||
|
.get::<MatchedPath>()
|
||||||
|
.map(MatchedPath::as_str);
|
||||||
|
|
||||||
|
tracing::info_span!(
|
||||||
|
"http_request",
|
||||||
|
method = ?request.method(),
|
||||||
|
matched_path,
|
||||||
|
some_other_field = tracing::field::Empty,
|
||||||
|
)
|
||||||
|
}), // ...
|
||||||
|
);
|
||||||
|
|
||||||
|
tracing::info!("listening on {}", self.host);
|
||||||
|
let listener = tokio::net::TcpListener::bind(self.host).await.unwrap();
|
||||||
|
axum::serve(listener, app.into_make_service())
|
||||||
|
.with_graceful_shutdown(async move { cancellation_token.cancelled().await })
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn root() -> &'static str {
|
||||||
|
"Hello, forge-enforce!"
|
||||||
|
}
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(clap::Parser, Clone)]
|
#[derive(clap::Parser, Clone)]
|
||||||
pub struct Config {}
|
pub struct Config {
|
||||||
|
#[arg(long, env = "FE_CONFIG_DIR")]
|
||||||
|
pub config_dir: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|||||||
5
templates/fe/configs/github.com.toml
Normal file
5
templates/fe/configs/github.com.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
type = "github"
|
||||||
|
deny = [".*"]
|
||||||
|
|
||||||
|
[policies]
|
||||||
|
squash_merge_only.enabled = true
|
||||||
Reference in New Issue
Block a user