WIP
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,3 +1,2 @@
|
||||
_secrets.yaml filter=git-crypt diff=git-crypt
|
||||
*.key filter=git-crypt diff=git-crypt
|
||||
secrets.yaml filter=git-crypt diff=git-crypt
|
||||
*.agekey filter=git-crypt diff=git-crypt
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
_secrets.yaml
|
||||
secrets.yaml
|
||||
*.agekey
|
||||
*.key
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
server:
|
||||
tftpIp: 192.168.1.1
|
||||
httpUrl: http://192.168.1.1:8000
|
||||
|
||||
tailscale:
|
||||
loginServer: https://headscale.huizinga.dev
|
||||
1
crete/.gitignore
vendored
Normal file
1
crete/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target/
|
||||
355
crete/Cargo.lock
generated
Normal file
355
crete/Cargo.lock
generated
Normal file
@@ -0,0 +1,355 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "crete"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"minijinja",
|
||||
"optional_struct",
|
||||
"regress",
|
||||
"repo_path_lib",
|
||||
"schemars",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "memo-map"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b"
|
||||
|
||||
[[package]]
|
||||
name = "minijinja"
|
||||
version = "2.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c54f3bcc034dd74496b5ca929fd0b710186672d5ff0b0f255a9ceb259042ece"
|
||||
dependencies = [
|
||||
"memo-map",
|
||||
"self_cell",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "optional_struct"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14199f59efce6ed2c5854f0abc725c32eedfbd02c6ef82c9733c726f3fc6dc91"
|
||||
dependencies = [
|
||||
"optional_struct_macro",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "optional_struct_macro"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5eba042d9efe5e108e0df9ce2f85c540fc4f94f41c6821cbcf70ed47c1221da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regress"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "repo_path_lib"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d4f67a73ddc563e6fb8bac035c9c76de29af2e4ad6031dc49bb5a2cd0742fca"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"ref-cast",
|
||||
"schemars_derive",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "self_cell"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive_internals"
|
||||
version = "0.29.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
17
crete/Cargo.toml
Normal file
17
crete/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "crete"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
default-run = "crete"
|
||||
|
||||
[dependencies]
|
||||
minijinja = { version = "2.16.0", features = ["loader"] }
|
||||
optional_struct = "0.5.2"
|
||||
regress = "0.10.5"
|
||||
repo_path_lib = "1.2.4"
|
||||
schemars = { version = "1.2.1", features = ["semver1"] }
|
||||
semver = { version = "1.0.27", features = ["serde"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.149"
|
||||
serde_yaml = "0.9.34"
|
||||
walkdir = "2.5.0"
|
||||
23
crete/src/bin/schemas.rs
Normal file
23
crete/src/bin/schemas.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
use crete::{cluster::Cluster, node::OptionalNode};
|
||||
use repo_path_lib::repo_dir;
|
||||
use schemars::{JsonSchema, schema_for};
|
||||
|
||||
fn write<T>(name: &str)
|
||||
where
|
||||
T: JsonSchema,
|
||||
{
|
||||
let mut path = repo_dir().join("schemas").join(name);
|
||||
path.add_extension("json");
|
||||
let mut file = File::create(path).unwrap();
|
||||
|
||||
let schema = serde_json::to_string_pretty(&schema_for!(T)).unwrap();
|
||||
file.write_all(schema.as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
// TODO: Create directory if it does not exist
|
||||
fn main() {
|
||||
write::<Cluster>("cluster");
|
||||
write::<OptionalNode>("node");
|
||||
}
|
||||
69
crete/src/cluster.rs
Normal file
69
crete/src/cluster.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
base_dir,
|
||||
node::{Node, OptionalNode},
|
||||
patch::{PatchEnv, Patches},
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize, JsonSchema, Clone)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Base {
|
||||
#[serde(default)]
|
||||
pub(crate) kernel_args: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub(crate) patches: Patches,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Version {
|
||||
kubernetes: semver::Version,
|
||||
talos: semver::Version,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub enum ClusterEnv {
|
||||
Production,
|
||||
Staging,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Cluster {
|
||||
#[serde(skip_deserializing)]
|
||||
name: String,
|
||||
version: Version,
|
||||
nodes: Vec<String>,
|
||||
cluster_env: ClusterEnv,
|
||||
control_plane_ip: Ipv4Addr,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub(crate) default: OptionalNode,
|
||||
#[serde(skip_serializing)]
|
||||
pub(crate) base: Base,
|
||||
// pub secrets_file: PathBuf,
|
||||
}
|
||||
|
||||
impl Cluster {
|
||||
pub fn get(cluster_name: &str) -> Self {
|
||||
let mut path = base_dir().join("clusters").join(cluster_name);
|
||||
path.add_extension("yaml");
|
||||
let content = std::fs::read_to_string(path).unwrap();
|
||||
|
||||
let mut cluster: Self = serde_yaml::from_str(&content).unwrap();
|
||||
cluster.name = cluster_name.to_string();
|
||||
|
||||
cluster
|
||||
}
|
||||
|
||||
pub fn nodes(&self, env: PatchEnv) -> Vec<Node> {
|
||||
self.nodes
|
||||
.iter()
|
||||
.map(|node_name| Node::get(node_name, &env, self))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
11
crete/src/lib.rs
Normal file
11
crete/src/lib.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
pub mod cluster;
|
||||
pub mod node;
|
||||
pub mod patch;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use repo_path_lib::repo_dir;
|
||||
|
||||
fn base_dir() -> PathBuf {
|
||||
repo_dir().join("talos")
|
||||
}
|
||||
10
crete/src/main.rs
Normal file
10
crete/src/main.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
use crete::{cluster::Cluster, patch::PatchEnv};
|
||||
|
||||
fn main() {
|
||||
let cluster = Cluster::get("testing");
|
||||
|
||||
let patches = PatchEnv::default();
|
||||
let nodes = cluster.nodes(patches);
|
||||
|
||||
println!("{:#?}", nodes);
|
||||
}
|
||||
174
crete/src/node.rs
Normal file
174
crete/src/node.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use optional_struct::{Applicable, optional_struct};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
use crate::{
|
||||
base_dir,
|
||||
cluster::Cluster,
|
||||
patch::{OptionalPatches, PatchEnv, Patches},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum NodeType {
|
||||
Worker,
|
||||
ControlPlane,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, Copy, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum NodeArch {
|
||||
Amd64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct FileSecret {
|
||||
file: String,
|
||||
}
|
||||
|
||||
fn deserialize_secret_file<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let secret: FileSecret = Deserialize::deserialize(deserializer)?;
|
||||
let path = base_dir().join("secrets").join(secret.file);
|
||||
let content = std::fs::read_to_string(path).unwrap().trim().to_owned();
|
||||
|
||||
Ok(content)
|
||||
}
|
||||
|
||||
fn file_secret_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
|
||||
generator.subschema_for::<FileSecret>()
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase", untagged)]
|
||||
enum Secret {
|
||||
String(String),
|
||||
#[serde(deserialize_with = "deserialize_secret_file")]
|
||||
#[schemars(schema_with = "file_secret_schema")]
|
||||
File(String),
|
||||
}
|
||||
|
||||
impl From<Secret> for String {
|
||||
fn from(value: Secret) -> Self {
|
||||
match value {
|
||||
Secret::String(value) | Secret::File(value) => value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[optional_struct]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct Tailscale {
|
||||
auth_key: Secret,
|
||||
advertise_routes: bool,
|
||||
#[serde(default)]
|
||||
server: Option<String>,
|
||||
}
|
||||
|
||||
#[optional_struct]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct Network {
|
||||
interface: String,
|
||||
ip: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
dns: [Ipv4Addr; 2],
|
||||
#[optional_rename(OptionalTailscale)]
|
||||
#[optional_wrap]
|
||||
tailscale: Tailscale,
|
||||
}
|
||||
|
||||
#[optional_struct]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct Install {
|
||||
auto: bool,
|
||||
disk: String,
|
||||
serial: Option<String>,
|
||||
}
|
||||
|
||||
#[optional_struct]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Node {
|
||||
#[serde(skip_deserializing)]
|
||||
hostname: String,
|
||||
arch: NodeArch,
|
||||
r#type: NodeType,
|
||||
#[optional_rename(OptionalNetwork)]
|
||||
#[optional_wrap]
|
||||
network: Network,
|
||||
ntp: String,
|
||||
#[optional_rename(OptionalInstall)]
|
||||
#[optional_wrap]
|
||||
install: Install,
|
||||
kernel_args: Vec<String>,
|
||||
#[optional_rename(OptionalPatches)]
|
||||
#[optional_wrap]
|
||||
pub(crate) patches: Patches,
|
||||
// TODO: Per machine patches, append to global list of patches
|
||||
// Any patches are specified under default they will get overridden
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn get(node_name: &str, env: &PatchEnv, cluster: &Cluster) -> Self {
|
||||
let mut path = base_dir().join("nodes").join(node_name);
|
||||
let named = OptionalNode {
|
||||
hostname: Some(
|
||||
path.file_name()
|
||||
.expect("Path should be valid")
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
),
|
||||
..OptionalNode::default()
|
||||
};
|
||||
|
||||
path.add_extension("yaml");
|
||||
let content = std::fs::read_to_string(path).unwrap();
|
||||
|
||||
let node: OptionalNode = serde_yaml::from_str(&content).unwrap();
|
||||
|
||||
// We want all vectors to be empty vectors by default
|
||||
// Sadly we have to this manually
|
||||
// TODO: Find a better way of doing this
|
||||
let default = OptionalNode {
|
||||
patches: Some(OptionalPatches {
|
||||
all: Some(vec![]),
|
||||
control_plane: Some(vec![]),
|
||||
}),
|
||||
kernel_args: vec![].into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Combine all the optional node parts into complete struct
|
||||
let mut node: Node = default
|
||||
// Apply cluster default settings
|
||||
.apply(cluster.default.clone())
|
||||
// Apply hostname based on filename
|
||||
.apply(named)
|
||||
// Override node specific settings
|
||||
.apply(node)
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Prepend the cluster base values
|
||||
let mut kernel_args = cluster.base.kernel_args.clone();
|
||||
kernel_args.extend(node.kernel_args);
|
||||
node.kernel_args = kernel_args;
|
||||
|
||||
let patches = cluster.base.patches.clone().extend(node.patches);
|
||||
node.patches = patches;
|
||||
|
||||
// Render patches
|
||||
node.patches = node.patches.clone().render(env, cluster, &node);
|
||||
|
||||
node
|
||||
}
|
||||
}
|
||||
113
crete/src/patch.rs
Normal file
113
crete/src/patch.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use std::{net::Ipv4Addr, str::FromStr};
|
||||
|
||||
use minijinja::{Environment, context};
|
||||
use optional_struct::optional_struct;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use crate::{base_dir, cluster::Cluster, node::Node};
|
||||
|
||||
pub struct PatchEnv<'e>(Environment<'e>);
|
||||
|
||||
impl<'e> Default for PatchEnv<'e> {
|
||||
fn default() -> Self {
|
||||
let mut env = Environment::new();
|
||||
env.set_undefined_behavior(minijinja::UndefinedBehavior::Strict);
|
||||
|
||||
let dir = base_dir().join("patches");
|
||||
for entry in WalkDir::new(&dir)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.metadata().unwrap().is_file())
|
||||
{
|
||||
let source = std::fs::read_to_string(entry.path()).unwrap();
|
||||
let name = entry
|
||||
.path()
|
||||
.strip_prefix(&dir)
|
||||
.unwrap()
|
||||
.with_extension("")
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
|
||||
env.add_filter("to_prefix", |netmask: String| {
|
||||
let netmask = Ipv4Addr::from_str(&netmask).map_err(|err| {
|
||||
minijinja::Error::new(minijinja::ErrorKind::InvalidOperation, err.to_string())
|
||||
})?;
|
||||
let mask = netmask.to_bits();
|
||||
let prefix = mask.leading_ones();
|
||||
|
||||
if mask.checked_shl(prefix).unwrap_or(0) == 0 {
|
||||
Ok(prefix as u8)
|
||||
} else {
|
||||
Err(minijinja::Error::new(
|
||||
minijinja::ErrorKind::InvalidOperation,
|
||||
"invalid IP prefix length",
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
env.add_template_owned(name, source).unwrap();
|
||||
}
|
||||
|
||||
Self(env)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq)]
|
||||
#[serde(untagged)]
|
||||
pub(crate) enum Patch {
|
||||
Name(String),
|
||||
#[serde(skip)]
|
||||
Resolved(serde_yaml::Value),
|
||||
}
|
||||
|
||||
#[optional_struct]
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone, PartialEq, Eq, Default)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
pub struct Patches {
|
||||
pub(crate) all: Vec<Patch>,
|
||||
pub(crate) control_plane: Vec<Patch>,
|
||||
}
|
||||
|
||||
fn render(patches: Vec<Patch>, env: &PatchEnv, cluster: &Cluster, node: &Node) -> Vec<Patch> {
|
||||
patches
|
||||
.into_iter()
|
||||
.map(|patch| {
|
||||
if let Patch::Name(name) = patch {
|
||||
Patch::Resolved(
|
||||
serde_yaml::from_str(
|
||||
&env.0
|
||||
.get_template(&name)
|
||||
.unwrap()
|
||||
.render(context!(node, cluster))
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
patch
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
impl Patches {
|
||||
pub(crate) fn extend(mut self, other: Self) -> Self {
|
||||
self.all.extend(other.all);
|
||||
self.control_plane.extend(other.control_plane);
|
||||
|
||||
Self {
|
||||
all: self.all,
|
||||
control_plane: self.control_plane,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render(self, env: &PatchEnv, cluster: &Cluster, node: &Node) -> Self {
|
||||
Self {
|
||||
all: render(self.all.clone(), env, cluster, node),
|
||||
control_plane: render(self.control_plane.clone(), env, cluster, node),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
schematicId: !schematic default
|
||||
arch: amd64
|
||||
talosVersion: 1.11.3
|
||||
kubernetesVersion: 1.34.1
|
||||
kernelArgs:
|
||||
- talos.platform=metal
|
||||
- console=tty0
|
||||
- init_on_alloc=1
|
||||
- init_on_free=1
|
||||
- slab_nomerge
|
||||
- pti=on
|
||||
- consoleblank=0
|
||||
- nvme_core.io_timeout=4294967295
|
||||
- printk.devkmsg=on
|
||||
- selinux=1
|
||||
- lockdown=confidentiality
|
||||
extraKernelArgs: []
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
ntp: nl.pool.ntp.org
|
||||
install: true
|
||||
autoInstall: false
|
||||
advertiseRoutes: true
|
||||
patches:
|
||||
- !patch hostname
|
||||
- !patch install-disk
|
||||
- !patch network
|
||||
- !patch vip
|
||||
- !patch tailscale
|
||||
- !patch cilium
|
||||
- !patch spegel
|
||||
- !patch longhorn
|
||||
- !patch longhorn-user-volume
|
||||
- !patch local-path-provisioner-volume
|
||||
- !patch limit-ephemeral
|
||||
- !patch metrics
|
||||
patchesControlPlane:
|
||||
- !patch allow-control-plane-workloads
|
||||
- !patch sops
|
||||
- !patch cluster-variables
|
||||
- !patch metrics-cluster
|
||||
- !patch gateway-api
|
||||
@@ -1,10 +0,0 @@
|
||||
netmask: 255.255.255.0
|
||||
gateway: 192.168.1.1
|
||||
installDisk: /dev/vda
|
||||
autoInstall: true
|
||||
cluster:
|
||||
name: testing
|
||||
production: false
|
||||
controlPlaneIp: 192.168.1.100
|
||||
secretsFile: !realpath _secrets.yaml
|
||||
sopsKeyFile: !realpath _age.agekey
|
||||
@@ -1,4 +0,0 @@
|
||||
serial: talos-vm
|
||||
interface: enp1s0
|
||||
ip: 192.168.1.2
|
||||
type: "controlplane"
|
||||
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
netmask: 255.255.252.0
|
||||
gateway: 10.0.0.1
|
||||
installDisk: /dev/sda
|
||||
cluster:
|
||||
name: titan
|
||||
production: true
|
||||
controlPlaneIp: 10.0.2.1
|
||||
secretsFile: !realpath _secrets.yaml
|
||||
sopsKeyFile: !realpath _age.agekey
|
||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
||||
serial: 5CZ7NX2
|
||||
interface: enp2s0
|
||||
ip: 10.0.0.202
|
||||
type: "controlplane"
|
||||
@@ -1,4 +0,0 @@
|
||||
serial: F3PKRH2
|
||||
interface: enp3s0
|
||||
ip: 10.0.0.201
|
||||
type: "controlplane"
|
||||
@@ -1,4 +0,0 @@
|
||||
serial: J33CHY2
|
||||
interface: enp2s0
|
||||
ip: 10.0.0.203
|
||||
type: "controlplane"
|
||||
@@ -1,8 +0,0 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json
|
||||
apiVersion: v1alpha1
|
||||
kind: ExtensionServiceConfig
|
||||
name: tailscale
|
||||
environment:
|
||||
- TS_AUTHKEY={{ config.tailscale.authKey }}
|
||||
- TS_EXTRA_ARGS=--login-server {{ config.tailscale.loginServer }} --advertise-tags=tag:cluster-{{ node.cluster.name }}
|
||||
- TS_ROUTES={% if node.advertiseRoutes -%} {{ helper.tailscale_subnet(node.gateway, node.netmask) }} {%- endif %}
|
||||
@@ -1,17 +0,0 @@
|
||||
[project]
|
||||
name = "bootstrap"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
# readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"gitpython==3.1.45",
|
||||
"jinja2==3.1.6",
|
||||
"mergedeep==1.3.4",
|
||||
"netaddr==1.3.0",
|
||||
"pydantic>=2.12.5",
|
||||
"pydantic-extra-types>=2.11.0",
|
||||
"pyyaml==6.0.3",
|
||||
"requests==2.32.5",
|
||||
"semver>=3.0.4",
|
||||
]
|
||||
354
schemas/cluster.json
Normal file
354
schemas/cluster.json
Normal file
@@ -0,0 +1,354 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "Cluster",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"base": {
|
||||
"$ref": "#/$defs/Base",
|
||||
"writeOnly": true
|
||||
},
|
||||
"clusterEnv": {
|
||||
"$ref": "#/$defs/ClusterEnv"
|
||||
},
|
||||
"controlPlaneIp": {
|
||||
"type": "string",
|
||||
"format": "ipv4"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/$defs/OptionalNode",
|
||||
"writeOnly": true
|
||||
},
|
||||
"nodes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"$ref": "#/$defs/Version"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"version",
|
||||
"nodes",
|
||||
"clusterEnv",
|
||||
"controlPlaneIp",
|
||||
"base"
|
||||
],
|
||||
"$defs": {
|
||||
"Base": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kernelArgs": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"patches": {
|
||||
"$ref": "#/$defs/Patches",
|
||||
"default": {
|
||||
"all": [],
|
||||
"controlPlane": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ClusterEnv": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"production",
|
||||
"staging"
|
||||
]
|
||||
},
|
||||
"FileSecret": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file"
|
||||
]
|
||||
},
|
||||
"NodeArch": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"amd64"
|
||||
]
|
||||
},
|
||||
"NodeType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"worker",
|
||||
"controlPlane"
|
||||
]
|
||||
},
|
||||
"OptionalInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auto": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"disk": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"serial": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalNetwork": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dns": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "ipv4"
|
||||
},
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"gateway": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "ipv4"
|
||||
},
|
||||
"interface": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"ip": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "ipv4"
|
||||
},
|
||||
"netmask": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "ipv4"
|
||||
},
|
||||
"tailscale": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalTailscale"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalNode": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arch": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NodeArch"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"install": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalInstall"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"kernelArgs": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalNetwork"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ntp": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"patches": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalPatches"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NodeType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalPatches": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/$defs/Patch"
|
||||
}
|
||||
},
|
||||
"controlPlane": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/$defs/Patch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalTailscale": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"advertiseRoutes": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"authKey": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/Secret"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"server": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Patch": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Patches": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/Patch"
|
||||
}
|
||||
},
|
||||
"controlPlane": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/Patch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"all",
|
||||
"controlPlane"
|
||||
]
|
||||
},
|
||||
"Secret": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/FileSecret"
|
||||
}
|
||||
]
|
||||
},
|
||||
"SemVer": {
|
||||
"type": "string",
|
||||
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"
|
||||
},
|
||||
"Version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kubernetes": {
|
||||
"$ref": "#/$defs/SemVer"
|
||||
},
|
||||
"talos": {
|
||||
"$ref": "#/$defs/SemVer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"kubernetes",
|
||||
"talos"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
248
schemas/node.json
Normal file
248
schemas/node.json
Normal file
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"title": "OptionalNode",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"arch": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NodeArch"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"install": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalInstall"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"kernelArgs": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"network": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalNetwork"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ntp": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"patches": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalPatches"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/NodeType"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"FileSecret": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file"
|
||||
]
|
||||
},
|
||||
"NodeArch": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"amd64"
|
||||
]
|
||||
},
|
||||
"NodeType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"worker",
|
||||
"controlPlane"
|
||||
]
|
||||
},
|
||||
"OptionalInstall": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auto": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"disk": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"serial": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalNetwork": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dns": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "ipv4"
|
||||
},
|
||||
"maxItems": 2,
|
||||
"minItems": 2
|
||||
},
|
||||
"gateway": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "ipv4"
|
||||
},
|
||||
"interface": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"ip": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "ipv4"
|
||||
},
|
||||
"netmask": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "ipv4"
|
||||
},
|
||||
"tailscale": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/OptionalTailscale"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalPatches": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"all": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/$defs/Patch"
|
||||
}
|
||||
},
|
||||
"controlPlane": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "#/$defs/Patch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"OptionalTailscale": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"advertiseRoutes": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"authKey": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/Secret"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"server": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"Patch": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Secret": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/FileSecret"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
secrets.yaml
BIN
secrets.yaml
Binary file not shown.
@@ -1,62 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import RootModel
|
||||
|
||||
from . import (
|
||||
block,
|
||||
extensions,
|
||||
hardware,
|
||||
network,
|
||||
runtime,
|
||||
security,
|
||||
siderolink,
|
||||
v1alpha1,
|
||||
)
|
||||
|
||||
|
||||
class Model(
|
||||
RootModel[
|
||||
block.ExistingVolumeConfigV1Alpha1
|
||||
| block.RawVolumeConfigV1Alpha1
|
||||
| block.SwapVolumeConfigV1Alpha1
|
||||
| block.UserVolumeConfigV1Alpha1
|
||||
| block.VolumeConfigV1Alpha1
|
||||
| block.ZswapConfigV1Alpha1
|
||||
| extensions.ServiceConfigV1Alpha1
|
||||
| hardware.PCIDriverRebindConfigV1Alpha1
|
||||
| network.DefaultActionConfigV1Alpha1
|
||||
| network.EthernetConfigV1Alpha1
|
||||
| network.KubespanEndpointsConfigV1Alpha1
|
||||
| network.RuleConfigV1Alpha1
|
||||
| runtime.EventSinkV1Alpha1
|
||||
| runtime.KmsgLogV1Alpha1
|
||||
| runtime.WatchdogTimerV1Alpha1
|
||||
| security.TrustedRootsConfigV1Alpha1
|
||||
| siderolink.ConfigV1Alpha1
|
||||
| v1alpha1.Config
|
||||
]
|
||||
):
|
||||
root: (
|
||||
block.ExistingVolumeConfigV1Alpha1
|
||||
| block.RawVolumeConfigV1Alpha1
|
||||
| block.SwapVolumeConfigV1Alpha1
|
||||
| block.UserVolumeConfigV1Alpha1
|
||||
| block.VolumeConfigV1Alpha1
|
||||
| block.ZswapConfigV1Alpha1
|
||||
| extensions.ServiceConfigV1Alpha1
|
||||
| hardware.PCIDriverRebindConfigV1Alpha1
|
||||
| network.DefaultActionConfigV1Alpha1
|
||||
| network.EthernetConfigV1Alpha1
|
||||
| network.KubespanEndpointsConfigV1Alpha1
|
||||
| network.RuleConfigV1Alpha1
|
||||
| runtime.EventSinkV1Alpha1
|
||||
| runtime.KmsgLogV1Alpha1
|
||||
| runtime.WatchdogTimerV1Alpha1
|
||||
| security.TrustedRootsConfigV1Alpha1
|
||||
| siderolink.ConfigV1Alpha1
|
||||
| v1alpha1.Config
|
||||
)
|
||||
@@ -1,403 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class DiskSelector(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
match: str | None = Field(
|
||||
None,
|
||||
description='The Common Expression Language (CEL) expression to match the disk.\n',
|
||||
title='match',
|
||||
)
|
||||
|
||||
|
||||
class EncryptionKeyKMS(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
endpoint: str | None = Field(
|
||||
None, description='KMS endpoint to Seal/Unseal the key.\n', title='endpoint'
|
||||
)
|
||||
|
||||
|
||||
class EncryptionKeyNodeID(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
|
||||
|
||||
class EncryptionKeyStatic(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
passphrase: str | None = Field(
|
||||
None, description='Defines the static passphrase value.\n', title='passphrase'
|
||||
)
|
||||
|
||||
|
||||
class EncryptionKeyTPM(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
checkSecurebootStatusOnEnroll: bool | None = Field(
|
||||
None,
|
||||
description='Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\n',
|
||||
title='checkSecurebootStatusOnEnroll',
|
||||
)
|
||||
|
||||
|
||||
class Provider(Enum):
|
||||
luks2 = 'luks2'
|
||||
|
||||
|
||||
class Cipher(Enum):
|
||||
aes_xts_plain64 = 'aes-xts-plain64'
|
||||
xchacha12_aes_adiantum_plain64 = 'xchacha12,aes-adiantum-plain64'
|
||||
xchacha20_aes_adiantum_plain64 = 'xchacha20,aes-adiantum-plain64'
|
||||
|
||||
|
||||
class Options(Enum):
|
||||
no_read_workqueue = 'no_read_workqueue'
|
||||
no_write_workqueue = 'no_write_workqueue'
|
||||
same_cpu_crypt = 'same_cpu_crypt'
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
ExistingVolumeConfig = 'ExistingVolumeConfig'
|
||||
|
||||
|
||||
class Type(Enum):
|
||||
ext4 = 'ext4'
|
||||
xfs = 'xfs'
|
||||
|
||||
|
||||
class FilesystemSpec(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
type: Type | None = Field(
|
||||
None, description='Filesystem type. Default is xfs.\n', title='type'
|
||||
)
|
||||
projectQuotaSupport: bool | None = Field(
|
||||
None,
|
||||
description='Enables project quota support, valid only for ‘xfs’ filesystem.\n\nNote: changing this value might require a full remount of the filesystem.\n',
|
||||
title='projectQuotaSupport',
|
||||
)
|
||||
|
||||
|
||||
class MountSpec(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
readOnly: bool | None = Field(
|
||||
None, description='Mount the volume read-only.\n', title='readOnly'
|
||||
)
|
||||
|
||||
|
||||
class ProvisioningSpec(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
diskSelector: DiskSelector | None = Field(
|
||||
None, description='The disk selector expression.\n', title='diskSelector'
|
||||
)
|
||||
grow: bool | None = Field(
|
||||
None,
|
||||
description='Should the volume grow to the size of the disk (if possible).\n',
|
||||
title='grow',
|
||||
)
|
||||
minSize: str | None = Field(
|
||||
None,
|
||||
description='The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\n',
|
||||
title='minSize',
|
||||
)
|
||||
maxSize: str | None = Field(
|
||||
None,
|
||||
description='The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\n',
|
||||
title='maxSize',
|
||||
)
|
||||
|
||||
|
||||
class KindModel(Enum):
|
||||
RawVolumeConfig = 'RawVolumeConfig'
|
||||
|
||||
|
||||
class KindModel1(Enum):
|
||||
SwapVolumeConfig = 'SwapVolumeConfig'
|
||||
|
||||
|
||||
class KindModel2(Enum):
|
||||
UserVolumeConfig = 'UserVolumeConfig'
|
||||
|
||||
|
||||
class KindModel3(Enum):
|
||||
VolumeConfig = 'VolumeConfig'
|
||||
|
||||
|
||||
class VolumeSelector(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
match: str | None = Field(
|
||||
None,
|
||||
description='The Common Expression Language (CEL) expression to match the volume.\n',
|
||||
title='match',
|
||||
)
|
||||
|
||||
|
||||
class KindModel4(Enum):
|
||||
ZswapConfig = 'ZswapConfig'
|
||||
|
||||
|
||||
class ZswapConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel4 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
maxPoolPercent: int | None = Field(
|
||||
None,
|
||||
description='The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\n',
|
||||
title='maxPoolPercent',
|
||||
)
|
||||
shrinkerEnabled: bool | None = Field(
|
||||
None,
|
||||
description='Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\n',
|
||||
title='shrinkerEnabled',
|
||||
)
|
||||
|
||||
|
||||
class EncryptionKey(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
slot: int | None = Field(
|
||||
None, description='Key slot number for LUKS2 encryption.\n', title='slot'
|
||||
)
|
||||
static: EncryptionKeyStatic | None = Field(
|
||||
None,
|
||||
description='Key which value is stored in the configuration file.\n',
|
||||
title='static',
|
||||
)
|
||||
nodeID: EncryptionKeyNodeID | None = Field(
|
||||
None,
|
||||
description='Deterministically generated key from the node UUID and PartitionLabel.\n',
|
||||
title='nodeID',
|
||||
)
|
||||
kms: EncryptionKeyKMS | None = Field(
|
||||
None, description='KMS managed encryption key.\n', title='kms'
|
||||
)
|
||||
tpm: EncryptionKeyTPM | None = Field(
|
||||
None, description='Enable TPM based disk encryption.\n', title='tpm'
|
||||
)
|
||||
lockToState: bool | None = Field(
|
||||
None,
|
||||
description='Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\n',
|
||||
title='lockToState',
|
||||
)
|
||||
|
||||
|
||||
class EncryptionSpec(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
provider: Provider | None = Field(
|
||||
None,
|
||||
description='Encryption provider to use for the encryption.\n',
|
||||
title='provider',
|
||||
)
|
||||
keys: list[EncryptionKey] | None = Field(
|
||||
None,
|
||||
description='Defines the encryption keys generation and storage method.\n',
|
||||
title='keys',
|
||||
)
|
||||
cipher: Cipher | None = Field(
|
||||
None,
|
||||
description='Cipher to use for the encryption. Depends on the encryption provider.\n',
|
||||
title='cipher',
|
||||
)
|
||||
keySize: int | None = Field(
|
||||
None, description='Defines the encryption key length.\n', title='keySize'
|
||||
)
|
||||
blockSize: int | None = Field(
|
||||
None, description='Defines the encryption sector size.\n', title='blockSize'
|
||||
)
|
||||
options: Options | None = Field(
|
||||
None,
|
||||
description='Additional –perf parameters for the LUKS2 encryption.\n',
|
||||
title='options',
|
||||
)
|
||||
|
||||
|
||||
class RawVolumeConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str | None = Field(
|
||||
None,
|
||||
description='Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n',
|
||||
title='name',
|
||||
)
|
||||
provisioning: ProvisioningSpec | None = Field(
|
||||
None,
|
||||
description='The provisioning describes how the volume is provisioned.\n',
|
||||
title='provisioning',
|
||||
)
|
||||
encryption: EncryptionSpec | None = Field(
|
||||
None,
|
||||
description='The encryption describes how the volume is encrypted.\n',
|
||||
title='encryption',
|
||||
)
|
||||
|
||||
|
||||
class SwapVolumeConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel1 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str | None = Field(
|
||||
None,
|
||||
description='Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n',
|
||||
title='name',
|
||||
)
|
||||
provisioning: ProvisioningSpec | None = Field(
|
||||
None,
|
||||
description='The provisioning describes how the volume is provisioned.\n',
|
||||
title='provisioning',
|
||||
)
|
||||
encryption: EncryptionSpec | None = Field(
|
||||
None,
|
||||
description='The encryption describes how the volume is encrypted.\n',
|
||||
title='encryption',
|
||||
)
|
||||
|
||||
|
||||
class UserVolumeConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel2 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str | None = Field(
|
||||
None,
|
||||
description='Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n',
|
||||
title='name',
|
||||
)
|
||||
provisioning: ProvisioningSpec | None = Field(
|
||||
None,
|
||||
description='The provisioning describes how the volume is provisioned.\n',
|
||||
title='provisioning',
|
||||
)
|
||||
filesystem: FilesystemSpec | None = Field(
|
||||
None,
|
||||
description='The filesystem describes how the volume is formatted.\n',
|
||||
title='filesystem',
|
||||
)
|
||||
encryption: EncryptionSpec | None = Field(
|
||||
None,
|
||||
description='The encryption describes how the volume is encrypted.\n',
|
||||
title='encryption',
|
||||
)
|
||||
|
||||
|
||||
class VolumeConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel3 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str | None = Field(None, description='Name of the volume.\n', title='name')
|
||||
provisioning: ProvisioningSpec | None = Field(
|
||||
None,
|
||||
description='The provisioning describes how the volume is provisioned.\n',
|
||||
title='provisioning',
|
||||
)
|
||||
encryption: EncryptionSpec | None = Field(
|
||||
None,
|
||||
description='The encryption describes how the volume is encrypted.\n',
|
||||
title='encryption',
|
||||
)
|
||||
|
||||
|
||||
class VolumeDiscoverySpec(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
volumeSelector: VolumeSelector | None = Field(
|
||||
None, description='The volume selector expression.\n', title='volumeSelector'
|
||||
)
|
||||
|
||||
|
||||
class ExistingVolumeConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str | None = Field(
|
||||
None,
|
||||
description='Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n',
|
||||
title='name',
|
||||
)
|
||||
discovery: VolumeDiscoverySpec | None = Field(
|
||||
None,
|
||||
description='The discovery describes how to find a volume.\n',
|
||||
title='discovery',
|
||||
)
|
||||
mount: MountSpec | None = Field(
|
||||
None,
|
||||
description='The mount describes additional mount options.\n',
|
||||
title='mount',
|
||||
)
|
||||
@@ -1,58 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ConfigFile(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
content: str | None = Field(
|
||||
None,
|
||||
description='The content of the extension service config file.\n',
|
||||
title='content',
|
||||
)
|
||||
mountPath: str | None = Field(
|
||||
None,
|
||||
description='The mount path of the extension service config file.\n',
|
||||
title='mountPath',
|
||||
)
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
ExtensionServiceConfig = 'ExtensionServiceConfig'
|
||||
|
||||
|
||||
class ServiceConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str = Field(..., description='Name of the extension service.\n', title='name')
|
||||
configFiles: list[ConfigFile] | None = Field(
|
||||
None,
|
||||
description='The config files for the extension service.\n',
|
||||
title='configFiles',
|
||||
)
|
||||
environment: list[str] | None = Field(
|
||||
None,
|
||||
description='The environment for the extension service.\n',
|
||||
title='environment',
|
||||
)
|
||||
@@ -1,37 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
PCIDriverRebindConfig = 'PCIDriverRebindConfig'
|
||||
|
||||
|
||||
class PCIDriverRebindConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str = Field(..., description='PCI device id\n', title='name')
|
||||
targetDriver: str = Field(
|
||||
...,
|
||||
description='Target driver to rebind the PCI device to.\n',
|
||||
title='targetDriver',
|
||||
)
|
||||
@@ -1,220 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, constr
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
NetworkDefaultActionConfig = 'NetworkDefaultActionConfig'
|
||||
|
||||
|
||||
class Ingress(Enum):
|
||||
accept = 'accept'
|
||||
block = 'block'
|
||||
|
||||
|
||||
class DefaultActionConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
ingress: Ingress | None = Field(
|
||||
None,
|
||||
description='Default action for all not explicitly configured ingress traffic: accept or block.\n',
|
||||
title='ingress',
|
||||
)
|
||||
|
||||
|
||||
class EthernetChannelsConfig(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
rx: int | None = Field(None, description='Number of RX channels.\n', title='rx')
|
||||
tx: int | None = Field(None, description='Number of TX channels.\n', title='tx')
|
||||
other: int | None = Field(
|
||||
None, description='Number of other channels.\n', title='other'
|
||||
)
|
||||
combined: int | None = Field(
|
||||
None, description='Number of combined channels.\n', title='combined'
|
||||
)
|
||||
|
||||
|
||||
class KindModel(Enum):
|
||||
EthernetConfig = 'EthernetConfig'
|
||||
|
||||
|
||||
class EthernetRingsConfig(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
rx: int | None = Field(None, description='Number of RX rings.\n', title='rx')
|
||||
tx: int | None = Field(None, description='Number of TX rings.\n', title='tx')
|
||||
rx_mini: int | None = Field(
|
||||
None, alias='rx-mini', description='Number of RX mini rings.\n', title='rx-mini'
|
||||
)
|
||||
rx_jumbo: int | None = Field(
|
||||
None,
|
||||
alias='rx-jumbo',
|
||||
description='Number of RX jumbo rings.\n',
|
||||
title='rx-jumbo',
|
||||
)
|
||||
rx_buf_len: int | None = Field(
|
||||
None, alias='rx-buf-len', description='RX buffer length.\n', title='rx-buf-len'
|
||||
)
|
||||
cqe_size: int | None = Field(
|
||||
None, alias='cqe-size', description='CQE size.\n', title='cqe-size'
|
||||
)
|
||||
tx_push: bool | None = Field(
|
||||
None, alias='tx-push', description='TX push enabled.\n', title='tx-push'
|
||||
)
|
||||
rx_push: bool | None = Field(
|
||||
None, alias='rx-push', description='RX push enabled.\n', title='rx-push'
|
||||
)
|
||||
tx_push_buf_len: int | None = Field(
|
||||
None,
|
||||
alias='tx-push-buf-len',
|
||||
description='TX push buffer length.\n',
|
||||
title='tx-push-buf-len',
|
||||
)
|
||||
tcp_data_split: bool | None = Field(
|
||||
None,
|
||||
alias='tcp-data-split',
|
||||
description='TCP data split enabled.\n',
|
||||
title='tcp-data-split',
|
||||
)
|
||||
|
||||
|
||||
class IngressRule(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
subnet: constr(pattern=r'^[0-9a-f.:]+/\d{1,3}$') | None = Field(
|
||||
None, description='Subnet defines a source subnet.\n', title='subnet'
|
||||
)
|
||||
except_: constr(pattern=r'^[0-9a-f.:]+/\d{1,3}$') | None = Field(
|
||||
None,
|
||||
alias='except',
|
||||
description='Except defines a source subnet to exclude from the rule, it gets excluded from the subnet.\n',
|
||||
title='except',
|
||||
)
|
||||
|
||||
|
||||
class KindModel1(Enum):
|
||||
KubeSpanEndpoints = 'KubeSpanEndpoints'
|
||||
|
||||
|
||||
class KubespanEndpointsConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel1 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
extraAnnouncedEndpoints: list[str] | None = Field(
|
||||
None,
|
||||
description='A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.\n',
|
||||
title='extraAnnouncedEndpoints',
|
||||
)
|
||||
|
||||
|
||||
class KindModel2(Enum):
|
||||
NetworkRuleConfig = 'NetworkRuleConfig'
|
||||
|
||||
|
||||
class Protocol(Enum):
|
||||
tcp = 'tcp'
|
||||
udp = 'udp'
|
||||
icmp = 'icmp'
|
||||
icmpv6 = 'icmpv6'
|
||||
|
||||
|
||||
class RulePortSelector(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
ports: list[int | str] | None = Field(
|
||||
None,
|
||||
description='Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.\n',
|
||||
title='ports',
|
||||
)
|
||||
protocol: Protocol | None = Field(
|
||||
None,
|
||||
description='Protocol defines traffic protocol (e.g. TCP or UDP).\n',
|
||||
title='protocol',
|
||||
)
|
||||
|
||||
|
||||
class EthernetConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str = Field(..., description='Name of the link (interface).\n', title='name')
|
||||
features: dict[constr(pattern=r'.*'), bool] | None = Field(
|
||||
None,
|
||||
description='Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse talosctl get ethernetstatus <link> -o yaml to get the list of available features and\ntheir current status.\n',
|
||||
title='features',
|
||||
)
|
||||
rings: EthernetRingsConfig | None = Field(
|
||||
None,
|
||||
description='Configuration for Ethernet link rings.\n\nThis is similar to ethtool -G command.\n',
|
||||
title='rings',
|
||||
)
|
||||
channels: EthernetChannelsConfig | None = Field(
|
||||
None,
|
||||
description='Configuration for Ethernet link channels.\n\nThis is similar to ethtool -L command.\n',
|
||||
title='channels',
|
||||
)
|
||||
|
||||
|
||||
class RuleConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel2 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str = Field(..., description='Name of the config document.\n', title='name')
|
||||
portSelector: RulePortSelector | None = Field(
|
||||
None,
|
||||
description='Port selector defines which ports and protocols on the host are affected by the rule.\n',
|
||||
title='portSelector',
|
||||
)
|
||||
ingress: list[IngressRule] | None = Field(
|
||||
None,
|
||||
description='Ingress defines which source subnets are allowed to access the host ports/protocols defined by the portSelector.\n',
|
||||
title='ingress',
|
||||
)
|
||||
@@ -1,90 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, constr
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
EventSinkConfig = 'EventSinkConfig'
|
||||
|
||||
|
||||
class EventSinkV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
endpoint: str | None = Field(
|
||||
None,
|
||||
description='The endpoint for the event sink as ‘host:port’.\n',
|
||||
title='endpoint',
|
||||
)
|
||||
|
||||
|
||||
class KindModel(Enum):
|
||||
KmsgLogConfig = 'KmsgLogConfig'
|
||||
|
||||
|
||||
class KmsgLogV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str | None = Field(
|
||||
None, description='Name of the config document.\n', title='name'
|
||||
)
|
||||
url: constr(pattern=r'^(tcp|udp)://') | None = Field(
|
||||
None,
|
||||
description='The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.\n',
|
||||
title='url',
|
||||
)
|
||||
|
||||
|
||||
class KindModel1(Enum):
|
||||
WatchdogTimerConfig = 'WatchdogTimerConfig'
|
||||
|
||||
|
||||
class WatchdogTimerV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: KindModel1 = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
device: str | None = Field(
|
||||
None, description='Path to the watchdog device.\n', title='device'
|
||||
)
|
||||
timeout: (
|
||||
constr(pattern=r'^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$') | None
|
||||
) = Field(
|
||||
None,
|
||||
description='Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.\n',
|
||||
title='timeout',
|
||||
)
|
||||
@@ -1,37 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
TrustedRootsConfig = 'TrustedRootsConfig'
|
||||
|
||||
|
||||
class TrustedRootsConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
name: str = Field(..., description='Name of the config document.\n', title='name')
|
||||
certificates: str | None = Field(
|
||||
None,
|
||||
description='List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.\n',
|
||||
title='certificates',
|
||||
)
|
||||
@@ -1,39 +0,0 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: schema.tRo.json
|
||||
# timestamp: 2026-02-20T04:31:38+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, constr
|
||||
|
||||
|
||||
class ApiVersion(Enum):
|
||||
v1alpha1 = 'v1alpha1'
|
||||
|
||||
|
||||
class Kind(Enum):
|
||||
SideroLinkConfig = 'SideroLinkConfig'
|
||||
|
||||
|
||||
class ConfigV1Alpha1(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
extra='forbid',
|
||||
)
|
||||
apiVersion: ApiVersion = Field(
|
||||
...,
|
||||
description='apiVersion is the API version of the resource.\n',
|
||||
title='apiVersion',
|
||||
)
|
||||
kind: Kind = Field(
|
||||
..., description='kind is the kind of the resource.\n', title='kind'
|
||||
)
|
||||
apiUrl: constr(pattern=r'^(https|grpc)://') | None = Field(
|
||||
None, description='SideroLink API URL to connect to.\n', title='apiUrl'
|
||||
)
|
||||
uniqueToken: str | None = Field(
|
||||
None,
|
||||
description='SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.\n',
|
||||
title='uniqueToken',
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
63
talos/clusters/testing.yaml
Normal file
63
talos/clusters/testing.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
# yaml-language-server: $schema=../../schemas/cluster.json
|
||||
version:
|
||||
kubernetes: 1.34.1
|
||||
talos: 1.11.3
|
||||
clusterEnv: staging
|
||||
controlPlaneIp: 192.168.1.100
|
||||
nodes:
|
||||
- testing/talos-vm
|
||||
# secrets:
|
||||
# age: !file testing/age.key
|
||||
# talos: testing/secrets.yaml
|
||||
|
||||
base:
|
||||
kernelArgs:
|
||||
- talos.platform=metal
|
||||
- console=tty0
|
||||
- init_on_alloc=1
|
||||
- init_on_free=1
|
||||
- slab_nomerge
|
||||
- pti=on
|
||||
- consoleblank=0
|
||||
- nvme_core.io_timeout=4294967295
|
||||
- printk.devkmsg=on
|
||||
- selinux=1
|
||||
- lockdown=confidentiality
|
||||
patches:
|
||||
all:
|
||||
- system/hostname
|
||||
- system/install-disk
|
||||
- system/network
|
||||
- networking/vip
|
||||
- networking/tailscale
|
||||
- networking/cilium
|
||||
- spegel
|
||||
- storage/longhorn
|
||||
- storage/longhorn/user-volume
|
||||
- storage/local-path-provisioner/user-volume
|
||||
- storage/limit-ephemeral
|
||||
- metrics/all
|
||||
controlPlane:
|
||||
- system/allow-control-plane-workloads
|
||||
# - sops
|
||||
- flux/cluster-variables
|
||||
- metrics/control-plane
|
||||
- networking/gateway-api
|
||||
default:
|
||||
arch: amd64
|
||||
network:
|
||||
interface: enp1s0
|
||||
netmask: 255.255.252.0
|
||||
gateway: 192.168.1.1
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.8.8
|
||||
tailscale:
|
||||
server: https://headscale.huizinga.dev
|
||||
authKey:
|
||||
file: tailscale.key
|
||||
advertiseRoutes: true
|
||||
ntp: nl.pool.ntp.org
|
||||
install:
|
||||
auto: true
|
||||
disk: /dev/vda
|
||||
4
talos/nodes/testing/talos-vm.yaml
Normal file
4
talos/nodes/testing/talos-vm.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
# yaml-language-server: $schema=../../../schemas/node.json
|
||||
type: controlPlane
|
||||
network:
|
||||
ip: 192.168.1.2
|
||||
@@ -4,17 +4,15 @@ cluster:
|
||||
- name: cluster-variables
|
||||
contents: |
|
||||
---
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/refs/heads/master/v1.34.1-standalone-strict/namespace.json
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flux-system
|
||||
---
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/refs/heads/master/v1.34.1-standalone-strict/configmap.json
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cluster-variables
|
||||
namespace: flux-system
|
||||
data:
|
||||
cluster_env: "{%- if node.cluster.production %} production {%- else %} staging {%- endif %}"
|
||||
cluster_env: {{ cluster.clusterEnv }}
|
||||
8
talos/patches/networking/tailscale.yaml
Normal file
8
talos/patches/networking/tailscale.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json
|
||||
apiVersion: v1alpha1
|
||||
kind: ExtensionServiceConfig
|
||||
name: tailscale
|
||||
environment:
|
||||
- TS_AUTHKEY={{ node.network.tailscale.authKey }}
|
||||
- TS_EXTRA_ARGS={% if node.network.tailscale.server %}--login-server {{ node.network.tailscale.server }} {% endif %}--advertise-tags=tag:cluster-{{ cluster.name }}
|
||||
- TS_ROUTES={% if node.network.tailscale.advertiseRoutes -%} {{node.network.ip}}/{{ node.network.netmask | to_prefix }} {%- endif %}
|
||||
@@ -2,6 +2,6 @@
|
||||
machine:
|
||||
network:
|
||||
interfaces:
|
||||
- interface: "{{node.interface}}"
|
||||
- interface: "{{node.network.interface}}"
|
||||
vip:
|
||||
ip: "{{node.cluster.controlPlaneIp}}"
|
||||
ip: "{{cluster.controlPlaneIp}}"
|
||||
@@ -1,4 +1,4 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json
|
||||
machine:
|
||||
install:
|
||||
disk: "{{node.installDisk}}"
|
||||
disk: "{{node.install.disk}}"
|
||||
@@ -2,10 +2,10 @@
|
||||
machine:
|
||||
network:
|
||||
interfaces:
|
||||
- interface: "{{node.interface}}"
|
||||
- interface: "{{node.network.interface}}"
|
||||
dhcp: false
|
||||
addresses:
|
||||
- "{{node.ip}}"
|
||||
- "{{node.network.ip}}"
|
||||
routes:
|
||||
- network: 0.0.0.0/0
|
||||
gateway: "{{node.gateway}}"
|
||||
gateway: "{{node.network.gateway}}"
|
||||
BIN
talos/secrets/tailscale.key
Normal file
BIN
talos/secrets/tailscale.key
Normal file
Binary file not shown.
298
tools/render
298
tools/render
@@ -1,298 +0,0 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# vim: set filetype=python :
|
||||
|
||||
# Adapted from: https://enix.io/en/blog/pxe-talos/
|
||||
|
||||
import base64
|
||||
import functools
|
||||
import pathlib
|
||||
import sys
|
||||
from typing import Annotated, Any, List, Literal
|
||||
|
||||
import git
|
||||
import requests
|
||||
import yaml
|
||||
from jinja2 import Environment, FileSystemLoader, StrictUndefined, Template
|
||||
from mergedeep import Strategy, merge
|
||||
from netaddr import IPAddress
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
BeforeValidator,
|
||||
ConfigDict,
|
||||
HttpUrl,
|
||||
IPvAnyAddress,
|
||||
ValidationInfo,
|
||||
)
|
||||
from pydantic_extra_types.semantic_version import SemanticVersion
|
||||
|
||||
from models import Model as TalosModel
|
||||
|
||||
REPO = git.Repo(sys.path[0], search_parent_directories=True)
|
||||
assert REPO.working_dir is not None
|
||||
|
||||
ROOT = pathlib.Path(REPO.working_dir)
|
||||
|
||||
NODES = ROOT.joinpath("nodes")
|
||||
SCHEMATICS = ROOT.joinpath("schematics")
|
||||
RENDERED = ROOT.joinpath("rendered")
|
||||
|
||||
EXTENSIONS = ["jinja2.ext.do"]
|
||||
|
||||
PATCHES = Environment(
|
||||
loader=FileSystemLoader(ROOT.joinpath("patches")),
|
||||
undefined=StrictUndefined,
|
||||
extensions=EXTENSIONS,
|
||||
)
|
||||
TEMPLATES = Environment(
|
||||
loader=FileSystemLoader(ROOT.joinpath("templates")),
|
||||
undefined=StrictUndefined,
|
||||
extensions=EXTENSIONS,
|
||||
)
|
||||
|
||||
|
||||
class ServerConfig(BaseModel):
|
||||
model_config = ConfigDict(strict=True, extra="forbid")
|
||||
|
||||
tftpIp: IPvAnyAddress
|
||||
httpUrl: HttpUrl
|
||||
|
||||
|
||||
class TailscaleConfig(BaseModel):
|
||||
model_config = ConfigDict(strict=True, extra="forbid")
|
||||
|
||||
loginServer: HttpUrl
|
||||
authKey: str
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
model_config = ConfigDict(strict=True, extra="forbid")
|
||||
|
||||
server: ServerConfig
|
||||
tailscale: TailscaleConfig
|
||||
|
||||
|
||||
class Cluster(BaseModel):
|
||||
model_config = ConfigDict(strict=True, extra="forbid")
|
||||
|
||||
name: str
|
||||
production: bool
|
||||
controlPlaneIp: IPvAnyAddress
|
||||
# TODO: Path
|
||||
secretsFile: str
|
||||
sopsKeyFile: str
|
||||
|
||||
|
||||
# When we try to make a deep copy of the nodes dict it fails as the Template
|
||||
# does not implement __deepcopy__, so this wrapper type facilitates that
|
||||
class TemplateWrapper:
|
||||
def __init__(self, template: Template):
|
||||
self.template = template
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
# NOTE: This is not a true deepcopy, but since we know we won't modify
|
||||
# the template this is fine.
|
||||
return self
|
||||
|
||||
|
||||
def render_patch(wrapper: Any, info: ValidationInfo):
|
||||
if not isinstance(wrapper, TemplateWrapper):
|
||||
raise RuntimeError("Expected TemplateWrapper")
|
||||
args = (info.context or {}) | {"node": info.data}
|
||||
try:
|
||||
rendered = wrapper.template.render(args)
|
||||
except Exception as e:
|
||||
e.add_note(f"While rendering for: {args['node']['hostname']}")
|
||||
raise e
|
||||
# Parse the rendered yaml
|
||||
return yaml.safe_load(rendered)
|
||||
|
||||
|
||||
class Node(BaseModel):
|
||||
model_config = ConfigDict(strict=True, extra="forbid")
|
||||
|
||||
schematicId: str
|
||||
arch: Literal["amd64"]
|
||||
talosVersion: SemanticVersion
|
||||
kubernetesVersion: SemanticVersion
|
||||
|
||||
kernelArgs: List[str]
|
||||
extraKernelArgs: List[str]
|
||||
dns: List[IPvAnyAddress]
|
||||
# TODO: Validation
|
||||
ntp: str
|
||||
|
||||
install: bool
|
||||
advertiseRoutes: bool
|
||||
|
||||
serial: str
|
||||
interface: str
|
||||
ip: IPvAnyAddress
|
||||
netmask: IPvAnyAddress
|
||||
gateway: IPvAnyAddress
|
||||
# TODO: Extra validation
|
||||
installDisk: str
|
||||
autoInstall: bool
|
||||
cluster: Cluster
|
||||
hostname: str
|
||||
filename: str
|
||||
|
||||
type: Literal["controlplane", "worker"]
|
||||
|
||||
patches: List[Annotated[TalosModel, BeforeValidator(render_patch)]]
|
||||
patchesControlPlane: List[Annotated[TalosModel, BeforeValidator(render_patch)]]
|
||||
|
||||
|
||||
def tailscale_subnet(gateway: IPvAnyAddress, netmask: IPvAnyAddress):
|
||||
netmask_bits = IPAddress(netmask.exploded).netmask_bits()
|
||||
return f"{IPAddress(gateway.exploded) & IPAddress(netmask.exploded)}/{netmask_bits}"
|
||||
|
||||
|
||||
def load_secret(path: str):
|
||||
with open(path) as f:
|
||||
return base64.b64encode(f.read().encode()).decode()
|
||||
|
||||
|
||||
def model_dump_json(model: BaseModel):
|
||||
return model.model_dump_json(exclude_none=True)
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_schematic_id(schematic: str):
|
||||
"""Lookup the schematic id associated with a given schematic"""
|
||||
r = requests.post("https://factory.talos.dev/schematics", data=schematic)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
return data["id"]
|
||||
|
||||
|
||||
def schematic_constructor(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode):
|
||||
"""Load specified schematic file and get the assocatied schematic id"""
|
||||
schematic_name = loader.construct_yaml_str(node)
|
||||
try:
|
||||
schematic = SCHEMATICS.joinpath(schematic_name).with_suffix(".yaml").read_text()
|
||||
return get_schematic_id(schematic)
|
||||
except Exception:
|
||||
raise yaml.MarkedYAMLError("Failed to load schematic", node.start_mark)
|
||||
|
||||
|
||||
def template_constructor(environment: Environment):
|
||||
def inner(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode):
|
||||
patch_name = loader.construct_scalar(node)
|
||||
try:
|
||||
template = environment.get_template(f"{patch_name}.yaml")
|
||||
return TemplateWrapper(template)
|
||||
except Exception:
|
||||
raise yaml.MarkedYAMLError("Failed to load patch", node.start_mark)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def realpath_constructor(directory: pathlib.Path):
|
||||
def inner(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode):
|
||||
try:
|
||||
realpath = directory.joinpath(loader.construct_scalar(node)).resolve(
|
||||
strict=True
|
||||
)
|
||||
return str(realpath)
|
||||
except Exception:
|
||||
raise yaml.MarkedYAMLError("Failed to get real path", node.start_mark)
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def get_loader(directory: pathlib.Path):
|
||||
"""Add special constructors to yaml loader"""
|
||||
loader = yaml.SafeLoader
|
||||
loader.add_constructor("!realpath", realpath_constructor(directory))
|
||||
loader.add_constructor("!schematic", schematic_constructor)
|
||||
loader.add_constructor("!patch", template_constructor(PATCHES))
|
||||
|
||||
return loader
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_defaults(directory: pathlib.Path, root: pathlib.Path):
|
||||
"""Compute the defaults from the provided directory and parents."""
|
||||
try:
|
||||
with open(directory.joinpath("_defaults.yaml")) as fyaml:
|
||||
yml_data = yaml.load(fyaml, Loader=get_loader(directory))
|
||||
except OSError:
|
||||
yml_data = {}
|
||||
|
||||
# Stop recursion when reaching root directory
|
||||
if directory != root:
|
||||
return merge(
|
||||
{},
|
||||
get_defaults(directory.parent, root),
|
||||
yml_data,
|
||||
strategy=Strategy.TYPESAFE_REPLACE,
|
||||
)
|
||||
else:
|
||||
return yml_data
|
||||
|
||||
|
||||
def walk_files(root: pathlib.Path):
|
||||
"""Get all files that do not start with and underscore"""
|
||||
for dirpath, _dirnames, filenames in root.walk():
|
||||
for fn in filenames:
|
||||
if not fn.startswith("_"):
|
||||
yield dirpath.joinpath(fn)
|
||||
|
||||
|
||||
def main():
|
||||
with open(ROOT.joinpath("config.yaml")) as fyaml:
|
||||
config = yaml.safe_load(fyaml)
|
||||
|
||||
with open(ROOT.joinpath("secrets.yaml")) as fyaml:
|
||||
merge(config, yaml.safe_load(fyaml), strategy=Strategy.TYPESAFE_REPLACE)
|
||||
|
||||
config = Config(**config)
|
||||
|
||||
template_args = {
|
||||
"config": config,
|
||||
"root": ROOT,
|
||||
"helper": {
|
||||
"tailscale_subnet": tailscale_subnet,
|
||||
"load_secret": load_secret,
|
||||
"model_dump_json": model_dump_json,
|
||||
},
|
||||
}
|
||||
|
||||
nodes: List[Node] = []
|
||||
for fullname in walk_files(NODES):
|
||||
filename = str(fullname.relative_to(NODES).parent) + "/" + fullname.stem
|
||||
|
||||
with open(fullname) as fyaml:
|
||||
yml_data = yaml.load(fyaml, Loader=get_loader(fullname.parent))
|
||||
yml_data = merge(
|
||||
{},
|
||||
get_defaults(fullname.parent, NODES),
|
||||
yml_data,
|
||||
strategy=Strategy.TYPESAFE_REPLACE,
|
||||
)
|
||||
yml_data["hostname"] = fullname.stem
|
||||
yml_data["filename"] = filename
|
||||
node = Node.model_validate(yml_data, context=template_args)
|
||||
nodes.append(node)
|
||||
|
||||
# HACK: We can't hash a dict, so we first convert it to json, the use set
|
||||
# to get all the unique entries, and then convert it back
|
||||
# NOTE: This assumes that all nodes in the cluster use the same definition for the cluster
|
||||
clusters = list(
|
||||
Cluster.model_validate_json(cluster)
|
||||
for cluster in set(node.cluster.model_dump_json() for node in nodes)
|
||||
)
|
||||
|
||||
template_args |= {"nodes": nodes, "clusters": clusters}
|
||||
|
||||
RENDERED.mkdir(exist_ok=True)
|
||||
for template_name in TEMPLATES.list_templates():
|
||||
template = TEMPLATES.get_template(template_name)
|
||||
|
||||
rendered = template.render(template_args)
|
||||
with open(RENDERED.joinpath(template_name), "w") as f:
|
||||
f.write(rendered)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
|
||||
MODELS_DIR=${ROOT}/src/models
|
||||
rm -rf ${MODELS_DIR}
|
||||
|
||||
SCHEMA_FILE=$(mktemp schema.XXX.json)
|
||||
function cleanup() {
|
||||
rm -rf ${SCHEMA_FILE}
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
curl https://raw.githubusercontent.com/siderolabs/talos/refs/heads/release-1.11/website/content/v1.11/schemas/config.schema.json > ${SCHEMA_FILE}
|
||||
uvx --from datamodel-code-generator datamodel-codegen --input ${SCHEMA_FILE} --input-file-type jsonschema --output ${MODELS_DIR} --output-model pydantic_v2.BaseModel
|
||||
386
uv.lock
generated
386
uv.lock
generated
@@ -1,386 +0,0 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.13"
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.7.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bootstrap"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "gitpython" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "mergedeep" },
|
||||
{ name = "netaddr" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pydantic-extra-types" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "requests" },
|
||||
{ name = "semver" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "gitpython", specifier = "==3.1.45" },
|
||||
{ name = "jinja2", specifier = "==3.1.6" },
|
||||
{ name = "mergedeep", specifier = "==1.3.4" },
|
||||
{ name = "netaddr", specifier = "==1.3.0" },
|
||||
{ name = "pydantic", specifier = ">=2.12.5" },
|
||||
{ name = "pydantic-extra-types", specifier = ">=2.11.0" },
|
||||
{ name = "pyyaml", specifier = "==6.0.3" },
|
||||
{ name = "requests", specifier = "==2.32.5" },
|
||||
{ name = "semver", specifier = ">=3.0.4" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.10.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4c/5b/b6ce21586237c77ce67d01dc5507039d444b630dd76611bbca2d8e5dcd91/certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43", size = 164519, upload-time = "2025-10-05T04:12:15.808Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/37/af0d2ef3967ac0d6113837b44a4f0bfe1328c2b9763bd5b1744520e5cfed/certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de", size = 163286, upload-time = "2025-10-05T04:12:14.03Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitdb"
|
||||
version = "4.0.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "smmap" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gitpython"
|
||||
version = "3.1.45"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "gitdb" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076, upload-time = "2025-07-24T03:45:54.871Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.11"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markupsafe" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mergedeep"
|
||||
version = "1.3.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "netaddr"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/90/188b2a69654f27b221fba92fda7217778208532c962509e959a9cee5229d/netaddr-1.3.0.tar.gz", hash = "sha256:5c3c3d9895b551b763779ba7db7a03487dc1f8e3b385af819af341ae9ef6e48a", size = 2260504, upload-time = "2024-05-28T21:30:37.743Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/12/cc/f4fe2c7ce68b92cbf5b2d379ca366e1edae38cccaad00f69f529b460c3ef/netaddr-1.3.0-py3-none-any.whl", hash = "sha256:c2c6a8ebe5554ce33b7d5b3a306b71bbb373e000bbbf2350dd5213cc56e3dbbe", size = 2262023, upload-time = "2024-05-28T21:30:34.191Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
{ name = "pydantic-core" },
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.41.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-extra-types"
|
||||
version = "2.11.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "3.0.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/d1/d3159231aec234a59dd7d601e9dd9fe96f3afff15efd33c1070019b26132/semver-3.0.4.tar.gz", hash = "sha256:afc7d8c584a5ed0a11033af086e8af226a9c0b206f313e0301f8dd7b6b589602", size = 269730, upload-time = "2025-01-24T13:19:27.617Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload-time = "2025-01-24T13:19:24.949Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smmap"
|
||||
version = "5.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-inspection"
|
||||
version = "0.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" },
|
||||
]
|
||||
Reference in New Issue
Block a user