opencode-pilot 0.1.0
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.
- package/.devcontainer/devcontainer.json +16 -0
- package/.github/workflows/ci.yml +67 -0
- package/.releaserc.cjs +28 -0
- package/AGENTS.md +71 -0
- package/CONTRIBUTING.md +102 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/bin/opencode-pilot +809 -0
- package/dist/opencode-ntfy.tar.gz +0 -0
- package/examples/config.yaml +73 -0
- package/examples/templates/default.md +7 -0
- package/examples/templates/devcontainer.md +7 -0
- package/examples/templates/review-feedback.md +7 -0
- package/examples/templates/review.md +15 -0
- package/install.sh +246 -0
- package/package.json +40 -0
- package/plugin/config.js +76 -0
- package/plugin/index.js +260 -0
- package/plugin/logger.js +125 -0
- package/plugin/notifier.js +110 -0
- package/service/actions.js +334 -0
- package/service/io.opencode.ntfy.plist +29 -0
- package/service/logger.js +82 -0
- package/service/poll-service.js +246 -0
- package/service/poller.js +339 -0
- package/service/readiness.js +234 -0
- package/service/repo-config.js +222 -0
- package/service/server.js +1523 -0
- package/service/utils.js +21 -0
- package/test/run_tests.bash +34 -0
- package/test/test_actions.bash +263 -0
- package/test/test_cli.bash +161 -0
- package/test/test_config.bash +438 -0
- package/test/test_helper.bash +140 -0
- package/test/test_logger.bash +401 -0
- package/test/test_notifier.bash +310 -0
- package/test/test_plist.bash +125 -0
- package/test/test_plugin.bash +952 -0
- package/test/test_poll_service.bash +179 -0
- package/test/test_poller.bash +120 -0
- package/test/test_readiness.bash +313 -0
- package/test/test_repo_config.bash +406 -0
- package/test/test_service.bash +1342 -0
- package/test/unit/actions.test.js +235 -0
- package/test/unit/config.test.js +86 -0
- package/test/unit/paths.test.js +77 -0
- package/test/unit/poll-service.test.js +142 -0
- package/test/unit/poller.test.js +347 -0
- package/test/unit/repo-config.test.js +441 -0
- package/test/unit/utils.test.js +53 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Tests for repo-config.js - Unified repository configuration
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
source "$SCRIPT_DIR/test_helper.bash"
|
|
10
|
+
|
|
11
|
+
SERVICE_DIR="$(dirname "$SCRIPT_DIR")/service"
|
|
12
|
+
|
|
13
|
+
echo "Testing repo-config.js module..."
|
|
14
|
+
echo ""
|
|
15
|
+
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# File Structure Tests
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
test_repo_config_file_exists() {
|
|
21
|
+
assert_file_exists "$SERVICE_DIR/repo-config.js"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test_repo_config_js_syntax() {
|
|
25
|
+
if ! command -v node &>/dev/null; then
|
|
26
|
+
echo "SKIP: node not available"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
node --check "$SERVICE_DIR/repo-config.js" 2>&1 || {
|
|
30
|
+
echo "repo-config.js has syntax errors"
|
|
31
|
+
return 1
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# Export Tests
|
|
37
|
+
# =============================================================================
|
|
38
|
+
|
|
39
|
+
test_repo_config_exports_load_config() {
|
|
40
|
+
grep -q "export.*function loadRepoConfig\|export.*loadRepoConfig" "$SERVICE_DIR/repo-config.js" || {
|
|
41
|
+
echo "loadRepoConfig export not found"
|
|
42
|
+
return 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
test_repo_config_exports_get_config() {
|
|
47
|
+
grep -q "export.*function getRepoConfig\|export.*getRepoConfig" "$SERVICE_DIR/repo-config.js" || {
|
|
48
|
+
echo "getRepoConfig export not found"
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
test_repo_config_exports_get_all_sources() {
|
|
54
|
+
grep -q "export.*function getAllSources\|export.*getAllSources" "$SERVICE_DIR/repo-config.js" || {
|
|
55
|
+
echo "getAllSources export not found"
|
|
56
|
+
return 1
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# =============================================================================
|
|
61
|
+
# Implementation Tests
|
|
62
|
+
# =============================================================================
|
|
63
|
+
|
|
64
|
+
test_repo_config_supports_yaml() {
|
|
65
|
+
grep -q "yaml\|YAML" "$SERVICE_DIR/repo-config.js" || {
|
|
66
|
+
echo "YAML support not found"
|
|
67
|
+
return 1
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
test_repo_config_supports_prefix_matching() {
|
|
72
|
+
grep -q "prefix\|endsWith.*/" "$SERVICE_DIR/repo-config.js" || {
|
|
73
|
+
echo "Prefix matching not found"
|
|
74
|
+
return 1
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
test_repo_config_merges_configs() {
|
|
79
|
+
grep -q "merge\|deep.*merge" "$SERVICE_DIR/repo-config.js" || {
|
|
80
|
+
echo "Config merging not found"
|
|
81
|
+
return 1
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
test_repo_config_expands_placeholders() {
|
|
86
|
+
grep -q "{repo}\|placeholder" "$SERVICE_DIR/repo-config.js" || {
|
|
87
|
+
echo "Placeholder expansion not found"
|
|
88
|
+
return 1
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# =============================================================================
|
|
93
|
+
# Functional Tests
|
|
94
|
+
# =============================================================================
|
|
95
|
+
|
|
96
|
+
test_repo_config_defaults() {
|
|
97
|
+
if ! command -v node &>/dev/null; then
|
|
98
|
+
echo "SKIP: node not available"
|
|
99
|
+
return 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
local result
|
|
103
|
+
result=$(node --experimental-vm-modules -e "
|
|
104
|
+
import { getRepoConfig } from './service/repo-config.js';
|
|
105
|
+
|
|
106
|
+
// Get config for non-existent repo should return defaults
|
|
107
|
+
const config = getRepoConfig('nonexistent/repo');
|
|
108
|
+
|
|
109
|
+
if (!config.readiness) {
|
|
110
|
+
console.log('FAIL: Missing readiness in defaults');
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
if (!config.readiness.labels) {
|
|
114
|
+
console.log('FAIL: Missing readiness.labels in defaults');
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
console.log('PASS');
|
|
118
|
+
" 2>&1) || {
|
|
119
|
+
echo "Functional test failed: $result"
|
|
120
|
+
return 1
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
124
|
+
echo "$result"
|
|
125
|
+
return 1
|
|
126
|
+
fi
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
test_repo_config_prefix_matching() {
|
|
130
|
+
if ! command -v node &>/dev/null; then
|
|
131
|
+
echo "SKIP: node not available"
|
|
132
|
+
return 0
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
local result
|
|
136
|
+
result=$(node --experimental-vm-modules -e "
|
|
137
|
+
import { loadRepoConfig, getRepoConfig } from './service/repo-config.js';
|
|
138
|
+
|
|
139
|
+
// Load test config
|
|
140
|
+
const testConfig = {
|
|
141
|
+
repos: {
|
|
142
|
+
'myorg/': {
|
|
143
|
+
repo_path: '~/code/{repo}',
|
|
144
|
+
readiness: { labels: { any_of: ['ready'] } }
|
|
145
|
+
},
|
|
146
|
+
'myorg/backend': {
|
|
147
|
+
readiness: { labels: { any_of: ['backend-ready'] } }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
loadRepoConfig(testConfig);
|
|
153
|
+
|
|
154
|
+
// Get config for myorg/backend - should merge prefix + exact
|
|
155
|
+
const config = getRepoConfig('myorg/backend');
|
|
156
|
+
|
|
157
|
+
// Should have readiness from exact match
|
|
158
|
+
if (!config.readiness.labels.any_of.includes('backend-ready')) {
|
|
159
|
+
console.log('FAIL: Expected any_of to include backend-ready from exact match');
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Get config for myorg/frontend - should get prefix config
|
|
164
|
+
const frontendConfig = getRepoConfig('myorg/frontend');
|
|
165
|
+
if (!frontendConfig.readiness.labels.any_of.includes('ready')) {
|
|
166
|
+
console.log('FAIL: Expected any_of to include ready from prefix');
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// repo_path should have {repo} expanded
|
|
171
|
+
if (frontendConfig.repo_path !== '~/code/frontend') {
|
|
172
|
+
console.log('FAIL: Expected expanded repo_path, got ' + frontendConfig.repo_path);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log('PASS');
|
|
177
|
+
" 2>&1) || {
|
|
178
|
+
echo "Functional test failed: $result"
|
|
179
|
+
return 1
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
183
|
+
echo "$result"
|
|
184
|
+
return 1
|
|
185
|
+
fi
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# =============================================================================
|
|
189
|
+
# Default Sources Tests
|
|
190
|
+
# =============================================================================
|
|
191
|
+
|
|
192
|
+
test_repo_config_default_sources() {
|
|
193
|
+
if ! command -v node &>/dev/null; then
|
|
194
|
+
echo "SKIP: node not available"
|
|
195
|
+
return 0
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
local result
|
|
199
|
+
result=$(node --experimental-vm-modules -e "
|
|
200
|
+
import { loadRepoConfig, getRepoConfig, getAllSources } from './service/repo-config.js';
|
|
201
|
+
|
|
202
|
+
// Load config with repo that has no sources specified
|
|
203
|
+
const testConfig = {
|
|
204
|
+
repos: {
|
|
205
|
+
'myorg/': {
|
|
206
|
+
repo_path: '~/code/{repo}'
|
|
207
|
+
},
|
|
208
|
+
'myorg/backend': {} // No sources specified
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
loadRepoConfig(testConfig);
|
|
213
|
+
|
|
214
|
+
// Get config - should have default github_issue source
|
|
215
|
+
const config = getRepoConfig('myorg/backend');
|
|
216
|
+
|
|
217
|
+
if (!config.sources || config.sources.length === 0) {
|
|
218
|
+
console.log('FAIL: Expected default sources, got empty array');
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const defaultSource = config.sources[0];
|
|
223
|
+
if (defaultSource.type !== 'github_issue') {
|
|
224
|
+
console.log('FAIL: Expected default source type github_issue, got ' + defaultSource.type);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (!defaultSource.fetch || defaultSource.fetch.assignee !== '@me') {
|
|
229
|
+
console.log('FAIL: Expected default fetch.assignee=@me, got ' + JSON.stringify(defaultSource.fetch));
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!defaultSource.fetch || defaultSource.fetch.state !== 'open') {
|
|
234
|
+
console.log('FAIL: Expected default fetch.state=open, got ' + JSON.stringify(defaultSource.fetch));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log('PASS');
|
|
239
|
+
" 2>&1) || {
|
|
240
|
+
echo "Functional test failed: $result"
|
|
241
|
+
return 1
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
245
|
+
echo "$result"
|
|
246
|
+
return 1
|
|
247
|
+
fi
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
test_repo_config_explicit_sources_override_defaults() {
|
|
251
|
+
if ! command -v node &>/dev/null; then
|
|
252
|
+
echo "SKIP: node not available"
|
|
253
|
+
return 0
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
local result
|
|
257
|
+
result=$(node --experimental-vm-modules -e "
|
|
258
|
+
import { loadRepoConfig, getRepoConfig } from './service/repo-config.js';
|
|
259
|
+
|
|
260
|
+
// Load config with explicit sources
|
|
261
|
+
const testConfig = {
|
|
262
|
+
repos: {
|
|
263
|
+
'myorg/backend': {
|
|
264
|
+
sources: [
|
|
265
|
+
{ type: 'github_pr', fetch: { state: 'open' } }
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
loadRepoConfig(testConfig);
|
|
272
|
+
|
|
273
|
+
// Get config - should have explicit sources, not defaults
|
|
274
|
+
const config = getRepoConfig('myorg/backend');
|
|
275
|
+
|
|
276
|
+
if (config.sources.length !== 1) {
|
|
277
|
+
console.log('FAIL: Expected 1 source, got ' + config.sources.length);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (config.sources[0].type !== 'github_pr') {
|
|
282
|
+
console.log('FAIL: Expected explicit github_pr source, got ' + config.sources[0].type);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log('PASS');
|
|
287
|
+
" 2>&1) || {
|
|
288
|
+
echo "Functional test failed: $result"
|
|
289
|
+
return 1
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
293
|
+
echo "$result"
|
|
294
|
+
return 1
|
|
295
|
+
fi
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
test_repo_config_get_all_sources_includes_defaults() {
|
|
299
|
+
if ! command -v node &>/dev/null; then
|
|
300
|
+
echo "SKIP: node not available"
|
|
301
|
+
return 0
|
|
302
|
+
fi
|
|
303
|
+
|
|
304
|
+
local result
|
|
305
|
+
result=$(node --experimental-vm-modules -e "
|
|
306
|
+
import { loadRepoConfig, getAllSources } from './service/repo-config.js';
|
|
307
|
+
|
|
308
|
+
// Load config with repo that has no sources specified
|
|
309
|
+
const testConfig = {
|
|
310
|
+
repos: {
|
|
311
|
+
'myorg/backend': {
|
|
312
|
+
repo_path: '~/code/backend'
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
loadRepoConfig(testConfig);
|
|
318
|
+
|
|
319
|
+
// getAllSources should include the default source
|
|
320
|
+
const sources = getAllSources();
|
|
321
|
+
|
|
322
|
+
if (sources.length !== 1) {
|
|
323
|
+
console.log('FAIL: Expected 1 source from getAllSources, got ' + sources.length);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (sources[0].type !== 'github_issue') {
|
|
328
|
+
console.log('FAIL: Expected github_issue source, got ' + sources[0].type);
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (sources[0].repo_key !== 'myorg/backend') {
|
|
333
|
+
console.log('FAIL: Expected repo_key myorg/backend, got ' + sources[0].repo_key);
|
|
334
|
+
process.exit(1);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log('PASS');
|
|
338
|
+
" 2>&1) || {
|
|
339
|
+
echo "Functional test failed: $result"
|
|
340
|
+
return 1
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
344
|
+
echo "$result"
|
|
345
|
+
return 1
|
|
346
|
+
fi
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
# =============================================================================
|
|
350
|
+
# Run Tests
|
|
351
|
+
# =============================================================================
|
|
352
|
+
|
|
353
|
+
echo "File Structure Tests:"
|
|
354
|
+
|
|
355
|
+
for test_func in \
|
|
356
|
+
test_repo_config_file_exists \
|
|
357
|
+
test_repo_config_js_syntax
|
|
358
|
+
do
|
|
359
|
+
run_test "${test_func#test_}" "$test_func"
|
|
360
|
+
done
|
|
361
|
+
|
|
362
|
+
echo ""
|
|
363
|
+
echo "Export Tests:"
|
|
364
|
+
|
|
365
|
+
for test_func in \
|
|
366
|
+
test_repo_config_exports_load_config \
|
|
367
|
+
test_repo_config_exports_get_config \
|
|
368
|
+
test_repo_config_exports_get_all_sources
|
|
369
|
+
do
|
|
370
|
+
run_test "${test_func#test_}" "$test_func"
|
|
371
|
+
done
|
|
372
|
+
|
|
373
|
+
echo ""
|
|
374
|
+
echo "Implementation Tests:"
|
|
375
|
+
|
|
376
|
+
for test_func in \
|
|
377
|
+
test_repo_config_supports_yaml \
|
|
378
|
+
test_repo_config_supports_prefix_matching \
|
|
379
|
+
test_repo_config_merges_configs \
|
|
380
|
+
test_repo_config_expands_placeholders
|
|
381
|
+
do
|
|
382
|
+
run_test "${test_func#test_}" "$test_func"
|
|
383
|
+
done
|
|
384
|
+
|
|
385
|
+
echo ""
|
|
386
|
+
echo "Functional Tests:"
|
|
387
|
+
|
|
388
|
+
for test_func in \
|
|
389
|
+
test_repo_config_defaults \
|
|
390
|
+
test_repo_config_prefix_matching
|
|
391
|
+
do
|
|
392
|
+
run_test "${test_func#test_}" "$test_func"
|
|
393
|
+
done
|
|
394
|
+
|
|
395
|
+
echo ""
|
|
396
|
+
echo "Default Sources Tests:"
|
|
397
|
+
|
|
398
|
+
for test_func in \
|
|
399
|
+
test_repo_config_default_sources \
|
|
400
|
+
test_repo_config_explicit_sources_override_defaults \
|
|
401
|
+
test_repo_config_get_all_sources_includes_defaults
|
|
402
|
+
do
|
|
403
|
+
run_test "${test_func#test_}" "$test_func"
|
|
404
|
+
done
|
|
405
|
+
|
|
406
|
+
print_summary
|