Release Surface RTT #138
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Surface RTT | |
| on: | |
| schedule: | |
| - cron: "35 */6 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| surfaces: | |
| description: Space or comma separated surface ids | |
| required: false | |
| default: "control-ui" | |
| type: string | |
| versions: | |
| description: Space or comma separated OpenClaw versions to measure | |
| required: false | |
| default: "" | |
| type: string | |
| version_limit: | |
| description: Maximum future versions per surface when versions is empty | |
| required: false | |
| default: "2" | |
| type: string | |
| samples: | |
| description: Number of RTT samples | |
| required: false | |
| default: "10" | |
| type: string | |
| permissions: | |
| actions: read | |
| contents: write | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| NODE_VERSION: "24.x" | |
| PNPM_VERSION: "11.2.2" | |
| jobs: | |
| resolve: | |
| name: Resolve OpenClaw surface release packages | |
| runs-on: ubuntu-24.04 | |
| outputs: | |
| matrix: ${{ steps.release.outputs.matrix }} | |
| reason: ${{ steps.release.outputs.reason }} | |
| should_run: ${{ steps.release.outputs.should_run }} | |
| surfaces: ${{ steps.release.outputs.surfaces }} | |
| versions: ${{ steps.release.outputs.versions }} | |
| steps: | |
| - name: Checkout RTT tracker | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Resolve OpenClaw surface release packages | |
| id: release | |
| env: | |
| INPUT_SURFACES: ${{ github.event_name == 'workflow_dispatch' && inputs.surfaces || '' }} | |
| INPUT_VERSIONS: ${{ github.event_name == 'workflow_dispatch' && inputs.versions || '' }} | |
| INPUT_VERSION_LIMIT: ${{ github.event_name == 'workflow_dispatch' && inputs.version_limit || '' }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| node scripts/resolve-openclaw-surface-package.mjs | |
| - name: Report skipped package | |
| if: steps.release.outputs.should_run != 'true' | |
| run: | | |
| echo "No surface release versions to measure (${{ steps.release.outputs.reason }})" | |
| measure: | |
| name: Measure OpenClaw ${{ matrix.package.label }} release RTT | |
| needs: resolve | |
| if: needs.resolve.outputs.should_run == 'true' | |
| runs-on: blacksmith-16vcpu-ubuntu-2404 | |
| timeout-minutes: 60 | |
| concurrency: | |
| group: surface-rtt-measure-${{ matrix.package.surface }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 1 | |
| matrix: | |
| package: ${{ fromJSON(needs.resolve.outputs.matrix) }} | |
| steps: | |
| - name: Checkout RTT tracker | |
| uses: actions/checkout@v7 | |
| with: | |
| path: openclaw-rtt | |
| fetch-depth: 0 | |
| - name: Checkout OpenClaw release | |
| uses: actions/checkout@v7 | |
| with: | |
| repository: openclaw/openclaw | |
| ref: ${{ matrix.package.tag }} | |
| path: openclaw | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup pnpm | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| corepack enable | |
| corepack prepare "pnpm@${PNPM_VERSION}" --activate | |
| - name: Locate pnpm store | |
| id: pnpm-store | |
| working-directory: openclaw | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| echo "path=$(pnpm store path --silent)" >>"$GITHUB_OUTPUT" | |
| - name: Restore pnpm store | |
| uses: actions/cache@v6 | |
| with: | |
| path: ${{ steps.pnpm-store.outputs.path }} | |
| key: openclaw-release-surface-rtt-pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('openclaw/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| openclaw-release-surface-rtt-pnpm-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Install OpenClaw dependencies | |
| working-directory: openclaw | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| time pnpm install --frozen-lockfile --prefer-offline --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true --config.side-effects-cache=true | |
| - name: Install Playwright Chromium | |
| working-directory: openclaw | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| node scripts/ensure-playwright-chromium.mjs | |
| - name: Verify OpenClaw release ref | |
| working-directory: openclaw | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version="$(node -p "require('./package.json').version")" | |
| if [[ "$version" != "${{ matrix.package.version }}" ]]; then | |
| echo "OpenClaw package version mismatch: expected ${{ matrix.package.version }}, got ${version}" >&2 | |
| exit 1 | |
| fi | |
| - name: Run ${{ matrix.package.label }} RTT samples | |
| id: surface_rtt | |
| working-directory: openclaw | |
| env: | |
| INPUT_SAMPLES: ${{ github.event_name == 'workflow_dispatch' && inputs.samples || '' }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| samples="${INPUT_SAMPLES:-10}" | |
| if ! [[ "$samples" =~ ^[1-9][0-9]*$ ]]; then | |
| echo "samples must be a positive integer, got: $samples" >&2 | |
| exit 1 | |
| fi | |
| output_root=".artifacts/control-ui-release-rtt-${{ matrix.package.version }}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" | |
| sample_paths="$RUNNER_TEMP/openclaw-control-ui-${{ matrix.package.version }}-rtt-samples.tsv" | |
| : >"$sample_paths" | |
| echo "output_root=${output_root}" >>"$GITHUB_OUTPUT" | |
| echo "sample_paths=${sample_paths}" >>"$GITHUB_OUTPUT" | |
| for sample in $(seq 1 "$samples"); do | |
| output_dir="${output_root}/sample-${sample}" | |
| metrics_path="${output_dir}/resource-metrics.env" | |
| rm -rf "$output_dir" | |
| mkdir -p "$output_dir" | |
| echo "SURFACE_RELEASE_RTT_PHASE:${{ matrix.package.surface }} version=${{ matrix.package.version }} sample=${sample}/${samples}" | |
| /usr/bin/time \ | |
| -f 'max_rss_kb=%M\nelapsed_seconds=%e' \ | |
| -o "$metrics_path" \ | |
| node --import tsx ../openclaw-rtt/scripts/measure-control-ui-rtt.mjs \ | |
| --output-dir "$output_dir" | |
| echo "attempts=1" >>"$metrics_path" | |
| printf '%s\t%s\t%s\n' \ | |
| "${PWD}/${output_dir}/qa-suite-summary.json" \ | |
| "${PWD}/${metrics_path}" \ | |
| "${PWD}/${output_dir}/control-ui-events.json" >>"$sample_paths" | |
| done | |
| - name: Prepare ${{ matrix.package.label }} release import artifact | |
| working-directory: openclaw | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| import_dir="$RUNNER_TEMP/release-surface-rtt-import-${{ matrix.package.surface }}-${{ matrix.package.version }}" | |
| rm -rf "$import_dir" | |
| mkdir -p "$import_dir" | |
| cp -R "${{ steps.surface_rtt.outputs.output_root }}" "$import_dir/artifacts" | |
| { | |
| echo "surface=${{ matrix.package.surface }}" | |
| echo "scenario=${{ matrix.package.scenario }}" | |
| echo "spec=${{ matrix.package.spec }}" | |
| echo "version=${{ matrix.package.version }}" | |
| } >"$import_dir/metadata.env" | |
| - name: Upload ${{ matrix.package.label }} release import artifact | |
| uses: actions/upload-artifact@v7.0.1 | |
| with: | |
| name: release-surface-rtt-import-${{ matrix.package.surface }}-${{ matrix.package.version }} | |
| path: ${{ runner.temp }}/release-surface-rtt-import-${{ matrix.package.surface }}-${{ matrix.package.version }} | |
| retention-days: 14 | |
| if-no-files-found: error | |
| - name: Upload ${{ matrix.package.label }} RTT artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v7.0.1 | |
| with: | |
| name: release-surface-rtt-${{ matrix.package.surface }}-${{ matrix.package.version }}-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: openclaw/.artifacts/control-ui-release-rtt-${{ matrix.package.version }}-${{ github.run_id }}-${{ github.run_attempt }} | |
| retention-days: 14 | |
| if-no-files-found: warn | |
| report: | |
| name: Import surface release RTT reports | |
| needs: | |
| - resolve | |
| - measure | |
| if: always() && needs.resolve.outputs.should_run == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| concurrency: | |
| group: rtt-report-writer-${{ github.ref }} | |
| cancel-in-progress: false | |
| steps: | |
| - name: Checkout RTT tracker | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Download surface release import artifacts | |
| uses: actions/download-artifact@v8.0.1 | |
| with: | |
| pattern: release-surface-rtt-import-* | |
| path: ${{ runner.temp }}/release-surface-rtt-imports | |
| - name: Import surface release RTT results | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git pull --rebase origin "${GITHUB_REF_NAME:-main}" | |
| import_root="$RUNNER_TEMP/release-surface-rtt-imports" | |
| shopt -s nullglob | |
| import_dirs=("$import_root"/release-surface-rtt-import-*) | |
| if [[ -f "$import_root/metadata.env" ]]; then | |
| import_dirs=("$import_root") | |
| fi | |
| if [[ "${#import_dirs[@]}" -eq 0 ]]; then | |
| echo "No surface release RTT import artifacts downloaded." >&2 | |
| exit 1 | |
| fi | |
| get_metadata() { | |
| local metadata_path="$1" | |
| local key="$2" | |
| grep -m 1 "^${key}=" "$metadata_path" | cut -d= -f2- | |
| } | |
| for import_dir in "${import_dirs[@]}"; do | |
| metadata_path="${import_dir}/metadata.env" | |
| surface="$(get_metadata "$metadata_path" surface)" | |
| scenario="$(get_metadata "$metadata_path" scenario)" | |
| spec="$(get_metadata "$metadata_path" spec)" | |
| version="$(get_metadata "$metadata_path" version)" | |
| sample_paths="$RUNNER_TEMP/openclaw-${surface}-${version}-rtt-samples.tsv" | |
| : >"$sample_paths" | |
| while IFS= read -r sample_dir; do | |
| summary_path="${sample_dir}/qa-suite-summary.json" | |
| metrics_path="${sample_dir}/resource-metrics.env" | |
| events_path="${sample_dir}/control-ui-events.json" | |
| if [[ ! -f "$summary_path" || ! -f "$metrics_path" || ! -f "$events_path" ]]; then | |
| echo "Missing ${surface} release artifacts under ${sample_dir}." >&2 | |
| exit 1 | |
| fi | |
| printf '%s\t%s\t%s\n' "$summary_path" "$metrics_path" "$events_path" >>"$sample_paths" | |
| done < <(find "${import_dir}/artifacts" -mindepth 1 -maxdepth 1 -type d -name 'sample-*' | sort -V) | |
| node scripts/import-surface-rtt.mjs "$sample_paths" \ | |
| --surface "$surface" \ | |
| --spec "$spec" \ | |
| --version "$version" \ | |
| --provider-mode mock-openai \ | |
| --scenario "$scenario" \ | |
| --require-pass | |
| done | |
| node scripts/update-readme.mjs | |
| node scripts/validate.mjs | |
| node scripts/surface-rtt-summary.mjs | |
| - name: Commit result | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git add data/surfaces/ runs/surfaces/ README.md | |
| if git diff --cached --quiet --exit-code; then | |
| echo "No surface release RTT changes to commit." | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git commit -m "data: record surface release rtt" | |
| git push || { | |
| git pull --rebase origin "${GITHUB_REF_NAME:-main}" | |
| git push | |
| } |