@@ -87,17 +87,31 @@ jobs:
8787 run :
8888 needs : discover
8989 runs-on : arc-runner-set
90- timeout-minutes : 20
91- strategy :
92- # No max-parallel: intentionally run every example at once. This doubles
93- # as a load test of the createos-sandbox control plane — surfacing its
94- # behavior under a full parallel burst (503s, pause/resume latency, /exec
95- # blips) is a signal we want, not noise to suppress. Add a cap only if the
96- # goal shifts back to deterministic green CI.
97- fail-fast : false # one failing example must not cancel the others
98- matrix :
99- example : ${{ fromJson(needs.discover.outputs.examples) }}
100- name : ${{ matrix.example }}
90+ # 5 concurrent waves × 20 min per example = up to 90 min worst-case
91+ timeout-minutes : 90
92+ env :
93+ EXAMPLES_JSON : ${{ needs.discover.outputs.examples }}
94+ # createos-sandbox control plane (read by the SDK + most examples)
95+ CREATEOS_SANDBOX_API_KEY : ${{ secrets.CREATEOS_SANDBOX_API_KEY }}
96+ CREATEOS_SANDBOX_BASE_URL : ${{ secrets.CREATEOS_SANDBOX_BASE_URL }}
97+ # OpenAI / OpenAI-compatible
98+ OPENAI_API_KEY : ${{ secrets.OPENAI_API_KEY }}
99+ OPENAI_API_URL : ${{ secrets.OPENAI_API_URL }}
100+ OPENAI_BASE_URL : ${{ secrets.OPENAI_BASE_URL }}
101+ OPENAI_MODEL : ${{ secrets.OPENAI_MODEL }}
102+ LLM_MODEL : ${{ secrets.LLM_MODEL }}
103+ # Anthropic
104+ ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
105+ ANTHROPIC_AUTH_TOKEN : ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
106+ ANTHROPIC_BASE_URL : ${{ secrets.ANTHROPIC_BASE_URL }}
107+ ANTHROPIC_MODEL : ${{ secrets.ANTHROPIC_MODEL }}
108+ # S3-compatible bucket (example 38)
109+ S3_BUCKET : ${{ secrets.S3_BUCKET }}
110+ S3_ENDPOINT : ${{ secrets.S3_ENDPOINT }}
111+ S3_REGION : ${{ secrets.S3_REGION }}
112+ S3_ACCESS_KEY : ${{ secrets.S3_ACCESS_KEY }}
113+ S3_SECRET_KEY : ${{ secrets.S3_SECRET_KEY }}
114+ S3_USE_PATH_STYLE : ${{ secrets.S3_USE_PATH_STYLE }}
101115 steps :
102116 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
103117
@@ -133,65 +147,63 @@ jobs:
133147 working-directory : examples
134148 run : bun install
135149
136- # example 20 creates a Python venv on first run; setup-python ensures
137- # venv support is present (plain python3 on Ubuntu lacks python3-venv).
138- - name : Set up Python (google-adk example)
139- if : matrix.example == '20-google-adk-agent'
150+ # example 20 needs python3-venv; install once since the job runs all examples
151+ - name : Set up Python
140152 uses : actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
141153 with :
142154 python-version : " 3.12"
143155
144- # example 38 mounts an S3 bucket as a disk; the script seeds + verifies
145- # objects but never creates the bucket. Backed by play.min.io, which
146- # purges buckets periodically — (re)create it before each run so a wipe
147- # between nightly runs can't fail the job on a missing bucket.
148- - name : Ensure S3 bucket (minio play, example 38)
149- if : matrix.example == '38-s3-disk-ffmpeg-transcode'
150- env :
151- S3_ENDPOINT : ${{ secrets.S3_ENDPOINT }}
152- S3_BUCKET : ${{ secrets.S3_BUCKET }}
153- S3_ACCESS_KEY : ${{ secrets.S3_ACCESS_KEY }}
154- S3_SECRET_KEY : ${{ secrets.S3_SECRET_KEY }}
156+ # example 38 mounts an S3 bucket; play.min.io purges buckets periodically
157+ - name : Ensure S3 bucket (example 38)
155158 run : |
156159 curl -sSfL https://dl.min.io/client/mc/release/linux-amd64/mc -o /tmp/mc
157160 chmod +x /tmp/mc
158161 /tmp/mc alias set ci "$S3_ENDPOINT" "$S3_ACCESS_KEY" "$S3_SECRET_KEY"
159162 /tmp/mc mb --ignore-existing "ci/$S3_BUCKET"
160163
161- - name : Run ${{ matrix.example }}
162- working-directory : examples/${{ matrix.example }}
163- env :
164- # --- createos-sandbox control plane (read by the SDK + most examples) ---
165- CREATEOS_SANDBOX_API_KEY : ${{ secrets.CREATEOS_SANDBOX_API_KEY }}
166- CREATEOS_SANDBOX_BASE_URL : ${{ secrets.CREATEOS_SANDBOX_BASE_URL }}
167- # --- OpenAI / OpenAI-compatible ---
168- OPENAI_API_KEY : ${{ secrets.OPENAI_API_KEY }}
169- OPENAI_API_URL : ${{ secrets.OPENAI_API_URL }}
170- OPENAI_BASE_URL : ${{ secrets.OPENAI_BASE_URL }}
171- OPENAI_MODEL : ${{ secrets.OPENAI_MODEL }}
172- # OPENAI_USE_CHAT_COMPLETIONS: ${{ secrets.OPENAI_USE_CHAT_COMPLETIONS }}
173- # OPENAI_AGENTS_DISABLE_TRACING: ${{ secrets.OPENAI_AGENTS_DISABLE_TRACING }}
174- LLM_MODEL : ${{ secrets.LLM_MODEL }}
175- # --- Anthropic ---
176- ANTHROPIC_API_KEY : ${{ secrets.ANTHROPIC_API_KEY }}
177- ANTHROPIC_AUTH_TOKEN : ${{ secrets.ANTHROPIC_AUTH_TOKEN }}
178- ANTHROPIC_BASE_URL : ${{ secrets.ANTHROPIC_BASE_URL }}
179- ANTHROPIC_MODEL : ${{ secrets.ANTHROPIC_MODEL }}
180- # --- third-party integrations (per-example) ---
181- # BROWSERBASE_API_KEY: ${{ secrets.BROWSERBASE_API_KEY }}
182- # BROWSERBASE_PROJECT_ID: ${{ secrets.BROWSERBASE_PROJECT_ID }}
183- # FIRECRAWL_API_KEY: ${{ secrets.FIRECRAWL_API_KEY }}
184- # FIRECRAWL_API_URL: ${{ secrets.FIRECRAWL_API_URL }}
185- # OPENCLAW_GATEWAY_TOKEN: ${{ secrets.OPENCLAW_GATEWAY_TOKEN }}
186- # SCRAPE_URL: ${{ secrets.SCRAPE_URL }}
187- # --- S3-compatible bucket for example 38 (s3-disk-ffmpeg-transcode) ---
188- # Backed by the public MinIO Playground (play.min.io). The bucket is
189- # (re)created by the "Ensure S3 bucket" step above, since play purges
190- # buckets periodically and the example does not create one itself.
191- S3_BUCKET : ${{ secrets.S3_BUCKET }}
192- S3_ENDPOINT : ${{ secrets.S3_ENDPOINT }}
193- S3_REGION : ${{ secrets.S3_REGION }}
194- S3_ACCESS_KEY : ${{ secrets.S3_ACCESS_KEY }}
195- S3_SECRET_KEY : ${{ secrets.S3_SECRET_KEY }}
196- S3_USE_PATH_STYLE : ${{ secrets.S3_USE_PATH_STYLE }}
197- run : bun index.ts
164+ - name : Run examples (parallel, max 5)
165+ run : |
166+ mkdir -p /tmp/example-logs /tmp/example-fails
167+ mapfile -t EXAMPLES < <(echo "$EXAMPLES_JSON" | jq -r '.[]')
168+
169+ printf '%s\n' "${EXAMPLES[@]}" | xargs -n1 -P5 bash -c '
170+ example="$1"
171+ log="/tmp/example-logs/$example.log"
172+ (cd "$GITHUB_WORKSPACE/examples/$example" && timeout 1200 bun index.ts) >"$log" 2>&1
173+ rc=$?
174+ [[ $rc -ne 0 ]] && echo "$rc" > "/tmp/example-fails/$example"
175+ printf "[%s] %s\n" "$([ $rc -eq 0 ] && echo "OK " || echo "FAIL")" "$example"
176+ exit $rc
177+ ' _ || true
178+
179+ - name : Report results
180+ if : always()
181+ run : |
182+ mapfile -t EXAMPLES < <(echo "$EXAMPLES_JSON" | jq -r '.[]')
183+ mapfile -t FAILED < <(ls /tmp/example-fails/ 2>/dev/null | sort)
184+ total=${#EXAMPLES[@]}
185+ nfailed=${#FAILED[@]}
186+
187+ {
188+ echo "## Examples: $(( total - nfailed ))/${total} passed"
189+ echo ""
190+ echo "| Example | Result |"
191+ echo "| --- | --- |"
192+ for ex in "${EXAMPLES[@]}"; do
193+ if [[ -f "/tmp/example-fails/$ex" ]]; then
194+ echo "| \`$ex\` | ❌ |"
195+ else
196+ echo "| \`$ex\` | ✅ |"
197+ fi
198+ done
199+ } >> "$GITHUB_STEP_SUMMARY"
200+
201+ if [[ $nfailed -gt 0 ]]; then
202+ echo "::error::${nfailed} of ${total} example(s) failed: ${FAILED[*]}"
203+ for ex in "${FAILED[@]}"; do
204+ echo "::group::FAILED: $ex"
205+ cat "/tmp/example-logs/$ex.log" || true
206+ echo "::endgroup::"
207+ done
208+ exit 1
209+ fi
0 commit comments