-
Notifications
You must be signed in to change notification settings - Fork 5
184 lines (163 loc) · 6.37 KB
/
Copy pathengine-smoke.yml
File metadata and controls
184 lines (163 loc) · 6.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
name: Engine Smoke
# Runs the shipped engine adapters against their REAL CLIs (#915).
#
# Three engines shipped with invocations that could not do any work — #913
# (opencode's non-existent --non-interactive), #914 (codex's invented
# app-server handshake), #1012 (kilocode's non-existent `run` subcommand) —
# every one of them behind a green suite, because the adapter tests mock
# subprocess.Popen and assert the adapter agrees with itself.
#
# Two legs, gated differently on purpose:
# contract — CLI exists, responds, still documents the adapter's entry point.
# No credentials, no model calls, seconds. Runs on every PR so it
# can be a required check for adapter changes.
# task — one trivial task per engine driven to a terminal state with a
# real file written. Needs credentials, costs money, takes minutes.
# Scheduled and manual only.
on:
pull_request:
schedule:
# Weekly — Sunday 4am UTC, an hour after the lifecycle run.
- cron: '0 4 * * 0'
workflow_dispatch:
env:
PYTHON_VERSION: '3.11'
jobs:
contract:
# Always runs — a path-filtered required check reports "skipped" and blocks
# the merge forever, so the filtering happens inside the job instead.
name: Engine CLI Contract
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Detect engine adapter changes
id: changed
run: |
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
exit 0
fi
base="origin/${{ github.base_ref }}"
git fetch -q origin "${{ github.base_ref }}"
if git diff --name-only "$base"...HEAD \
| grep -qE '^(codeframe/core/adapters/|tests/core/adapters/|\.github/workflows/engine-smoke\.yml)'; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
else
echo "relevant=false" >> "$GITHUB_OUTPUT"
echo "No engine adapter changes — contract checks not required."
fi
- name: Set up Python
if: steps.changed.outputs.relevant == 'true'
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
if: steps.changed.outputs.relevant == 'true'
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
- name: Install dependencies
if: steps.changed.outputs.relevant == 'true'
run: |
uv venv
uv sync --extra dev
uv pip install -e .
- name: Install engine CLIs
if: steps.changed.outputs.relevant == 'true'
# No `|| true` here. A failed install leaves the binary absent, the tier
# skips that engine, and pytest still exits 0 — a required check that is
# green while covering nothing, which is the exact failure mode this
# tier exists to end.
run: |
npm install -g \
@anthropic-ai/claude-code \
@openai/codex \
opencode-ai \
@kilocode/cli
- name: Verify every engine CLI is present
if: steps.changed.outputs.relevant == 'true'
run: |
missing=()
for b in claude codex opencode kilo; do
if command -v "$b" >/dev/null; then
echo "ok: $b -> $(command -v "$b")"
else
missing+=("$b")
fi
done
if [ ${#missing[@]} -ne 0 ]; then
echo "::error::engine CLIs missing: ${missing[*]}. The contract" \
"checks for these would skip, leaving this required check" \
"green while covering nothing."
exit 1
fi
- name: Run the contract leg
if: steps.changed.outputs.relevant == 'true'
# No CODEFRAME_ENGINE_SMOKE and no secrets: the task leg stays skipped.
run: |
uv run pytest tests/core/adapters/test_engine_smoke_tier.py \
-v --no-header -p no:randomly
task:
# Real credentials and real model calls — never on a pull request.
name: Engine Task Smoke
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
environment: staging
timeout-minutes: 45
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
- name: Install dependencies
run: |
uv venv
uv sync --extra dev
uv pip install -e .
- name: Install engine CLIs
run: |
npm install -g \
@anthropic-ai/claude-code \
@openai/codex \
opencode-ai \
@kilocode/cli
- name: Verify every engine CLI is present
# Same reasoning as the contract job: a canary that silently stops
# canarying is worse than no canary.
run: |
missing=()
for b in claude codex opencode kilo; do
if command -v "$b" >/dev/null; then
echo "ok: $b -> $(command -v "$b")"
else
missing+=("$b")
fi
done
if [ ${#missing[@]} -ne 0 ]; then
echo "::error::engine CLIs missing: ${missing[*]}"
exit 1
fi
- name: Configure git
run: |
git config --global user.name "Engine Smoke"
git config --global user.email "engine-smoke@codeframe.test"
- name: Run the full tier
env:
CODEFRAME_ENGINE_SMOKE: '1'
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
uv run pytest tests/core/adapters/test_engine_smoke_tier.py \
tests/core/adapters/test_opencode_smoke_913.py \
tests/core/adapters/test_kilocode_smoke_1012.py \
-v --no-header -p no:randomly -rs