


















@@ -23,6 +23,11 @@ on:
2323required: false
2424default: true
2525type: boolean
26+docker_lanes:
27+description: Comma/space separated Docker scheduler lane names to run against the prepared image
28+required: false
29+default: ""
30+type: string
2631include_live_suites:
2732description: Whether to run live-provider coverage
2833required: false
@@ -54,6 +59,11 @@ on:
5459required: false
5560default: true
5661type: boolean
62+docker_lanes:
63+description: Comma/space separated Docker scheduler lane names to run against the prepared image
64+required: false
65+default: ""
66+type: string
5767include_live_suites:
5868description: Whether to run live-provider coverage
5969required: false
@@ -363,7 +373,7 @@ jobs:
363373364374validate_docker_e2e:
365375needs: [validate_selected_ref, prepare_docker_e2e_image]
366-if: inputs.include_release_path_suites
376+if: inputs.include_release_path_suites && inputs.docker_lanes == ''
367377name: Docker E2E (${{ matrix.label }})
368378runs-on: blacksmith-32vcpu-ubuntu-2404
369379timeout-minutes: ${{ matrix.timeout_minutes }}
@@ -517,11 +527,12 @@ jobs:
517527 console.log("");
518528 console.log(`Status: \`${summary.status}\``);
519529 console.log("");
520- console.log("| Lane | Status | Seconds | Timed out |");
521- console.log("| --- | ---: | ---: | --- |");
530+ console.log("| Lane | Status | Seconds | Timed out | Rerun |");
531+ console.log("| --- | ---: | ---: | --- | --- |");
522532 for (const lane of lanes) {
523533 const status = lane.status === 0 ? "pass" : `fail ${lane.status}`;
524- console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} |`);
534+ const rerun = String(lane.rerunCommand ?? "").replaceAll("`", "\\`");
535+ console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} | \`${rerun}\` |`);
525536 }
526537 NODE
527538@@ -533,9 +544,170 @@ jobs:
533544path: .artifacts/docker-tests/
534545if-no-files-found: ignore
535546547+validate_docker_lanes:
548+needs: [validate_selected_ref, prepare_docker_e2e_image]
549+if: inputs.docker_lanes != ''
550+name: Docker E2E targeted lanes
551+runs-on: blacksmith-32vcpu-ubuntu-2404
552+timeout-minutes: 180
553+env:
554+OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
555+OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
556+ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
557+ANTHROPIC_API_TOKEN: ${{ secrets.ANTHROPIC_API_TOKEN }}
558+ANTHROPIC_API_KEY_OLD: ${{ secrets.ANTHROPIC_API_KEY_OLD }}
559+BYTEPLUS_API_KEY: ${{ secrets.BYTEPLUS_API_KEY }}
560+CEREBRAS_API_KEY: ${{ secrets.CEREBRAS_API_KEY }}
561+DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
562+GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
563+KIMI_API_KEY: ${{ secrets.KIMI_API_KEY }}
564+MODELSTUDIO_API_KEY: ${{ secrets.MODELSTUDIO_API_KEY }}
565+MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }}
566+MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
567+MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
568+OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
569+OPENCODE_ZEN_API_KEY: ${{ secrets.OPENCODE_ZEN_API_KEY }}
570+OPENCLAW_LIVE_BROWSER_CDP_URL: ${{ secrets.OPENCLAW_LIVE_BROWSER_CDP_URL }}
571+OPENCLAW_LIVE_SETUP_TOKEN: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN }}
572+OPENCLAW_LIVE_SETUP_TOKEN_MODEL: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_MODEL }}
573+OPENCLAW_LIVE_SETUP_TOKEN_PROFILE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_PROFILE }}
574+OPENCLAW_LIVE_SETUP_TOKEN_VALUE: ${{ secrets.OPENCLAW_LIVE_SETUP_TOKEN_VALUE }}
575+GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
576+GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
577+OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
578+QWEN_API_KEY: ${{ secrets.QWEN_API_KEY }}
579+FAL_KEY: ${{ secrets.FAL_KEY }}
580+RUNWAY_API_KEY: ${{ secrets.RUNWAY_API_KEY }}
581+DEEPGRAM_API_KEY: ${{ secrets.DEEPGRAM_API_KEY }}
582+TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
583+VYDRA_API_KEY: ${{ secrets.VYDRA_API_KEY }}
584+XAI_API_KEY: ${{ secrets.XAI_API_KEY }}
585+ZAI_API_KEY: ${{ secrets.ZAI_API_KEY }}
586+Z_AI_API_KEY: ${{ secrets.Z_AI_API_KEY }}
587+BYTEPLUS_ACCESS_KEY_ID: ${{ secrets.BYTEPLUS_ACCESS_KEY_ID }}
588+BYTEPLUS_SECRET_ACCESS_KEY: ${{ secrets.BYTEPLUS_SECRET_ACCESS_KEY }}
589+CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
590+OPENCLAW_CODEX_AUTH_JSON: ${{ secrets.OPENCLAW_CODEX_AUTH_JSON }}
591+OPENCLAW_CODEX_CONFIG_TOML: ${{ secrets.OPENCLAW_CODEX_CONFIG_TOML }}
592+OPENCLAW_CLAUDE_JSON: ${{ secrets.OPENCLAW_CLAUDE_JSON }}
593+OPENCLAW_CLAUDE_CREDENTIALS_JSON: ${{ secrets.OPENCLAW_CLAUDE_CREDENTIALS_JSON }}
594+OPENCLAW_CLAUDE_SETTINGS_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_JSON }}
595+OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON: ${{ secrets.OPENCLAW_CLAUDE_SETTINGS_LOCAL_JSON }}
596+OPENCLAW_GEMINI_SETTINGS_JSON: ${{ secrets.OPENCLAW_GEMINI_SETTINGS_JSON }}
597+FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }}
598+OPENCLAW_DOCKER_E2E_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.image }}
599+OPENCLAW_SKIP_DOCKER_BUILD: "1"
600+INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }}
601+DOCKER_E2E_LANES: ${{ inputs.docker_lanes }}
602+steps:
603+ - name: Checkout selected ref
604+uses: actions/checkout@v6
605+with:
606+ref: ${{ needs.validate_selected_ref.outputs.selected_sha }}
607+fetch-depth: 1
608+609+ - name: Log in to GHCR for shared Docker E2E image
610+uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
611+with:
612+registry: ghcr.io
613+username: ${{ github.actor }}
614+password: ${{ github.token }}
615+616+ - name: Setup Node environment
617+uses: ./.github/actions/setup-node-env
618+with:
619+node-version: ${{ env.NODE_VERSION }}
620+pnpm-version: ${{ env.PNPM_VERSION }}
621+install-bun: "true"
622+623+ - name: Hydrate live auth/profile inputs
624+run: bash scripts/ci-hydrate-live-auth.sh
625+626+ - name: Pull shared Docker E2E image
627+shell: bash
628+run: |
629+ set -euo pipefail
630+ docker pull "${OPENCLAW_DOCKER_E2E_IMAGE}"
631+632+ - name: Validate targeted lane credentials
633+shell: bash
634+run: |
635+ set -euo pipefail
636+ lanes=" ${DOCKER_E2E_LANES//,/ } "
637+ if [[ "$lanes" == *" install-e2e "* ]]; then
638+ [[ -n "${OPENAI_API_KEY:-}" ]] || {
639+ echo "OPENAI_API_KEY is required for installer Docker E2E." >&2
640+ exit 1
641+ }
642+ if [[ -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then
643+ echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for installer Docker E2E." >&2
644+ exit 1
645+ fi
646+ fi
647+ if [[ "$lanes" == *" openwebui "* || "$lanes" == *" openai-web-search-minimal "* ]]; then
648+ [[ -n "${OPENAI_API_KEY:-}" ]] || {
649+ echo "OPENAI_API_KEY is required for selected OpenAI Docker lanes." >&2
650+ exit 1
651+ }
652+ fi
653+654+ - name: Run targeted Docker E2E lanes
655+shell: bash
656+run: |
657+ set -euo pipefail
658+ lanes=" ${DOCKER_E2E_LANES//,/ } "
659+ export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}"
660+ export OPENCLAW_DOCKER_ALL_PREFLIGHT=0
661+ export OPENCLAW_DOCKER_ALL_FAIL_FAST=0
662+ export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}"
663+ export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/targeted"
664+ export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/targeted-timings.json"
665+ if [[ "$lanes" == *" live-"* ]]; then
666+ export OPENCLAW_DOCKER_ALL_BUILD=1
667+ else
668+ export OPENCLAW_DOCKER_ALL_BUILD=0
669+ fi
670+671+ pnpm test:docker:all
672+673+ - name: Summarize targeted Docker E2E lanes
674+if: always()
675+shell: bash
676+run: |
677+ set -euo pipefail
678+ summary=".artifacts/docker-tests/targeted/summary.json"
679+ if [[ ! -f "$summary" ]]; then
680+ echo "Docker targeted summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY"
681+ exit 0
682+ fi
683+ node --input-type=module - "$summary" <<'NODE' >> "$GITHUB_STEP_SUMMARY"
684+ import fs from "node:fs";
685+ const summary = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
686+ const lanes = Array.isArray(summary.lanes) ? summary.lanes : [];
687+ console.log("### Docker E2E targeted lanes");
688+ console.log("");
689+ console.log(`Status: \`${summary.status}\``);
690+ console.log("");
691+ console.log("| Lane | Status | Seconds | Timed out | Rerun |");
692+ console.log("| --- | ---: | ---: | --- | --- |");
693+ for (const lane of lanes) {
694+ const status = lane.status === 0 ? "pass" : `fail ${lane.status}`;
695+ const rerun = String(lane.rerunCommand ?? "").replaceAll("`", "\\`");
696+ console.log(`| \`${lane.name}\` | ${status} | ${lane.elapsedSeconds ?? ""} | ${lane.timedOut ? "yes" : "no"} | \`${rerun}\` |`);
697+ }
698+ NODE
699+700+ - name: Upload targeted Docker E2E artifacts
701+if: always()
702+uses: actions/upload-artifact@v7
703+with:
704+name: docker-e2e-targeted
705+path: .artifacts/docker-tests/
706+if-no-files-found: ignore
707+536708validate_docker_openwebui:
537709needs: [validate_selected_ref, prepare_docker_e2e_image]
538-if: inputs.include_openwebui && !inputs.include_release_path_suites
710+if: inputs.include_openwebui && !inputs.include_release_path_suites && inputs.docker_lanes == ''
539711runs-on: blacksmith-32vcpu-ubuntu-2404
540712timeout-minutes: 75
541713env:
@@ -578,7 +750,7 @@ jobs:
578750579751prepare_docker_e2e_image:
580752needs: validate_selected_ref
581-if: inputs.include_release_path_suites || inputs.include_openwebui
753+if: inputs.include_release_path_suites || inputs.include_openwebui || inputs.docker_lanes != ''
582754runs-on: blacksmith-32vcpu-ubuntu-2404
583755timeout-minutes: 90
584756permissions:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。