omneval-devloop 0.0.2__tar.gz → 0.0.4__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.
Files changed (91) hide show
  1. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/build-image.yml +27 -8
  2. omneval_devloop-0.0.4/.github/workflows/publish-devloop-chart.yml +50 -0
  3. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/release.yml +9 -2
  4. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/PKG-INFO +1 -1
  5. omneval_devloop-0.0.4/charts/devloop/Chart.yaml +10 -0
  6. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/templates/discord-bot-deployment.yaml +24 -0
  7. omneval_devloop-0.0.4/charts/devloop/templates/poller-deployment.yaml +103 -0
  8. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/templates/temporal-worker-deployment.yaml +10 -0
  9. omneval_devloop-0.0.4/charts/devloop/tests/discord-bot_test.yaml +43 -0
  10. omneval_devloop-0.0.4/charts/devloop/tests/poller_test.yaml +120 -0
  11. omneval_devloop-0.0.4/charts/devloop/tests/temporal-worker_test.yaml +74 -0
  12. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/values.yaml +47 -2
  13. omneval_devloop-0.0.2/.github/workflows/publish-devloop-chart.yml +0 -30
  14. omneval_devloop-0.0.2/charts/devloop/Chart.yaml +0 -6
  15. omneval_devloop-0.0.2/charts/devloop/templates/poller-deployment.yaml +0 -55
  16. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/build-agent-base-image.yml +0 -0
  17. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/ci.yml +0 -0
  18. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/publish-discord-bot-image.yml +0 -0
  19. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/publish-poller-image.yml +0 -0
  20. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.github/workflows/publish-temporal-worker-image.yml +0 -0
  21. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/.gitignore +0 -0
  22. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/CODEOWNERS +0 -0
  23. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/CONTEXT.md +0 -0
  24. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/LICENSE +0 -0
  25. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/README.md +0 -0
  26. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/templates/NOTES.txt +0 -0
  27. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/templates/_helpers.tpl +0 -0
  28. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/templates/temporal-worker-service.yaml +0 -0
  29. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/charts/devloop/test-values.yaml +0 -0
  30. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/docs/getting-started.md +0 -0
  31. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/docs/temporal-prerequisites.md +0 -0
  32. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/Dockerfile +0 -0
  33. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/entrypoint.py +0 -0
  34. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/prompts/diagnosis.md +0 -0
  35. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/prompts/implement.md +0 -0
  36. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/prompts/merge.md +0 -0
  37. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/prompts/plan.md +0 -0
  38. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/prompts/review.md +0 -0
  39. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/pyproject.toml +0 -0
  40. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/pytest.ini +0 -0
  41. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/test_entrypoint.py +0 -0
  42. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/test_human_question.py +0 -0
  43. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/test_project_tests.py +0 -0
  44. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/agent-base/test_run_agent.py +0 -0
  45. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/Dockerfile +0 -0
  46. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/activities.py +0 -0
  47. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/discord_client.py +0 -0
  48. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/main.py +0 -0
  49. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/pyproject.toml +0 -0
  50. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/pytest.ini +0 -0
  51. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/test_text_utils.py +0 -0
  52. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/test_thread_store.py +0 -0
  53. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/text_utils.py +0 -0
  54. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/thread_store.py +0 -0
  55. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/discord-bot/uv.lock +0 -0
  56. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/poller/Dockerfile +0 -0
  57. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/poller/poll.py +0 -0
  58. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/poller/pyproject.toml +0 -0
  59. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/poller/pytest.ini +0 -0
  60. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/poller/test_poll.py +0 -0
  61. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/poller/uv.lock +0 -0
  62. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/temporal-worker/Dockerfile +0 -0
  63. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/images/temporal-worker/pyproject.toml +0 -0
  64. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/plan.md +0 -0
  65. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/pyproject.toml +0 -0
  66. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/__init__.py +0 -0
  67. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/cluster.py +0 -0
  68. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/dev_loop.py +0 -0
  69. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/dev_loop_logic.py +0 -0
  70. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/github_ops.py +0 -0
  71. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/k8s_jobs.py +0 -0
  72. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/projects.py +0 -0
  73. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/schedules.py +0 -0
  74. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/shared.py +0 -0
  75. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/summarization.py +0 -0
  76. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/summarize_activities.py +0 -0
  77. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/webhook.py +0 -0
  78. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/worker.py +0 -0
  79. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/src/devloop/workflows.py +0 -0
  80. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_cluster.py +0 -0
  81. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_dev_loop.py +0 -0
  82. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_docs.py +0 -0
  83. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_github_ops.py +0 -0
  84. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_github_webhook.py +0 -0
  85. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_k8s_jobs.py +0 -0
  86. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_projects.py +0 -0
  87. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_pure_logic.py +0 -0
  88. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_stub_roundtrip.py +0 -0
  89. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_summarization.py +0 -0
  90. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/tests/test_worker.py +0 -0
  91. {omneval_devloop-0.0.2 → omneval_devloop-0.0.4}/uv.lock +0 -0
@@ -62,24 +62,43 @@ jobs:
62
62
  username: ${{ github.actor }}
63
63
  password: ${{ secrets.GITHUB_TOKEN }}
64
64
 
65
- - name: Wait for the pinned SDK to appear on PyPI
65
+ - name: Wait for the pinned SDK to be downloadable from PyPI
66
66
  if: inputs.wait_for_pypi != ''
67
67
  run: |
68
68
  pkg_ver='${{ inputs.wait_for_pypi }}'
69
69
  name="${pkg_ver%%==*}"
70
70
  ver="${pkg_ver##*==}"
71
71
  url="https://pypi.org/pypi/${name}/${ver}/json"
72
- echo "Waiting for ${pkg_ver} at ${url}"
72
+ echo "Waiting for ${pkg_ver} files to be downloadable (metadata at ${url})"
73
+ # The JSON metadata endpoint flips to 200 the instant the release is
74
+ # registered, but the wheel/sdist files on files.pythonhosted.org lag a
75
+ # few seconds behind. Polling the metadata alone let the build start in
76
+ # that window, where uv saw the version, failed to download the file,
77
+ # and exited 1 (see v0.0.3). So wait until every published file actually
78
+ # HEADs 200 — the same CDN uv pulls from — not just until metadata exists.
73
79
  for i in $(seq 1 40); do
74
- code=$(curl -s -o /dev/null -w '%{http_code}' "$url")
75
- if [ "$code" = "200" ]; then
76
- echo "Found ${pkg_ver} on PyPI (attempt $i)"
77
- exit 0
80
+ file_urls=$(curl -s "$url" | jq -r '.urls[].url')
81
+ if [ -n "$file_urls" ]; then
82
+ all_ok=1
83
+ for fu in $file_urls; do
84
+ code=$(curl -s -o /dev/null -w '%{http_code}' --head "$fu")
85
+ if [ "$code" != "200" ]; then
86
+ all_ok=0
87
+ echo "Attempt $i/40: ${fu##*/} not downloadable yet (HTTP $code)"
88
+ break
89
+ fi
90
+ done
91
+ if [ "$all_ok" = "1" ]; then
92
+ echo "Found all ${pkg_ver} files downloadable (attempt $i)"
93
+ exit 0
94
+ fi
95
+ else
96
+ echo "Attempt $i/40: ${pkg_ver} metadata not on PyPI yet"
78
97
  fi
79
- echo "Attempt $i/40: ${pkg_ver} not on PyPI yet (HTTP $code); retrying in 15s"
98
+ echo "Retrying in 15s"
80
99
  sleep 15
81
100
  done
82
- echo "ERROR: ${pkg_ver} did not appear on PyPI within ~10 minutes"
101
+ echo "ERROR: ${pkg_ver} files did not become downloadable within ~10 minutes"
83
102
  exit 1
84
103
 
85
104
  - name: Compute sha-<short>-<epoch> tag
@@ -0,0 +1,50 @@
1
+ name: Test & Lint Devloop Helm Chart
2
+
3
+ # Validates the devloop Helm chart on pull requests: unit tests (helm-unittest)
4
+ # plus packaging + lint. Packaging and publishing the chart as an OCI artifact to
5
+ # ghcr.io/omneval/charts/devloop happens in release.yml on a v* tag.
6
+
7
+ on:
8
+ pull_request:
9
+
10
+ jobs:
11
+ unittest:
12
+ name: Unit test chart
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Check out the repo
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Helm
19
+ uses: azure/setup-helm@v4
20
+ with:
21
+ # helm-unittest's plugin.yaml uses platformHooks, which requires Helm
22
+ # >= 3.15; this tracks the Helm 4 line used for local development.
23
+ version: v4.2.0
24
+
25
+ - name: Install helm-unittest plugin
26
+ run: helm plugin install https://github.com/helm-unittest/helm-unittest --verify=false
27
+
28
+ - name: Run chart unit tests
29
+ run: helm unittest charts/devloop
30
+
31
+ lint:
32
+ name: Package and lint chart
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - name: Check out the repo
36
+ uses: actions/checkout@v4
37
+
38
+ - name: Set up Helm
39
+ uses: azure/setup-helm@v4
40
+ with:
41
+ version: v4.2.0
42
+
43
+ - name: Package chart
44
+ id: package
45
+ run: |
46
+ helm package charts/devloop/ -u -d .cr-release-packages
47
+ echo "chart=$(ls .cr-release-packages/devloop-*.tgz)" >> "$GITHUB_OUTPUT"
48
+
49
+ - name: Lint chart
50
+ run: helm lint ${{ steps.package.outputs.chart }}
@@ -87,7 +87,7 @@ jobs:
87
87
 
88
88
  chart:
89
89
  name: Package and push Helm chart
90
- needs: test
90
+ needs: [test, meta]
91
91
  runs-on: ubuntu-latest
92
92
  permissions:
93
93
  packages: write
@@ -109,8 +109,15 @@ jobs:
109
109
 
110
110
  - name: Package chart
111
111
  id: package
112
+ # The git tag is the single source of truth (see header): override the
113
+ # placeholder version in Chart.yaml so the chart publishes at the same
114
+ # semver as the SDK and images. Without --version, helm reads Chart.yaml
115
+ # and a v0.0.2 tag would publish e.g. chart 0.1.0 — out of step with the
116
+ # 0.0.2 images.
112
117
  run: |
113
- helm package charts/devloop/ -u -d .cr-release-packages
118
+ helm package charts/devloop/ -u -d .cr-release-packages \
119
+ --version "${{ needs.meta.outputs.version }}" \
120
+ --app-version "${{ needs.meta.outputs.version }}"
114
121
  echo "chart=$(ls .cr-release-packages/devloop-*.tgz)" >> "$GITHUB_OUTPUT"
115
122
 
116
123
  - name: Lint chart
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omneval-devloop
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  License-File: LICENSE
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: fastapi>=0.115.6
@@ -0,0 +1,10 @@
1
+ apiVersion: v2
2
+ name: devloop
3
+ description: Helm chart for deploying the devloop components (Temporal Orchestration Worker, Discord Bot, GitHub Issue Poller)
4
+ type: application
5
+ # Placeholder only. The release pipeline (release.yml) packages the chart with
6
+ # --version/--app-version set to the git tag, so the PUBLISHED chart semver always
7
+ # matches the SDK and image releases. This static value is used solely by PR lint
8
+ # and local `helm template`; it is never what gets pushed to the OCI registry.
9
+ version: 0.0.0
10
+ appVersion: "0.0.0"
@@ -19,6 +19,13 @@ spec:
19
19
  app.kubernetes.io/component: discord-bot
20
20
  app.kubernetes.io/instance: {{ .Release.Name }}
21
21
  spec:
22
+ {{- if .Values.discordBot.serviceAccountName }}
23
+ serviceAccountName: {{ .Values.discordBot.serviceAccountName }}
24
+ {{- end }}
25
+ {{- with .Values.discordBot.imagePullSecrets }}
26
+ imagePullSecrets:
27
+ {{- toYaml . | nindent 8 }}
28
+ {{- end }}
22
29
  containers:
23
30
  - name: discord-bot
24
31
  image: "{{ .Values.discordBot.image.repository }}:{{ .Values.discordBot.image.tag }}"
@@ -31,11 +38,28 @@ spec:
31
38
  - name: TEMPORAL_HOST
32
39
  value: {{ .Values.temporalHost | quote }}
33
40
  - name: DISCORD_TOKEN
41
+ {{- if .Values.discordBot.tokenSecret }}
42
+ valueFrom:
43
+ secretKeyRef:
44
+ name: {{ .Values.discordBot.tokenSecret.name }}
45
+ key: {{ .Values.discordBot.tokenSecret.key }}
46
+ {{- else }}
34
47
  value: {{ .Values.discordBot.token | quote }}
48
+ {{- end }}
49
+ {{- with .Values.discordBot.applicationIdSecret }}
50
+ - name: DISCORD_APPLICATION_ID
51
+ valueFrom:
52
+ secretKeyRef:
53
+ name: {{ .name }}
54
+ key: {{ .key }}
55
+ {{- end }}
35
56
  - name: TASK_QUEUE
36
57
  value: {{ .Values.discordBot.taskQueue | quote }}
37
58
  - name: HEALTH_PORT
38
59
  value: {{ .Values.discordBot.healthPort | quote }}
60
+ {{- with .Values.discordBot.extraEnv }}
61
+ {{- toYaml . | nindent 12 }}
62
+ {{- end }}
39
63
  {{- include "devloop.healthProbes" . | nindent 10 }}
40
64
  {{- if .Values.discordBot.resources }}
41
65
  resources:
@@ -0,0 +1,103 @@
1
+ {{- include "devloop.validate.temporalHost" . }}
2
+ {{- if and .Values.poller.enabled .Values.poller.projects }}
3
+ {{- range $idx, $project := .Values.poller.projects }}
4
+ {{- $sanitizedRepoName := regexReplaceAll "[^a-zA-Z0-9]" .repo "" | lower | trunc 52 -}}
5
+ {{- /* A Kubernetes label value cannot contain "/" (as in owner/name), so derive a valid value. */ -}}
6
+ {{- $projectLabel := .repo | replace "/" "." | trunc 63 | trimSuffix "." -}}
7
+ {{- if $.Values.poller.persistence.enabled }}
8
+ ---
9
+ apiVersion: v1
10
+ kind: PersistentVolumeClaim
11
+ metadata:
12
+ name: {{ $.Release.Name }}-poller-{{ $sanitizedRepoName }}-state
13
+ labels:
14
+ {{- include "devloop.labels" $ | nindent 4 }}
15
+ app.kubernetes.io/component: poller
16
+ app.kubernetes.io/project: {{ $projectLabel | quote }}
17
+ spec:
18
+ accessModes:
19
+ - {{ $.Values.poller.persistence.accessMode }}
20
+ {{- if $.Values.poller.persistence.storageClassName }}
21
+ storageClassName: {{ $.Values.poller.persistence.storageClassName }}
22
+ {{- end }}
23
+ resources:
24
+ requests:
25
+ storage: {{ $.Values.poller.persistence.size }}
26
+ {{- end }}
27
+ ---
28
+ apiVersion: apps/v1
29
+ kind: Deployment
30
+ metadata:
31
+ name: {{ $.Release.Name }}-poller-{{ $sanitizedRepoName }}
32
+ labels:
33
+ {{- include "devloop.labels" $ | nindent 4 }}
34
+ app.kubernetes.io/component: poller
35
+ app.kubernetes.io/project: {{ $projectLabel | quote }}
36
+ spec:
37
+ replicas: 1
38
+ selector:
39
+ matchLabels:
40
+ app.kubernetes.io/component: poller
41
+ app.kubernetes.io/instance: {{ $.Release.Name }}
42
+ app.kubernetes.io/project: {{ $projectLabel | quote }}
43
+ template:
44
+ metadata:
45
+ labels:
46
+ app.kubernetes.io/component: poller
47
+ app.kubernetes.io/instance: {{ $.Release.Name }}
48
+ app.kubernetes.io/project: {{ $projectLabel | quote }}
49
+ spec:
50
+ {{- with $.Values.poller.imagePullSecrets }}
51
+ imagePullSecrets:
52
+ {{- toYaml . | nindent 8 }}
53
+ {{- end }}
54
+ containers:
55
+ - name: poller
56
+ image: "{{ $.Values.poller.image.repository }}:{{ $.Values.poller.image.tag }}"
57
+ imagePullPolicy: {{ $.Values.poller.image.pullPolicy }}
58
+ ports:
59
+ - name: health
60
+ containerPort: {{ $.Values.poller.healthPort }}
61
+ protocol: TCP
62
+ env:
63
+ - name: GITHUB_TOKEN
64
+ {{- if $.Values.poller.githubTokenSecret }}
65
+ valueFrom:
66
+ secretKeyRef:
67
+ name: {{ $.Values.poller.githubTokenSecret.name }}
68
+ key: {{ $.Values.poller.githubTokenSecret.key }}
69
+ {{- else }}
70
+ value: {{ $.Values.poller.githubToken | quote }}
71
+ {{- end }}
72
+ - name: GITHUB_REPO
73
+ value: {{ .repo | quote }}
74
+ - name: AGENT_LABEL
75
+ value: {{ default "agent-ready" .label | quote }}
76
+ - name: WEBHOOK_URL
77
+ value: {{ .webhookUrl | quote }}
78
+ - name: POLL_INTERVAL_SECONDS
79
+ value: {{ $.Values.poller.pollIntervalSeconds | quote }}
80
+ - name: HEALTH_PORT
81
+ value: {{ $.Values.poller.healthPort | quote }}
82
+ {{- if $.Values.poller.persistence.enabled }}
83
+ - name: STATE_FILE
84
+ value: {{ printf "%s/state.json" $.Values.poller.persistence.mountPath | quote }}
85
+ {{- end }}
86
+ {{- if $.Values.poller.persistence.enabled }}
87
+ volumeMounts:
88
+ - name: state
89
+ mountPath: {{ $.Values.poller.persistence.mountPath }}
90
+ {{- end }}
91
+ {{- include "devloop.healthProbes" $ | nindent 10 }}
92
+ {{- if $.Values.poller.resources }}
93
+ resources:
94
+ {{- toYaml $.Values.poller.resources | nindent 12 }}
95
+ {{- end }}
96
+ {{- if $.Values.poller.persistence.enabled }}
97
+ volumes:
98
+ - name: state
99
+ persistentVolumeClaim:
100
+ claimName: {{ $.Release.Name }}-poller-{{ $sanitizedRepoName }}-state
101
+ {{- end }}
102
+ {{- end }}
103
+ {{- end }}
@@ -19,6 +19,13 @@ spec:
19
19
  app.kubernetes.io/component: temporal-worker
20
20
  app.kubernetes.io/instance: {{ .Release.Name }}
21
21
  spec:
22
+ {{- if .Values.temporalWorker.serviceAccountName }}
23
+ serviceAccountName: {{ .Values.temporalWorker.serviceAccountName }}
24
+ {{- end }}
25
+ {{- with .Values.temporalWorker.imagePullSecrets }}
26
+ imagePullSecrets:
27
+ {{- toYaml . | nindent 8 }}
28
+ {{- end }}
22
29
  containers:
23
30
  - name: temporal-worker
24
31
  image: "{{ .Values.temporalWorker.image.repository }}:{{ .Values.temporalWorker.image.tag }}"
@@ -41,6 +48,9 @@ spec:
41
48
  value: {{ .Values.temporalWorker.webhookPort | quote }}
42
49
  - name: PROJECTS_FILE
43
50
  value: {{ .Values.temporalWorker.projectsFile | quote }}
51
+ {{- with .Values.temporalWorker.extraEnv }}
52
+ {{- toYaml . | nindent 12 }}
53
+ {{- end }}
44
54
  {{- include "devloop.healthProbes" . | nindent 10 }}
45
55
  {{- if .Values.temporalWorker.resources }}
46
56
  resources:
@@ -0,0 +1,43 @@
1
+ suite: discord bot deployment
2
+ templates:
3
+ - templates/discord-bot-deployment.yaml
4
+ tests:
5
+ - it: renders nothing when the bot is disabled
6
+ set:
7
+ temporalHost: "temporal-frontend.agents.svc:7233"
8
+ discordBot.enabled: false
9
+ asserts:
10
+ - hasDocuments:
11
+ count: 0
12
+
13
+ - it: sources the token from a Secret when tokenSecret is set
14
+ set:
15
+ temporalHost: "temporal-frontend.agents.svc:7233"
16
+ discordBot.tokenSecret:
17
+ name: discord-homelab-agent-bot
18
+ key: TOKEN
19
+ asserts:
20
+ - containsDocument:
21
+ kind: Deployment
22
+ apiVersion: apps/v1
23
+ name: RELEASE-NAME-discord-bot
24
+ - contains:
25
+ path: spec.template.spec.containers[0].env
26
+ content:
27
+ name: DISCORD_TOKEN
28
+ valueFrom:
29
+ secretKeyRef:
30
+ name: discord-homelab-agent-bot
31
+ key: TOKEN
32
+
33
+ - it: falls back to a plain token value when no secret is given
34
+ set:
35
+ temporalHost: "temporal-frontend.agents.svc:7233"
36
+ discordBot.tokenSecret: {}
37
+ discordBot.token: "plain-token"
38
+ asserts:
39
+ - contains:
40
+ path: spec.template.spec.containers[0].env
41
+ content:
42
+ name: DISCORD_TOKEN
43
+ value: "plain-token"
@@ -0,0 +1,120 @@
1
+ suite: poller deployment and PVC
2
+ templates:
3
+ - templates/poller-deployment.yaml
4
+ tests:
5
+ - it: renders nothing when the poller is disabled
6
+ set:
7
+ temporalHost: "temporal-frontend.agents.svc:7233"
8
+ poller.enabled: false
9
+ poller.projects:
10
+ - repo: "omneval/omneval"
11
+ webhookUrl: "http://x"
12
+ asserts:
13
+ - hasDocuments:
14
+ count: 0
15
+
16
+ - it: renders nothing when no projects are configured
17
+ set:
18
+ temporalHost: "temporal-frontend.agents.svc:7233"
19
+ poller.enabled: true
20
+ poller.projects: []
21
+ asserts:
22
+ - hasDocuments:
23
+ count: 0
24
+
25
+ - it: creates one Deployment per project (no PVC when persistence is off)
26
+ set:
27
+ temporalHost: "temporal-frontend.agents.svc:7233"
28
+ poller.enabled: true
29
+ poller.persistence.enabled: false
30
+ poller.projects:
31
+ - repo: "omneval/omneval"
32
+ webhookUrl: "http://x"
33
+ - repo: "omneval/devloop"
34
+ webhookUrl: "http://x"
35
+ asserts:
36
+ - hasDocuments:
37
+ count: 2
38
+ - containsDocument:
39
+ kind: Deployment
40
+ apiVersion: apps/v1
41
+ name: RELEASE-NAME-poller-omnevalomneval
42
+ documentIndex: 0
43
+ - containsDocument:
44
+ kind: Deployment
45
+ apiVersion: apps/v1
46
+ name: RELEASE-NAME-poller-omnevaldevloop
47
+ documentIndex: 1
48
+
49
+ # --- Regression guard for the bug that blocked install: an owner/name repo
50
+ # was used verbatim as a label value, which Kubernetes rejects (no "/"). ---
51
+ - it: derives a label-safe project value (never contains a slash)
52
+ set:
53
+ temporalHost: "temporal-frontend.agents.svc:7233"
54
+ poller.enabled: true
55
+ poller.persistence.enabled: true
56
+ poller.projects:
57
+ - repo: "omneval/omneval"
58
+ webhookUrl: "http://x"
59
+ documentIndex: 0
60
+ asserts:
61
+ - isKind:
62
+ of: PersistentVolumeClaim
63
+ - equal:
64
+ path: metadata.labels["app.kubernetes.io/project"]
65
+ value: omneval.omneval
66
+ - notMatchRegex:
67
+ path: metadata.labels["app.kubernetes.io/project"]
68
+ pattern: "/"
69
+ # Must satisfy the Kubernetes label-value grammar.
70
+ - matchRegex:
71
+ path: metadata.labels["app.kubernetes.io/project"]
72
+ pattern: "^[A-Za-z0-9]([-A-Za-z0-9_.]*[A-Za-z0-9])?$"
73
+
74
+ - it: keeps the PVC and its claimName name in sync with the Deployment
75
+ set:
76
+ temporalHost: "temporal-frontend.agents.svc:7233"
77
+ poller.enabled: true
78
+ poller.persistence.enabled: true
79
+ poller.persistence.storageClassName: longhorn
80
+ poller.persistence.size: 100Mi
81
+ poller.projects:
82
+ - repo: "omneval/omneval"
83
+ webhookUrl: "http://x"
84
+ asserts:
85
+ - hasDocuments:
86
+ count: 2
87
+ - containsDocument:
88
+ kind: PersistentVolumeClaim
89
+ apiVersion: v1
90
+ name: RELEASE-NAME-poller-omnevalomneval-state
91
+ documentIndex: 0
92
+ - equal:
93
+ path: spec.storageClassName
94
+ value: longhorn
95
+ documentIndex: 0
96
+ - equal:
97
+ path: spec.template.spec.volumes[0].persistentVolumeClaim.claimName
98
+ value: RELEASE-NAME-poller-omnevalomneval-state
99
+ documentIndex: 1
100
+
101
+ - it: passes the full repo to GITHUB_REPO and defaults the agent label
102
+ set:
103
+ temporalHost: "temporal-frontend.agents.svc:7233"
104
+ poller.enabled: true
105
+ poller.persistence.enabled: false
106
+ poller.projects:
107
+ - repo: "omneval/omneval"
108
+ webhookUrl: "http://hook"
109
+ documentIndex: 0
110
+ asserts:
111
+ - contains:
112
+ path: spec.template.spec.containers[0].env
113
+ content:
114
+ name: GITHUB_REPO
115
+ value: "omneval/omneval"
116
+ - contains:
117
+ path: spec.template.spec.containers[0].env
118
+ content:
119
+ name: AGENT_LABEL
120
+ value: "agent-ready"
@@ -0,0 +1,74 @@
1
+ suite: temporal worker deployment and service
2
+ templates:
3
+ - templates/temporal-worker-deployment.yaml
4
+ - templates/temporal-worker-service.yaml
5
+ tests:
6
+ - it: fails when temporalHost is not provided
7
+ # The validation helper is included in every template; helm aborts the whole
8
+ # render at the first fail() call, which it attributes to this template.
9
+ template: templates/temporal-worker-service.yaml
10
+ set:
11
+ temporalHost: ""
12
+ asserts:
13
+ - failedTemplate:
14
+ errorMessage: "temporalHost is required but was not set. Provide a value like 'temporal-frontend.agents.svc:7233'"
15
+
16
+ - it: renders nothing when the worker is disabled
17
+ set:
18
+ temporalHost: "temporal-frontend.agents.svc:7233"
19
+ temporalWorker.enabled: false
20
+ asserts:
21
+ - hasDocuments:
22
+ count: 0
23
+
24
+ - it: names the workload and wires the temporal host
25
+ template: templates/temporal-worker-deployment.yaml
26
+ set:
27
+ temporalHost: "temporal-frontend.agents.svc:7233"
28
+ asserts:
29
+ - containsDocument:
30
+ kind: Deployment
31
+ apiVersion: apps/v1
32
+ name: RELEASE-NAME-temporal-worker
33
+ - contains:
34
+ path: spec.template.spec.containers[0].env
35
+ content:
36
+ name: TEMPORAL_HOST
37
+ value: "temporal-frontend.agents.svc:7233"
38
+
39
+ - it: appends extraEnv verbatim and sets the service account
40
+ template: templates/temporal-worker-deployment.yaml
41
+ set:
42
+ temporalHost: "temporal-frontend.agents.svc:7233"
43
+ temporalWorker.serviceAccountName: orchestration-worker
44
+ temporalWorker.extraEnv:
45
+ - name: ORCHESTRATION_QUEUE
46
+ value: homelab-orchestration
47
+ asserts:
48
+ - equal:
49
+ path: spec.template.spec.serviceAccountName
50
+ value: orchestration-worker
51
+ - contains:
52
+ path: spec.template.spec.containers[0].env
53
+ content:
54
+ name: ORCHESTRATION_QUEUE
55
+ value: homelab-orchestration
56
+
57
+ - it: exposes webhook and health ports on the service
58
+ template: templates/temporal-worker-service.yaml
59
+ set:
60
+ temporalHost: "temporal-frontend.agents.svc:7233"
61
+ temporalWorker.webhookPort: 8088
62
+ temporalWorker.healthPort: 8080
63
+ asserts:
64
+ - containsDocument:
65
+ kind: Service
66
+ apiVersion: v1
67
+ name: RELEASE-NAME-temporal-worker
68
+ - contains:
69
+ path: spec.ports
70
+ content:
71
+ name: webhook
72
+ port: 8088
73
+ targetPort: webhook
74
+ protocol: TCP
@@ -12,6 +12,20 @@ temporalWorker:
12
12
  repository: ghcr.io/omneval/devloop-temporal-worker
13
13
  tag: "latest"
14
14
  pullPolicy: IfNotPresent
15
+ # Name of a pre-existing ServiceAccount to run the pod as (e.g. one bound to a
16
+ # Role that can create Agent Execution Jobs). Empty uses the namespace default.
17
+ serviceAccountName: ""
18
+ # Image pull secrets (e.g. when the worker image lives in a private registry).
19
+ imagePullSecrets: []
20
+ # - name: regcred
21
+ # Extra environment variables appended verbatim to the container. Use this for
22
+ # consumer-specific config (alternate task queues, custom workflow settings,
23
+ # agent LLM endpoints, etc.). Entries are standard core/v1 EnvVar objects.
24
+ extraEnv: []
25
+ # - name: ORCHESTRATION_QUEUE
26
+ # value: "homelab-orchestration"
27
+ # - name: AGENT_MODEL
28
+ # value: "openai/my-model"
15
29
  # Resource limits for the worker container
16
30
  resources: {}
17
31
  # requests:
@@ -40,8 +54,24 @@ discordBot:
40
54
  repository: ghcr.io/omneval/devloop-discord-bot
41
55
  tag: "latest"
42
56
  pullPolicy: IfNotPresent
43
- # Discord bot token (use a Kubernetes Secret in production)
57
+ # Discord bot token. Prefer `tokenSecret` (a Kubernetes Secret reference) over
58
+ # the plain `token` value, which is only suitable for local/dev use.
44
59
  token: ""
60
+ # tokenSecret: { name: "discord-bot", key: "TOKEN" }
61
+ tokenSecret: {}
62
+ # Optional Discord application ID, sourced from a Secret the same way.
63
+ # applicationIdSecret: { name: "discord-bot", key: "APPLICATION_ID" }
64
+ applicationIdSecret: {}
65
+ # Name of a pre-existing ServiceAccount (e.g. one allowed to read/write the
66
+ # thread-map ConfigMap). Empty uses the namespace default.
67
+ serviceAccountName: ""
68
+ # Image pull secrets for a private bot image.
69
+ imagePullSecrets: []
70
+ # Extra environment variables appended verbatim (e.g. channel IDs,
71
+ # K8S_NAMESPACE, CONFIGMAP_NAME). Standard core/v1 EnvVar objects.
72
+ extraEnv: []
73
+ # - name: DISCORD_CHANNEL_APPROVALS
74
+ # value: "123456789"
45
75
  # Resource limits
46
76
  resources: {}
47
77
  # requests:
@@ -64,12 +94,27 @@ discordBot:
64
94
  # GITHUB_TOKEN is shared across all pollers; repo-specific fields are per-entry.
65
95
  poller:
66
96
  enabled: true
67
- # Shared GitHub token for all poller instances (use a Kubernetes Secret)
97
+ # Shared GitHub token for all poller instances. Prefer `githubTokenSecret`
98
+ # (a Kubernetes Secret reference) over the plain `githubToken` value.
68
99
  githubToken: ""
100
+ # githubTokenSecret: { name: "agent-github-token", key: "GITHUB_TOKEN" }
101
+ githubTokenSecret: {}
69
102
  image:
70
103
  repository: ghcr.io/omneval/devloop-poller
71
104
  tag: "latest"
72
105
  pullPolicy: IfNotPresent
106
+ # Image pull secrets for a private poller image.
107
+ imagePullSecrets: []
108
+ # Persist poll state across restarts. Without this the poller re-notifies
109
+ # every issue on each restart (the state file lives on an emptyDir otherwise).
110
+ # When enabled, a PVC per poller deployment is created and mounted at
111
+ # `mountPath`, and STATE_FILE points inside it.
112
+ persistence:
113
+ enabled: false
114
+ storageClassName: ""
115
+ accessMode: ReadWriteOnce
116
+ size: 100Mi
117
+ mountPath: /data
73
118
  # Resource limits (applied to all poller instances)
74
119
  resources: {}
75
120
  # requests:
@@ -1,30 +0,0 @@
1
- name: Lint Devloop Helm Chart
2
-
3
- # Validates the devloop Helm chart on pull requests. Packaging and publishing the
4
- # chart as an OCI artifact to ghcr.io/omneval/charts/devloop happens in
5
- # release.yml on a v* tag.
6
-
7
- on:
8
- pull_request:
9
-
10
- jobs:
11
- lint:
12
- name: Package and lint chart
13
- runs-on: ubuntu-latest
14
- steps:
15
- - name: Check out the repo
16
- uses: actions/checkout@v4
17
-
18
- - name: Set up Helm
19
- uses: azure/setup-helm@v4
20
- with:
21
- version: v3.14.0
22
-
23
- - name: Package chart
24
- id: package
25
- run: |
26
- helm package charts/devloop/ -u -d .cr-release-packages
27
- echo "chart=$(ls .cr-release-packages/devloop-*.tgz)" >> "$GITHUB_OUTPUT"
28
-
29
- - name: Lint chart
30
- run: helm lint ${{ steps.package.outputs.chart }}
@@ -1,6 +0,0 @@
1
- apiVersion: v2
2
- name: devloop
3
- description: Helm chart for deploying the devloop components (Temporal Orchestration Worker, Discord Bot, GitHub Issue Poller)
4
- type: application
5
- version: 0.1.0
6
- appVersion: "0.1.0"
@@ -1,55 +0,0 @@
1
- {{- include "devloop.validate.temporalHost" . }}
2
- {{- if and .Values.poller.enabled .Values.poller.projects }}
3
- {{- range $idx, $project := .Values.poller.projects }}
4
- {{- $sanitizedRepoName := regexReplaceAll "[^a-zA-Z0-9]" .repo "" | lower | trunc 52 -}}
5
- ---
6
- apiVersion: apps/v1
7
- kind: Deployment
8
- metadata:
9
- name: {{ $.Release.Name }}-poller-{{ $sanitizedRepoName }}
10
- labels:
11
- {{- include "devloop.labels" $ | nindent 4 }}
12
- app.kubernetes.io/component: poller
13
- app.kubernetes.io/project: {{ .repo | quote }}
14
- spec:
15
- replicas: 1
16
- selector:
17
- matchLabels:
18
- app.kubernetes.io/component: poller
19
- app.kubernetes.io/instance: {{ $.Release.Name }}
20
- app.kubernetes.io/project: {{ .repo | quote }}
21
- template:
22
- metadata:
23
- labels:
24
- app.kubernetes.io/component: poller
25
- app.kubernetes.io/instance: {{ $.Release.Name }}
26
- app.kubernetes.io/project: {{ .repo | quote }}
27
- spec:
28
- containers:
29
- - name: poller
30
- image: "{{ $.Values.poller.image.repository }}:{{ $.Values.poller.image.tag }}"
31
- imagePullPolicy: {{ $.Values.poller.image.pullPolicy }}
32
- ports:
33
- - name: health
34
- containerPort: {{ $.Values.poller.healthPort }}
35
- protocol: TCP
36
- env:
37
- - name: GITHUB_TOKEN
38
- value: {{ $.Values.poller.githubToken | quote }}
39
- - name: GITHUB_REPO
40
- value: {{ .repo | quote }}
41
- - name: AGENT_LABEL
42
- value: {{ default "agent-ready" .label | quote }}
43
- - name: WEBHOOK_URL
44
- value: {{ .webhookUrl | quote }}
45
- - name: POLL_INTERVAL_SECONDS
46
- value: {{ $.Values.poller.pollIntervalSeconds | quote }}
47
- - name: HEALTH_PORT
48
- value: {{ $.Values.poller.healthPort | quote }}
49
- {{- include "devloop.healthProbes" $ | nindent 10 }}
50
- {{- if $.Values.poller.resources }}
51
- resources:
52
- {{- toYaml $.Values.poller.resources | nindent 12 }}
53
- {{- end }}
54
- {{- end }}
55
- {{- end }}
File without changes
File without changes
File without changes