Compare commits
1 Commits
main
...
55fdcc78c2
| Author | SHA1 | Date | |
|---|---|---|---|
|
55fdcc78c2
|
@@ -5,8 +5,4 @@ indent_style = tab
|
||||
|
||||
[*.yaml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{*.py,tools/render}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
4
.git-crypt/.gitattributes
vendored
4
.git-crypt/.gitattributes
vendored
@@ -1,4 +0,0 @@
|
||||
# Do not edit this file. To specify the files to encrypt, create your own
|
||||
# .gitattributes file in the directory where your files are.
|
||||
* !filter !diff
|
||||
*.gpg binary
|
||||
Binary file not shown.
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,3 +0,0 @@
|
||||
_secrets.yaml filter=git-crypt diff=git-crypt
|
||||
secrets.yaml filter=git-crypt diff=git-crypt
|
||||
*.agekey filter=git-crypt diff=git-crypt
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,2 @@
|
||||
.ipxe/
|
||||
rendered/
|
||||
configs/
|
||||
*.egg-info
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
default_install_hook_types: [pre-commit, commit-msg]
|
||||
exclude: gotk-.*.yaml
|
||||
repos:
|
||||
- repo: builtin
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
args:
|
||||
- --allow-multiple-documents
|
||||
- id: check-added-large-files
|
||||
- id: check-merge-conflict
|
||||
- id: check-executables-have-shebangs
|
||||
|
||||
- repo: https://github.com/jmlrt/check-yamlschema
|
||||
rev: v0.0.7
|
||||
hooks:
|
||||
- id: check-yamlschema
|
||||
files: ^patches/.*\.yaml$
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.40.0
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
- repo: https://github.com/sirwart/ripsecrets
|
||||
rev: v0.1.11
|
||||
hooks:
|
||||
- id: ripsecrets-system
|
||||
|
||||
- repo: https://github.com/crate-ci/committed
|
||||
rev: v1.1.8
|
||||
hooks:
|
||||
- id: committed
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"bracketSpacing": false
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
3.13
|
||||
@@ -1,3 +0,0 @@
|
||||
_secrets.yaml
|
||||
secrets.yaml
|
||||
*.agekey
|
||||
73
README.md
73
README.md
@@ -1,73 +0,0 @@
|
||||
# Talos
|
||||
|
||||
To decrypt the secrets file:
|
||||
|
||||
```
|
||||
git-crypt unlock
|
||||
```
|
||||
|
||||
Generate the config files:
|
||||
|
||||
```bash
|
||||
talosctl gen config <cluster_name> https://<controlplane_ip>:6443 -f \
|
||||
--with-secrets secrets.yaml \
|
||||
--config-patch @<path_to_patch> \
|
||||
--config-patch-control-plane @<path_to_controlplane_patch> \
|
||||
--install-image factory.talos.dev/metal-installer/<schematic_id>:<version> \
|
||||
-o configs
|
||||
```
|
||||
|
||||
Set TALOSCONFIG:
|
||||
|
||||
```bash
|
||||
export TALOSCONFIG=$(realpath configs/talosconfig)
|
||||
```
|
||||
|
||||
Apply the configs for each node, use worker.yaml for worker nodes:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --insecure --nodes <node_id> --file configs/controlplane.yaml
|
||||
```
|
||||
|
||||
Set endpoint to one of the nodes:
|
||||
|
||||
```bash
|
||||
talosctl config endpoint <node_ip>
|
||||
```
|
||||
|
||||
Bootstrap Kubernetes:
|
||||
|
||||
```bash
|
||||
talosctl -n <node_id> bootstrap
|
||||
```
|
||||
|
||||
Set endpoint to control plane:
|
||||
|
||||
```bash
|
||||
talosctl config endpoint <controlplane_ip>
|
||||
```
|
||||
|
||||
Get kubeconfig and set KUBECONFIG:
|
||||
|
||||
```bash
|
||||
talosctl -n 192.168.1.100 kubeconfig $PWD/configs/kubeconfig
|
||||
export KUBECONFIG=$(realpath configs/kubeconfig)
|
||||
```
|
||||
|
||||
For applying updated config to node:
|
||||
|
||||
```bash
|
||||
talosctl apply-config --nodes <node_id> --file configs/controlplane.yaml
|
||||
```
|
||||
|
||||
Upgrading talos or changing the schematic:
|
||||
|
||||
```bash
|
||||
talosctl upgrade --nodes <node_id> --image factory.talos.dev/metal-installer/<schematic_id>:<version>
|
||||
```
|
||||
|
||||
To upgrade kubernetes or inline manifests, first apply the updated controlplane configs, then run:
|
||||
|
||||
```bash
|
||||
talosctl upgrade-k8s
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
style = "conventional"
|
||||
ignore_author_re = "Flux"
|
||||
@@ -1,6 +1,2 @@
|
||||
server:
|
||||
tftpIp: 192.168.1.1
|
||||
httpUrl: http://192.168.1.1:8000
|
||||
|
||||
tailscale:
|
||||
loginServer: https://headscale.huizinga.dev
|
||||
dhcp:
|
||||
tftpIp: 10.0.0.3
|
||||
|
||||
@@ -1,43 +1,9 @@
|
||||
schematicId: !schematic default
|
||||
schematicID: !schematic "_schematic.yaml"
|
||||
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
|
||||
talosVersion: v1.11.3
|
||||
kernelArgs: talos.platform=metal console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on selinux=1 lockdown=confidentiality
|
||||
dns0: 1.1.1.1
|
||||
dns1: 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
|
||||
install: false
|
||||
upgradeIPXE: false
|
||||
|
||||
@@ -4,5 +4,3 @@ customization:
|
||||
- siderolabs/iscsi-tools
|
||||
- siderolabs/util-linux-tools
|
||||
- siderolabs/intel-ucode
|
||||
- siderolabs/i915
|
||||
- siderolabs/tailscale
|
||||
3
nodes/production/_defaults.yaml
Normal file
3
nodes/production/_defaults.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
netmask: 255.255.252.0
|
||||
gateway: 10.0.0.1
|
||||
install: true
|
||||
@@ -1,4 +1,3 @@
|
||||
serial: 5CZ7NX2
|
||||
interface: enp2s0
|
||||
ip: 10.0.0.202
|
||||
type: "controlplane"
|
||||
@@ -1,4 +1,3 @@
|
||||
serial: F3PKRH2
|
||||
interface: enp3s0
|
||||
ip: 10.0.0.201
|
||||
type: "controlplane"
|
||||
@@ -1,4 +1,3 @@
|
||||
serial: J33CHY2
|
||||
interface: enp2s0
|
||||
ip: 10.0.0.203
|
||||
type: "controlplane"
|
||||
Binary file not shown.
@@ -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
|
||||
Binary file not shown.
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.
3
nodes/vm/_defaults.yaml
Normal file
3
nodes/vm/_defaults.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
netmask: 255.255.255.0
|
||||
gateway: 192.168.1.1
|
||||
upgradeIPXE: ipxe.pxe
|
||||
@@ -1,4 +1,4 @@
|
||||
serial: talos-vm
|
||||
interface: enp1s0
|
||||
ip: 192.168.1.2
|
||||
type: "controlplane"
|
||||
install: true
|
||||
@@ -1,3 +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
|
||||
cluster:
|
||||
allowSchedulingOnControlPlanes: true
|
||||
@@ -1,12 +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
|
||||
machine:
|
||||
features:
|
||||
hostDNS:
|
||||
# This option is enabled by default and causes issues with cilium
|
||||
forwardKubeDNSToHost: false
|
||||
cluster:
|
||||
network:
|
||||
cni:
|
||||
name: none
|
||||
proxy:
|
||||
disabled: true
|
||||
@@ -1,20 +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
|
||||
cluster:
|
||||
inlineManifests:
|
||||
- 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 %}"
|
||||
@@ -1,4 +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
|
||||
cluster:
|
||||
extraManifests:
|
||||
- https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml
|
||||
@@ -1,4 +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
|
||||
machine:
|
||||
network:
|
||||
hostname: "{{node.hostname}}"
|
||||
@@ -1,4 +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
|
||||
machine:
|
||||
install:
|
||||
disk: "{{node.installDisk}}"
|
||||
@@ -1,6 +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: VolumeConfig
|
||||
name: EPHEMERAL
|
||||
provisioning:
|
||||
maxSize: 30GB
|
||||
@@ -1,9 +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: UserVolumeConfig
|
||||
name: local-path-provisioner
|
||||
provisioning:
|
||||
diskSelector:
|
||||
match: system_disk
|
||||
grow: true
|
||||
maxSize: 10GB
|
||||
@@ -1,9 +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: UserVolumeConfig
|
||||
name: longhorn
|
||||
provisioning:
|
||||
diskSelector:
|
||||
match: system_disk
|
||||
grow: true
|
||||
maxSize: 2000GB
|
||||
@@ -1,11 +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
|
||||
machine:
|
||||
kubelet:
|
||||
extraMounts:
|
||||
- destination: /var/lib/longhorn
|
||||
type: bind
|
||||
source: /var/lib/longhorn
|
||||
options:
|
||||
- bind
|
||||
- rshared
|
||||
- rw
|
||||
@@ -1,5 +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
|
||||
cluster:
|
||||
extraManifests:
|
||||
- https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml
|
||||
- https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||
@@ -1,5 +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
|
||||
machine:
|
||||
kubelet:
|
||||
extraArgs:
|
||||
rotate-server-certificates: "true"
|
||||
@@ -1,11 +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
|
||||
machine:
|
||||
network:
|
||||
interfaces:
|
||||
- interface: "{{node.interface}}"
|
||||
dhcp: false
|
||||
addresses:
|
||||
- "{{node.ip}}"
|
||||
routes:
|
||||
- network: 0.0.0.0/0
|
||||
gateway: "{{node.gateway}}"
|
||||
@@ -1,17 +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
|
||||
machine:
|
||||
# This is only needed on nodes that will have storage
|
||||
sysctls:
|
||||
vm.nr_hugepages: "1024"
|
||||
nodeLabels:
|
||||
openebs.io/engine: mayastor
|
||||
# This is needed on ALL nodes
|
||||
kubelet:
|
||||
extraMounts:
|
||||
- destination: /var/local
|
||||
type: bind
|
||||
source: /var/local
|
||||
options:
|
||||
- bind
|
||||
- rshared
|
||||
- rw
|
||||
@@ -1,18 +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
|
||||
cluster:
|
||||
inlineManifests:
|
||||
- name: sops-key
|
||||
contents: |
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flux-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: sops-gpg
|
||||
namespace: flux-system
|
||||
data:
|
||||
age.agekey: |
|
||||
{{ helper.load_secret(node.cluster.sopsKeyFile) }}
|
||||
@@ -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
|
||||
machine:
|
||||
files:
|
||||
- path: /etc/cri/conf.d/20-customization.part
|
||||
op: create
|
||||
content: |
|
||||
[plugins."io.containerd.cri.v1.images"]
|
||||
discard_unpacked_layers = false
|
||||
@@ -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,7 +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
|
||||
machine:
|
||||
network:
|
||||
interfaces:
|
||||
- interface: "{{node.interface}}"
|
||||
vip:
|
||||
ip: "{{node.cluster.controlPlaneIp}}"
|
||||
@@ -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",
|
||||
]
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
PyYAML==6.0.3
|
||||
requests==2.32.5
|
||||
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
@@ -1,24 +1,36 @@
|
||||
#!ipxe
|
||||
|
||||
dhcp
|
||||
|
||||
echo Starting ${serial}
|
||||
|
||||
:start
|
||||
goto node_${serial} || exit
|
||||
# Is a known serial is set, execute that
|
||||
# If an unknown serial is set, exit
|
||||
# If no serial is set, ask the user
|
||||
goto node_${serial} || goto manual
|
||||
# Default behavior (non install mode) is to exit iPXE script
|
||||
|
||||
{% for node in nodes %}
|
||||
{%- if node.install -%}
|
||||
# {{ node.filename }}
|
||||
:node_{{ node.serial }}
|
||||
{% set ipArg = "ip=" ~ [node.ip, "" , node.gateway, node.netmask, node.hostname, node.interface, "", node.dns[0], node.dns[1], node.ntp]|join(":") -%}
|
||||
{% set kernelArgs = [ipArg, node.kernelArgs|join(" "), node.extraKernelArgs|join(" ")] -%}
|
||||
{% if node.autoInstall %}
|
||||
{% do kernelArgs.append("talos.config=" ~ config.server.httpUrl ~ "/configs/" ~ node.filename ~ ".yaml") %}
|
||||
{% endif %}
|
||||
{{ range datasource "nodes" }}
|
||||
{{- if .install }}
|
||||
# {{ .filename }}
|
||||
:node_{{ .serial }}
|
||||
{{- $ipArg := printf "ip=%s::%s:%s:%s:%s::%s:%s:%s" .ip .gateway .netmask .hostname .interface .dns0 .dns1 .ntp }}
|
||||
{{- $kernelArgs := printf "%s %s" $ipArg .kernelArgs }}
|
||||
imgfree
|
||||
kernel https://pxe.factory.talos.dev/image/{{ node.schematicId }}/v{{ node.talosVersion }}/kernel-{{ node.arch }} {{ kernelArgs|join(" ") }}
|
||||
initrd https://pxe.factory.talos.dev/image/{{ node.schematicId }}/v{{ node.talosVersion }}/initramfs-{{ node.arch }}.xz
|
||||
kernel https://pxe.factory.talos.dev/image/{{ .schematicID }}/{{ .talosVersion }}/kernel-{{ .arch }} {{ $kernelArgs }} {{- if .upgradeIPXE }} || boot {{ .upgradeIPXE }} {{- end }}
|
||||
initrd https://pxe.factory.talos.dev/image/{{ .schematicID }}/{{ .talosVersion }}/initramfs-{{ .arch }}.xz
|
||||
boot
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
|
||||
:manual
|
||||
menu Select node
|
||||
{{ range datasource "nodes" }}
|
||||
item {{ .serial }} {{ .hostname }}
|
||||
{{ end }}
|
||||
choose selected || goto cancel
|
||||
goto node_${selected}
|
||||
|
||||
:cancel
|
||||
echo Type exit to restart script
|
||||
shell
|
||||
goto start
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% set tftpIp = config.server.tftpIp -%}
|
||||
{{ $tftpIp := (ds "config").dhcp.tftpIp -}}
|
||||
|
||||
enable-tftp
|
||||
tftp-root=/tftproot
|
||||
@@ -9,9 +9,9 @@ dhcp-vendorclass=UEFI,PXEClient:Arch:00007
|
||||
dhcp-vendorclass=UEFI64,PXEClient:Arch:00009
|
||||
|
||||
# 1st stage: pxe rom boot on ipxe
|
||||
dhcp-boot=net:BIOS,ipxe.pxe,{{ tftpIp }},{{ tftpIp }}
|
||||
dhcp-boot=net:UEFI,ipxe.efi,{{ tftpIp }},{{ tftpIp }}
|
||||
dhcp-boot=net:UEFI64,ipxe.efi,{{ tftpIp }},{{ tftpIp }}
|
||||
dhcp-boot=net:BIOS,ipxe.pxe,{{ $tftpIp }},{{ $tftpIp }}
|
||||
dhcp-boot=net:UEFI,ipxe.efi,{{ $tftpIp }},{{ $tftpIp }}
|
||||
dhcp-boot=net:UEFI64,ipxe.efi,{{ $tftpIp }},{{ $tftpIp }}
|
||||
|
||||
# Based on logic in https://gist.github.com/robinsmidsrod/4008017
|
||||
# iPXE sends a 175 option, checking suboptions
|
||||
@@ -30,11 +30,11 @@ tag-if=set:ipxe-ok,tag:ipxe-http,tag:ipxe-https
|
||||
|
||||
# these create option 43 cruft, which is required in proxy mode
|
||||
# TFTP IP is required on all dhcp-boot lines (unless dnsmasq itself acts as tftp server?)
|
||||
pxe-service=tag:!ipxe-ok,X86PC,PXE,undionly.kpxe,{{ tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,IA32_EFI,PXE,snponlyx32.efi,{{ tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,BC_EFI,PXE,snponly.efi,{{ tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,X86-64_EFI,PXE,snponly.efi,{{ tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,X86PC,PXE,undionly.kpxe,{{ $tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,IA32_EFI,PXE,snponlyx32.efi,{{ $tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,BC_EFI,PXE,snponly.efi,{{ $tftpIp }}
|
||||
pxe-service=tag:!ipxe-ok,X86-64_EFI,PXE,snponly.efi,{{ $tftpIp }}
|
||||
|
||||
# later match overrides previous, keep ipxe script last
|
||||
# server address must be non zero, but can be anything as long as iPXE script is not fetched over TFTP
|
||||
dhcp-boot=tag:ipxe-ok,boot.ipxe,,{{ tftpIp }}
|
||||
dhcp-boot=tag:ipxe-ok,boot.ipxe,,{{ $tftpIp }}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
CONFIGS={{ root }}/configs
|
||||
|
||||
# Generate the configuration for each node
|
||||
{% for node in nodes -%}
|
||||
talosctl gen config {{ node.cluster.name }} https://{{ node.cluster.controlPlaneIp }}:6443 -f \
|
||||
--with-secrets {{ node.cluster.secretsFile }} \
|
||||
--talos-version v{{ node.talosVersion }} \
|
||||
--kubernetes-version v{{ node.kubernetesVersion }} \
|
||||
--output-types {{ node.type }} \
|
||||
--install-image factory.talos.dev/metal-installer/{{ node.schematicId }}:v{{ node.talosVersion }} \
|
||||
{% for patch in node.patches -%}
|
||||
{# The double call to tojson is needed to properly escape the patch (object -> json -> string) -#}
|
||||
--config-patch {{ helper.model_dump_json(patch)|tojson }} \
|
||||
{% endfor -%}
|
||||
{% for patch in node.patchesControlPlane -%}
|
||||
--config-patch-control-plane {{ helper.model_dump_json(patch)|tojson }} \
|
||||
{% endfor -%}
|
||||
--with-docs=false \
|
||||
--with-examples=false \
|
||||
-o ${CONFIGS}/{{ node.filename }}.yaml
|
||||
{% endfor %}
|
||||
|
||||
# Generate the talosconfig file for each cluster
|
||||
{% for cluster in clusters -%}
|
||||
talosctl gen config {{ cluster.name }} https://{{ cluster.controlPlaneIp }}:6443 -f \
|
||||
--with-secrets {{ cluster.secretsFile }} \
|
||||
--output-types talosconfig \
|
||||
-o ${CONFIGS}/{{ cluster.name }}/talosconfig
|
||||
{% endfor %}
|
||||
|
||||
# Create merged talosconfig
|
||||
TALOSCONFIG=${CONFIGS}/talosconfig
|
||||
rm -f ${TALOSCONFIG}
|
||||
{% for cluster in clusters -%}
|
||||
talosctl config --talosconfig=${CONFIGS}/{{ cluster.name }}/talosconfig endpoint {{ cluster.controlPlaneIp }}
|
||||
talosctl config --talosconfig=${TALOSCONFIG} merge ${CONFIGS}/{{ cluster.name }}/talosconfig
|
||||
{% endfor %}
|
||||
@@ -1,6 +0,0 @@
|
||||
export TALOSCONFIG={{ root }}/configs/talosconfig
|
||||
{% set paths = [] %}
|
||||
{%- for cluster in clusters -%}
|
||||
{%- do paths.append(root ~ "/configs/" ~ cluster.name ~ "/kubeconfig") -%}
|
||||
{% endfor -%}
|
||||
export KUBECONFIG={{ paths|join(":") }}
|
||||
96
tools/merge
Executable file
96
tools/merge
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Adapted from: https://enix.io/en/blog/pxe-talos/
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import json
|
||||
import pathlib
|
||||
|
||||
import requests
|
||||
import yaml
|
||||
|
||||
|
||||
@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(directory: pathlib.Path):
|
||||
"""Load specified schematic file and get the assocatied schematic id"""
|
||||
|
||||
def constructor(loader: yaml.SafeLoader, node: yaml.nodes.ScalarNode):
|
||||
filename = str(loader.construct_scalar(node))
|
||||
try:
|
||||
schematic = directory.joinpath(filename).read_text()
|
||||
return get_schematic_id(schematic)
|
||||
except Exception:
|
||||
raise yaml.MarkedYAMLError("Failed to load schematic", node.start_mark)
|
||||
|
||||
return constructor
|
||||
|
||||
|
||||
def get_loader(directory: pathlib.Path):
|
||||
"""Add special constructors to yaml loader"""
|
||||
loader = yaml.SafeLoader
|
||||
loader.add_constructor("!schematic", schematic_constructor(directory))
|
||||
|
||||
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 get_defaults(directory.parent, root) | yml_data
|
||||
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():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("directory", type=pathlib.Path)
|
||||
parser.add_argument("-f", "--filter")
|
||||
args = parser.parse_args()
|
||||
|
||||
data = []
|
||||
for fullname in walk_files(args.directory):
|
||||
filename = (
|
||||
str(fullname.relative_to(args.directory).parent) + "/" + fullname.stem
|
||||
)
|
||||
|
||||
if args.filter is not None and not filename.startswith(args.filter):
|
||||
continue
|
||||
|
||||
with open(fullname) as fyaml:
|
||||
yml_data = yaml.load(fyaml, Loader=get_loader(fullname.parent))
|
||||
yml_data = get_defaults(fullname.parent, args.directory) | yml_data
|
||||
yml_data["hostname"] = fullname.stem
|
||||
yml_data["filename"] = filename
|
||||
data.append(yml_data)
|
||||
|
||||
# Dump everything to json
|
||||
print(json.dumps(data))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
305
tools/render
305
tools/render
@@ -1,298 +1,11 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
# vim: set filetype=python :
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
RENDERED=${ROOT}/rendered
|
||||
TEMPLATES=${ROOT}/templates
|
||||
|
||||
# Adapted from: https://enix.io/en/blog/pxe-talos/
|
||||
${ROOT}/tools/merge ./nodes > ${RENDERED}/nodes.json
|
||||
|
||||
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()
|
||||
gomplate --input-dir ${TEMPLATES} --output-dir ${RENDERED} \
|
||||
-d nodes=file://${RENDERED}/nodes.json \
|
||||
-d config=${ROOT}/config.yaml \
|
||||
|
||||
@@ -6,7 +6,6 @@ IPXE_VERSION=b41bda4413bf286d7b7a449bc05e1531da1eec2e
|
||||
IPXE_BIN=(bin/ipxe.pxe bin-x86_64-efi/ipxe.efi)
|
||||
|
||||
IPXE_DIR=${ROOT}/.ipxe/ipxe-${IPXE_VERSION}
|
||||
HTTP_URL=$(cat ${ROOT}/config.yaml | yq .server.httpUrl)
|
||||
|
||||
function download_ipxe() {
|
||||
base_dir=$(dirname ${IPXE_DIR})
|
||||
@@ -21,32 +20,22 @@ function patch_ipxe() {
|
||||
# Apply patches to iPXE source
|
||||
cd "${IPXE_DIR}/src"
|
||||
sed -i 's/^#undef[\t ]DOWNLOAD_PROTO_HTTPS.*$/#define DOWNLOAD_PROTO_HTTPS/g' config/general.h
|
||||
|
||||
cat > embed.ipxe << EOF
|
||||
#!ipxe
|
||||
|
||||
dhcp
|
||||
chain ${HTTP_URL}/boot.ipxe || shell
|
||||
# chain boot.ipxe || shell
|
||||
EOF
|
||||
|
||||
cd - > /dev/null
|
||||
}
|
||||
|
||||
function build_ipxe() {
|
||||
cd "${IPXE_DIR}/src"
|
||||
for bin in "${IPXE_BIN[@]}"; do
|
||||
path=${IPXE_DIR}/src/${bin}
|
||||
if [ ! -f "${path}" ]; then
|
||||
make -j$(nproc) ${bin} EMBED=embed.ipxe
|
||||
cd "${IPXE_DIR}/src"
|
||||
make -j$(nproc) ${bin}
|
||||
cd -
|
||||
fi
|
||||
done
|
||||
cd - > /dev/null
|
||||
}
|
||||
|
||||
function render() {
|
||||
${ROOT}/tools/render
|
||||
${ROOT}/rendered/generate_configs.sh
|
||||
}
|
||||
|
||||
function host_tftp() {
|
||||
@@ -67,30 +56,8 @@ function host_tftp() {
|
||||
sudo in.tftpd --verbosity 100 --permissive -L --secure ${TFTP_DIR}
|
||||
}
|
||||
|
||||
function host_http() {
|
||||
HTTP_DIR=$(mktemp --tmpdir -d http.XXX)
|
||||
chmod 755 ${HTTP_DIR}
|
||||
function cleanup() {
|
||||
rm -rf ${HTTP_DIR}
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
ln -s ${ROOT}/rendered/boot.ipxe ${HTTP_DIR}
|
||||
for bin in "${IPXE_BIN[@]}"; do
|
||||
path=${IPXE_DIR}/src/${bin}
|
||||
ln -s ${path} ${HTTP_DIR}
|
||||
done
|
||||
|
||||
ln -s ${ROOT}/configs ${HTTP_DIR}
|
||||
|
||||
echo "Starting http"
|
||||
cd ${HTTP_DIR}
|
||||
python -m http.server 8000
|
||||
cd -
|
||||
}
|
||||
|
||||
download_ipxe
|
||||
patch_ipxe
|
||||
build_ipxe
|
||||
render
|
||||
host_http
|
||||
host_tftp
|
||||
@@ -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
|
||||
14
tools/vm
14
tools/vm
@@ -3,9 +3,9 @@ set -euo pipefail
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
|
||||
VM_NAME="talos-vm"
|
||||
VCPUS="6"
|
||||
RAM_MB="16384"
|
||||
DISK_GB="100"
|
||||
VCPUS="2"
|
||||
RAM_MB="2048"
|
||||
DISK_GB="10"
|
||||
NETWORK=talos
|
||||
CONNECTION="qemu:///system"
|
||||
|
||||
@@ -21,7 +21,7 @@ function define_network() {
|
||||
<ip address="192.168.1.1" netmask="255.255.255.0">
|
||||
<dhcp>
|
||||
<range start="192.168.1.2" end="192.168.1.254"/>
|
||||
<bootp file='http://192.168.1.1:8000/ipxe.pxe'/>
|
||||
<bootp file='boot.ipxe'/>
|
||||
</dhcp>
|
||||
</ip>
|
||||
</network>
|
||||
@@ -51,7 +51,6 @@ function create() {
|
||||
--os-variant="linux2022" \
|
||||
--disk="size=${DISK_GB}" \
|
||||
--pxe \
|
||||
--sysinfo system.serial=${VM_NAME} \
|
||||
--network network="${NETWORK}"
|
||||
else
|
||||
echo -n "VM already exists, start it with:
|
||||
@@ -111,12 +110,13 @@ function delete() {
|
||||
virsh --connect="${CONNECTION}" destroy "${VM_NAME}"
|
||||
fi
|
||||
virsh --connect="${CONNECTION}" undefine "${VM_NAME}" --remove-all-storage
|
||||
else
|
||||
echo "VM doest not exists"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
if [[ $(virsh --connect="${CONNECTION}" net-list --all | grep -c "${NETWORK}") > "0" ]]; then
|
||||
if [[ $(virsh --connect="${CONNECTION}" list | grep -c "${VM_NAME}") > "0" ]]; then
|
||||
virsh --connect="${CONNECTION}" net-destroy "${NETWORK}"
|
||||
fi
|
||||
virsh --connect="${CONNECTION}" net-undefine "${NETWORK}"
|
||||
fi
|
||||
}
|
||||
|
||||
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