Skip to content

Add Helm o6n deploy path - #590

Merged
delete-cloud merged 2 commits into
mainfrom
feat/deploy-helm-o6n
Jun 13, 2026
Merged

delete-cloud merged 2 commits into
mainfrom
feat/deploy-helm-o6n

Conversation

@delete-cloud

@delete-cloud delete-cloud commented Jun 13, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add o6n DeepSeek Helm values with NodePort, Secret references, NetworkPolicy egress, and runtime bootstrap coverage
  • switch the manual Woodpecker deploy script to Helm dry-run/apply, with a CI-built kubectl+helm deploy-tools image
  • wire chart base_url and observability endpoint_env into runtime config parsing

Verification

  • uv run pytest tests/deploy/test_deploy_script.py tests/deploy/test_helm_chart.py tests/agentkit/config/test_loader.py::TestLoadConfig::test_load_toml_exposes_agent_base_url tests/coding_agent/test_bootstrap.py tests/coding_agent/test_observability.py::test_build_observation_sink_supports_endpoint_env tests/coding_agent/test_observability.py::test_build_observation_sink_rejects_ambiguous_endpoint_config -q
  • helm lint ./helm
  • helm template coding-agent ./helm -f helm/values-o6n-deepseek.yaml >/tmp/coding-agent-o6n-render.yaml
  • uv run ruff format --check src/agentkit/config/loader.py src/coding_agent/app.py src/coding_agent/observability.py tests/agentkit/config/test_loader.py tests/coding_agent/test_bootstrap.py tests/coding_agent/test_observability.py tests/deploy/test_helm_chart.py tests/deploy/test_deploy_script.py
  • uv run ruff check src/agentkit/config/loader.py src/coding_agent/app.py src/coding_agent/observability.py tests/agentkit/config/test_loader.py tests/coding_agent/test_bootstrap.py tests/coding_agent/test_observability.py tests/deploy/test_helm_chart.py tests/deploy/test_deploy_script.py
  • local subagent review: final P1/P2 clean

Summary by CodeRabbit

  • New Features

    • Helm-based Kubernetes deployment system with dry-run and apply modes
    • Observability integration with Langfuse tracing support
    • Configurable agent base URL for API endpoint customization
    • NodePort service configuration option for network access
  • Deployment Updates

    • New deploy-tools container image with kubectl and helm binaries
    • Enhanced Kubernetes deployment configuration with environment-based settings
  • Documentation

    • Expanded Phase 2 Kubernetes deployment runbook with Helm guidance
    • Updated Woodpecker CI pipeline documentation with Helm workflow details

@coderabbitai

coderabbitai Bot commented Jun 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@delete-cloud, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 51 minutes and 41 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 33277c1e-b239-47db-a650-26684b758484

📥 Commits

Reviewing files that changed from the base of the PR and between 71d181b and 8a41e7c.

📒 Files selected for processing (4)
  • coding-agent/helm/values.yaml
  • coding-agent/tests/deploy/test_deploy_script.py
  • docker/deploy-tools.Dockerfile
  • scripts/deploy-coding-agent-k8s.sh
📝 Walkthrough

Walkthrough

This PR introduces Helm-based deployment infrastructure for coding-agent by adding configurable agent base_url support, flexible observability endpoint resolution, deploy-tools Docker image with kubectl/helm, and comprehensive Helm values/templates including a complete o6n DeepSeek configuration. The deployment script converts from manual kubectl commands to Helm upgrade/install with dry-run and apply modes.

Changes

Helm Deployment Infrastructure and Agent Configuration

Layer / File(s) Summary
Agent base_url configuration and LLM provider resolution
coding-agent/src/agentkit/config/loader.py, coding-agent/src/coding_agent/app.py, coding-agent/tests/agentkit/config/test_loader.py, coding-agent/tests/coding_agent/test_bootstrap.py
AgentConfig adds optional base_url field; load_config() reads from [agent] TOML section; create_child_pipeline resolves LLM provider base_url with fallback precedence: override → env → cfg.base_url. Tests verify config defaults and bootstrap flow.
Observability endpoint flexibility with endpoint_env support
coding-agent/src/coding_agent/observability.py, coding-agent/tests/coding_agent/test_observability.py
_required_env() accepts optional purpose parameter for customized error messages; new _tracing_endpoint() validates and resolves from either endpoint or endpoint_env with mutual-exclusion enforcement; _has_flat_tracing_config recognizes endpoint_env. Tests expanded to cover OTLP HTTP endpoint loading from env, ambiguous configuration rejection, and tracing/metrics combinations.
Helm values schema: service, agent config, and observability defaults
coding-agent/helm/values.yaml
Adds service.nodePort, agent.config.baseUrl, and new top-level observability block with enabled, backend, endpoint/endpointEnv, timeoutSeconds, headers, publicKeyEnv, and secretKeyEnv fields.
Helm templates: conditional service nodePort and agent config rendering
coding-agent/helm/templates/service.yaml, coding-agent/helm/templates/configmap-agent-config.yaml
service.yaml conditionally renders nodePort only when service.type=NodePort; configmap-agent-config.yaml adds optional base_url in [agent], removes topic from agent.plugins.enabled, and adds conditional [observability] section with backend/endpoint validation and optional header generation.
O6N DeepSeek complete Helm values configuration
coding-agent/helm/values-o6n-deepseek.yaml
Fully defines coding-agent deployment: image (git.mesh.kinaz.me/kina/coding-agent:main), bearer token auth and CORS origin, NodePort service on 30082, DeepSeek provider/model with approvalPolicy: auto and sandbox/timeout constraints, Langfuse observability with environment-backed keys, network policy egress to 100.123.220.136/32, and CPU/memory limits.
Deploy-tools Docker image and deployment script Helm integration
docker/deploy-tools.Dockerfile, scripts/deploy-coding-agent-k8s.sh
Dockerfile builds from Python base with parameterized KUBECTL_VERSION/HELM_VERSION, architecture detection, SHA-256 checksum verification, and binary installation to /usr/local/bin. Script converts to helm upgrade --install with Helm environment configuration (HELM_DEPLOY_MODE, HELM_DRIVER, HELM_RELEASE, HELM_CHART_DIR, HELM_VALUES); dry-run mode renders without applying; apply mode executes upgrade, waits for rollout, and performs health check.
CI pipeline: deploy-tools build job and deploy-o6n environment configuration
.woodpecker/ci.yml, .woodpecker/deploy.yml
ci.yml adds build-deploy-tools Kaniko job on main push to build and push deploy-tools image with versioned kubectl/helm tags. deploy.yml updates deploy-o6n image to combined kubectl/helm tooling and adds Helm environment variables with defaults (dry-run mode, configmap driver).
Deploy script and Helm chart integration tests
coding-agent/tests/deploy/test_deploy_script.py, coding-agent/tests/deploy/test_helm_chart.py
test_deploy_script.py validates default behavior (Helm dry-run without kubectl), apply mode (upgrade + rollout + health check), and CI/CD wiring (Woodpecker config and Dockerfile assertions). test_helm_chart.py extended with o6n DeepSeek tests: base_url override rendering, runtime contract validation (service/image/auth/observability/policy), and bootstrap integration via create_agent.
Deployment documentation: Woodpecker and Phase 2 K8s runbooks
coding-agent/docs/deployment/woodpecker.md, coding-agent/docs/deployment/phase2-k8s.md
woodpecker.md documents manual Helm deploy default (dry-run), deploy-tools image, Helm environment variables, RBAC/permissions for configmap driver, and apply mode checklist. phase2-k8s.md documents conditional service.nodePort rendering and o6n DeepSeek values preservation requirements (non-secret config items, secret names/keys only, Langfuse egress exception).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • delete-cloud/Coding-Agent-Loop#589: Both PRs configure Helm networkPolicy settings; main PR adds networkPolicy.extraEgress in values-o6n-deepseek.yaml while retrieved PR manages Helm NetworkPolicy template/defaults.
  • delete-cloud/Coding-Agent-Loop#543: Both PRs modify create_child_pipeline in coding-agent/src/coding_agent/app.py; main PR adjusts LLM base_url resolution while retrieved PR propagates additional_workspace_roots_override.
  • delete-cloud/Coding-Agent-Loop#390: Main PR introduces Kaniko build job in .woodpecker/ci.yml using registry_username/registry_password Docker auth, directly aligned with retrieved PR's Docker auth pattern in same file.

🐰 Helm charts and configs align so fair,
Deploy-tools built with manifest care,
Base URLs flow and observability shines,
O6N DeepSeek in values well-defined,
From dry-run checks to rollout waits,
Kubernetes deployment now celebrates! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Helm o6n deploy path' clearly and specifically summarizes the main change—introducing Helm-based deployment for the o6n (mesh) configuration with a DeepSeek agent.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/deploy-helm-o6n

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docker/deploy-tools.Dockerfile (1)

1-32: ⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Add a non-root USER directive.

The image runs as root, violating the principle of least privilege. Deploy tooling should run with minimal permissions.

🔒 Proposed fix to add a non-root user
 RUN set -eux; \
     apt-get update; \
     apt-get install -y --no-install-recommends ca-certificates curl tar gzip; \
     arch="${TARGETARCH:-$(dpkg --print-architecture)}"; \
     case "$arch" in \
       amd64|x86_64) arch="amd64" ;; \
       arm64|aarch64) arch="arm64" ;; \
       *) echo "unsupported architecture: $arch" >&2; exit 1 ;; \
     esac; \
     kubectl_url="https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/${arch}/kubectl"; \
     curl -fsSL -o /tmp/kubectl "${kubectl_url}"; \
     curl -fsSL -o /tmp/kubectl.sha256 "${kubectl_url}.sha256"; \
     echo "$(cat /tmp/kubectl.sha256)  /tmp/kubectl" | sha256sum -c -; \
     install -m 0755 /tmp/kubectl /usr/local/bin/kubectl; \
     helm_archive="helm-v${HELM_VERSION}-linux-${arch}.tar.gz"; \
     helm_url="https://get.helm.sh/${helm_archive}"; \
     curl -fsSL -o /tmp/helm.tgz "${helm_url}"; \
     curl -fsSL -o /tmp/helm.tgz.sha256sum "${helm_url}.sha256sum"; \
     echo "$(cut -d ' ' -f 1 /tmp/helm.tgz.sha256sum)  /tmp/helm.tgz" | sha256sum -c -; \
     tar -xzf /tmp/helm.tgz -C /tmp; \
     mv "/tmp/linux-${arch}/helm" /usr/local/bin/helm; \
     chmod 0755 /usr/local/bin/helm; \
     rm -rf /tmp/kubectl /tmp/kubectl.sha256 /tmp/helm.tgz /tmp/helm.tgz.sha256sum "/tmp/linux-${arch}"; \
-    rm -rf /var/lib/apt/lists/*
+    rm -rf /var/lib/apt/lists/*; \
+    useradd -r -u 10001 -g 0 deploy-user
+
+USER 10001:0
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker/deploy-tools.Dockerfile` around lines 1 - 32, The image currently runs
as root (the FROM ${PYTHON_BASE_IMAGE} + RUN block installs kubectl/helm into
/usr/local/bin), so add a non-root user and switch to it with a USER directive.
Modify the Dockerfile to: 1) after installing and moving kubectl/helm in the
existing RUN block, create a non-root group/user (e.g., groupadd/useradd or
addgroup/adduser) and set a fixed UID/GID; 2) chown the installed binaries and
any runtime directories (e.g., /usr/local/bin/kubectl, /usr/local/bin/helm and
any HOME) to that user inside the same RUN so ownership is correct; 3) set ENV
HOME to the new user home if needed; and 4) add USER <username> near the end of
the Dockerfile so subsequent containers run unprivileged. Ensure all privileged
install steps remain before the USER change so installs succeed.

Source: Linters/SAST tools

🧹 Nitpick comments (2)
scripts/deploy-coding-agent-k8s.sh (1)

42-58: ⚡ Quick win

Consider using a bash array for helm arguments.

The current approach builds helm_args as a multi-line string and relies on unquoted expansion for word splitting. While this works with the current controlled inputs, it is fragile and will break if any value contains spaces or special characters.

♻️ Proposed refactor using bash array
-helm_args="
-upgrade --install ${HELM_RELEASE} ${HELM_CHART_DIR}
---namespace ${K8S_NAMESPACE}
---values ${HELM_VALUES}
---set image.repository=${IMAGE_REPOSITORY}
---set image.tag=${IMAGE_TAG}
---wait
---timeout ${ROLLOUT_TIMEOUT}
-"
+helm_args=(
+  upgrade --install "${HELM_RELEASE}" "${HELM_CHART_DIR}"
+  --namespace "${K8S_NAMESPACE}"
+  --values "${HELM_VALUES}"
+  --set "image.repository=${IMAGE_REPOSITORY}"
+  --set "image.tag=${IMAGE_TAG}"
+  --wait
+  --timeout "${ROLLOUT_TIMEOUT}"
+)

 if [ "$HELM_DEPLOY_MODE" = "dry-run" ]; then
   # Default mode proves the rendered release without mutating the cluster.
-  helm $helm_args --dry-run=client
+  helm "${helm_args[@]}" --dry-run=client
   exit 0
 fi

-helm $helm_args
+helm "${helm_args[@]}"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy-coding-agent-k8s.sh` around lines 42 - 58, The helm argument
construction uses a multi-line string variable helm_args and unquoted expansion
which is brittle; change helm_args into a bash array (e.g., helm_args=(upgrade
--install "${HELM_RELEASE}" "${HELM_CHART_DIR}" --namespace "${K8S_NAMESPACE}"
--values "${HELM_VALUES}" --set "image.repository=${IMAGE_REPOSITORY}" --set
"image.tag=${IMAGE_TAG}" --wait --timeout "${ROLLOUT_TIMEOUT}")) and update both
invocations to call helm "${helm_args[@]}" (and helm "${helm_args[@]}"
--dry-run=client in the HELM_DEPLOY_MODE dry-run branch) so each value is
properly quoted and word-splitting is safe; ensure you use array append or
literal array initialization where helm_args is defined and replace the existing
string usage accordingly.
coding-agent/helm/values.yaml (1)

100-108: 💤 Low value

Consider using null instead of empty strings for consistency.

The observability defaults use empty strings (backend: "", endpoint: "", endpointEnv: "") while other optional fields in the schema use null (e.g., nodePort: null, baseUrl: null). Using null consistently would better express "no value provided" vs. "explicitly set to empty."

♻️ Proposed consistency improvement
 observability:
   enabled: false
-  backend: ""
-  endpoint: ""
-  endpointEnv: ""
+  backend: null
+  endpoint: null
+  endpointEnv: null
   timeoutSeconds: 2.0
   headers: {}
   publicKeyEnv: LANGFUSE_PUBLIC_KEY
   secretKeyEnv: LANGFUSE_SECRET_KEY
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@coding-agent/helm/values.yaml` around lines 100 - 108, The observability
defaults use empty strings for optional fields which is inconsistent with other
schema fields that use null; change observability.backend,
observability.endpoint, and observability.endpointEnv from "" to null so they
consistently represent "no value provided" (leave other keys like enabled,
timeoutSeconds, headers, publicKeyEnv, secretKeyEnv unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@docker/deploy-tools.Dockerfile`:
- Around line 1-32: The image currently runs as root (the FROM
${PYTHON_BASE_IMAGE} + RUN block installs kubectl/helm into /usr/local/bin), so
add a non-root user and switch to it with a USER directive. Modify the
Dockerfile to: 1) after installing and moving kubectl/helm in the existing RUN
block, create a non-root group/user (e.g., groupadd/useradd or addgroup/adduser)
and set a fixed UID/GID; 2) chown the installed binaries and any runtime
directories (e.g., /usr/local/bin/kubectl, /usr/local/bin/helm and any HOME) to
that user inside the same RUN so ownership is correct; 3) set ENV HOME to the
new user home if needed; and 4) add USER <username> near the end of the
Dockerfile so subsequent containers run unprivileged. Ensure all privileged
install steps remain before the USER change so installs succeed.

---

Nitpick comments:
In `@coding-agent/helm/values.yaml`:
- Around line 100-108: The observability defaults use empty strings for optional
fields which is inconsistent with other schema fields that use null; change
observability.backend, observability.endpoint, and observability.endpointEnv
from "" to null so they consistently represent "no value provided" (leave other
keys like enabled, timeoutSeconds, headers, publicKeyEnv, secretKeyEnv
unchanged).

In `@scripts/deploy-coding-agent-k8s.sh`:
- Around line 42-58: The helm argument construction uses a multi-line string
variable helm_args and unquoted expansion which is brittle; change helm_args
into a bash array (e.g., helm_args=(upgrade --install "${HELM_RELEASE}"
"${HELM_CHART_DIR}" --namespace "${K8S_NAMESPACE}" --values "${HELM_VALUES}"
--set "image.repository=${IMAGE_REPOSITORY}" --set "image.tag=${IMAGE_TAG}"
--wait --timeout "${ROLLOUT_TIMEOUT}")) and update both invocations to call helm
"${helm_args[@]}" (and helm "${helm_args[@]}" --dry-run=client in the
HELM_DEPLOY_MODE dry-run branch) so each value is properly quoted and
word-splitting is safe; ensure you use array append or literal array
initialization where helm_args is defined and replace the existing string usage
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 834bac96-ba5b-4bd7-8177-723ae13883a5

📥 Commits

Reviewing files that changed from the base of the PR and between 1494724 and 71d181b.

📒 Files selected for processing (18)
  • .woodpecker/ci.yml
  • .woodpecker/deploy.yml
  • coding-agent/docs/deployment/phase2-k8s.md
  • coding-agent/docs/deployment/woodpecker.md
  • coding-agent/helm/templates/configmap-agent-config.yaml
  • coding-agent/helm/templates/service.yaml
  • coding-agent/helm/values-o6n-deepseek.yaml
  • coding-agent/helm/values.yaml
  • coding-agent/src/agentkit/config/loader.py
  • coding-agent/src/coding_agent/app.py
  • coding-agent/src/coding_agent/observability.py
  • coding-agent/tests/agentkit/config/test_loader.py
  • coding-agent/tests/coding_agent/test_bootstrap.py
  • coding-agent/tests/coding_agent/test_observability.py
  • coding-agent/tests/deploy/test_deploy_script.py
  • coding-agent/tests/deploy/test_helm_chart.py
  • docker/deploy-tools.Dockerfile
  • scripts/deploy-coding-agent-k8s.sh

@delete-cloud
delete-cloud merged commit cf07d79 into main Jun 13, 2026
2 checks passed
@delete-cloud
delete-cloud deleted the feat/deploy-helm-o6n branch June 13, 2026 07:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant