mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 14:30:45 +00:00
903 lines
34 KiB
YAML
903 lines
34 KiB
YAML
name: OpenClaw Live And E2E Checks (Reusable)
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
ref:
|
|
description: Ref, tag, or SHA to validate
|
|
required: true
|
|
default: main
|
|
type: string
|
|
include_repo_e2e:
|
|
description: Whether to run pnpm test:e2e plus repo-specific extra E2E lanes
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
include_release_path_suites:
|
|
description: Whether to run the Docker release-path suites
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
include_openwebui:
|
|
description: Whether to run the Open WebUI Docker smoke
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
include_live_suites:
|
|
description: Whether to run live-provider coverage
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
live_models_only:
|
|
description: Whether to run only the Docker live model matrix when live suites are enabled
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
workflow_call:
|
|
inputs:
|
|
ref:
|
|
description: Ref, tag, or SHA to validate
|
|
required: true
|
|
type: string
|
|
include_repo_e2e:
|
|
description: Whether to run pnpm test:e2e
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
include_release_path_suites:
|
|
description: Whether to run the Docker release-path suites
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
include_openwebui:
|
|
description: Whether to run the Open WebUI Docker smoke
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
include_live_suites:
|
|
description: Whether to run live-provider coverage
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
live_models_only:
|
|
description: Whether to run only the Docker live model matrix when live suites are enabled
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
secrets:
|
|
OPENAI_API_KEY:
|
|
required: false
|
|
OPENAI_BASE_URL:
|
|
required: false
|
|
ANTHROPIC_API_KEY:
|
|
required: false
|
|
ANTHROPIC_API_KEY_OLD:
|
|
required: false
|
|
ANTHROPIC_API_TOKEN:
|
|
required: false
|
|
BYTEPLUS_API_KEY:
|
|
required: false
|
|
CEREBRAS_API_KEY:
|
|
required: false
|
|
DASHSCOPE_API_KEY:
|
|
required: false
|
|
GROQ_API_KEY:
|
|
required: false
|
|
KIMI_API_KEY:
|
|
required: false
|
|
MODELSTUDIO_API_KEY:
|
|
required: false
|
|
MOONSHOT_API_KEY:
|
|
required: false
|
|
MISTRAL_API_KEY:
|
|
required: false
|
|
MINIMAX_API_KEY:
|
|
required: false
|
|
OPENCODE_API_KEY:
|
|
required: false
|
|
OPENCODE_ZEN_API_KEY:
|
|
required: false
|
|
OPENCLAW_LIVE_BROWSER_CDP_URL:
|
|
required: false
|
|
OPENCLAW_LIVE_SETUP_TOKEN:
|
|
required: false
|
|
OPENCLAW_LIVE_SETUP_TOKEN_MODEL:
|
|
required: false
|
|
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE:
|
|
required: false
|
|
OPENCLAW_LIVE_SETUP_TOKEN_VALUE:
|
|
required: false
|
|
GEMINI_API_KEY:
|
|
required: false
|
|
GOOGLE_API_KEY:
|
|
required: false
|
|
OPENROUTER_API_KEY:
|
|
required: false
|
|
QWEN_API_KEY:
|
|
required: false
|
|
FAL_KEY:
|
|
required: false
|
|
RUNWAY_API_KEY:
|
|
required: false
|
|
DEEPGRAM_API_KEY:
|
|
required: false
|
|
TOGETHER_API_KEY:
|
|
required: false
|
|
VYDRA_API_KEY:
|
|
required: false
|
|
XAI_API_KEY:
|
|
required: false
|
|
ZAI_API_KEY:
|
|
required: false
|
|
Z_AI_API_KEY:
|
|
required: false
|
|
BYTEPLUS_ACCESS_KEY_ID:
|
|
required: false
|
|
BYTEPLUS_SECRET_ACCESS_KEY:
|
|
required: false
|
|
CLAUDE_CODE_OAUTH_TOKEN:
|
|
required: false
|
|
OPENCLAW_CODEX_AUTH_JSON:
|
|
required: false
|
|
OPENCLAW_CODEX_CONFIG_TOML:
|
|
required: false
|
|
OPENCLAW_CLAUDE_JSON:
|
|
required: false
|
|
OPENCLAW_CLAUDE_CREDENTIALS_JSON:
|
|
required: false
|
|
OPENCLAW_CLAUDE_SETTINGS_JSON:
|
|
required: false
|
|
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON:
|
|
required: false
|
|
OPENCLAW_GEMINI_SETTINGS_JSON:
|
|
required: false
|
|
FIREWORKS_API_KEY:
|
|
required: false
|
|
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
pull-requests: read
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
NODE_VERSION: "24.x"
|
|
PNPM_VERSION: "10.32.1"
|
|
|
|
jobs:
|
|
validate_selected_ref:
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
selected_sha: ${{ steps.validate.outputs.selected_sha }}
|
|
trusted_reason: ${{ steps.validate.outputs.trusted_reason }}
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
fetch-depth: 0
|
|
|
|
- name: Validate selected ref
|
|
id: validate
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
INPUT_REF: ${{ inputs.ref }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
selected_sha="$(git rev-parse HEAD)"
|
|
trusted_reason=""
|
|
|
|
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
|
|
|
|
if git merge-base --is-ancestor "$selected_sha" refs/remotes/origin/main; then
|
|
trusted_reason="main-ancestor"
|
|
elif git tag --points-at "$selected_sha" | grep -Eq '^v'; then
|
|
trusted_reason="release-tag"
|
|
else
|
|
pr_head_count="$(
|
|
gh api \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"repos/${GITHUB_REPOSITORY}/commits/${selected_sha}/pulls" \
|
|
--jq '[.[] | select(.state == "open" and .head.repo.full_name == "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${selected_sha}"'")] | length'
|
|
)"
|
|
if [[ "$pr_head_count" != "0" ]]; then
|
|
trusted_reason="open-pr-head"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$trusted_reason" ]]; then
|
|
echo "Ref '${INPUT_REF}' resolved to $selected_sha, which is not trusted for secret-bearing live/E2E checks." >&2
|
|
echo "Allowed refs must be on main, point to a release tag, or match an open PR head in ${GITHUB_REPOSITORY}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "selected_sha=$selected_sha" >> "$GITHUB_OUTPUT"
|
|
echo "trusted_reason=$trusted_reason" >> "$GITHUB_OUTPUT"
|
|
{
|
|
echo "Validated ref: \`${INPUT_REF}\`"
|
|
echo "Resolved SHA: \`$selected_sha\`"
|
|
echo "Trust reason: \`$trusted_reason\`"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
validate_release_live_cache:
|
|
needs: validate_selected_ref
|
|
if: inputs.include_live_suites && !inputs.live_models_only
|
|
runs-on: blacksmith-8vcpu-ubuntu-2404
|
|
timeout-minutes: 60
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
OPENCLAW_LIVE_CACHE_TEST: "1"
|
|
OPENCLAW_LIVE_TEST: "1"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Validate live cache credentials
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ -z "${OPENAI_API_KEY:-}" ]]; then
|
|
echo "Missing OPENAI_API_KEY secret for live-cache validation." >&2
|
|
exit 1
|
|
fi
|
|
if [[ -z "${ANTHROPIC_API_KEY:-}" ]]; then
|
|
echo "Missing ANTHROPIC_API_KEY secret for live-cache validation." >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: Verify live prompt cache floors
|
|
run: pnpm test:live:cache
|
|
|
|
validate_repo_e2e:
|
|
needs: validate_selected_ref
|
|
if: inputs.include_repo_e2e
|
|
runs-on: blacksmith-32vcpu-ubuntu-2404
|
|
timeout-minutes: 90
|
|
env:
|
|
OPENCLAW_VITEST_MAX_WORKERS: "2"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Build dist for repo E2E
|
|
run: pnpm build
|
|
|
|
- name: Run repo E2E suite
|
|
run: pnpm test:e2e
|
|
|
|
validate_special_e2e:
|
|
needs: validate_selected_ref
|
|
if: inputs.include_repo_e2e || (inputs.include_live_suites && !inputs.live_models_only)
|
|
runs-on: blacksmith-32vcpu-ubuntu-2404
|
|
timeout-minutes: ${{ matrix.timeout_minutes }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- suite_id: openshell-e2e
|
|
label: OpenShell repo E2E
|
|
command: pnpm test:e2e:openshell
|
|
timeout_minutes: 120
|
|
requires_repo_e2e: true
|
|
requires_live_suites: false
|
|
- suite_id: openai-ws-stream-live-e2e
|
|
label: OpenAI WebSocket live E2E
|
|
command: pnpm test:e2e -- src/agents/openai-ws-stream.e2e.test.ts
|
|
timeout_minutes: 90
|
|
requires_repo_e2e: false
|
|
requires_live_suites: true
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENCLAW_E2E_WORKERS: "1"
|
|
OPENCLAW_VITEST_MAX_WORKERS: "1"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Build dist for special E2E
|
|
if: |
|
|
(inputs.include_repo_e2e && matrix.requires_repo_e2e) ||
|
|
(inputs.include_live_suites && matrix.requires_live_suites)
|
|
run: pnpm build
|
|
|
|
- name: Configure suite-specific env
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
case "${{ matrix.suite_id }}" in
|
|
openai-ws-stream-live-e2e)
|
|
echo "OPENAI_LIVE_TEST=1" >> "$GITHUB_ENV"
|
|
echo "OPENCLAW_LIVE_TEST=1" >> "$GITHUB_ENV"
|
|
;;
|
|
esac
|
|
|
|
- name: Validate suite credentials
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
case "${{ matrix.suite_id }}" in
|
|
openai-ws-stream-live-e2e)
|
|
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
|
echo "OPENAI_API_KEY is required for the OpenAI WebSocket live E2E suite." >&2
|
|
exit 1
|
|
}
|
|
;;
|
|
esac
|
|
|
|
- name: Run ${{ matrix.label }}
|
|
if: |
|
|
(inputs.include_repo_e2e && matrix.requires_repo_e2e) ||
|
|
(inputs.include_live_suites && matrix.requires_live_suites)
|
|
run: ${{ matrix.command }}
|
|
|
|
validate_docker_e2e:
|
|
needs: [validate_selected_ref, prepare_docker_e2e_image]
|
|
if: inputs.include_release_path_suites
|
|
runs-on: blacksmith-32vcpu-ubuntu-2404
|
|
timeout-minutes: ${{ matrix.timeout_minutes }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- suite_id: docker-onboard
|
|
label: Onboarding Docker E2E
|
|
command: pnpm test:docker:onboard
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-npm-onboard-channel-agent
|
|
label: Npm Onboard Channel Agent Docker E2E
|
|
command: pnpm test:docker:npm-onboard-channel-agent
|
|
timeout_minutes: 90
|
|
release_path: true
|
|
- suite_id: docker-gateway-network
|
|
label: Gateway Network Docker E2E
|
|
command: pnpm test:docker:gateway-network
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-openai-web-search-minimal
|
|
label: OpenAI Web Search Minimal Docker E2E
|
|
command: pnpm test:docker:openai-web-search-minimal
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-mcp-channels
|
|
label: MCP Channels Docker E2E
|
|
command: pnpm test:docker:mcp-channels
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-pi-bundle-mcp-tools
|
|
label: Pi Bundle MCP Tools Docker E2E
|
|
command: pnpm test:docker:pi-bundle-mcp-tools
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-cron-mcp-cleanup
|
|
label: Cron MCP Cleanup Docker E2E
|
|
command: pnpm test:docker:cron-mcp-cleanup
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-plugins
|
|
label: Plugins Docker E2E
|
|
command: pnpm test:docker:plugins
|
|
timeout_minutes: 75
|
|
release_path: true
|
|
- suite_id: docker-plugin-update
|
|
label: Plugin Update Docker E2E
|
|
command: pnpm test:docker:plugin-update
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-config-reload
|
|
label: Config Reload Docker E2E
|
|
command: pnpm test:docker:config-reload
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-bundled-channel-deps
|
|
label: Bundled Channel Runtime Deps Docker E2E
|
|
command: pnpm test:docker:bundled-channel-deps
|
|
timeout_minutes: 75
|
|
release_path: true
|
|
- suite_id: docker-doctor-switch
|
|
label: Doctor Install Switch Docker E2E
|
|
command: pnpm test:docker:doctor-switch
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-qr
|
|
label: QR Import Docker E2E
|
|
command: pnpm test:docker:qr
|
|
timeout_minutes: 60
|
|
release_path: true
|
|
- suite_id: docker-install-e2e
|
|
label: Installer Docker E2E
|
|
command: pnpm test:install:e2e
|
|
timeout_minutes: 120
|
|
release_path: true
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
|
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
|
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
|
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
|
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
|
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
|
|
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
|
|
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
|
|
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
|
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
|
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
|
|
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
|
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
|
|
FAL_KEY: ${{ secrets.FAL_KEY }}
|
|
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
|
|
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
|
|
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
|
|
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
|
|
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
|
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
|
|
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
|
|
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
|
|
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
|
|
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
|
|
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
|
|
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
|
|
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
|
|
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
|
|
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
|
|
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
|
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
|
|
OPENCLAW_SKIP_DOCKER_BUILD: "1"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Log in to GHCR for shared Docker E2E image
|
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ github.token }}
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Hydrate live auth/profile inputs
|
|
run: bash scripts/ci-hydrate-live-auth.sh
|
|
|
|
- name: Configure suite-specific env
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
case "${{ matrix.suite_id }}" in
|
|
docker-install-e2e)
|
|
echo "OPENCLAW_E2E_MODELS=both" >> "$GITHUB_ENV"
|
|
;;
|
|
esac
|
|
|
|
- name: Validate suite credentials
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
case "${{ matrix.suite_id }}" in
|
|
docker-install-e2e)
|
|
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
|
echo "OPENAI_API_KEY is required for installer Docker E2E." >&2
|
|
exit 1
|
|
}
|
|
if [[ -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
|
|
echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for installer Docker E2E." >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
- name: Run ${{ matrix.label }}
|
|
run: ${{ matrix.command }}
|
|
|
|
validate_docker_openwebui:
|
|
needs: [validate_selected_ref, prepare_docker_e2e_image]
|
|
if: inputs.include_openwebui
|
|
runs-on: blacksmith-32vcpu-ubuntu-2404
|
|
timeout-minutes: 75
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
|
OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
|
|
OPENCLAW_SKIP_DOCKER_BUILD: "1"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Log in to GHCR for shared Docker E2E image
|
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ github.token }}
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Validate Open WebUI credentials
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
[[ -n "${OPENAI_API_KEY:-}" ]] || {
|
|
echo "OPENAI_API_KEY is required for the Open WebUI Docker smoke." >&2
|
|
exit 1
|
|
}
|
|
|
|
- name: Run Open WebUI Docker E2E
|
|
run: pnpm test:docker:openwebui
|
|
|
|
prepare_docker_e2e_image:
|
|
needs: validate_selected_ref
|
|
if: inputs.include_release_path_suites || inputs.include_openwebui
|
|
runs-on: blacksmith-32vcpu-ubuntu-2404
|
|
timeout-minutes: 90
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
outputs:
|
|
image: ${{ steps.image.outputs.image }}
|
|
env:
|
|
DOCKER_BUILD_SUMMARY: "false"
|
|
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Resolve shared Docker E2E image tag
|
|
id: image
|
|
shell: bash
|
|
env:
|
|
SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
repository="${GITHUB_REPOSITORY,,}"
|
|
image="ghcr.io/${repository}-docker-e2e:${SELECTED_SHA}"
|
|
echo "image=$image" >> "$GITHUB_OUTPUT"
|
|
echo "Shared Docker E2E image: \`$image\`" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Log in to GHCR
|
|
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ github.token }}
|
|
|
|
- name: Build and push shared Docker E2E image
|
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
|
with:
|
|
context: .
|
|
file: ./scripts/e2e/Dockerfile
|
|
target: build
|
|
platforms: linux/amd64
|
|
cache-from: type=gha,scope=docker-e2e
|
|
cache-to: type=gha,mode=max,scope=docker-e2e
|
|
tags: ${{ steps.image.outputs.image }}
|
|
provenance: false
|
|
push: true
|
|
|
|
validate_live_models_docker:
|
|
name: Docker live models (${{ matrix.provider_label }})
|
|
needs: validate_selected_ref
|
|
if: inputs.include_live_suites
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 75
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- provider_label: Anthropic
|
|
providers: anthropic
|
|
- provider_label: Google
|
|
providers: google
|
|
- provider_label: MiniMax
|
|
providers: minimax
|
|
- provider_label: OpenAI
|
|
providers: openai
|
|
- provider_label: OpenCode
|
|
providers: opencode-go
|
|
- provider_label: OpenRouter
|
|
providers: openrouter
|
|
- provider_label: xAI
|
|
providers: xai
|
|
- provider_label: Z.ai
|
|
providers: zai
|
|
- provider_label: Fireworks
|
|
providers: fireworks
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
|
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
|
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
|
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
|
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
|
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
|
|
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
|
|
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
|
|
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
|
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
|
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
|
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
|
|
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
|
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
|
|
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
|
|
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
|
|
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
|
|
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
|
|
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
|
|
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
|
|
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
|
|
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
|
OPENCLAW_LIVE_PROVIDERS: ${{ matrix.providers }}
|
|
OPENCLAW_VITEST_MAX_WORKERS: "2"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Hydrate live auth/profile inputs
|
|
run: bash scripts/ci-hydrate-live-auth.sh
|
|
|
|
- name: Validate provider credential
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
require_any() {
|
|
local label="$1"
|
|
shift
|
|
local key
|
|
for key in "$@"; do
|
|
if [[ -n "${!key:-}" ]]; then
|
|
return 0
|
|
fi
|
|
done
|
|
echo "Missing credential for ${label}: expected one of $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
case "${{ matrix.providers }}" in
|
|
anthropic) require_any Anthropic ANTHROPIC_API_KEY ANTHROPIC_API_KEY_OLD ANTHROPIC_API_TOKEN ;;
|
|
google) require_any Google GEMINI_API_KEY GOOGLE_API_KEY ;;
|
|
minimax) require_any MiniMax MINIMAX_API_KEY ;;
|
|
openai) require_any OpenAI OPENAI_API_KEY ;;
|
|
opencode-go) require_any OpenCode OPENCODE_API_KEY OPENCODE_ZEN_API_KEY ;;
|
|
openrouter) require_any OpenRouter OPENROUTER_API_KEY ;;
|
|
xai) require_any xAI XAI_API_KEY ;;
|
|
zai) require_any Z.ai ZAI_API_KEY Z_AI_API_KEY ;;
|
|
fireworks) require_any Fireworks FIREWORKS_API_KEY ;;
|
|
*)
|
|
echo "Unhandled live model provider shard: ${{ matrix.providers }}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
- name: Run Docker live model sweep
|
|
run: pnpm test:docker:live-models
|
|
|
|
validate_live_provider_suites:
|
|
needs: validate_selected_ref
|
|
if: inputs.include_live_suites && !inputs.live_models_only
|
|
runs-on: blacksmith-32vcpu-ubuntu-2404
|
|
timeout-minutes: ${{ matrix.timeout_minutes }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- suite_id: live-all
|
|
label: pnpm test:live
|
|
command: pnpm test:live
|
|
timeout_minutes: 180
|
|
profile_env_only: false
|
|
- suite_id: live-gateway-docker
|
|
label: Docker live gateway
|
|
command: pnpm test:docker:live-gateway
|
|
timeout_minutes: 120
|
|
profile_env_only: false
|
|
- suite_id: live-cli-backend-docker
|
|
label: Docker live CLI backend
|
|
command: pnpm test:docker:live-cli-backend
|
|
timeout_minutes: 120
|
|
profile_env_only: false
|
|
- suite_id: live-acp-bind-docker
|
|
label: Docker live ACP bind
|
|
command: pnpm test:docker:live-acp-bind
|
|
timeout_minutes: 120
|
|
profile_env_only: false
|
|
- suite_id: live-codex-harness-docker
|
|
label: Docker live Codex harness
|
|
command: pnpm test:docker:live-codex-harness
|
|
timeout_minutes: 120
|
|
profile_env_only: false
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
|
|
ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
|
|
BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
|
|
CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
|
|
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
|
|
KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
|
|
MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
|
|
MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
|
|
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
|
|
MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
|
|
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
|
OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
|
|
OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
|
|
OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
|
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
|
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
|
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
|
QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
|
|
FAL_KEY: ${{ secrets.FAL_KEY }}
|
|
RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
|
|
DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
|
|
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
|
|
VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
|
|
XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
|
|
ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
|
|
Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
|
|
BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
|
|
BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
|
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
|
|
OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
|
|
OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
|
|
OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
|
|
OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
|
|
OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
|
|
OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
|
|
FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
|
|
OPENCLAW_LIVE_VIDEO_GENERATION_SKIP_PROVIDERS: ""
|
|
OPENCLAW_LIVE_VYDRA_VIDEO: "1"
|
|
OPENCLAW_VITEST_MAX_WORKERS: "2"
|
|
steps:
|
|
- name: Checkout selected ref
|
|
uses: actions/checkout@v6
|
|
with:
|
|
ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
|
|
fetch-depth: 1
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
pnpm-version: ${{ env.PNPM_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Hydrate live auth/profile inputs
|
|
run: bash scripts/ci-hydrate-live-auth.sh
|
|
|
|
- name: Configure suite-specific env
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ "${{ matrix.profile_env_only }}" == "true" ]]; then
|
|
echo "OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1" >> "$GITHUB_ENV"
|
|
fi
|
|
case "${{ matrix.suite_id }}" in
|
|
live-cli-backend-docker)
|
|
echo "OPENCLAW_LIVE_CLI_BACKEND_MODEL=codex-cli/gpt-5.4" >> "$GITHUB_ENV"
|
|
# The CLI backend Docker lane should exercise the same staged
|
|
# Codex auth path Peter uses locally so MCP cron creation and
|
|
# multimodal probes stay covered in CI. Replace the staged
|
|
# config.toml with a minimal CI-safe config so the repo stays
|
|
# trusted for MCP/tool use without inheriting maintainer-local
|
|
# provider/profile overrides that do not exist inside CI.
|
|
# Codex's workspace-write sandbox relies on user namespaces that
|
|
# this Docker lane does not provide, so run Codex unsandboxed
|
|
# inside the already-isolated container to keep MCP cron/tool
|
|
# execution representative instead of failing on nested sandbox
|
|
# setup.
|
|
echo 'OPENCLAW_LIVE_CLI_BACKEND_CLEAR_ENV=["OPENAI_API_KEY","OPENAI_BASE_URL"]' >> "$GITHUB_ENV"
|
|
echo 'OPENCLAW_LIVE_CLI_BACKEND_ARGS=["exec","--json","--color","never","--sandbox","danger-full-access","--skip-git-repo-check"]' >> "$GITHUB_ENV"
|
|
echo 'OPENCLAW_LIVE_CLI_BACKEND_RESUME_ARGS=["exec","resume","{sessionId}","-c","sandbox_mode=\"danger-full-access\"","--skip-git-repo-check"]' >> "$GITHUB_ENV"
|
|
echo "OPENCLAW_LIVE_CLI_BACKEND_DEBUG=1" >> "$GITHUB_ENV"
|
|
echo "OPENCLAW_CLI_BACKEND_LOG_OUTPUT=1" >> "$GITHUB_ENV"
|
|
echo "OPENCLAW_LIVE_CLI_BACKEND_USE_CI_SAFE_CODEX_CONFIG=1" >> "$GITHUB_ENV"
|
|
;;
|
|
live-codex-harness-docker)
|
|
# Keep CI on the API-key path for now. The staged Codex auth secret
|
|
# is currently stale, but the wrapper still supports codex-auth for
|
|
# local maintainer reruns without changing Peter's flow.
|
|
echo "OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key" >> "$GITHUB_ENV"
|
|
;;
|
|
live-acp-bind-docker)
|
|
if [[ -n "${GEMINI_API_KEY:-}" || -n "${GOOGLE_API_KEY:-}" ]]; then
|
|
echo "OPENCLAW_LIVE_ACP_BIND_AGENTS=claude,codex,gemini" >> "$GITHUB_ENV"
|
|
else
|
|
# The hydrated Gemini settings file only selects Gemini CLI auth
|
|
# mode. CI still needs a usable Gemini or Google API key before
|
|
# ACP bind can initialize a Gemini session.
|
|
echo "OPENCLAW_LIVE_ACP_BIND_AGENTS=claude,codex" >> "$GITHUB_ENV"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
- name: Run ${{ matrix.label }}
|
|
run: ${{ matrix.command }}
|