From ba0f2e948fc0a3cfcb03b3ddf6329197203d540d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 29 Apr 2026 03:48:23 +0100 Subject: [PATCH] ci: preinstall ffmpeg for live media checks --- .agents/skills/openclaw-testing/SKILL.md | 7 + .github/images/live-media-runner/Dockerfile | 16 ++ .github/workflows/live-media-runner-image.yml | 54 +++++ .../openclaw-live-and-e2e-checks-reusable.yml | 220 ++++++++++++------ docs/ci.md | 7 + 5 files changed, 227 insertions(+), 77 deletions(-) create mode 100644 .github/images/live-media-runner/Dockerfile create mode 100644 .github/workflows/live-media-runner-image.yml diff --git a/.agents/skills/openclaw-testing/SKILL.md b/.agents/skills/openclaw-testing/SKILL.md index 5a762d3a3de..b345a4380e7 100644 --- a/.agents/skills/openclaw-testing/SKILL.md +++ b/.agents/skills/openclaw-testing/SKILL.md @@ -222,6 +222,13 @@ When `Full Release Validation` dispatches release checks, it passes the requeste branch/tag plus an `expected_sha` so branch/tag refs resolve through the fast remote-ref path while the package and QA jobs still validate the exact SHA. +The full-profile native live media shards use the prebuilt +`ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04` container so +`ffmpeg`/`ffprobe` are already present. If those jobs suddenly spend minutes in +dependency setup again, first check the `Live Media Runner Image` workflow and +the `Verify preinstalled live media dependencies` step before assuming the media +tests themselves slowed down. + The release Docker path intentionally shards the plugin/runtime tail. The workflow uses `plugins-runtime-plugins`, `plugins-runtime-services`, and `plugins-runtime-install-a` through `plugins-runtime-install-d`; aggregate diff --git a/.github/images/live-media-runner/Dockerfile b/.github/images/live-media-runner/Dockerfile new file mode 100644 index 00000000000..cb5f451caa8 --- /dev/null +++ b/.github/images/live-media-runner/Dockerfile @@ -0,0 +1,16 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bash \ + ca-certificates \ + curl \ + ffmpeg \ + git \ + openssh-client \ + unzip \ + xz-utils \ + zstd \ + && rm -rf /var/lib/apt/lists/* diff --git a/.github/workflows/live-media-runner-image.yml b/.github/workflows/live-media-runner-image.yml new file mode 100644 index 00000000000..bd64e5ffa08 --- /dev/null +++ b/.github/workflows/live-media-runner-image.yml @@ -0,0 +1,54 @@ +name: Live Media Runner Image + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - ".github/images/live-media-runner/Dockerfile" + - ".github/workflows/live-media-runner-image.yml" + +permissions: + contents: read + packages: write + +concurrency: + group: live-media-runner-image-${{ github.ref }} + cancel-in-progress: true + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + +jobs: + build: + name: Build live media runner image + runs-on: blacksmith-8vcpu-ubuntu-2404 + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Login to GHCR + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Set up Blacksmith Docker Builder + uses: useblacksmith/setup-docker-builder@ac083cc84672d01c60d5e8561d0a939b697de542 # v1 + with: + max-cache-size-mb: 800000 + + - name: Build and push live media runner image + uses: useblacksmith/build-push-action@cbd1f60d194a98cb3be5523b15134501eaf0fbf3 # v2 + with: + context: .github/images/live-media-runner + file: .github/images/live-media-runner/Dockerfile + platforms: linux/amd64 + tags: | + ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04 + ghcr.io/openclaw/openclaw-live-media-runner:${{ github.sha }} + sbom: true + provenance: mode=max + push: true diff --git a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml index 659957af3c9..7b9c0ac53c0 100644 --- a/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml +++ b/.github/workflows/openclaw-live-and-e2e-checks-reusable.yml @@ -1578,196 +1578,138 @@ jobs: label: Native live agents command: node .release-harness/scripts/test-live-shard.mjs native-live-src-agents timeout_minutes: 90 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false profile_env_only: false profiles: stable full - - 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 - needs_ffmpeg: true - profile_env_only: false - profiles: 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false - 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 - needs_ffmpeg: true - 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 - needs_ffmpeg: true - 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 - needs_ffmpeg: true - 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 - needs_ffmpeg: true profile_env_only: false profiles: full - 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false 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 - needs_ffmpeg: false profile_env_only: false profiles: stable full env: @@ -1847,25 +1789,6 @@ jobs: if: contains(matrix.profiles, inputs.release_test_profile) run: bash scripts/ci-hydrate-live-auth.sh - - name: Install live media dependencies - if: matrix.needs_ffmpeg && contains(matrix.profiles, inputs.release_test_profile) - shell: bash - run: | - set -euo pipefail - if ! command -v ffmpeg >/dev/null 2>&1; then - for attempt in 1 2 3; do - if sudo apt-get update -o Acquire::Retries=3; then - break - fi - if [[ "${attempt}" == "3" ]]; then - exit 1 - fi - sleep $((attempt * 5)) - done - sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ffmpeg - fi - ffmpeg -version | head -1 - - name: Configure suite-specific env if: contains(matrix.profiles, inputs.release_test_profile) shell: bash @@ -1922,3 +1845,146 @@ jobs: - name: Run ${{ matrix.label }} if: contains(matrix.profiles, inputs.release_test_profile) run: ${{ matrix.command }} + + 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 }} diff --git a/docs/ci.md b/docs/ci.md index 2e657d12692..ba1d723db4d 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -45,6 +45,13 @@ provider failures easier to rerun and diagnose. The aggregate `native-live-extensions-media-music` shard names remain valid for manual one-shot reruns. +The native live media shards run in +`ghcr.io/openclaw/openclaw-live-media-runner:ubuntu-24.04`, built by the +`Live Media Runner Image` workflow. That image preinstalls `ffmpeg` and +`ffprobe`; media jobs only verify the binaries before setup. Keep Docker-backed +live suites on normal Blacksmith runners, because container jobs are the wrong +place to launch nested Docker tests. + `OpenClaw Release Checks` uses the trusted workflow ref to resolve the selected ref once into a `release-package-under-test` tarball, then passes that artifact to both the live/E2E release-path Docker workflow and the package acceptance