Skip to content

Release Discord RTT #412

Release Discord RTT

Release Discord RTT #412

name: Release Discord RTT
on:
schedule:
- cron: "45 */6 * * *"
workflow_dispatch:
inputs:
versions:
description: Space or comma separated OpenClaw versions to measure
required: false
default: ""
type: string
samples:
description: Number of Discord canary RTT samples
required: false
default: "20"
type: string
rss_backfill:
description: Backfill RSS onto existing Discord release rows without changing RTT values
required: false
default: "false"
type: choice
options:
- "false"
- "true"
rss_backfill_limit:
description: Maximum existing release versions to backfill in this run
required: false
default: "14"
type: string
permissions:
actions: read
contents: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
NODE_VERSION: "26.x"
PNPM_VERSION: "11.2.2"
jobs:
resolve:
name: Resolve OpenClaw Discord release packages
runs-on: blacksmith-16vcpu-ubuntu-2404
outputs:
anchor: ${{ steps.release.outputs.anchor }}
matrix: ${{ steps.release.outputs.matrix }}
reason: ${{ steps.release.outputs.reason }}
rss_backfill: ${{ steps.release.outputs.rss_backfill }}
should_run: ${{ steps.release.outputs.should_run }}
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 Discord packages
id: release
env:
INPUT_VERSIONS: ${{ github.event_name == 'workflow_dispatch' && inputs.versions || '' }}
INPUT_RSS_BACKFILL: ${{ github.event_name == 'workflow_dispatch' && inputs.rss_backfill || 'false' }}
INPUT_RSS_BACKFILL_LIMIT: ${{ github.event_name == 'workflow_dispatch' && inputs.rss_backfill_limit || '' }}
shell: bash
run: |
set -euo pipefail
node scripts/resolve-openclaw-discord-package.mjs
- name: Report skipped package
if: steps.release.outputs.should_run != 'true'
run: |
echo "No new Discord release versions to measure after ${{ steps.release.outputs.anchor }} (${{ steps.release.outputs.reason }})"
measure:
name: Measure OpenClaw Discord release RTT
needs: resolve
if: needs.resolve.outputs.should_run == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 120
environment: qa-live-shared
concurrency:
group: release-discord-rtt-measure-${{ github.ref }}-${{ matrix.package.version }}
cancel-in-progress: false
strategy:
fail-fast: false
max-parallel: 2
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: Checkout OpenClaw QA harness
uses: actions/checkout@v7
with:
repository: openclaw/openclaw
ref: main
path: openclaw-qa
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-discord-rtt-pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('openclaw/pnpm-lock.yaml', 'openclaw-qa/pnpm-lock.yaml') }}
restore-keys: |
openclaw-release-discord-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 OpenClaw QA harness dependencies
working-directory: openclaw-qa
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: Patch release QA compatibility
working-directory: openclaw-rtt
shell: bash
run: |
set -euo pipefail
node scripts/patch-openclaw-release-qa-harness.mjs ../openclaw-qa
- name: Patch Discord RTT measurement
working-directory: openclaw-rtt
shell: bash
run: |
set -euo pipefail
node scripts/patch-openclaw-rtt-harness.mjs ../openclaw-qa
- 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: Build OpenClaw release
working-directory: openclaw
env:
NODE_OPTIONS: --max-old-space-size=8192
OPENCLAW_BUILD_PRIVATE_QA: "1"
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
shell: bash
run: |
set -euo pipefail
time pnpm build
- name: Build OpenClaw QA harness
working-directory: openclaw-qa
env:
NODE_OPTIONS: --max-old-space-size=8192
OPENCLAW_BUILD_PRIVATE_QA: "1"
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
shell: bash
run: |
set -euo pipefail
time pnpm build
- name: Run Discord RTT samples
id: discord_rtt
working-directory: openclaw
env:
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS: "180000"
OPENCLAW_QA_CREDENTIAL_HTTP_TIMEOUT_MS: "60000"
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
OPENCLAW_QA_RELEASE_AUTH_RUNTIME_PATH: ${{ github.workspace }}/openclaw/dist/plugin-sdk/agent-runtime.js
OPENCLAW_QA_RELEASE_PACKAGE_SPEC: ${{ matrix.package.spec }}
INPUT_SAMPLES: ${{ github.event_name == 'workflow_dispatch' && inputs.samples || '20' }}
shell: bash
run: |
set -euo pipefail
samples="${INPUT_SAMPLES:-20}"
if ! [[ "$samples" =~ ^[1-9][0-9]*$ ]]; then
echo "samples must be a positive integer, got: $samples" >&2
exit 1
fi
workspace_root="${GITHUB_WORKSPACE:?}"
output_root=".artifacts/qa-e2e/discord-release-rtt-${{ matrix.package.version }}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
sample_paths="$RUNNER_TEMP/openclaw-discord-release-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}"
summary_path="${output_dir}/qa-evidence.json"
observed_path="${output_dir}/discord-qa-observed-messages.json"
metrics_path="${output_dir}/resource-metrics.env"
status=1
for attempt in 1 2 3; do
rm -rf "$output_dir"
mkdir -p "$output_dir"
# The release checkout can reactivate its shipped pnpm. Switch
# back before invoking the current OpenClaw QA harness.
corepack prepare "pnpm@${PNPM_VERSION}" --activate
set +e
(
cd "${workspace_root}/openclaw-qa"
/usr/bin/time \
-f 'max_rss_kb=%M\nelapsed_seconds=%e' \
-o "${workspace_root}/openclaw/${metrics_path}" \
pnpm openclaw qa discord \
--repo-root "${workspace_root}/openclaw" \
--output-dir "${output_dir}" \
--provider-mode mock-openai \
--model mock-openai/gpt-5.5 \
--alt-model mock-openai/gpt-5.5-alt \
--fast \
--credential-source convex \
--credential-role ci \
--scenario discord-canary
)
status="$?"
set -e
{
echo "sample=${sample}"
echo "attempts=${attempt}"
echo "exit_status=${status}"
} >>"$metrics_path"
if [[ -f "$summary_path" ]]; then
break
fi
if [[ "$attempt" -lt 3 ]]; then
sleep $((attempt * 15))
fi
done
if [[ ! -f "$summary_path" ]]; then
echo "No Discord QA evidence produced for sample ${sample}." >&2
if [[ "$status" -eq 0 ]]; then
status=1
fi
exit "$status"
fi
observed_value=""
if [[ -f "$observed_path" ]]; then
observed_value="${PWD}/${observed_path}"
fi
printf '%s\t%s\t%s\n' "${PWD}/${summary_path}" "$observed_value" "${PWD}/${metrics_path}" >>"$sample_paths"
done
- name: Prepare Discord release import artifact
working-directory: openclaw
shell: bash
run: |
set -euo pipefail
import_dir="$RUNNER_TEMP/release-discord-rtt-import-${{ matrix.package.version }}"
rm -rf "$import_dir"
mkdir -p "$import_dir"
cp -R "${{ steps.discord_rtt.outputs.output_root }}" "$import_dir/artifacts"
{
echo "spec=${{ matrix.package.spec }}"
echo "version=${{ matrix.package.version }}"
} >"$import_dir/metadata.env"
- name: Upload Discord release import artifact
uses: actions/upload-artifact@v7.0.1
with:
name: release-discord-rtt-import-${{ matrix.package.version }}
path: ${{ runner.temp }}/release-discord-rtt-import-${{ matrix.package.version }}
retention-days: 14
if-no-files-found: error
- name: Upload Discord RTT artifacts
if: always()
uses: actions/upload-artifact@v7.0.1
with:
name: release-discord-rtt-${{ matrix.package.version }}-${{ github.run_id }}-${{ github.run_attempt }}
path: openclaw/.artifacts/qa-e2e/discord-release-rtt-${{ matrix.package.version }}-${{ github.run_id }}-${{ github.run_attempt }}
retention-days: 14
if-no-files-found: warn
report:
name: Import Discord release RTT reports
needs:
- resolve
- measure
if: always() && needs.resolve.outputs.should_run == 'true' && needs.measure.result != 'cancelled'
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 Discord release import artifacts
uses: actions/download-artifact@v8.0.1
with:
pattern: release-discord-rtt-import-*
path: ${{ runner.temp }}/release-discord-rtt-imports
- name: Import Discord release RTT results
shell: bash
run: |
set -euo pipefail
git pull --rebase origin "${GITHUB_REF_NAME:-main}"
import_root="$RUNNER_TEMP/release-discord-rtt-imports"
shopt -s nullglob
import_dirs=("$import_root"/release-discord-rtt-import-*)
if [[ -f "$import_root/metadata.env" ]]; then
import_dirs=("$import_root")
fi
if [[ "${#import_dirs[@]}" -eq 0 ]]; then
echo "No Discord 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"
spec="$(get_metadata "$metadata_path" spec)"
version="$(get_metadata "$metadata_path" version)"
sample_paths="$RUNNER_TEMP/openclaw-discord-release-${version}-rtt-samples.tsv"
: >"$sample_paths"
while IFS= read -r sample_dir; do
summary_path="${sample_dir}/qa-evidence.json"
observed_path="${sample_dir}/discord-qa-observed-messages.json"
metrics_path="${sample_dir}/resource-metrics.env"
if [[ ! -f "$summary_path" || ! -f "$metrics_path" ]]; then
echo "Missing Discord release artifacts under ${sample_dir}." >&2
exit 1
fi
observed_value=""
if [[ -f "$observed_path" ]]; then
observed_value="$observed_path"
fi
printf '%s\t%s\t%s\n' "$summary_path" "$observed_value" "$metrics_path" >>"$sample_paths"
done < <(find "${import_dir}/artifacts" -mindepth 1 -maxdepth 1 -type d -name 'sample-*' | sort -V)
if [[ "${{ needs.resolve.outputs.rss_backfill }}" == "true" ]]; then
node scripts/backfill-release-rss.mjs \
--family discord \
--spec "$spec" \
--version "$version" \
--sample-paths "$sample_paths"
else
node scripts/import-discord-rtt.mjs "$sample_paths" \
--spec "$spec" \
--version "$version" \
--provider-mode mock-openai
fi
done
node scripts/validate.mjs
node scripts/discord-rtt-summary.mjs
- name: Commit result
shell: bash
run: |
set -euo pipefail
git add data/channels/discord/ runs/discord/
if git diff --cached --quiet --exit-code; then
echo "No Discord 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 discord release rtt"
git push || {
git pull --rebase origin "${GITHUB_REF_NAME:-main}"
git push
}