From 9634e0bd2cfc785487e7e9499642cda5ea45b5e6 Mon Sep 17 00:00:00 2001 From: hey-intent Date: Sat, 2 May 2026 10:32:56 +0200 Subject: [PATCH] fix: ingress configuration fix: commands --- ansible/playbook.yml | 23 ++++++++++------------- app/agent_orchestrator.py | 3 ++- k8s/orchestrator.env.example | 1 + k8s/orchestrator.yaml | 10 +++++----- providers/source/__init__.py | 12 +++++++++++- providers/source/github.py | 5 ++--- providers/source/models.py | 4 +++- tests/app/test_webhook.py | 4 ++++ 8 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 k8s/orchestrator.env.example diff --git a/ansible/playbook.yml b/ansible/playbook.yml index 4d26d04..63451e7 100644 --- a/ansible/playbook.yml +++ b/ansible/playbook.yml @@ -20,6 +20,7 @@ - git - curl - jq + - gettext-base state: present update_cache: true - name: Add Docker GPG key @@ -251,22 +252,18 @@ when: openrouter_api_key | length > 0 # no_log: true - - name: Copy orchestrator manifest to temporary file + - name: Write .env.k8s on VPS ansible.builtin.copy: - remote_src: true - src: "{{ project_dir }}/k8s/orchestrator.yaml" - dest: /tmp/orchestrator-rendered.yaml - mode: "0644" - - - name: Patch ingress host in temporary manifest - ansible.builtin.replace: - path: /tmp/orchestrator-rendered.yaml - regexp: "bot\\.yourdomain\\.com" - replace: "{{ ingress_host }}" + dest: "{{ project_dir }}/.env.k8s" + content: "ORCHESTRATOR_HOST={{ ingress_host }}\n" + mode: "0600" - name: Apply orchestrator (Deployment + Service + Ingress) - ansible.builtin.command: - cmd: "k3s kubectl apply -f /tmp/orchestrator-rendered.yaml" + ansible.builtin.shell: | + set -a && . {{ project_dir }}/.env.k8s && set +a + envsubst < {{ project_dir }}/k8s/orchestrator.yaml | k3s kubectl apply -f - + args: + executable: /bin/bash register: apply_orchestrator changed_when: "'created' in apply_orchestrator.stdout or 'configured' in apply_orchestrator.stdout" diff --git a/app/agent_orchestrator.py b/app/agent_orchestrator.py index 5f521e9..4b1a53a 100644 --- a/app/agent_orchestrator.py +++ b/app/agent_orchestrator.py @@ -15,10 +15,11 @@ SourceProviderConfigurationError, SourceProviderError, ) +from providers.source.models import AGENT_ACTION_NAMES MAX_ISSUE_BODY_CHARS = 65536 -IMPLEMENTED_ACTIONS = {"implement"} +IMPLEMENTED_ACTIONS = AGENT_ACTION_NAMES AGENT_STATUS_PREFIX = "agent:status:" AGENT_STATUS_IDLE = "agent:status:idle" AGENT_STATUS_RUNNING = "agent:status:running" diff --git a/k8s/orchestrator.env.example b/k8s/orchestrator.env.example new file mode 100644 index 0000000..b376959 --- /dev/null +++ b/k8s/orchestrator.env.example @@ -0,0 +1 @@ +ORCHESTRATOR_HOST=bot.yourdomain.com diff --git a/k8s/orchestrator.yaml b/k8s/orchestrator.yaml index 76d1038..02adca4 100644 --- a/k8s/orchestrator.yaml +++ b/k8s/orchestrator.yaml @@ -28,8 +28,8 @@ spec: secretKeyRef: name: orchestrator-config key: JOB_TTL_SECONDS - - name: PROVIDER_LABEL_PREFIX - value: "ai-pr-" + - name: PROVIDER_LABEL_PREFIX + value: "ai-pr-" - name: ADMIN_TOKEN valueFrom: secretKeyRef: @@ -94,12 +94,12 @@ metadata: traefik.ingress.kubernetes.io/router.tls.certresolver: le spec: ingressClassName: traefik - # Replace bot.yourdomain.com with your domain (Ansible does this automatically) + # Set ORCHESTRATOR_HOST before applying: envsubst < k8s/orchestrator.yaml | kubectl apply -f - tls: - hosts: - - bot.yourdomain.com + - ${ORCHESTRATOR_HOST} rules: - - host: bot.yourdomain.com + - host: ${ORCHESTRATOR_HOST} http: paths: - path: /webhook/github diff --git a/providers/source/__init__.py b/providers/source/__init__.py index ad69709..0f86b16 100644 --- a/providers/source/__init__.py +++ b/providers/source/__init__.py @@ -1,8 +1,18 @@ from .base import SourceProvider, SourceProviderAPIError, SourceProviderConfigurationError, SourceProviderError from .factory import get_provider, register_provider -from .models import ActionName, ActionTrigger, Comment, Issue, PullRequest, WebhookEvent, WebhookEventType +from .models import ( + AGENT_ACTION_NAMES, + ActionName, + ActionTrigger, + Comment, + Issue, + PullRequest, + WebhookEvent, + WebhookEventType, +) __all__ = [ + "AGENT_ACTION_NAMES", "ActionName", "ActionTrigger", "Comment", diff --git a/providers/source/github.py b/providers/source/github.py index 3aae0d8..133dac6 100644 --- a/providers/source/github.py +++ b/providers/source/github.py @@ -12,7 +12,7 @@ import jwt from .base import SourceProvider, SourceProviderAPIError, SourceProviderConfigurationError -from .models import ActionTrigger, Comment, Issue, PullRequest, WebhookEvent +from .models import AGENT_ACTION_NAMES, ActionTrigger, Comment, Issue, PullRequest, WebhookEvent logger = logging.getLogger("uvicorn.error") @@ -21,7 +21,6 @@ JWT_LEEWAY_SECONDS = 60 JWT_TTL_SECONDS = 600 DEFAULT_INSTALLATION_TOKEN_TTL_SECONDS = 3300 -AGENT_ACTIONS = {"spec", "plan", "implement", "review", "continue"} ACTION_TRIGGER_COMMAND_MAX_CHARS = 64 ACTION_TRIGGER_ALLOWED_PERMISSIONS = {"write", "maintain", "admin"} @@ -177,7 +176,7 @@ def get_action_trigger(self, event: WebhookEvent) -> ActionTrigger | None: if len(parts) < 2 or parts[0] != "/agent": continue action = parts[1].lower() - if action not in AGENT_ACTIONS: + if action not in AGENT_ACTION_NAMES: continue raw_command = f"/agent {action}"[:ACTION_TRIGGER_COMMAND_MAX_CHARS] return ActionTrigger(action=action, source="comment", raw_command=raw_command) diff --git a/providers/source/models.py b/providers/source/models.py index a225110..beeee44 100644 --- a/providers/source/models.py +++ b/providers/source/models.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import Literal +from typing import Literal, get_args from pydantic import BaseModel, Field @@ -44,6 +44,8 @@ class PullRequest(BaseModel): ActionName = Literal["spec", "plan", "implement", "review", "continue"] +AGENT_ACTION_NAMES: frozenset[str] = frozenset(get_args(ActionName)) + class ActionTrigger(BaseModel): action: ActionName diff --git a/tests/app/test_webhook.py b/tests/app/test_webhook.py index 121587f..0eef991 100644 --- a/tests/app/test_webhook.py +++ b/tests/app/test_webhook.py @@ -245,6 +245,10 @@ async def test_normal_issue_comment_does_not_trigger_job(monkeypatch): @pytest.mark.asyncio async def test_known_but_unimplemented_action_sets_awaiting_human(monkeypatch): + import app.agent_orchestrator as agent_orchestrator_module + + monkeypatch.setattr(agent_orchestrator_module, "IMPLEMENTED_ACTIONS", frozenset({"implement"})) + payload = load_payload("issue_labeled.json") event = issue_comment_event(payload, body="/agent plan")