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 docker_lanes: description: Comma/space separated Docker scheduler lane names to run against the prepared image required: false default: "" type: string package_artifact_name: description: Existing workflow artifact containing openclaw-current.tgz; blank packs the selected ref required: false default: "" type: string package_artifact_run_id: description: Prior run id containing package_artifact_name; blank uses this run or packs the selected ref required: false default: "" type: string docker_e2e_bare_image: description: Existing bare Docker E2E image to reuse; blank derives from package SHA/ref required: false default: "" type: string docker_e2e_functional_image: description: Existing functional Docker E2E image to reuse; blank derives from package SHA/ref required: false default: "" type: string 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 live_model_providers: description: Comma/space separated provider ids for the Docker live model matrix; blank runs all providers required: false default: "" type: string release_test_profile: description: Release coverage profile for live/Docker/provider breadth required: false default: full type: choice options: - minimum - stable - full 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 docker_lanes: description: Comma/space separated Docker scheduler lane names to run against the prepared image required: false default: "" type: string package_artifact_name: description: Existing workflow artifact containing openclaw-current.tgz; blank packs the selected ref required: false default: "" type: string package_artifact_run_id: description: Prior run id containing package_artifact_name; blank uses this run or packs the selected ref required: false default: "" type: string docker_e2e_bare_image: description: Existing bare Docker E2E image to reuse; blank derives from package SHA/ref required: false default: "" type: string docker_e2e_functional_image: description: Existing functional Docker E2E image to reuse; blank derives from package SHA/ref required: false default: "" type: string 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 live_model_providers: description: Comma/space separated provider ids for the Docker live model matrix; blank runs all providers required: false default: "" type: string release_test_profile: description: Release coverage profile for live/Docker/provider breadth required: false default: full type: string 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 DEEPINFRA_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 workflow repository uses: actions/checkout@v6 with: fetch-depth: 0 - name: Validate selected ref id: validate env: INPUT_REF: ${{ inputs.ref }} shell: bash run: | set -euo pipefail trusted_reason="" git fetch --no-tags origin '+refs/heads/*:refs/remotes/origin/*' git fetch --tags origin '+refs/tags/*:refs/tags/*' # Resolve here instead of in actions/checkout so short SHAs work too. if ! selected_sha="$(git rev-parse --verify "${INPUT_REF}^{commit}")"; then echo "Ref '${INPUT_REF}' could not be resolved to a commit." >&2 exit 1 fi 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" elif git for-each-ref --format='%(refname:short)' --contains "$selected_sha" refs/remotes/origin | grep -Eq '^origin/'; then trusted_reason="repository-branch-history" else trusted_reason="" 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 reachable from an OpenClaw branch or release tag." >&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 && inputs.docker_lanes == '' name: Docker E2E (${{ matrix.label }}) runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: ${{ matrix.timeout_minutes }} strategy: fail-fast: false matrix: include: - chunk_id: core label: core timeout_minutes: 120 - chunk_id: package-update-openai label: package/update OpenAI install timeout_minutes: 180 - chunk_id: package-update-anthropic label: package/update Anthropic install timeout_minutes: 180 - chunk_id: package-update-core label: package/update core timeout_minutes: 120 - chunk_id: plugins-runtime-plugins label: plugins/runtime plugins timeout_minutes: 120 - chunk_id: plugins-runtime-services label: plugins/runtime services timeout_minutes: 120 - chunk_id: plugins-runtime-install-a label: plugins/runtime install A timeout_minutes: 120 - chunk_id: plugins-runtime-install-b label: plugins/runtime install B timeout_minutes: 120 - chunk_id: plugins-runtime-install-c label: plugins/runtime install C timeout_minutes: 120 - chunk_id: plugins-runtime-install-d label: plugins/runtime install D timeout_minutes: 120 - chunk_id: plugins-runtime-install-e label: plugins/runtime install E timeout_minutes: 120 - chunk_id: plugins-runtime-install-f label: plugins/runtime install F timeout_minutes: 120 - chunk_id: plugins-runtime-install-g label: plugins/runtime install G timeout_minutes: 120 - chunk_id: plugins-runtime-install-h label: plugins/runtime install H timeout_minutes: 120 - chunk_id: bundled-channels-core label: bundled channels core timeout_minutes: 90 - chunk_id: bundled-channels-update-a label: bundled channels update A timeout_minutes: 45 - chunk_id: bundled-channels-update-discord label: bundled channels update Discord timeout_minutes: 30 - chunk_id: bundled-channels-update-b label: bundled channels update B timeout_minutes: 45 - chunk_id: bundled-channels-contracts label: bundled channels contracts timeout_minutes: 90 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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_DOCKER_E2E_BARE_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.bare_image }} OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }} OPENCLAW_DOCKER_E2E_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} OPENCLAW_DOCKER_E2E_REPO_ROOT: ${{ github.workspace }} OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }} OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz OPENCLAW_SKIP_DOCKER_BUILD: "1" INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }} DOCKER_E2E_CHUNK: ${{ matrix.chunk_id }} steps: - name: Checkout selected ref uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted release harness uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - 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: Plan Docker E2E chunk id: plan shell: bash env: CHUNK: ${{ matrix.chunk_id }} INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }} run: | set -euo pipefail if [[ -z "$CHUNK" ]]; then echo "chunk input is required for Docker E2E chunk planning." >&2 exit 1 fi mkdir -p .artifacts/docker-tests export OPENCLAW_DOCKER_ALL_PROFILE=release-path export OPENCLAW_DOCKER_ALL_CHUNK="$CHUNK" export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI" plan_path=".artifacts/docker-tests/release-${CHUNK}-plan.json" node .release-harness/scripts/test-docker-all.mjs --plan-json > "$plan_path" node .release-harness/scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT" echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT" - name: Download OpenClaw Docker E2E package if: steps.plan.outputs.needs_package == '1' uses: actions/download-artifact@v8 with: name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} path: .artifacts/docker-e2e-package - name: Pull shared bare Docker E2E image if: steps.plan.outputs.needs_bare_image == '1' shell: bash run: | set -euo pipefail bash .release-harness/scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}" - name: Pull shared functional Docker E2E image if: steps.plan.outputs.needs_functional_image == '1' shell: bash run: | set -euo pipefail bash .release-harness/scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}" - name: Validate Docker E2E credentials shell: bash env: CREDENTIALS: ${{ steps.plan.outputs.credentials }} run: | set -euo pipefail credentials=",$CREDENTIALS," if [[ "$credentials" == *",openai,"* ]]; then [[ -n "${OPENAI_API_KEY:-}" ]] || { echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2 exit 1 } fi if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2 exit 1 fi - name: Run Docker E2E chunk shell: bash run: | set -euo pipefail export OPENCLAW_DOCKER_ALL_PROFILE=release-path export OPENCLAW_DOCKER_ALL_CHUNK="${DOCKER_E2E_CHUNK}" export OPENCLAW_DOCKER_ALL_BUILD=0 export OPENCLAW_DOCKER_ALL_PREFLIGHT=0 export OPENCLAW_DOCKER_ALL_FAIL_FAST=0 export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}" export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}" export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}-timings.json" export OPENCLAW_DOCKER_ALL_PNPM_COMMAND="$(command -v pnpm)" node .release-harness/scripts/test-docker-all.mjs - name: Summarize Docker E2E chunk if: always() shell: bash run: | set -euo pipefail summary=".artifacts/docker-tests/release-${DOCKER_E2E_CHUNK}/summary.json" if [[ ! -f "$summary" ]]; then echo "Docker chunk summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY" exit 0 fi node .release-harness/scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: ${DOCKER_E2E_CHUNK:-unknown}" >> "$GITHUB_STEP_SUMMARY" - name: Upload Docker E2E chunk artifacts if: always() uses: actions/upload-artifact@v7 with: name: docker-e2e-${{ matrix.chunk_id }} path: .artifacts/docker-tests/ if-no-files-found: ignore plan_docker_lane_groups: needs: validate_selected_ref if: inputs.docker_lanes != '' runs-on: blacksmith-4vcpu-ubuntu-2404 timeout-minutes: 5 outputs: groups_json: ${{ steps.plan.outputs.groups_json }} steps: - name: Plan targeted Docker lane groups id: plan shell: bash env: LANES: ${{ inputs.docker_lanes }} run: | set -euo pipefail groups_json="$( LANES="$LANES" node <<'NODE' const lanes = [...new Set(String(process.env.LANES || "").split(/[,\s]+/u).map((lane) => lane.trim()).filter(Boolean))]; if (lanes.length === 0) { throw new Error("docker_lanes is required when planning targeted Docker lane groups."); } const sanitize = (lane) => lane.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "targeted"; process.stdout.write(JSON.stringify(lanes.map((lane) => ({ label: sanitize(lane), docker_lanes: lane })))); NODE )" echo "groups_json=${groups_json}" >> "$GITHUB_OUTPUT" validate_docker_lanes: needs: [validate_selected_ref, prepare_docker_e2e_image, plan_docker_lane_groups] if: inputs.docker_lanes != '' name: Docker E2E targeted lanes (${{ matrix.group.label }}) runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 180 strategy: fail-fast: false matrix: group: ${{ fromJson(needs.plan_docker_lane_groups.outputs.groups_json) }} 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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_DOCKER_E2E_BARE_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.bare_image }} OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }} OPENCLAW_DOCKER_E2E_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} OPENCLAW_DOCKER_E2E_REPO_ROOT: ${{ github.workspace }} OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }} OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz OPENCLAW_SKIP_DOCKER_BUILD: "1" INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }} DOCKER_E2E_LANES: ${{ matrix.group.docker_lanes }} steps: - name: Checkout selected ref uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted release harness uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - 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: Plan targeted Docker E2E lanes id: plan shell: bash env: LANES: ${{ matrix.group.docker_lanes }} INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }} run: | set -euo pipefail if [[ -z "$LANES" ]]; then echo "lanes input is required for Docker E2E targeted planning." >&2 exit 1 fi mkdir -p .artifacts/docker-tests export OPENCLAW_DOCKER_ALL_LANES="$LANES" export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI" plan_path=".artifacts/docker-tests/targeted-plan.json" node .release-harness/scripts/test-docker-all.mjs --plan-json > "$plan_path" node .release-harness/scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT" suffix="$(printf '%s' "$LANES" | tr ',[:space:]' '-' | tr -cd 'A-Za-z0-9._-' | sed -E 's/-+/-/g; s/^-//; s/-$//')" echo "artifact_suffix=${suffix:-targeted}" >> "$GITHUB_OUTPUT" echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT" - name: Download OpenClaw Docker E2E package if: steps.plan.outputs.needs_package == '1' uses: actions/download-artifact@v8 with: name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} path: .artifacts/docker-e2e-package - name: Pull shared bare Docker E2E image if: steps.plan.outputs.needs_bare_image == '1' shell: bash run: | set -euo pipefail bash .release-harness/scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}" - name: Pull shared functional Docker E2E image if: steps.plan.outputs.needs_functional_image == '1' shell: bash run: | set -euo pipefail bash .release-harness/scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}" - name: Validate Docker E2E credentials shell: bash env: CREDENTIALS: ${{ steps.plan.outputs.credentials }} run: | set -euo pipefail credentials=",$CREDENTIALS," if [[ "$credentials" == *",openai,"* ]]; then [[ -n "${OPENAI_API_KEY:-}" ]] || { echo "OPENAI_API_KEY is required for selected Docker E2E lanes." >&2 exit 1 } fi if [[ "$credentials" == *",anthropic,"* && -z "${ANTHROPIC_API_TOKEN:-}" && -z "${ANTHROPIC_API_KEY:-}" ]]; then echo "ANTHROPIC_API_TOKEN or ANTHROPIC_API_KEY is required for selected Docker E2E lanes." >&2 exit 1 fi - name: Run targeted Docker E2E lanes shell: bash run: | set -euo pipefail export OPENCLAW_DOCKER_ALL_LANES="${DOCKER_E2E_LANES}" export OPENCLAW_DOCKER_ALL_PREFLIGHT=0 export OPENCLAW_DOCKER_ALL_FAIL_FAST=0 export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="${INCLUDE_OPENWEBUI}" export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/targeted-${{ steps.plan.outputs.artifact_suffix }}" export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/targeted-${{ steps.plan.outputs.artifact_suffix }}-timings.json" export OPENCLAW_DOCKER_ALL_PNPM_COMMAND="$(command -v pnpm)" if [[ "${{ steps.plan.outputs.needs_live_image }}" == "1" ]]; then OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-build-docker.sh fi export OPENCLAW_DOCKER_ALL_BUILD=0 node .release-harness/scripts/test-docker-all.mjs - name: Summarize targeted Docker E2E lanes if: always() shell: bash run: | set -euo pipefail summary=".artifacts/docker-tests/targeted-${{ steps.plan.outputs.artifact_suffix }}/summary.json" if [[ ! -f "$summary" ]]; then echo "Docker targeted summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY" exit 0 fi node .release-harness/scripts/docker-e2e.mjs summary "$summary" "Docker E2E targeted lanes" >> "$GITHUB_STEP_SUMMARY" - name: Upload targeted Docker E2E artifacts if: always() uses: actions/upload-artifact@v7 with: name: docker-e2e-${{ steps.plan.outputs.artifact_suffix }} path: .artifacts/docker-tests/ if-no-files-found: ignore validate_docker_openwebui: needs: [validate_selected_ref, prepare_docker_e2e_image] if: inputs.include_openwebui && !inputs.include_release_path_suites && inputs.docker_lanes == '' name: Docker E2E (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_DOCKER_E2E_FUNCTIONAL_IMAGE: ${{ needs.prepare_docker_e2e_image.outputs.functional_image }} OPENCLAW_DOCKER_E2E_PACKAGE_ARTIFACT_NAME: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} OPENCLAW_DOCKER_E2E_REPO_ROOT: ${{ github.workspace }} OPENCLAW_DOCKER_E2E_SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }} OPENCLAW_CURRENT_PACKAGE_TGZ: .artifacts/docker-e2e-package/openclaw-current.tgz 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: Checkout trusted release harness uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - 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: Plan Open WebUI Docker E2E chunk id: plan shell: bash run: | set -euo pipefail mkdir -p .artifacts/docker-tests export OPENCLAW_DOCKER_ALL_PROFILE=release-path export OPENCLAW_DOCKER_ALL_CHUNK=openwebui export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI=true plan_path=".artifacts/docker-tests/release-openwebui-plan.json" node .release-harness/scripts/test-docker-all.mjs --plan-json > "$plan_path" node .release-harness/scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT" echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT" - name: Download OpenClaw Docker E2E package if: steps.plan.outputs.needs_package == '1' uses: actions/download-artifact@v8 with: name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} path: .artifacts/docker-e2e-package - name: Pull shared bare Docker E2E image if: steps.plan.outputs.needs_bare_image == '1' shell: bash run: | set -euo pipefail bash .release-harness/scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_BARE_IMAGE}" - name: Pull shared functional Docker E2E image if: steps.plan.outputs.needs_functional_image == '1' shell: bash run: | set -euo pipefail bash .release-harness/scripts/ci-docker-pull-retry.sh "${OPENCLAW_DOCKER_E2E_FUNCTIONAL_IMAGE}" - name: Run Open WebUI Docker E2E chunk shell: bash run: | set -euo pipefail export OPENCLAW_DOCKER_ALL_PROFILE=release-path export OPENCLAW_DOCKER_ALL_CHUNK=openwebui export OPENCLAW_DOCKER_ALL_BUILD=0 export OPENCLAW_DOCKER_ALL_PREFLIGHT=0 export OPENCLAW_DOCKER_ALL_FAIL_FAST=0 export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI=1 export OPENCLAW_DOCKER_ALL_LOG_DIR=".artifacts/docker-tests/release-openwebui" export OPENCLAW_DOCKER_ALL_TIMINGS_FILE=".artifacts/docker-tests/release-openwebui-timings.json" export OPENCLAW_DOCKER_ALL_PNPM_COMMAND="$(command -v pnpm)" node .release-harness/scripts/test-docker-all.mjs - name: Summarize Open WebUI Docker E2E chunk if: always() shell: bash run: | set -euo pipefail summary=".artifacts/docker-tests/release-openwebui/summary.json" if [[ ! -f "$summary" ]]; then echo "Docker Open WebUI summary missing: \`$summary\`" >> "$GITHUB_STEP_SUMMARY" exit 0 fi node .release-harness/scripts/docker-e2e.mjs summary "$summary" "Docker E2E chunk: openwebui" >> "$GITHUB_STEP_SUMMARY" - name: Upload Open WebUI Docker E2E artifacts if: always() uses: actions/upload-artifact@v7 with: name: docker-e2e-openwebui path: .artifacts/docker-tests/ if-no-files-found: ignore prepare_docker_e2e_image: needs: validate_selected_ref if: inputs.include_release_path_suites || inputs.include_openwebui || inputs.docker_lanes != '' runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 90 permissions: actions: read contents: read packages: write outputs: image: ${{ steps.image.outputs.image }} bare_image: ${{ steps.image.outputs.bare_image }} functional_image: ${{ steps.image.outputs.functional_image }} needs_bare_image: ${{ steps.plan.outputs.needs_bare_image }} needs_e2e_image: ${{ steps.plan.outputs.needs_e2e_image }} needs_functional_image: ${{ steps.plan.outputs.needs_functional_image }} needs_live_image: ${{ steps.plan.outputs.needs_live_image }} needs_package: ${{ steps.plan.outputs.needs_package }} env: DOCKER_BUILD_SUMMARY: "false" DOCKER_BUILD_RECORD_UPLOAD: "false" OPENCLAW_DOCKER_E2E_REPO_ROOT: ${{ github.workspace }} steps: - name: Checkout selected ref uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted release harness uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - name: Plan Docker E2E images id: plan shell: bash env: LANES: ${{ inputs.docker_lanes }} INCLUDE_RELEASE_PATH_SUITES: ${{ inputs.include_release_path_suites }} INCLUDE_OPENWEBUI: ${{ inputs.include_openwebui }} run: | set -euo pipefail mkdir -p .artifacts/docker-tests if [[ "$INCLUDE_RELEASE_PATH_SUITES" == "true" ]]; then export OPENCLAW_DOCKER_ALL_PROFILE=release-path export OPENCLAW_DOCKER_ALL_PLAN_RELEASE_ALL=1 elif [[ -n "$LANES" ]]; then export OPENCLAW_DOCKER_ALL_LANES="$LANES" elif [[ "$INCLUDE_OPENWEBUI" == "true" ]]; then export OPENCLAW_DOCKER_ALL_LANES=openwebui fi export OPENCLAW_DOCKER_ALL_INCLUDE_OPENWEBUI="$INCLUDE_OPENWEBUI" plan_path=".artifacts/docker-tests/plan.json" node .release-harness/scripts/test-docker-all.mjs --plan-json > "$plan_path" node .release-harness/scripts/docker-e2e.mjs github-outputs "$plan_path" >> "$GITHUB_OUTPUT" echo "plan_json=$plan_path" >> "$GITHUB_OUTPUT" - name: Setup Node environment if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name == '' && inputs.package_artifact_run_id == '' uses: ./.github/actions/setup-node-env with: node-version: ${{ env.NODE_VERSION }} pnpm-version: ${{ env.PNPM_VERSION }} install-bun: "true" - name: Download current-run OpenClaw Docker E2E package if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name != '' && inputs.package_artifact_run_id == '' uses: actions/download-artifact@v8 with: name: ${{ inputs.package_artifact_name }} path: .artifacts/docker-e2e-package - name: Download previous-run OpenClaw Docker E2E package if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_run_id != '' uses: actions/download-artifact@v8 with: name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} path: .artifacts/docker-e2e-package run-id: ${{ inputs.package_artifact_run_id }} github-token: ${{ github.token }} - name: Pack OpenClaw package for Docker E2E if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name == '' && inputs.package_artifact_run_id == '' shell: bash run: | set -euo pipefail mkdir -p .artifacts/docker-e2e-package node scripts/package-openclaw-for-docker.mjs \ --output-dir .artifacts/docker-e2e-package \ --output-name openclaw-current.tgz - name: Validate OpenClaw Docker E2E package id: package if: steps.plan.outputs.needs_package == '1' shell: bash run: | set -euo pipefail mkdir -p .artifacts/docker-e2e-package target=".artifacts/docker-e2e-package/openclaw-current.tgz" if [[ ! -f "$target" ]]; then mapfile -t tgzs < <(find .artifacts/docker-e2e-package -type f -name '*.tgz' | sort) if [[ "${#tgzs[@]}" -ne 1 ]]; then echo "Expected exactly one package tarball in .artifacts/docker-e2e-package; found ${#tgzs[@]}." >&2 printf '%s\n' "${tgzs[@]}" >&2 exit 1 fi cp "${tgzs[0]}" "$target" fi echo "Validating Docker E2E package tarball: $target" started_at="$(date +%s)" timeout --foreground 5m node scripts/check-openclaw-package-tarball.mjs "$target" finished_at="$(date +%s)" echo "Docker E2E package tarball validation finished in $((finished_at - started_at))s." digest="$(sha256sum "$target" | awk '{print $1}')" tag="pkg-${digest:0:32}" echo "sha256=$digest" >> "$GITHUB_OUTPUT" echo "tag=$tag" >> "$GITHUB_OUTPUT" { echo "Docker E2E package: \`$target\`" echo "Docker E2E package SHA-256: \`$digest\`" } >> "$GITHUB_STEP_SUMMARY" - name: Upload OpenClaw Docker E2E package if: steps.plan.outputs.needs_package == '1' && (inputs.package_artifact_name == '' || inputs.package_artifact_run_id != '') uses: actions/upload-artifact@v7 with: name: ${{ inputs.package_artifact_name || 'docker-e2e-package' }} path: .artifacts/docker-e2e-package/openclaw-current.tgz if-no-files-found: error - name: Resolve shared Docker E2E image tags id: image shell: bash env: PACKAGE_TAG: ${{ steps.package.outputs.tag }} SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }} PROVIDED_BARE_IMAGE: ${{ inputs.docker_e2e_bare_image }} PROVIDED_FUNCTIONAL_IMAGE: ${{ inputs.docker_e2e_functional_image }} run: | set -euo pipefail repository="${GITHUB_REPOSITORY,,}" image_tag="${PACKAGE_TAG:-$SELECTED_SHA}" bare_image="${PROVIDED_BARE_IMAGE:-ghcr.io/${repository}-docker-e2e-bare:${image_tag}}" functional_image="${PROVIDED_FUNCTIONAL_IMAGE:-ghcr.io/${repository}-docker-e2e-functional:${image_tag}}" image="$functional_image" echo "image=$image" >> "$GITHUB_OUTPUT" echo "bare_image=$bare_image" >> "$GITHUB_OUTPUT" echo "functional_image=$functional_image" >> "$GITHUB_OUTPUT" echo "Shared Docker E2E bare image: \`$bare_image\`" >> "$GITHUB_STEP_SUMMARY" echo "Shared Docker E2E functional image: \`$functional_image\`" >> "$GITHUB_STEP_SUMMARY" - name: Log in to GHCR if: steps.plan.outputs.needs_e2e_image == '1' uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} - name: Check existing shared Docker E2E images id: image_exists if: steps.plan.outputs.needs_e2e_image == '1' shell: bash env: PROVIDED_BARE_IMAGE: ${{ inputs.docker_e2e_bare_image }} PROVIDED_FUNCTIONAL_IMAGE: ${{ inputs.docker_e2e_functional_image }} run: | set -euo pipefail bare_exists=0 functional_exists=0 needs_build=0 if [[ "${{ steps.plan.outputs.needs_bare_image }}" == "1" ]]; then if docker manifest inspect "${{ steps.image.outputs.bare_image }}" >/dev/null 2>&1; then bare_exists=1 echo "Shared Docker E2E bare image already exists: ${{ steps.image.outputs.bare_image }}" elif [[ -n "$PROVIDED_BARE_IMAGE" ]]; then echo "Provided bare Docker E2E image does not exist: $PROVIDED_BARE_IMAGE" >&2 exit 1 else needs_build=1 fi fi if [[ "${{ steps.plan.outputs.needs_functional_image }}" == "1" ]]; then if docker manifest inspect "${{ steps.image.outputs.functional_image }}" >/dev/null 2>&1; then functional_exists=1 echo "Shared Docker E2E functional image already exists: ${{ steps.image.outputs.functional_image }}" elif [[ -n "$PROVIDED_FUNCTIONAL_IMAGE" ]]; then echo "Provided functional Docker E2E image does not exist: $PROVIDED_FUNCTIONAL_IMAGE" >&2 exit 1 else needs_build=1 fi fi echo "bare_exists=$bare_exists" >> "$GITHUB_OUTPUT" echo "functional_exists=$functional_exists" >> "$GITHUB_OUTPUT" echo "needs_build=$needs_build" >> "$GITHUB_OUTPUT" - name: Setup Docker builder if: steps.image_exists.outputs.needs_build == '1' uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1 with: max-cache-size-mb: 800000 - name: Build and push bare Docker E2E image if: steps.plan.outputs.needs_bare_image == '1' && steps.image_exists.outputs.bare_exists != '1' uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2 with: context: . file: ./scripts/e2e/Dockerfile target: bare platforms: linux/amd64 tags: ${{ steps.image.outputs.bare_image }} sbom: true provenance: mode=max push: true - name: Build and push functional Docker E2E image if: steps.plan.outputs.needs_functional_image == '1' && steps.image_exists.outputs.functional_exists != '1' uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2 with: context: . file: ./scripts/e2e/Dockerfile target: functional build-contexts: | openclaw_package=.artifacts/docker-e2e-package platforms: linux/amd64 tags: ${{ steps.image.outputs.functional_image }} sbom: true provenance: mode=max push: true prepare_live_test_image: needs: validate_selected_ref if: inputs.include_live_suites runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 60 permissions: contents: read packages: write outputs: live_image: ${{ steps.image.outputs.live_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 live-test image tag id: image shell: bash env: SELECTED_SHA: ${{ needs.validate_selected_ref.outputs.selected_sha }} run: | set -euo pipefail repository="${GITHUB_REPOSITORY,,}" live_image="ghcr.io/${repository}-live-test:${SELECTED_SHA}" echo "live_image=${live_image}" >> "$GITHUB_OUTPUT" echo "Shared live-test image: \`${live_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: Check existing shared live-test image id: image_exists shell: bash run: | set -euo pipefail if docker manifest inspect "${{ steps.image.outputs.live_image }}" >/dev/null 2>&1; then echo "Shared live-test image already exists: ${{ steps.image.outputs.live_image }}" echo "exists=1" >> "$GITHUB_OUTPUT" else echo "exists=0" >> "$GITHUB_OUTPUT" fi - name: Setup Docker builder if: steps.image_exists.outputs.exists != '1' uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1 with: max-cache-size-mb: 800000 - name: Build and push shared live-test image if: steps.image_exists.outputs.exists != '1' uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2 with: context: . file: ./Dockerfile target: build build-args: | OPENCLAW_EXTENSIONS=matrix platforms: linux/amd64 tags: ${{ steps.image.outputs.live_image }} sbom: true provenance: mode=max push: true validate_live_models_docker: name: Docker live models (${{ matrix.provider_label }}) needs: [validate_selected_ref, prepare_live_test_image] if: inputs.include_live_suites && inputs.live_model_providers == '' runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 75 strategy: fail-fast: false matrix: include: - provider_label: Anthropic providers: anthropic profiles: stable full - provider_label: Google providers: google profiles: stable full - provider_label: MiniMax providers: minimax profiles: stable full - provider_label: OpenAI providers: openai profiles: minimum stable full - provider_label: OpenCode providers: opencode-go profiles: full - provider_label: OpenRouter providers: openrouter profiles: full - provider_label: xAI providers: xai profiles: full - provider_label: Z.ai providers: zai profiles: full - provider_label: Fireworks providers: fireworks profiles: full 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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_LIVE_IMAGE: ${{ needs.prepare_live_test_image.outputs.live_image }} OPENCLAW_SKIP_DOCKER_BUILD: "1" OPENCLAW_VITEST_MAX_WORKERS: "2" steps: - name: Checkout selected ref if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted live Docker harness if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - name: Setup Node environment if: contains(matrix.profiles, inputs.release_test_profile) 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 if: contains(matrix.profiles, inputs.release_test_profile) run: bash scripts/ci-hydrate-live-auth.sh - name: Log in to GHCR if: contains(matrix.profiles, inputs.release_test_profile) uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} - name: Validate provider credential if: contains(matrix.profiles, inputs.release_test_profile) 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 if: contains(matrix.profiles, inputs.release_test_profile) run: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-models-docker.sh validate_live_models_docker_targeted: name: Docker live models (selected providers) needs: [validate_selected_ref, prepare_live_test_image] if: inputs.include_live_suites && inputs.live_model_providers != '' runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 75 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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 }} REQUESTED_LIVE_MODEL_PROVIDERS: ${{ inputs.live_model_providers }} OPENCLAW_LIVE_IMAGE: ${{ needs.prepare_live_test_image.outputs.live_image }} OPENCLAW_SKIP_DOCKER_BUILD: "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: Checkout trusted live Docker harness uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - 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: Normalize provider allowlist shell: bash run: | set -euo pipefail all_providers=(anthropic google minimax openai opencode-go openrouter xai zai fireworks) normalize_provider() { local value="${1,,}" case "$value" in z.ai|z-ai) echo "zai" ;; opencode|opencode-go) echo "opencode-go" ;; open-router|openrouter) echo "openrouter" ;; *) echo "$value" ;; esac } is_known_provider() { local value="$1" local provider for provider in "${all_providers[@]}"; do [[ "$provider" == "$value" ]] && return 0 done return 1 } selected=() declare -A seen=() raw="${REQUESTED_LIVE_MODEL_PROVIDERS:-}" normalized_all="${raw,,}" normalized_all="${normalized_all//[[:space:],]/}" if [[ -z "$normalized_all" || "$normalized_all" == "all" ]]; then selected=("${all_providers[@]}") else while IFS= read -r entry; do [[ -z "$entry" ]] && continue provider="$(normalize_provider "$entry")" if ! is_known_provider "$provider"; then echo "Unknown live model provider '${entry}'. Expected one of: ${all_providers[*]}" >&2 exit 1 fi if [[ -z "${seen[$provider]:-}" ]]; then selected+=("$provider") seen[$provider]=1 fi done < <(printf '%s\n' "$raw" | tr ',' '\n' | tr '[:space:]' '\n') fi if [[ "${#selected[@]}" -eq 0 ]]; then echo "No live model providers selected." >&2 exit 1 fi providers_csv="$(IFS=,; echo "${selected[*]}")" echo "OPENCLAW_LIVE_PROVIDERS=$providers_csv" >> "$GITHUB_ENV" { echo "Live model providers: \`$providers_csv\`" } >> "$GITHUB_STEP_SUMMARY" - name: Hydrate live auth/profile inputs run: bash scripts/ci-hydrate-live-auth.sh - name: Log in to GHCR uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} - name: Validate provider credentials 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 } IFS=',' read -r -a providers <<<"${OPENCLAW_LIVE_PROVIDERS}" for provider in "${providers[@]}"; do case "$provider" 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: ${provider}" >&2 exit 1 ;; esac done - name: Run Docker live model sweep run: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-models-docker.sh 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: native-live-src-agents label: Native live agents command: node .release-harness/scripts/test-live-shard.mjs native-live-src-agents timeout_minutes: 90 profile_env_only: false profiles: stable full - suite_id: native-live-src-gateway-core label: Native live gateway core command: node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-core timeout_minutes: 90 profile_env_only: false profiles: minimum stable full - suite_id: native-live-src-gateway-profiles-anthropic label: Native live gateway profiles Anthropic command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=anthropic node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: stable full - suite_id: native-live-src-gateway-profiles-google label: Native live gateway profiles Google command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=google OPENCLAW_LIVE_GATEWAY_MODELS=google/gemini-3.1-pro-preview,google/gemini-3-flash-preview node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: stable full - suite_id: native-live-src-gateway-profiles-minimax label: Native live gateway profiles MiniMax command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=minimax,minimax-portal node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: stable full - suite_id: native-live-src-gateway-profiles-openai label: Native live gateway profiles OpenAI command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=openai OPENCLAW_LIVE_GATEWAY_MODELS=openai/gpt-5.5 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: minimum stable full - suite_id: native-live-src-gateway-profiles-fireworks label: Native live gateway profiles Fireworks command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=fireworks node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-src-gateway-profiles-deepseek label: Native live gateway profiles DeepSeek command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=deepseek node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-src-gateway-profiles-opencode-go label: Native live gateway profiles OpenCode Go deep command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=opencode-go node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-src-gateway-profiles-opencode-go-smoke label: Native live gateway profiles OpenCode Go smoke command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=opencode-go OPENCLAW_LIVE_GATEWAY_SMOKE=1 OPENCLAW_LIVE_GATEWAY_MAX_MODELS=1 node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 45 profile_env_only: false profiles: stable - suite_id: native-live-src-gateway-profiles-openrouter label: Native live gateway profiles OpenRouter command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=openrouter node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-src-gateway-profiles-xai label: Native live gateway profiles xAI command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=xai node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-src-gateway-profiles-zai label: Native live gateway profiles Z.ai command: OPENCLAW_LIVE_GATEWAY_PROVIDERS=zai node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-profiles timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-src-gateway-backends label: Native live gateway backends command: node .release-harness/scripts/test-live-shard.mjs native-live-src-gateway-backends timeout_minutes: 90 profile_env_only: false profiles: stable full - suite_id: native-live-test label: Native live test harnesses command: node .release-harness/scripts/test-live-shard.mjs native-live-test timeout_minutes: 90 profile_env_only: false profiles: stable full - suite_id: native-live-extensions-l-n label: Native live plugins L-N command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-l-n timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-extensions-openai label: Native live OpenAI plugin command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-openai timeout_minutes: 90 profile_env_only: false profiles: minimum stable full - suite_id: native-live-extensions-o-z-other label: Native live plugins O-Z other command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-o-z-other timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-extensions-xai label: Native live xAI plugin command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-xai timeout_minutes: 90 profile_env_only: false profiles: full 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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 if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted live shard harness if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - name: Setup Node environment if: contains(matrix.profiles, inputs.release_test_profile) 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 if: contains(matrix.profiles, inputs.release_test_profile) run: bash scripts/ci-hydrate-live-auth.sh - name: Configure suite-specific env if: contains(matrix.profiles, inputs.release_test_profile) 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.5" >> "$GITHUB_ENV" # Keep the release-blocking CI lane on Codex API-key auth. The # staged auth-file path remains supported for local maintainer # reruns, but it can hang on stale subscription/session state in # an otherwise healthy release run. echo "OPENCLAW_LIVE_CLI_BACKEND_AUTH=api-key" >> "$GITHUB_ENV" # 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_ARGS=["exec","--json","--color","never","--sandbox","danger-full-access","-c","service_tier=\"fast\"","--skip-git-repo-check"]' >> "$GITHUB_ENV" echo 'OPENCLAW_LIVE_CLI_BACKEND_RESUME_ARGS=["exec","resume","{sessionId}","-c","sandbox_mode=\"danger-full-access\"","-c","service_tier=\"fast\"","--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_TEST_CONSOLE=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" echo "OPENCLAW_LIVE_CODEX_HARNESS_DEBUG=1" >> "$GITHUB_ENV" echo "OPENCLAW_CLI_BACKEND_LOG_OUTPUT=1" >> "$GITHUB_ENV" echo "OPENCLAW_TEST_CONSOLE=1" >> "$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 }} if: contains(matrix.profiles, inputs.release_test_profile) env: OPENCLAW_LIVE_COMMAND: ${{ matrix.command }} run: bash .release-harness/scripts/ci-live-command-retry.sh validate_live_docker_provider_suites: name: Docker live suites (${{ matrix.label }}) needs: [validate_selected_ref, prepare_live_test_image] 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-gateway-docker label: Docker live gateway command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-gateway-models-docker.sh timeout_minutes: 120 profile_env_only: false profiles: minimum stable full - suite_id: live-cli-backend-docker label: Docker live CLI backend command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-cli-backend-docker.sh timeout_minutes: 120 profile_env_only: false profiles: stable full - suite_id: live-acp-bind-docker label: Docker live ACP bind command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-acp-bind-docker.sh timeout_minutes: 120 profile_env_only: false profiles: stable full - suite_id: live-codex-harness-docker label: Docker live Codex harness command: OPENCLAW_LIVE_DOCKER_REPO_ROOT="$GITHUB_WORKSPACE" bash .release-harness/scripts/test-live-codex-harness-docker.sh timeout_minutes: 120 profile_env_only: false profiles: stable full 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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_IMAGE: ${{ needs.prepare_live_test_image.outputs.live_image }} OPENCLAW_SKIP_DOCKER_BUILD: "1" OPENCLAW_LIVE_VIDEO_GENERATION_SKIP_PROVIDERS: "" OPENCLAW_LIVE_VYDRA_VIDEO: "1" OPENCLAW_VITEST_MAX_WORKERS: "2" steps: - name: Checkout selected ref if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted live shard harness if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - name: Setup Node environment if: contains(matrix.profiles, inputs.release_test_profile) 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 if: contains(matrix.profiles, inputs.release_test_profile) run: bash scripts/ci-hydrate-live-auth.sh - name: Log in to GHCR if: contains(matrix.profiles, inputs.release_test_profile) uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ github.token }} - name: Configure suite-specific env if: contains(matrix.profiles, inputs.release_test_profile) 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.5" >> "$GITHUB_ENV" echo "OPENCLAW_LIVE_CLI_BACKEND_AUTH=api-key" >> "$GITHUB_ENV" echo 'OPENCLAW_LIVE_CLI_BACKEND_ARGS=["exec","--json","--color","never","--sandbox","danger-full-access","-c","service_tier=\"fast\"","--skip-git-repo-check"]' >> "$GITHUB_ENV" echo 'OPENCLAW_LIVE_CLI_BACKEND_RESUME_ARGS=["exec","resume","{sessionId}","-c","sandbox_mode=\"danger-full-access\"","-c","service_tier=\"fast\"","--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_TEST_CONSOLE=1" >> "$GITHUB_ENV" echo "OPENCLAW_LIVE_CLI_BACKEND_USE_CI_SAFE_CODEX_CONFIG=1" >> "$GITHUB_ENV" ;; live-codex-harness-docker) echo "OPENCLAW_LIVE_CODEX_HARNESS_AUTH=api-key" >> "$GITHUB_ENV" echo "OPENCLAW_LIVE_CODEX_HARNESS_DEBUG=1" >> "$GITHUB_ENV" echo "OPENCLAW_CLI_BACKEND_LOG_OUTPUT=1" >> "$GITHUB_ENV" echo "OPENCLAW_TEST_CONSOLE=1" >> "$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 echo "OPENCLAW_LIVE_ACP_BIND_AGENTS=claude,codex" >> "$GITHUB_ENV" fi ;; esac - name: Run ${{ matrix.label }} if: contains(matrix.profiles, inputs.release_test_profile) env: OPENCLAW_LIVE_COMMAND: ${{ matrix.command }} run: bash .release-harness/scripts/ci-live-command-retry.sh validate_live_media_provider_suites: name: Live media suites (${{ matrix.label }}) needs: validate_selected_ref if: inputs.include_live_suites && !inputs.live_models_only runs-on: blacksmith-32vcpu-ubuntu-2404 container: image: ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04 credentials: username: ${{ github.actor }} password: ${{ github.token }} timeout-minutes: ${{ matrix.timeout_minutes }} strategy: fail-fast: false matrix: include: - suite_id: native-live-extensions-a-k label: Native live plugins A-K command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-a-k timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-extensions-media-audio label: Native live media audio plugins command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-media-audio timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-extensions-media-music-google label: Native live media music Google command: OPENCLAW_LIVE_MUSIC_GENERATION_PROVIDERS=google node .release-harness/scripts/test-live-shard.mjs native-live-extensions-media-music-google timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-extensions-media-music-minimax label: Native live media music MiniMax command: OPENCLAW_LIVE_MUSIC_GENERATION_PROVIDERS=minimax node .release-harness/scripts/test-live-shard.mjs native-live-extensions-media-music-minimax timeout_minutes: 90 profile_env_only: false profiles: full - suite_id: native-live-extensions-media-video label: Native live media video plugins command: node .release-harness/scripts/test-live-shard.mjs native-live-extensions-media-video timeout_minutes: 90 profile_env_only: false profiles: full 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 }} DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_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 if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ needs.validate_selected_ref.outputs.selected_sha }} fetch-depth: 1 - name: Checkout trusted live shard harness if: contains(matrix.profiles, inputs.release_test_profile) uses: actions/checkout@v6 with: ref: ${{ github.sha }} fetch-depth: 1 path: .release-harness - name: Verify preinstalled live media dependencies if: contains(matrix.profiles, inputs.release_test_profile) shell: bash run: | set -euo pipefail ffmpeg -version | head -1 ffprobe -version | head -1 - name: Setup Node environment if: contains(matrix.profiles, inputs.release_test_profile) 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 if: contains(matrix.profiles, inputs.release_test_profile) run: bash scripts/ci-hydrate-live-auth.sh - name: Configure suite-specific env if: contains(matrix.profiles, inputs.release_test_profile) shell: bash run: | set -euo pipefail if [[ "${{ matrix.profile_env_only }}" == "true" ]]; then echo "OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1" >> "$GITHUB_ENV" fi - name: Run ${{ matrix.label }} if: contains(matrix.profiles, inputs.release_test_profile) run: ${{ matrix.command }}