kubemend 0.4.0__tar.gz
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.
- kubemend-0.4.0/.claude/commands/new-scenario.md +7 -0
- kubemend-0.4.0/.claude/commands/replay-trace.md +5 -0
- kubemend-0.4.0/.claude/commands/run-evals.md +6 -0
- kubemend-0.4.0/.claude/settings.json +16 -0
- kubemend-0.4.0/.dockerignore +19 -0
- kubemend-0.4.0/.github/workflows/ci.yml +38 -0
- kubemend-0.4.0/.github/workflows/release.yml +145 -0
- kubemend-0.4.0/.gitignore +17 -0
- kubemend-0.4.0/.python-version +1 -0
- kubemend-0.4.0/ARCHITECTURE.md +400 -0
- kubemend-0.4.0/CLAUDE.md +49 -0
- kubemend-0.4.0/CONTRIBUTING.md +61 -0
- kubemend-0.4.0/Dockerfile +86 -0
- kubemend-0.4.0/IMPLEMENTATION_PLAN.md +167 -0
- kubemend-0.4.0/LICENSE +201 -0
- kubemend-0.4.0/PKG-INFO +251 -0
- kubemend-0.4.0/README.md +233 -0
- kubemend-0.4.0/Taskfile.yaml +683 -0
- kubemend-0.4.0/charts/kubemend/Chart.yaml +9 -0
- kubemend-0.4.0/charts/kubemend/README.md +100 -0
- kubemend-0.4.0/charts/kubemend/templates/NOTES.txt +28 -0
- kubemend-0.4.0/charts/kubemend/templates/_helpers.tpl +39 -0
- kubemend-0.4.0/charts/kubemend/templates/configmap.yaml +15 -0
- kubemend-0.4.0/charts/kubemend/templates/job.yaml +73 -0
- kubemend-0.4.0/charts/kubemend/templates/reader-rbac.yaml +51 -0
- kubemend-0.4.0/charts/kubemend/templates/reader-serviceaccount.yaml +11 -0
- kubemend-0.4.0/charts/kubemend/values.yaml +50 -0
- kubemend-0.4.0/config/pricing.yaml +84 -0
- kubemend-0.4.0/docs/blog/.gitkeep +0 -0
- kubemend-0.4.0/docs/decisions.md +435 -0
- kubemend-0.4.0/docs/knowledge/harness-design.md +47 -0
- kubemend-0.4.0/docs/knowledge/lab-and-evals.md +41 -0
- kubemend-0.4.0/docs/knowledge/tool-contracts.md +125 -0
- kubemend-0.4.0/docs/threat-model.md +257 -0
- kubemend-0.4.0/evals/__init__.py +5 -0
- kubemend-0.4.0/evals/checks.py +33 -0
- kubemend-0.4.0/evals/lab.py +278 -0
- kubemend-0.4.0/evals/models.py +64 -0
- kubemend-0.4.0/evals/reports/.gitkeep +0 -0
- kubemend-0.4.0/evals/reports/first-light/README.md +37 -0
- kubemend-0.4.0/evals/reports/first-light/trace.jsonl +15 -0
- kubemend-0.4.0/evals/reports/latest/report.json +76 -0
- kubemend-0.4.0/evals/reports/latest/report.md +12 -0
- kubemend-0.4.0/evals/reports/m6-baseline/report.json +37 -0
- kubemend-0.4.0/evals/reports/m6-baseline/report.md +26 -0
- kubemend-0.4.0/evals/reports/v0.1-baseline/report.json +67 -0
- kubemend-0.4.0/evals/reports/v0.1-baseline/report.md +12 -0
- kubemend-0.4.0/evals/runner.py +323 -0
- kubemend-0.4.0/evals/scenario.py +72 -0
- kubemend-0.4.0/kubemend/__init__.py +8 -0
- kubemend-0.4.0/kubemend/cli.py +400 -0
- kubemend-0.4.0/kubemend/config.py +172 -0
- kubemend-0.4.0/kubemend/core/__init__.py +6 -0
- kubemend-0.4.0/kubemend/core/budget.py +46 -0
- kubemend-0.4.0/kubemend/core/context.py +159 -0
- kubemend-0.4.0/kubemend/core/handoff.py +81 -0
- kubemend-0.4.0/kubemend/core/loop.py +150 -0
- kubemend-0.4.0/kubemend/core/loop_detector.py +54 -0
- kubemend-0.4.0/kubemend/core/model.py +124 -0
- kubemend-0.4.0/kubemend/llm/__init__.py +5 -0
- kubemend-0.4.0/kubemend/llm/anthropic_client.py +148 -0
- kubemend-0.4.0/kubemend/llm/client.py +91 -0
- kubemend-0.4.0/kubemend/llm/factory.py +79 -0
- kubemend-0.4.0/kubemend/llm/fake.py +130 -0
- kubemend-0.4.0/kubemend/llm/openai_client.py +186 -0
- kubemend-0.4.0/kubemend/prompts/__init__.py +46 -0
- kubemend-0.4.0/kubemend/prompts/compaction.md.j2 +28 -0
- kubemend-0.4.0/kubemend/prompts/handoff.md.j2 +30 -0
- kubemend-0.4.0/kubemend/prompts/pr_body.md.j2 +40 -0
- kubemend-0.4.0/kubemend/prompts/system.md.j2 +67 -0
- kubemend-0.4.0/kubemend/py.typed +0 -0
- kubemend-0.4.0/kubemend/tools/__init__.py +7 -0
- kubemend-0.4.0/kubemend/tools/base.py +59 -0
- kubemend-0.4.0/kubemend/tools/gitops/__init__.py +6 -0
- kubemend-0.4.0/kubemend/tools/gitops/backend.py +49 -0
- kubemend-0.4.0/kubemend/tools/gitops/gitea_backend.py +112 -0
- kubemend-0.4.0/kubemend/tools/gitops/local_backend.py +90 -0
- kubemend-0.4.0/kubemend/tools/gitops/proposer.py +158 -0
- kubemend-0.4.0/kubemend/tools/gitops/reader.py +153 -0
- kubemend-0.4.0/kubemend/tools/gitops/validator.py +487 -0
- kubemend-0.4.0/kubemend/tools/kubernetes/__init__.py +5 -0
- kubemend-0.4.0/kubemend/tools/kubernetes/api.py +95 -0
- kubemend-0.4.0/kubemend/tools/kubernetes/factory.py +17 -0
- kubemend-0.4.0/kubemend/tools/kubernetes/reader.py +225 -0
- kubemend-0.4.0/kubemend/tools/observability/__init__.py +5 -0
- kubemend-0.4.0/kubemend/tools/observability/loki.py +155 -0
- kubemend-0.4.0/kubemend/tools/observability/prometheus.py +189 -0
- kubemend-0.4.0/kubemend/tools/observability/provider.py +131 -0
- kubemend-0.4.0/kubemend/tools/redact.py +108 -0
- kubemend-0.4.0/kubemend/tools/registry.py +211 -0
- kubemend-0.4.0/kubemend/trace/__init__.py +5 -0
- kubemend-0.4.0/kubemend/trace/cost.py +72 -0
- kubemend-0.4.0/kubemend/trace/meter.py +49 -0
- kubemend-0.4.0/kubemend/trace/recorder.py +151 -0
- kubemend-0.4.0/kubemend/trace/replay.py +24 -0
- kubemend-0.4.0/kubemend/verify/__init__.py +4 -0
- kubemend-0.4.0/kubemend/verify/gate.py +126 -0
- kubemend-0.4.0/kubemend.yaml +81 -0
- kubemend-0.4.0/lab/bootstrap/kind-cluster.yaml +17 -0
- kubemend-0.4.0/lab/bootstrap/rbac.yaml +64 -0
- kubemend-0.4.0/lab/bootstrap/values/argo-cd.yaml +50 -0
- kubemend-0.4.0/lab/bootstrap/values/gitea.yaml +48 -0
- kubemend-0.4.0/lab/bootstrap/values/kube-prometheus-stack.yaml +44 -0
- kubemend-0.4.0/lab/bootstrap/values/kyverno.yaml +24 -0
- kubemend-0.4.0/lab/bootstrap/values/loki.yaml +76 -0
- kubemend-0.4.0/lab/bootstrap/values/promtail.yaml +19 -0
- kubemend-0.4.0/lab/gitops/apps/shop-api/Chart.yaml +6 -0
- kubemend-0.4.0/lab/gitops/apps/shop-api/templates/deployment.yaml +128 -0
- kubemend-0.4.0/lab/gitops/apps/shop-api/templates/resourcequota.yaml +10 -0
- kubemend-0.4.0/lab/gitops/apps/shop-api/templates/service.yaml +28 -0
- kubemend-0.4.0/lab/gitops/apps/shop-api/values.yaml +61 -0
- kubemend-0.4.0/lab/gitops/apps/shop-worker/Chart.yaml +6 -0
- kubemend-0.4.0/lab/gitops/apps/shop-worker/templates/deployment.yaml +77 -0
- kubemend-0.4.0/lab/gitops/apps/shop-worker/templates/service.yaml +23 -0
- kubemend-0.4.0/lab/gitops/apps/shop-worker/values.yaml +31 -0
- kubemend-0.4.0/lab/gitops/argocd/apps/shop-api.yaml +22 -0
- kubemend-0.4.0/lab/gitops/argocd/apps/shop-worker.yaml +22 -0
- kubemend-0.4.0/lab/scenarios/.gitkeep +0 -0
- kubemend-0.4.0/lab/scenarios/bad-env-endpoint/break.patch +13 -0
- kubemend-0.4.0/lab/scenarios/bad-env-endpoint/checker.py +37 -0
- kubemend-0.4.0/lab/scenarios/bad-env-endpoint/scenario.yaml +11 -0
- kubemend-0.4.0/lab/scenarios/bad-image-tag/break.patch +13 -0
- kubemend-0.4.0/lab/scenarios/bad-image-tag/checker.py +34 -0
- kubemend-0.4.0/lab/scenarios/bad-image-tag/scenario.yaml +11 -0
- kubemend-0.4.0/lab/scenarios/bad-probe-path/break.patch +13 -0
- kubemend-0.4.0/lab/scenarios/bad-probe-path/checker.py +35 -0
- kubemend-0.4.0/lab/scenarios/bad-probe-path/scenario.yaml +12 -0
- kubemend-0.4.0/lab/scenarios/fix-needs-template-change/break.patch +12 -0
- kubemend-0.4.0/lab/scenarios/fix-needs-template-change/checker.py +45 -0
- kubemend-0.4.0/lab/scenarios/fix-needs-template-change/scenario.yaml +12 -0
- kubemend-0.4.0/lab/scenarios/log-injection/break.patch +19 -0
- kubemend-0.4.0/lab/scenarios/log-injection/checker.py +78 -0
- kubemend-0.4.0/lab/scenarios/log-injection/scenario.yaml +11 -0
- kubemend-0.4.0/lab/scenarios/missing-configmap-key/break.patch +12 -0
- kubemend-0.4.0/lab/scenarios/missing-configmap-key/checker.py +37 -0
- kubemend-0.4.0/lab/scenarios/missing-configmap-key/scenario.yaml +11 -0
- kubemend-0.4.0/lab/scenarios/oom-limit/break.patch +13 -0
- kubemend-0.4.0/lab/scenarios/oom-limit/checker.py +50 -0
- kubemend-0.4.0/lab/scenarios/oom-limit/scenario.yaml +11 -0
- kubemend-0.4.0/lab/scenarios/quota-conflict/break.patch +13 -0
- kubemend-0.4.0/lab/scenarios/quota-conflict/checker.py +53 -0
- kubemend-0.4.0/lab/scenarios/quota-conflict/scenario.yaml +11 -0
- kubemend-0.4.0/lab/scenarios/scope-trap/break.patch +13 -0
- kubemend-0.4.0/lab/scenarios/scope-trap/checker.py +58 -0
- kubemend-0.4.0/lab/scenarios/scope-trap/scenario.yaml +11 -0
- kubemend-0.4.0/policies/.gitkeep +0 -0
- kubemend-0.4.0/policies/disallow-latest-tag.yaml +38 -0
- kubemend-0.4.0/policies/disallow-privileged.yaml +39 -0
- kubemend-0.4.0/policies/require-labels.yaml +25 -0
- kubemend-0.4.0/policies/require-resource-limits.yaml +32 -0
- kubemend-0.4.0/policies/restrict-registries.yaml +34 -0
- kubemend-0.4.0/pyproject.toml +80 -0
- kubemend-0.4.0/tests/__init__.py +1 -0
- kubemend-0.4.0/tests/fixtures/.gitkeep +0 -0
- kubemend-0.4.0/tests/integration/__init__.py +1 -0
- kubemend-0.4.0/tests/integration/test_lab_read_tools.py +205 -0
- kubemend-0.4.0/tests/unit/__init__.py +1 -0
- kubemend-0.4.0/tests/unit/conftest.py +122 -0
- kubemend-0.4.0/tests/unit/test_anthropic_client.py +90 -0
- kubemend-0.4.0/tests/unit/test_budget.py +83 -0
- kubemend-0.4.0/tests/unit/test_config.py +148 -0
- kubemend-0.4.0/tests/unit/test_container_image.py +161 -0
- kubemend-0.4.0/tests/unit/test_context.py +118 -0
- kubemend-0.4.0/tests/unit/test_cost.py +115 -0
- kubemend-0.4.0/tests/unit/test_eval_runner.py +296 -0
- kubemend-0.4.0/tests/unit/test_fix_needs_template_change_checker.py +106 -0
- kubemend-0.4.0/tests/unit/test_gate.py +195 -0
- kubemend-0.4.0/tests/unit/test_gitops_reader.py +143 -0
- kubemend-0.4.0/tests/unit/test_handoff_and_trace.py +127 -0
- kubemend-0.4.0/tests/unit/test_k8s_reader.py +183 -0
- kubemend-0.4.0/tests/unit/test_kube_factory.py +101 -0
- kubemend-0.4.0/tests/unit/test_lab_handle.py +426 -0
- kubemend-0.4.0/tests/unit/test_llm_conformance.py +465 -0
- kubemend-0.4.0/tests/unit/test_llm_error_handling.py +175 -0
- kubemend-0.4.0/tests/unit/test_log_injection_checker.py +118 -0
- kubemend-0.4.0/tests/unit/test_loop_detector.py +76 -0
- kubemend-0.4.0/tests/unit/test_loop_model_window.py +59 -0
- kubemend-0.4.0/tests/unit/test_meter.py +82 -0
- kubemend-0.4.0/tests/unit/test_missing_configmap_key_checker.py +88 -0
- kubemend-0.4.0/tests/unit/test_observability.py +233 -0
- kubemend-0.4.0/tests/unit/test_packaging.py +88 -0
- kubemend-0.4.0/tests/unit/test_path_policy.py +181 -0
- kubemend-0.4.0/tests/unit/test_prompts.py +61 -0
- kubemend-0.4.0/tests/unit/test_redaction.py +84 -0
- kubemend-0.4.0/tests/unit/test_retry_policy.py +120 -0
- kubemend-0.4.0/tests/unit/test_scenario_loader.py +117 -0
- kubemend-0.4.0/tests/unit/test_scope_trap_checker.py +93 -0
- kubemend-0.4.0/tests/unit/test_smoke.py +12 -0
- kubemend-0.4.0/tests/unit/test_truncation.py +76 -0
- kubemend-0.4.0/tests/unit/test_validator.py +623 -0
- kubemend-0.4.0/tests/unit/test_verification_path.py +169 -0
- kubemend-0.4.0/uv.lock +1117 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Create a new fault-injection scenario named $ARGUMENTS.
|
|
2
|
+
|
|
3
|
+
1. Read docs/knowledge/lab-and-evals.md (scenario format + checker rules) first.
|
|
4
|
+
2. Propose scenario.yaml (scope, task_prompt written as an on-call human would phrase it, expected_outcome, symptom_probe) and the break.patch against lab/gitops — show me both before writing files.
|
|
5
|
+
3. Write checker.py asserting PROPERTIES only (never diff equality); include at least: gate passed (or handoff-without-PR for negative scenarios), scope compliance, and one scenario-specific rendered-value predicate.
|
|
6
|
+
4. Run it once end-to-end with the cheap model: `task evals -- -s $ARGUMENTS -n 1 --model cheap`, and paste the checker report.
|
|
7
|
+
5. If it fails, write the triage note per the eval rules before changing anything.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Replay and analyze trace $ARGUMENTS.
|
|
2
|
+
|
|
3
|
+
1. Run `kubemend trace replay $ARGUMENTS` and reconstruct the run timeline: iterations, tool calls with durations/raw_bytes, truncations, verdicts, cost.
|
|
4
|
+
2. Identify the decisive moment (wrong hypothesis, missed evidence due to truncation, loop, gate failure) with event references.
|
|
5
|
+
3. Recommend exactly one of: new unit fixture, new scenario, tool contract change, prompt change — with a one-paragraph justification referencing harness-design.md trade-offs.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Run an eval sweep and summarize.
|
|
2
|
+
|
|
3
|
+
1. Ensure the lab is up (`task lab:up` is idempotent).
|
|
4
|
+
2. Run: `task evals -- --scenarios all -n ${1:-5} --model ${2:-cheap}`.
|
|
5
|
+
3. Present the report table (pass, iters avg, cost avg, p95 wall) and compare against the latest committed report in evals/reports/ — call out every regression explicitly.
|
|
6
|
+
4. For each scenario below its previous pass rate, open the worst failing trace and give a one-paragraph diagnosis per docs/knowledge/lab-and-evals.md triage rules. Do NOT change prompts or code in this session.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(task test:*)", "Bash(task lint:*)", "Bash(task evals:*)",
|
|
5
|
+
"Bash(task lab:up)", "Bash(task lab:forward)",
|
|
6
|
+
"Bash(uv run:*)", "Bash(uv add:*)",
|
|
7
|
+
"Bash(git status)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(git add:*)", "Bash(git commit:*)"
|
|
8
|
+
],
|
|
9
|
+
"deny": [
|
|
10
|
+
"Bash(kubectl apply:*)", "Bash(kubectl delete:*)", "Bash(kubectl edit:*)", "Bash(kubectl patch:*)",
|
|
11
|
+
"Bash(helm install:*)", "Bash(helm upgrade:*)", "Bash(helm uninstall:*)",
|
|
12
|
+
"Bash(git push --force:*)", "Bash(git push origin main:*)",
|
|
13
|
+
"Read(.lab/**)"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
|
|
18
|
+
- uses: go-task/setup-task@v1
|
|
19
|
+
with:
|
|
20
|
+
version: 3.x
|
|
21
|
+
|
|
22
|
+
- name: Install dependencies
|
|
23
|
+
run: uv sync --locked
|
|
24
|
+
|
|
25
|
+
# ruff check + ruff format --check + mypy --strict, zero warnings.
|
|
26
|
+
- name: Lint and typecheck
|
|
27
|
+
run: task lint
|
|
28
|
+
|
|
29
|
+
# Unit tests only; `lab`-marked integration tests need a kind cluster
|
|
30
|
+
# and `docker`-marked ones need a Docker daemon — both are excluded by
|
|
31
|
+
# the default pytest addopts.
|
|
32
|
+
- name: Unit tests
|
|
33
|
+
run: task test
|
|
34
|
+
|
|
35
|
+
# helm lint + a render smoke test across the chart's real value
|
|
36
|
+
# permutations (default, cluster-scoped RBAC, job-enabled).
|
|
37
|
+
- name: Helm chart lint
|
|
38
|
+
run: task chart:lint
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Triggered by pushing an annotated tag (v0.1, v0.2, ...). GitHub reads this
|
|
4
|
+
# workflow from the tag's own commit, so it only fires for tags pushed after
|
|
5
|
+
# this file first landed on main. workflow_dispatch is a manual fallback to
|
|
6
|
+
# (re-)publish the release for a tag that already exists, without pushing
|
|
7
|
+
# anything new — e.g. if the tag-push run failed partway through.
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
tag:
|
|
14
|
+
description: "Existing tag to (re-)publish a release for"
|
|
15
|
+
required: true
|
|
16
|
+
type: string
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: write
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
ref: ${{ inputs.tag || github.ref }}
|
|
27
|
+
|
|
28
|
+
- name: Create GitHub Release
|
|
29
|
+
uses: softprops/action-gh-release@v2
|
|
30
|
+
with:
|
|
31
|
+
tag_name: ${{ inputs.tag || github.ref_name }}
|
|
32
|
+
# Auto-generated from merged PRs since the previous tag (or since
|
|
33
|
+
# the repo's start, for the first release).
|
|
34
|
+
generate_release_notes: true
|
|
35
|
+
|
|
36
|
+
# -- M8a: multi-arch container image, published to ghcr.io ------------------
|
|
37
|
+
#
|
|
38
|
+
# Native arm64 + amd64 runners (ubuntu-24.04-arm is free and unlimited on
|
|
39
|
+
# public repos as of 2025) rather than QEMU emulation — each leg builds and
|
|
40
|
+
# pushes its own single-arch image by digest, and merge-manifest below
|
|
41
|
+
# combines the two digests into one multi-arch manifest under the release
|
|
42
|
+
# tag. Splitting it this way is the documented way to avoid QEMU: a single
|
|
43
|
+
# buildx invocation targeting both platforms on one runner would emulate
|
|
44
|
+
# whichever platform isn't native to that runner.
|
|
45
|
+
build-container:
|
|
46
|
+
strategy:
|
|
47
|
+
matrix:
|
|
48
|
+
include:
|
|
49
|
+
- platform: linux/amd64
|
|
50
|
+
runs-on: ubuntu-24.04
|
|
51
|
+
digest-name: amd64
|
|
52
|
+
- platform: linux/arm64
|
|
53
|
+
runs-on: ubuntu-24.04-arm
|
|
54
|
+
digest-name: arm64
|
|
55
|
+
runs-on: ${{ matrix.runs-on }}
|
|
56
|
+
permissions:
|
|
57
|
+
packages: write
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
with:
|
|
61
|
+
ref: ${{ inputs.tag || github.ref }}
|
|
62
|
+
|
|
63
|
+
- uses: docker/setup-buildx-action@v3
|
|
64
|
+
|
|
65
|
+
- uses: docker/login-action@v3
|
|
66
|
+
with:
|
|
67
|
+
registry: ghcr.io
|
|
68
|
+
username: ${{ github.actor }}
|
|
69
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
70
|
+
|
|
71
|
+
- name: Build and push by digest
|
|
72
|
+
id: build
|
|
73
|
+
uses: docker/build-push-action@v6
|
|
74
|
+
with:
|
|
75
|
+
platforms: ${{ matrix.platform }}
|
|
76
|
+
push: true
|
|
77
|
+
outputs: type=image,name=ghcr.io/${{ github.repository }},push-by-digest=true,name-canonical=true
|
|
78
|
+
|
|
79
|
+
- name: Export digest for the merge job
|
|
80
|
+
run: |
|
|
81
|
+
mkdir -p /tmp/digests
|
|
82
|
+
digest="${{ steps.build.outputs.digest }}"
|
|
83
|
+
touch "/tmp/digests/${digest#sha256:}"
|
|
84
|
+
|
|
85
|
+
- uses: actions/upload-artifact@v4
|
|
86
|
+
with:
|
|
87
|
+
name: digests-${{ matrix.digest-name }}
|
|
88
|
+
path: /tmp/digests/*
|
|
89
|
+
if-no-files-found: error
|
|
90
|
+
retention-days: 1
|
|
91
|
+
|
|
92
|
+
merge-manifest:
|
|
93
|
+
needs: build-container
|
|
94
|
+
runs-on: ubuntu-24.04
|
|
95
|
+
permissions:
|
|
96
|
+
packages: write
|
|
97
|
+
steps:
|
|
98
|
+
- uses: actions/download-artifact@v4
|
|
99
|
+
with:
|
|
100
|
+
path: /tmp/digests
|
|
101
|
+
pattern: digests-*
|
|
102
|
+
merge-multiple: true
|
|
103
|
+
|
|
104
|
+
- uses: docker/login-action@v3
|
|
105
|
+
with:
|
|
106
|
+
registry: ghcr.io
|
|
107
|
+
username: ${{ github.actor }}
|
|
108
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
109
|
+
|
|
110
|
+
- name: Create the multi-arch manifest and push it
|
|
111
|
+
working-directory: /tmp/digests
|
|
112
|
+
run: |
|
|
113
|
+
set -eu
|
|
114
|
+
IMAGE=ghcr.io/${{ github.repository }}
|
|
115
|
+
TAG=${{ inputs.tag || github.ref_name }}
|
|
116
|
+
refs=()
|
|
117
|
+
for digest in *; do
|
|
118
|
+
refs+=("$IMAGE@sha256:$digest")
|
|
119
|
+
done
|
|
120
|
+
docker buildx imagetools create -t "$IMAGE:$TAG" -t "$IMAGE:latest" "${refs[@]}"
|
|
121
|
+
|
|
122
|
+
# -- M8a: PyPI publish --------------------------------------------------
|
|
123
|
+
#
|
|
124
|
+
# Trusted publishing (OIDC, no stored secret) — requires a one-time,
|
|
125
|
+
# manual registration on PyPI's own site first: pypi.org project settings
|
|
126
|
+
# -> "Add a new pending publisher" -> this repo, this workflow filename,
|
|
127
|
+
# environment left blank unless one is added below. Until that's done,
|
|
128
|
+
# this job will fail at the publish step with an authorization error;
|
|
129
|
+
# the build step itself has no such dependency and always works.
|
|
130
|
+
publish-pypi:
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
permissions:
|
|
133
|
+
id-token: write
|
|
134
|
+
steps:
|
|
135
|
+
- uses: actions/checkout@v4
|
|
136
|
+
with:
|
|
137
|
+
ref: ${{ inputs.tag || github.ref }}
|
|
138
|
+
|
|
139
|
+
- uses: astral-sh/setup-uv@v5
|
|
140
|
+
|
|
141
|
+
- name: Build sdist + wheel
|
|
142
|
+
run: uv build
|
|
143
|
+
|
|
144
|
+
- name: Publish to PyPI
|
|
145
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.mypy_cache/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.coverage
|
|
11
|
+
htmlcov/
|
|
12
|
+
|
|
13
|
+
# Lab credentials and generated kubeconfigs (never committed)
|
|
14
|
+
.lab/
|
|
15
|
+
|
|
16
|
+
# Per-run JSONL traces
|
|
17
|
+
traces/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|