kekkai-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kekkai/__init__.py +7 -0
- kekkai/cli.py +1038 -0
- kekkai/config.py +403 -0
- kekkai/dojo.py +419 -0
- kekkai/dojo_import.py +213 -0
- kekkai/github/__init__.py +16 -0
- kekkai/github/commenter.py +198 -0
- kekkai/github/models.py +56 -0
- kekkai/github/sanitizer.py +112 -0
- kekkai/installer/__init__.py +39 -0
- kekkai/installer/errors.py +23 -0
- kekkai/installer/extract.py +161 -0
- kekkai/installer/manager.py +252 -0
- kekkai/installer/manifest.py +189 -0
- kekkai/installer/verify.py +86 -0
- kekkai/manifest.py +77 -0
- kekkai/output.py +218 -0
- kekkai/paths.py +46 -0
- kekkai/policy.py +326 -0
- kekkai/runner.py +70 -0
- kekkai/scanners/__init__.py +67 -0
- kekkai/scanners/backends/__init__.py +14 -0
- kekkai/scanners/backends/base.py +73 -0
- kekkai/scanners/backends/docker.py +178 -0
- kekkai/scanners/backends/native.py +240 -0
- kekkai/scanners/base.py +110 -0
- kekkai/scanners/container.py +144 -0
- kekkai/scanners/falco.py +237 -0
- kekkai/scanners/gitleaks.py +237 -0
- kekkai/scanners/semgrep.py +227 -0
- kekkai/scanners/trivy.py +246 -0
- kekkai/scanners/url_policy.py +163 -0
- kekkai/scanners/zap.py +340 -0
- kekkai/threatflow/__init__.py +94 -0
- kekkai/threatflow/artifacts.py +476 -0
- kekkai/threatflow/chunking.py +361 -0
- kekkai/threatflow/core.py +438 -0
- kekkai/threatflow/mermaid.py +374 -0
- kekkai/threatflow/model_adapter.py +491 -0
- kekkai/threatflow/prompts.py +277 -0
- kekkai/threatflow/redaction.py +228 -0
- kekkai/threatflow/sanitizer.py +643 -0
- kekkai/triage/__init__.py +33 -0
- kekkai/triage/app.py +168 -0
- kekkai/triage/audit.py +203 -0
- kekkai/triage/ignore.py +269 -0
- kekkai/triage/models.py +185 -0
- kekkai/triage/screens.py +341 -0
- kekkai/triage/widgets.py +169 -0
- kekkai_cli-1.0.0.dist-info/METADATA +135 -0
- kekkai_cli-1.0.0.dist-info/RECORD +90 -0
- kekkai_cli-1.0.0.dist-info/WHEEL +5 -0
- kekkai_cli-1.0.0.dist-info/entry_points.txt +3 -0
- kekkai_cli-1.0.0.dist-info/top_level.txt +3 -0
- kekkai_core/__init__.py +3 -0
- kekkai_core/ci/__init__.py +11 -0
- kekkai_core/ci/benchmarks.py +354 -0
- kekkai_core/ci/metadata.py +104 -0
- kekkai_core/ci/validators.py +92 -0
- kekkai_core/docker/__init__.py +17 -0
- kekkai_core/docker/metadata.py +153 -0
- kekkai_core/docker/sbom.py +173 -0
- kekkai_core/docker/security.py +158 -0
- kekkai_core/docker/signing.py +135 -0
- kekkai_core/redaction.py +84 -0
- kekkai_core/slsa/__init__.py +13 -0
- kekkai_core/slsa/verify.py +121 -0
- kekkai_core/windows/__init__.py +29 -0
- kekkai_core/windows/chocolatey.py +335 -0
- kekkai_core/windows/installer.py +256 -0
- kekkai_core/windows/scoop.py +165 -0
- kekkai_core/windows/validators.py +220 -0
- portal/__init__.py +19 -0
- portal/api.py +155 -0
- portal/auth.py +103 -0
- portal/enterprise/__init__.py +32 -0
- portal/enterprise/audit.py +435 -0
- portal/enterprise/licensing.py +342 -0
- portal/enterprise/rbac.py +276 -0
- portal/enterprise/saml.py +595 -0
- portal/ops/__init__.py +53 -0
- portal/ops/backup.py +553 -0
- portal/ops/log_shipper.py +469 -0
- portal/ops/monitoring.py +517 -0
- portal/ops/restore.py +469 -0
- portal/ops/secrets.py +408 -0
- portal/ops/upgrade.py +591 -0
- portal/tenants.py +340 -0
- portal/uploads.py +259 -0
- portal/web.py +384 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kekkai-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Kekkai/Regulon monorepo (local-first AppSec orchestration + compliance checker)
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: rich>=13.0.0
|
|
8
|
+
Requires-Dist: jsonschema>=4.20.0
|
|
9
|
+
Requires-Dist: textual>=0.50.0
|
|
10
|
+
Requires-Dist: httpx>=0.24.0
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<img src="https://raw.githubusercontent.com/kademoslabs/assets/main/logos/kekkai-slim.png" alt="Kekkai CLI Logo" width="250"/>
|
|
14
|
+
</p>
|
|
15
|
+
<p align="center"><i>One command. Clean AppSec reports.</i></p>
|
|
16
|
+
<p align="center">
|
|
17
|
+
<img src="https://img.shields.io/badge/license-MIT-blue.svg"/>
|
|
18
|
+
<img src="https://img.shields.io/badge/status-active-brightgreen"/>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
# Kekkai 🛡️
|
|
22
|
+
|
|
23
|
+
**Security that moves at developer speed.**
|
|
24
|
+
*Local-first orchestration for Trivy, Semgrep, and DefectDojo.*
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ⚡ Quick Start
|
|
31
|
+
|
|
32
|
+
Stop fighting with Docker Compose. Start scanning in 30 seconds.
|
|
33
|
+
|
|
34
|
+
### Installation
|
|
35
|
+
|
|
36
|
+
**Option 1: pipx (Recommended - Isolated Environment)**
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pipx install kekkai-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Option 2: Homebrew (macOS/Linux)**
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
brew tap kademoslabs/tap
|
|
46
|
+
brew install kekkai
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Option 3: Docker (No Python Required)**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Build image
|
|
53
|
+
docker build -t kademoslabs/kekkai:latest -f apps/kekkai/Dockerfile .
|
|
54
|
+
|
|
55
|
+
# Run via wrapper script
|
|
56
|
+
./scripts/kekkai-docker --help
|
|
57
|
+
|
|
58
|
+
# Or set up alias
|
|
59
|
+
alias kekkai="$(pwd)/scripts/kekkai-docker"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Option 4: Scoop (Windows)**
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
scoop bucket add kademoslabs https://github.com/kademoslabs/scoop-bucket
|
|
66
|
+
scoop install kekkai
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Option 5: pip (Traditional)**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install kekkai-cli
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### 1. Scan your project (Local)
|
|
78
|
+
|
|
79
|
+
Run industry-standard scanners (Trivy, Semgrep, Gitleaks) in unified Docker containers without installing them individually.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
cd your-repo
|
|
83
|
+
kekkai scan
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 2. Spin up DefectDojo
|
|
88
|
+
|
|
89
|
+
Launch a full local vulnerability management platform (Nginx, Postgres, Redis, Celery) with one command.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
kekkai dojo up --wait --open
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 3. Generate a Threat Model (AI)
|
|
97
|
+
|
|
98
|
+
Generate a STRIDE threat model and Data Flow Diagram using your local LLM.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
kekkai threatflow --repo . --model-mode local
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🛑 The Problem vs. Kekkai
|
|
108
|
+
|
|
109
|
+
| Feature | The Old Way | The Kekkai Way |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| **Tooling** | Manually install/update 5+ tools (Trivy, Semgrep, etc.) | **One Binary.** `kekkai scan` auto-pulls and runs the latest scanner containers. |
|
|
112
|
+
| **Reporting** | Parse 5 different JSON formats manually. | **Unified Output.** One deduplicated `kekkai-report.json` for all findings. |
|
|
113
|
+
| **DefectDojo** | Write a 200-line `docker-compose.yml` and debug networking. | **One Command.** `kekkai dojo up` automates the entire stack setup. |
|
|
114
|
+
| **Threat Modeling** | Expensive consultants or manual whiteboarding. | **AI Agent.** `kekkai threatflow` generates `THREATS.md` locally. |
|
|
115
|
+
| **CI/CD** | Write complex bash scripts to break builds. | **Policy Engine.** `kekkai scan --ci --fail-on high`. |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🔒 Enterprise Features (Portal)
|
|
120
|
+
|
|
121
|
+
For teams that need centralized management, **Kekkai Portal** offers:
|
|
122
|
+
|
|
123
|
+
* **SAML 2.0 SSO** with Replay Protection
|
|
124
|
+
* **Role-Based Access Control (RBAC)**
|
|
125
|
+
* **Cryptographically Signed Audit Logs**
|
|
126
|
+
|
|
127
|
+
*Built by Kademos Labs.*
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📚 Documentation
|
|
132
|
+
|
|
133
|
+
- **[Automated Distribution Updates](docs/ci/automated-distributions.md)** - CI/CD distribution triggers
|
|
134
|
+
- **[CI Architecture](/.docs/development/ci-architecture.md)** - Developer guide for distribution automation
|
|
135
|
+
- **[Homebrew Maintenance](docs/ci/homebrew-maintenance.md)** - Homebrew tap management
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
kekkai/__init__.py,sha256=_VrBvJRyqHiXs31S8HOhATk_O2iy-ac0_9X7rHH75j8,143
|
|
2
|
+
kekkai/cli.py,sha256=u-5GHXUT9G8Np-5SOLbSTtebQDv8yFYYCFZAjiDAAWU,35903
|
|
3
|
+
kekkai/config.py,sha256=LE7bKsmv5dim5KnZya0V7_LtviNQ1V0pMN_6FyAsMpc,13084
|
|
4
|
+
kekkai/dojo.py,sha256=DchLaTnDBwX0D14lTRdCtwql_II8aDEZ0JEq9F-n4MI,15887
|
|
5
|
+
kekkai/dojo_import.py,sha256=oI-vwpLITA7-U2_MxhaTp_PYfr5HqvcFy3VzKsWA6IY,6911
|
|
6
|
+
kekkai/manifest.py,sha256=Ph5xGDKuVxMW1GVIisRhxUelaiVZQe-W5sZWsq4lHqs,1887
|
|
7
|
+
kekkai/output.py,sha256=oEt-T49wXUvmhyAwnsdxPGrw3Ql5jUWApdXpzhlKRN8,6008
|
|
8
|
+
kekkai/paths.py,sha256=EcyG3CEOQFQygowu7O5Mp85dKkXWWvnm1h0j_BetGxY,1190
|
|
9
|
+
kekkai/policy.py,sha256=0XCUH-SbnO1PsM-exjSFHYHRnLkiNa50QfkyPakwNko,9792
|
|
10
|
+
kekkai/runner.py,sha256=MBFUiJ4sSVEGNbJ6cv-8p1WHaHqjio6yWEfr_K4GuTs,2037
|
|
11
|
+
kekkai/github/__init__.py,sha256=3EQ7LkRqgQwr5uTt7hNvVXLiKTpzE47woc8lZQjy5cE,386
|
|
12
|
+
kekkai/github/commenter.py,sha256=v19pEctYJvUvA7e-t6eOA5dZaNIt16ocCxC92IUxQeM,5906
|
|
13
|
+
kekkai/github/models.py,sha256=baW5prDEVncKrfC8aLoKjaTpPKYtRzZOBOg4Zje3qug,1340
|
|
14
|
+
kekkai/github/sanitizer.py,sha256=GWvVRHH_EGdhnHZi4rLWQDWTOrxw_U74Fi9fT5qUfBs,3239
|
|
15
|
+
kekkai/installer/__init__.py,sha256=pgUELK_2JzSGtzQuRzclF7M5FK9EulKmgFH5Xr8kxZI,858
|
|
16
|
+
kekkai/installer/errors.py,sha256=4SsTmcjIiQUmXDZVfeS2H2GCtpcMqEskN-9EyzFbBJc,561
|
|
17
|
+
kekkai/installer/extract.py,sha256=r4wYGCZ7zV2lIki5kns7t9bFRV1fahqOral8Jl7LZcQ,5232
|
|
18
|
+
kekkai/installer/manager.py,sha256=FqHTmHvTc2YkWWISvdS1uW7IZV6bHqyEg33TDb4Ldtc,7881
|
|
19
|
+
kekkai/installer/manifest.py,sha256=oZFquI9pUtgtB4ZXontQND5D7m4WzjjJuybhlePk3k4,5544
|
|
20
|
+
kekkai/installer/verify.py,sha256=ThtWfKnjrdx90_XBJclBGiw4yzTFs5HKoFJ0IyVPsMM,2186
|
|
21
|
+
kekkai/scanners/__init__.py,sha256=uKJqnBgcf47eJlDB3mvHpLsobR6M6N6uO2L0Dor1MaE,1552
|
|
22
|
+
kekkai/scanners/base.py,sha256=uy7HgOaQxNcp6-X1gfXAecSYpKXaEsuVeluf6SwkbwM,2678
|
|
23
|
+
kekkai/scanners/container.py,sha256=OhD0Meld_Zm4YcTuON91kx08Cj5h4R1FR0ABGbx7kQI,4197
|
|
24
|
+
kekkai/scanners/falco.py,sha256=Y0kjg9QArIZnXw8Q-EEZv8o7iUOehOY3jTKh3AMR1yU,7384
|
|
25
|
+
kekkai/scanners/gitleaks.py,sha256=8hRWXsH_EMhkqGcP2AtJdaMWHmQa91XdG2oVnq8g-kI,7159
|
|
26
|
+
kekkai/scanners/semgrep.py,sha256=v4RDV_mHv1UXqdhV7FSyQUCu0EUCOoERGzDVowqwgVA,6758
|
|
27
|
+
kekkai/scanners/trivy.py,sha256=E0B31eomyNF0k3ULnDkqfFoQM54UtrIIFPm3jqgE9VA,7788
|
|
28
|
+
kekkai/scanners/url_policy.py,sha256=0V4ESDd2R1MSyI1bs_WtFZxZpKX33O154qFIrD6uk5U,5209
|
|
29
|
+
kekkai/scanners/zap.py,sha256=64NHzM-GF3oOV4UZ_W9N2v55mz91FPPg_k6hgcwlX1I,10932
|
|
30
|
+
kekkai/scanners/backends/__init__.py,sha256=Rdo17FeCPGTI-1QeKtBSkg4NrW26RnvX9vgzdsxY5fg,372
|
|
31
|
+
kekkai/scanners/backends/base.py,sha256=bFvY0qLvawTKOSgmGyEfTm7OlFJwfivzNMUexPZD2Z0,1698
|
|
32
|
+
kekkai/scanners/backends/docker.py,sha256=mLrYL8MxM7cbhX_-po00hTLV6DtSi1bP9c6RfE3tStQ,5867
|
|
33
|
+
kekkai/scanners/backends/native.py,sha256=3kwmwHMkB1AY57ZWOYpw8CtxuK2yMET49MgH9OV1ns8,7409
|
|
34
|
+
kekkai/threatflow/__init__.py,sha256=J07MqRdIYMIIBq3TevZk6gQ43y-kJetDPItgafB2pe4,2113
|
|
35
|
+
kekkai/threatflow/artifacts.py,sha256=8h3IGk798U4Wkco4x0uKgzUsZCh7VfTOufwrxs7rTTo,17119
|
|
36
|
+
kekkai/threatflow/chunking.py,sha256=0FNVnaQoU3FEmtjYHVaiLsKBSzHx6Vo4oozVwwWkOHM,9981
|
|
37
|
+
kekkai/threatflow/core.py,sha256=CYLUI38n30zEq3DbUNI_H9mqBwwlPoL7TtFOiiwC3wI,15421
|
|
38
|
+
kekkai/threatflow/mermaid.py,sha256=Brp-x-LUHMRjC7OBh4Vxzlk3NeCcdmWfWXlv7WL1ZdE,11579
|
|
39
|
+
kekkai/threatflow/model_adapter.py,sha256=xqVPYc0rDys5RgvqJwR2VULJyzx8eToLQsOtKO1fKRE,15394
|
|
40
|
+
kekkai/threatflow/prompts.py,sha256=lgbj7FJ1c3UYj4ofGnlLoRmywYBfdAKY0QEHmIB_JFw,8525
|
|
41
|
+
kekkai/threatflow/redaction.py,sha256=mGUcNQB6YPVKArtMrEYcXCWslgUiCkloiowY9IlZ1iY,7622
|
|
42
|
+
kekkai/threatflow/sanitizer.py,sha256=uQsxYZ5VDXutZoj-WMl7fo5T07uHuQZqgVzoVMoaKec,22688
|
|
43
|
+
kekkai/triage/__init__.py,sha256=5La5HUnO6ehoUoRbOfZ_QvRj0U4ud4W2o79oraBhpCg,798
|
|
44
|
+
kekkai/triage/app.py,sha256=MU2tBI50d8sOdDKESGNrWYiREG9bBtrSccaMoiMv5gM,5027
|
|
45
|
+
kekkai/triage/audit.py,sha256=UVaSKKC6tZkHxEoMcnIZkMOT_ngj7QzHWYuDAHas_sc,5842
|
|
46
|
+
kekkai/triage/ignore.py,sha256=uBKM7zKyzORj9LJ5AAnoYWZQTRy57P0ZofSapiDWcfI,7305
|
|
47
|
+
kekkai/triage/models.py,sha256=nRmWtELMqHWHX1NqZ2upH2ZAJVeBxa3Wh8f3kkB9WYo,5384
|
|
48
|
+
kekkai/triage/screens.py,sha256=6eEiHvuuS_gGESS_K3NjPiQx8G7CR18-j9upU1p5nRg,11004
|
|
49
|
+
kekkai/triage/widgets.py,sha256=eOF6Qoo5uBqjxiEkbpgcO1tbIOGBQBKn75wP9Jw_AaE,4733
|
|
50
|
+
kekkai_core/__init__.py,sha256=gREN4oarM0azTkSTWTnlDnPZGgv1msai2Deq9Frj3gc,122
|
|
51
|
+
kekkai_core/redaction.py,sha256=EeWYPjAs2hIXlLKGmGn_PRdK08G4KcOBmbRCoFklbHc,2893
|
|
52
|
+
kekkai_core/ci/__init__.py,sha256=uldVoVW4HNdAOq0X5k4NAMOUxbue73zqOvd1BbiywGQ,328
|
|
53
|
+
kekkai_core/ci/benchmarks.py,sha256=9sVVObk3tqKKukH3i2g9voPKN1NFIuzOEUmjoiW6Q0o,11013
|
|
54
|
+
kekkai_core/ci/metadata.py,sha256=7NACmqzQQzSsfjDLR5cl6m7R2T3i-8frUihs8rkitic,2801
|
|
55
|
+
kekkai_core/ci/validators.py,sha256=j9KMsAauGWVArXFeWouACTjFgn7PlUTFUuAqIhI8DNo,2330
|
|
56
|
+
kekkai_core/docker/__init__.py,sha256=WnuDHW7JP0dPdV95a8L5SxAI6OJHh9umzVgRE-ZkaGQ,585
|
|
57
|
+
kekkai_core/docker/metadata.py,sha256=OE7C0NYSHZqpciyoRqCvdb2YcbiW4Y7KR8weC5FrsOE,4103
|
|
58
|
+
kekkai_core/docker/sbom.py,sha256=hAJtHqYRNSsjes9lrE7cHPYnX3elOdXhX7tXyo9kf7c,4644
|
|
59
|
+
kekkai_core/docker/security.py,sha256=EpjAUUp5kf-wy-sRbRosR4RerJKkKQcUIFYAxM7x02A,4439
|
|
60
|
+
kekkai_core/docker/signing.py,sha256=kH2F4ibXAqbPGJ483cSP1kh_cNiJM6rUHImMrfqOV1k,3362
|
|
61
|
+
kekkai_core/slsa/__init__.py,sha256=_MrP7YX10UYbYclD9qCqisIYdG5h1yzKZQQZoqtUIpQ,260
|
|
62
|
+
kekkai_core/slsa/verify.py,sha256=jOqROy6Q57Xwv9yj31_cgZ51dePxA9NRAsELuj0WtiY,3579
|
|
63
|
+
kekkai_core/windows/__init__.py,sha256=taJXYcLYTPucwhJw0rR4NJbNQIrDbIN6cheRPBMkOA4,770
|
|
64
|
+
kekkai_core/windows/chocolatey.py,sha256=tF5S5eN-HeENRt6yQ4TZgwng0oRMX_ScskQ3-ebEIUg,10187
|
|
65
|
+
kekkai_core/windows/installer.py,sha256=MePAywHH3JTIAENv52XtkUMOGqmYqZqkH77VW5PST8o,6945
|
|
66
|
+
kekkai_core/windows/scoop.py,sha256=lvothICrAoB3lGfkvhqVeNTB50eMmVGA0BE7JNCfHdI,5284
|
|
67
|
+
kekkai_core/windows/validators.py,sha256=45xUuAbHcKc0WLIZ-0rByPeDD88MAV8KvopngyYBHpQ,6525
|
|
68
|
+
portal/__init__.py,sha256=vLjCqUgIqzHbT-oIMMWuWQ-lDA5jvuOOEa9qdBRLcIY,507
|
|
69
|
+
portal/api.py,sha256=4_hQwkUnP8P3EjCdB5Tb7uRcuH3H7M6GxTvwTTmhLv4,4066
|
|
70
|
+
portal/auth.py,sha256=4K_Ya9W_2sZl2MF0FNVr9QASjTOKAO3CMdgGUuYbb9s,3102
|
|
71
|
+
portal/tenants.py,sha256=91SOqzjGefcHXodfN8LIHER8boeSB-Jb-WoHPTWI5GI,11394
|
|
72
|
+
portal/uploads.py,sha256=WhosreaTKFYHNKXW9F4jOmB_OwUl1YGtT5DeaXnRMqk,7352
|
|
73
|
+
portal/web.py,sha256=nW9ShBI18TitVFxaN0OmGgqtMdUnv5UPZcBMT12VuvM,14173
|
|
74
|
+
portal/enterprise/__init__.py,sha256=djxFlSUZ5-YwhT9SXJsAOaD1rRHDL14BXigh6l4WDC4,763
|
|
75
|
+
portal/enterprise/audit.py,sha256=VTm-M4gVKOxcBREqIJBs4r5wyqqqf1eCOsHi3FFiDcI,13772
|
|
76
|
+
portal/enterprise/licensing.py,sha256=M8PFfE_v73UJL6Lfr4qhqfAGrvtJyPwDPb4SMRMGfV0,11002
|
|
77
|
+
portal/enterprise/rbac.py,sha256=vrZoyIVmWM0C90CIgZaprwqhiDbAM-ggNNg36Zu-5lU,8548
|
|
78
|
+
portal/enterprise/saml.py,sha256=TXHBbILI7qMe0ertcFPnuSUSPbJzEeBiHmZzhY9-Ix8,20367
|
|
79
|
+
portal/ops/__init__.py,sha256=ZyEYmFM_4LFWfQfgp9Kh2vqmolSjVKFdk1vX1vkhjqc,1391
|
|
80
|
+
portal/ops/backup.py,sha256=eLUnZcUtS0svEoagb0jQQmT7TcAGjBA3fUlM2YoCfLg,20102
|
|
81
|
+
portal/ops/log_shipper.py,sha256=Age3YfvsJ5YWrPQYdHELr4Qa9jJCATHiwv3Q-rMJwJs,15237
|
|
82
|
+
portal/ops/monitoring.py,sha256=xhLbKjVaob709K4x0dEsOo4lh7Ddm2A4UE2ZmhfmMtI,17908
|
|
83
|
+
portal/ops/restore.py,sha256=rgzKoBIilgoPPv5gZhSSBuLKG1skKw5ryoCRR3d7CPQ,17058
|
|
84
|
+
portal/ops/secrets.py,sha256=wu2bUfJGctbGjyuGUgvUc_Y6IH1SCW16dExtqcKu_kg,14338
|
|
85
|
+
portal/ops/upgrade.py,sha256=fXsIXCJYYABdWDECDXkt7F2PidzNtO6Zr-g0Y5PLlVU,20106
|
|
86
|
+
kekkai_cli-1.0.0.dist-info/METADATA,sha256=htJH1nyVjsze_4rHWPSQ4_AVSDbZJzZYltPh-krm0pY,3660
|
|
87
|
+
kekkai_cli-1.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
88
|
+
kekkai_cli-1.0.0.dist-info/entry_points.txt,sha256=WUEX6IISnRcwlQAdhisPfIIV3Us2MYCwtJoyPpLJO44,75
|
|
89
|
+
kekkai_cli-1.0.0.dist-info/top_level.txt,sha256=u0J4T-Rnb0cgs0LfzZAUNt6nx1d5l7wKn8vOuo9FBEY,26
|
|
90
|
+
kekkai_cli-1.0.0.dist-info/RECORD,,
|
kekkai_core/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""CI/CD automation utilities for distribution triggers."""
|
|
2
|
+
|
|
3
|
+
from kekkai_core.ci.metadata import calculate_sha256, extract_version_from_tag
|
|
4
|
+
from kekkai_core.ci.validators import validate_semver, verify_checksum
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"extract_version_from_tag",
|
|
8
|
+
"calculate_sha256",
|
|
9
|
+
"validate_semver",
|
|
10
|
+
"verify_checksum",
|
|
11
|
+
]
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"""Performance benchmarking utilities for cross-platform testing."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import platform
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import psutil # type: ignore[import-untyped]
|
|
14
|
+
|
|
15
|
+
PSUTIL_AVAILABLE = True
|
|
16
|
+
except ImportError:
|
|
17
|
+
PSUTIL_AVAILABLE = False
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class BenchmarkResult:
|
|
22
|
+
"""Results from a performance benchmark."""
|
|
23
|
+
|
|
24
|
+
name: str
|
|
25
|
+
duration_seconds: float
|
|
26
|
+
memory_peak_mb: float | None = None
|
|
27
|
+
memory_start_mb: float | None = None
|
|
28
|
+
memory_end_mb: float | None = None
|
|
29
|
+
platform: str = field(default_factory=lambda: sys.platform)
|
|
30
|
+
python_version: str = field(default_factory=lambda: platform.python_version())
|
|
31
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
32
|
+
|
|
33
|
+
def to_dict(self) -> dict[str, Any]:
|
|
34
|
+
"""Convert to dictionary for serialization."""
|
|
35
|
+
return {
|
|
36
|
+
"name": self.name,
|
|
37
|
+
"duration_seconds": self.duration_seconds,
|
|
38
|
+
"memory_peak_mb": self.memory_peak_mb,
|
|
39
|
+
"memory_start_mb": self.memory_start_mb,
|
|
40
|
+
"memory_end_mb": self.memory_end_mb,
|
|
41
|
+
"platform": self.platform,
|
|
42
|
+
"python_version": self.python_version,
|
|
43
|
+
"metadata": self.metadata,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PerformanceBenchmark:
|
|
48
|
+
"""Context manager for performance benchmarking."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, name: str, metadata: dict[str, Any] | None = None):
|
|
51
|
+
"""Initialize benchmark.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
name: Name of the benchmark
|
|
55
|
+
metadata: Additional metadata to include
|
|
56
|
+
"""
|
|
57
|
+
self.name = name
|
|
58
|
+
self.metadata = metadata or {}
|
|
59
|
+
self.start_time: float | None = None
|
|
60
|
+
self.end_time: float | None = None
|
|
61
|
+
self.memory_start: float | None = None
|
|
62
|
+
self.memory_end: float | None = None
|
|
63
|
+
self.memory_peak: float | None = None
|
|
64
|
+
|
|
65
|
+
def __enter__(self) -> "PerformanceBenchmark":
|
|
66
|
+
"""Start benchmarking."""
|
|
67
|
+
self.start_time = time.perf_counter()
|
|
68
|
+
|
|
69
|
+
if PSUTIL_AVAILABLE:
|
|
70
|
+
process = psutil.Process()
|
|
71
|
+
self.memory_start = process.memory_info().rss / (1024 * 1024) # MB
|
|
72
|
+
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
76
|
+
"""End benchmarking."""
|
|
77
|
+
self.end_time = time.perf_counter()
|
|
78
|
+
|
|
79
|
+
if PSUTIL_AVAILABLE:
|
|
80
|
+
process = psutil.Process()
|
|
81
|
+
self.memory_end = process.memory_info().rss / (1024 * 1024) # MB
|
|
82
|
+
self.memory_peak = self.memory_end # Simplified; real peak tracking needs monitoring
|
|
83
|
+
|
|
84
|
+
def get_result(self) -> BenchmarkResult:
|
|
85
|
+
"""Get benchmark result."""
|
|
86
|
+
if self.start_time is None or self.end_time is None:
|
|
87
|
+
raise RuntimeError("Benchmark not completed")
|
|
88
|
+
|
|
89
|
+
duration = self.end_time - self.start_time
|
|
90
|
+
|
|
91
|
+
return BenchmarkResult(
|
|
92
|
+
name=self.name,
|
|
93
|
+
duration_seconds=duration,
|
|
94
|
+
memory_peak_mb=self.memory_peak,
|
|
95
|
+
memory_start_mb=self.memory_start,
|
|
96
|
+
memory_end_mb=self.memory_end,
|
|
97
|
+
metadata=self.metadata,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def benchmark_function(
|
|
102
|
+
func: Callable[..., Any],
|
|
103
|
+
name: str | None = None,
|
|
104
|
+
iterations: int = 1,
|
|
105
|
+
warmup: int = 0,
|
|
106
|
+
metadata: dict[str, Any] | None = None,
|
|
107
|
+
) -> BenchmarkResult:
|
|
108
|
+
"""Benchmark a function.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
func: Function to benchmark
|
|
112
|
+
name: Name for the benchmark (defaults to function name)
|
|
113
|
+
iterations: Number of times to run the function
|
|
114
|
+
warmup: Number of warmup iterations (not measured)
|
|
115
|
+
metadata: Additional metadata
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Benchmark result
|
|
119
|
+
"""
|
|
120
|
+
benchmark_name = name or func.__name__
|
|
121
|
+
|
|
122
|
+
# Warmup
|
|
123
|
+
for _ in range(warmup):
|
|
124
|
+
func()
|
|
125
|
+
|
|
126
|
+
# Measure
|
|
127
|
+
with PerformanceBenchmark(benchmark_name, metadata=metadata) as bench:
|
|
128
|
+
for _ in range(iterations):
|
|
129
|
+
func()
|
|
130
|
+
|
|
131
|
+
result = bench.get_result()
|
|
132
|
+
|
|
133
|
+
# Adjust for iterations
|
|
134
|
+
if iterations > 1:
|
|
135
|
+
result.duration_seconds /= iterations
|
|
136
|
+
result.metadata["iterations"] = iterations
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class BenchmarkRunner:
|
|
142
|
+
"""Runner for multiple benchmarks."""
|
|
143
|
+
|
|
144
|
+
def __init__(self, output_dir: Path | None = None):
|
|
145
|
+
"""Initialize benchmark runner.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
output_dir: Directory to store benchmark results
|
|
149
|
+
"""
|
|
150
|
+
self.output_dir = output_dir or Path(".benchmarks")
|
|
151
|
+
self.results: list[BenchmarkResult] = []
|
|
152
|
+
|
|
153
|
+
def run_benchmark(
|
|
154
|
+
self,
|
|
155
|
+
func: Callable[..., Any],
|
|
156
|
+
name: str | None = None,
|
|
157
|
+
iterations: int = 1,
|
|
158
|
+
metadata: dict[str, Any] | None = None,
|
|
159
|
+
) -> BenchmarkResult:
|
|
160
|
+
"""Run a benchmark and store result.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
func: Function to benchmark
|
|
164
|
+
name: Benchmark name
|
|
165
|
+
iterations: Number of iterations
|
|
166
|
+
metadata: Additional metadata
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Benchmark result
|
|
170
|
+
"""
|
|
171
|
+
result = benchmark_function(func, name=name, iterations=iterations, metadata=metadata)
|
|
172
|
+
self.results.append(result)
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
def save_results(self, filename: str | None = None) -> Path:
|
|
176
|
+
"""Save benchmark results to file.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
filename: Output filename (default: benchmark_{timestamp}.json)
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Path to saved file
|
|
183
|
+
"""
|
|
184
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
185
|
+
|
|
186
|
+
if filename is None:
|
|
187
|
+
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
|
188
|
+
filename = f"benchmark_{timestamp}.json"
|
|
189
|
+
|
|
190
|
+
output_path = self.output_dir / filename
|
|
191
|
+
|
|
192
|
+
results_data = {
|
|
193
|
+
"timestamp": time.time(),
|
|
194
|
+
"platform": {
|
|
195
|
+
"system": platform.system(),
|
|
196
|
+
"release": platform.release(),
|
|
197
|
+
"machine": platform.machine(),
|
|
198
|
+
"python_version": platform.python_version(),
|
|
199
|
+
},
|
|
200
|
+
"results": [r.to_dict() for r in self.results],
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
output_path.write_text(json.dumps(results_data, indent=2))
|
|
204
|
+
|
|
205
|
+
return output_path
|
|
206
|
+
|
|
207
|
+
def load_results(self, filepath: Path) -> list[BenchmarkResult]:
|
|
208
|
+
"""Load benchmark results from file.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
filepath: Path to results file
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
List of benchmark results
|
|
215
|
+
"""
|
|
216
|
+
data = json.loads(filepath.read_text())
|
|
217
|
+
|
|
218
|
+
loaded_results = []
|
|
219
|
+
for result_dict in data.get("results", []):
|
|
220
|
+
result = BenchmarkResult(
|
|
221
|
+
name=result_dict["name"],
|
|
222
|
+
duration_seconds=result_dict["duration_seconds"],
|
|
223
|
+
memory_peak_mb=result_dict.get("memory_peak_mb"),
|
|
224
|
+
memory_start_mb=result_dict.get("memory_start_mb"),
|
|
225
|
+
memory_end_mb=result_dict.get("memory_end_mb"),
|
|
226
|
+
platform=result_dict.get("platform", "unknown"),
|
|
227
|
+
python_version=result_dict.get("python_version", "unknown"),
|
|
228
|
+
metadata=result_dict.get("metadata", {}),
|
|
229
|
+
)
|
|
230
|
+
loaded_results.append(result)
|
|
231
|
+
|
|
232
|
+
return loaded_results
|
|
233
|
+
|
|
234
|
+
def compare_with_baseline(
|
|
235
|
+
self, baseline_file: Path, threshold_percent: float = 20.0
|
|
236
|
+
) -> dict[str, Any]:
|
|
237
|
+
"""Compare current results with baseline.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
baseline_file: Path to baseline results file
|
|
241
|
+
threshold_percent: Threshold for regression detection (e.g., 20.0 = 20%)
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Comparison report
|
|
245
|
+
"""
|
|
246
|
+
baseline_results = self.load_results(baseline_file)
|
|
247
|
+
|
|
248
|
+
# Create lookup by name
|
|
249
|
+
baseline_by_name = {r.name: r for r in baseline_results}
|
|
250
|
+
|
|
251
|
+
regressions: list[dict[str, Any]] = []
|
|
252
|
+
improvements: list[dict[str, Any]] = []
|
|
253
|
+
|
|
254
|
+
for current in self.results:
|
|
255
|
+
if current.name not in baseline_by_name:
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
baseline = baseline_by_name[current.name]
|
|
259
|
+
|
|
260
|
+
# Calculate percentage change
|
|
261
|
+
percent_change = (
|
|
262
|
+
(current.duration_seconds - baseline.duration_seconds)
|
|
263
|
+
/ baseline.duration_seconds
|
|
264
|
+
* 100.0
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
comparison = {
|
|
268
|
+
"name": current.name,
|
|
269
|
+
"baseline_duration": baseline.duration_seconds,
|
|
270
|
+
"current_duration": current.duration_seconds,
|
|
271
|
+
"percent_change": percent_change,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if percent_change > threshold_percent:
|
|
275
|
+
regressions.append(comparison)
|
|
276
|
+
elif percent_change < -threshold_percent:
|
|
277
|
+
improvements.append(comparison)
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
"threshold_percent": threshold_percent,
|
|
281
|
+
"regressions": regressions,
|
|
282
|
+
"improvements": improvements,
|
|
283
|
+
"total_benchmarks": len(self.results),
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def get_system_info() -> dict[str, Any]:
|
|
288
|
+
"""Get system information for benchmarking context.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Dictionary with system information
|
|
292
|
+
"""
|
|
293
|
+
info = {
|
|
294
|
+
"platform": {
|
|
295
|
+
"system": platform.system(),
|
|
296
|
+
"release": platform.release(),
|
|
297
|
+
"version": platform.version(),
|
|
298
|
+
"machine": platform.machine(),
|
|
299
|
+
"processor": platform.processor(),
|
|
300
|
+
},
|
|
301
|
+
"python": {
|
|
302
|
+
"version": platform.python_version(),
|
|
303
|
+
"implementation": platform.python_implementation(),
|
|
304
|
+
"compiler": platform.python_compiler(),
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if PSUTIL_AVAILABLE:
|
|
309
|
+
info["memory"] = {
|
|
310
|
+
"total_gb": psutil.virtual_memory().total / (1024**3),
|
|
311
|
+
"available_gb": psutil.virtual_memory().available / (1024**3),
|
|
312
|
+
}
|
|
313
|
+
info["cpu"] = {
|
|
314
|
+
"physical_cores": psutil.cpu_count(logical=False),
|
|
315
|
+
"logical_cores": psutil.cpu_count(logical=True),
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return info
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def format_benchmark_report(results: list[BenchmarkResult]) -> str:
|
|
322
|
+
"""Format benchmark results as human-readable report.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
results: List of benchmark results
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
Formatted report string
|
|
329
|
+
"""
|
|
330
|
+
lines = ["# Performance Benchmark Report", ""]
|
|
331
|
+
|
|
332
|
+
# System info
|
|
333
|
+
sys_info = get_system_info()
|
|
334
|
+
lines.append("## System Information")
|
|
335
|
+
lines.append(f"- Platform: {sys_info['platform']['system']} {sys_info['platform']['release']}")
|
|
336
|
+
lines.append(f"- Machine: {sys_info['platform']['machine']}")
|
|
337
|
+
lines.append(
|
|
338
|
+
f"- Python: {sys_info['python']['version']} ({sys_info['python']['implementation']})"
|
|
339
|
+
)
|
|
340
|
+
lines.append("")
|
|
341
|
+
|
|
342
|
+
# Results table
|
|
343
|
+
lines.append("## Benchmark Results")
|
|
344
|
+
lines.append("")
|
|
345
|
+
lines.append("| Benchmark | Duration (s) | Memory Peak (MB) |")
|
|
346
|
+
lines.append("|-----------|--------------|------------------|")
|
|
347
|
+
|
|
348
|
+
for result in results:
|
|
349
|
+
memory_str = f"{result.memory_peak_mb:.2f}" if result.memory_peak_mb else "N/A"
|
|
350
|
+
lines.append(f"| {result.name} | {result.duration_seconds:.4f} | {memory_str} |")
|
|
351
|
+
|
|
352
|
+
lines.append("")
|
|
353
|
+
|
|
354
|
+
return "\n".join(lines)
|