Skip to content

Release Surface RTT #138

Release Surface RTT

Release Surface RTT #138

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
}