-
Notifications
You must be signed in to change notification settings - Fork 11
355 lines (322 loc) · 12.8 KB
/
Copy pathmodel-e2e.yml
File metadata and controls
355 lines (322 loc) · 12.8 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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
name: Model E2E
# Live-model tests are intentionally manual: they consume external API quota,
# require repository secrets, and are non-deterministic. They are observability
# checks, not merge-queue gates.
on:
workflow_dispatch:
inputs:
suite:
description: 'Live-model suite to run'
required: true
type: choice
default: all
options:
- all
- none-runtime
- opensandbox
- docker
concurrency:
group: model-e2e-${{ github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
e2e:
name: E2E (none runtime, live models)
if: inputs.suite == 'all' || inputs.suite == 'none-runtime'
runs-on: [self-hosted, linux, x64, trusted]
timeout-minutes: 35
permissions:
contents: read
steps:
- name: Checkout source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Check secret availability
id: secrets
env:
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
QODER_ACCESS_TOKEN: ${{ secrets.QODER_ACCESS_TOKEN }}
run: |
set -euo pipefail
if [ -z "$DASHSCOPE_API_KEY" ]; then
echo "::error::DASHSCOPE_API_KEY is required for full e2e."
exit 1
fi
if [ -z "$QODER_ACCESS_TOKEN" ]; then
echo "::error::QODER_ACCESS_TOKEN is required for full e2e."
exit 1
fi
echo "available=true" >> "$GITHUB_OUTPUT"
- name: Set up Go
if: steps.secrets.outputs.available == 'true'
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25.x"
cache: true
- name: Set up Node.js
if: steps.secrets.outputs.available == 'true'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "22"
- name: Install claude-code CLI
if: steps.secrets.outputs.available == 'true'
run: |
set -euo pipefail
npm install -g --include=optional '@anthropic-ai/claude-code@2.1.217'
claude --version
- name: Install codex CLI
if: steps.secrets.outputs.available == 'true'
run: |
set -euo pipefail
npm install -g --include=optional '@openai/codex@0.80.0'
codex --version
- name: Install qwen-code CLI
if: steps.secrets.outputs.available == 'true'
run: |
set -euo pipefail
npm install -g '@qwen-code/qwen-code@0.20.1'
qwen --version
- name: Install qodercli (pinned v1.0.14)
if: steps.secrets.outputs.available == 'true'
env:
QODER_VERSION: "1.0.14"
QODER_SHA256: "4d83ec3d3a0948b73012cfdd6e2757be0a5da5c89a288425a5607fd42be1de7b"
run: |
set -euo pipefail
tarball=$(mktemp)
curl -fsSL "https://qoder-ide.oss-accelerate.aliyuncs.com/qodercli/releases/${QODER_VERSION}/qodercli-linux-x64.tar.gz" -o "$tarball"
echo "$QODER_SHA256 $tarball" | sha256sum -c -
mkdir -p "$HOME/.qoder/bin"
tar -xzf "$tarball" -C "$HOME/.qoder/bin"
rm -f "$tarball"
echo "$HOME/.qoder/bin" >> "$GITHUB_PATH"
- name: Verify qodercli on PATH
if: steps.secrets.outputs.available == 'true'
run: |
set -euo pipefail
command -v qodercli
if ! qodercli --version; then
echo "::warning::qodercli does not support a successful --version probe"
fi
- name: Run e2e tests (none runtime, full mode)
if: steps.secrets.outputs.available == 'true'
run: go test -tags e2e -timeout 1800s -count=1 -v ./e2e
env:
SKILL_UP_FULL_E2E: "1"
SKILL_UP_E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
DASHSCOPE_BASE_URL: https://dashscope.aliyuncs.com/apps/anthropic
ANTHROPIC_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
ANTHROPIC_BASE_URL: https://dashscope.aliyuncs.com/apps/anthropic
ANTHROPIC_MODEL: qwen3.6-plus
OPENAI_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
OPENAI_BASE_URL: https://dashscope.aliyuncs.com/compatible-mode/v1
OPENAI_MODEL: qwen3.6-plus
DASHSCOPE_MODEL: qwen3.6-plus
QODER_PERSONAL_ACCESS_TOKEN: ${{ secrets.QODER_ACCESS_TOKEN }}
- name: Upload e2e workspace artifacts
if: always() && steps.secrets.outputs.available == 'true' && hashFiles('e2e-artifacts/**') != ''
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: model-e2e-none-workspaces
path: e2e-artifacts/
if-no-files-found: ignore
retention-days: 14
e2e-opensandbox:
name: E2E (OpenSandbox, live model)
if: inputs.suite == 'all' || inputs.suite == 'opensandbox'
runs-on: [self-hosted, linux, x64, docker, trusted]
timeout-minutes: 35
permissions:
contents: read
steps:
- name: Checkout source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Check secret availability
id: secrets
env:
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
run: |
set -euo pipefail
if [ -z "$DASHSCOPE_API_KEY" ]; then
echo "::error::DASHSCOPE_API_KEY is required for OpenSandbox e2e."
exit 1
fi
echo "available=true" >> "$GITHUB_OUTPUT"
- name: Set up Go
if: steps.secrets.outputs.available == 'true'
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25.x"
cache: true
- name: Install uv
if: steps.secrets.outputs.available == 'true'
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
- name: Install OpenSandbox server
if: steps.secrets.outputs.available == 'true'
run: |
set -euo pipefail
uv tool install 'opensandbox-server==0.1.13'
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Start OpenSandbox server
if: steps.secrets.outputs.available == 'true'
env:
OPENSANDBOX_IMAGE: node:22@sha256:5647be709086c696ff32edaaf1c70cd26d1da6ab2b39c32f3c7b4c4a31957e37
OPENSANDBOX_EXECD_IMAGE: opensandbox/execd:v1.0.16@sha256:af7b55c861926c1304371c4578007fbaa424538219154a6a49a5d636217d2a3a
run: |
set -euo pipefail
docker version
api_key="ci-$(openssl rand -hex 16)"
echo "::add-mask::$api_key"
config="$RUNNER_TEMP/sandbox.toml"
cat > "$config" <<EOF
[server]
host = "127.0.0.1"
port = 8080
api_key = "$api_key"
[runtime]
type = "docker"
execd_image = "$OPENSANDBOX_EXECD_IMAGE"
[docker]
network_mode = "host"
EOF
log="$RUNNER_TEMP/opensandbox-server.log"
nohup opensandbox-server --config "$config" > "$log" 2>&1 &
echo "OPENSANDBOX_SERVER_PID=$!" >> "$GITHUB_ENV"
for i in $(seq 1 60); do
if curl -fsS http://127.0.0.1:8080/health 2>/dev/null | grep -q healthy; then
echo "OpenSandbox server is healthy"
break
fi
if [ "$i" -eq 60 ]; then
echo "::error::OpenSandbox server did not become healthy within 120s"
cat "$log"
exit 1
fi
sleep 2
done
{
echo "OPENSANDBOX_API_KEY=$api_key"
echo "OPENSANDBOX_BASE_URL=http://127.0.0.1:8080"
echo "OPENSANDBOX_IMAGE=$OPENSANDBOX_IMAGE"
} >> "$GITHUB_ENV"
- name: Run e2e tests (OpenSandbox runtime)
if: steps.secrets.outputs.available == 'true'
run: go test -tags e2e -timeout 1800s -count=1 -v -run TestAgent_Codex_OpenSandboxRuntime ./e2e
env:
OPENAI_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
OPENAI_BASE_URL: https://dashscope.aliyuncs.com/compatible-mode/v1
OPENAI_MODEL: qwen3.6-plus
SKILL_UP_E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-opensandbox-artifacts
- name: Stop OpenSandbox server
if: always() && steps.secrets.outputs.available == 'true'
run: |
set -euo pipefail
if [ -n "${OPENSANDBOX_SERVER_PID:-}" ]; then
if kill "$OPENSANDBOX_SERVER_PID" 2>/dev/null; then
echo "OpenSandbox server stopped"
else
echo "::notice::OpenSandbox server was already stopped"
fi
fi
- name: Upload OpenSandbox server log
if: always() && steps.secrets.outputs.available == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: model-e2e-opensandbox-server-log
path: ${{ runner.temp }}/opensandbox-server.log
if-no-files-found: ignore
retention-days: 14
- name: Upload OpenSandbox workspace artifacts
if: always() && steps.secrets.outputs.available == 'true' && hashFiles('e2e-opensandbox-artifacts/**') != ''
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: model-e2e-opensandbox-workspaces
path: e2e-opensandbox-artifacts/
if-no-files-found: ignore
retention-days: 14
e2e-docker:
name: E2E (Docker runtime, live model)
if: inputs.suite == 'all' || inputs.suite == 'docker'
runs-on: [self-hosted, linux, x64, docker, trusted]
timeout-minutes: 25
permissions:
contents: read
steps:
- name: Checkout source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Check secret availability
id: secrets
env:
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
run: |
set -euo pipefail
if [ -z "$DASHSCOPE_API_KEY" ]; then
echo "::error::DASHSCOPE_API_KEY is required for Docker e2e."
exit 1
fi
echo "available=true" >> "$GITHUB_OUTPUT"
- name: Set up Go
if: steps.secrets.outputs.available == 'true'
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: "1.25.x"
cache: true
- name: Pre-pull Docker image
if: steps.secrets.outputs.available == 'true'
run: docker pull node:22@sha256:5647be709086c696ff32edaaf1c70cd26d1da6ab2b39c32f3c7b4c4a31957e37
- name: Run Docker full e2e
if: steps.secrets.outputs.available == 'true'
run: go test -tags e2e -timeout 1800s -count=1 -v -run TestAgent_ClaudeCode_DockerRuntime ./e2e
env:
SKILL_UP_FULL_E2E: "1"
SKILL_UP_E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-docker-full-artifacts
ANTHROPIC_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
ANTHROPIC_BASE_URL: https://dashscope.aliyuncs.com/apps/anthropic
ANTHROPIC_MODEL: qwen3.6-plus
- name: Upload Docker e2e workspace artifacts
if: always() && steps.secrets.outputs.available == 'true' && hashFiles('e2e-docker-full-artifacts/**') != ''
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: model-e2e-docker-workspaces
path: e2e-docker-full-artifacts/
if-no-files-found: ignore
retention-days: 14
summary:
name: Model E2E Summary
if: always()
needs: [e2e, e2e-opensandbox, e2e-docker]
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions: {}
steps:
- name: Write job summary
env:
NONE_RESULT: ${{ needs.e2e.result }}
OPENSANDBOX_RESULT: ${{ needs.e2e-opensandbox.result }}
DOCKER_RESULT: ${{ needs.e2e-docker.result }}
SELECTED_SUITE: ${{ inputs.suite }}
run: |
set -euo pipefail
{
echo "### Model E2E"
echo
echo "- Selected suite: \`${SELECTED_SUITE}\`"
echo
echo "| Runtime | Result |"
echo "|---|---|"
echo "| none | ${NONE_RESULT} |"
echo "| OpenSandbox | ${OPENSANDBOX_RESULT} |"
echo "| Docker | ${DOCKER_RESULT} |"
} >> "$GITHUB_STEP_SUMMARY"
for result in "$NONE_RESULT" "$OPENSANDBOX_RESULT" "$DOCKER_RESULT"; do
if [ "$result" != success ] && [ "$result" != skipped ]; then
echo "::error::One or more selected live-model suites failed"
exit 1
fi
done