mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
ci: resource profiler
This commit is contained in:
parent
67c61c3d29
commit
ead990a048
11 changed files with 889 additions and 42 deletions
88
.github/actions/profile/action.yml
vendored
Normal file
88
.github/actions/profile/action.yml
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
name: Profile a command (CPU/RAM/Disk)
|
||||
description: >-
|
||||
Run a shell command while sampling CPU, RAM, and disk IO every second.
|
||||
Produces a resource-profile.json artifact per job so the CI timing
|
||||
report can show per-job resource usage and identify bottlenecks.
|
||||
|
||||
inputs:
|
||||
command:
|
||||
description: Shell command to run (and profile).
|
||||
required: true
|
||||
label:
|
||||
description: Label for this profile (e.g. "tests slice 1/8").
|
||||
required: true
|
||||
working-directory:
|
||||
description: Directory to run in.
|
||||
default: '.'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Start resource profiler
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
# Start profiler in background. It writes to resource-profile.json
|
||||
# on SIGTERM (or when the command finishes and we signal it).
|
||||
python3 scripts/ci/resource_profile.py \
|
||||
--output resource-profile.json \
|
||||
--label "$PROFILE_LABEL" &
|
||||
echo $! > "$RUNNER_TEMP/profiler.pid"
|
||||
env:
|
||||
PROFILE_LABEL: ${{ inputs.label }}
|
||||
|
||||
- name: Run command
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
env:
|
||||
_CMD: ${{ inputs.command }}
|
||||
run: |
|
||||
bash -c "$_CMD"
|
||||
|
||||
- name: Stop profiler and collect results
|
||||
id: stop-profiler
|
||||
if: always()
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
if [ -f "$RUNNER_TEMP/profiler.pid" ]; then
|
||||
PID=$(cat "$RUNNER_TEMP/profiler.pid")
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
kill -TERM "$PID"
|
||||
# Give it a moment to write the JSON
|
||||
for i in 1 2 3 4 5; do
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
sleep 0.2
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
kill -KILL "$PID" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
# hashFiles() only matches inside the workspace, so surface file
|
||||
# existence as a step output instead for the upload condition.
|
||||
if [ -s resource-profile.json ]; then
|
||||
echo "profile_written=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "profile_written=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Sanitize resource profile label
|
||||
id: sanitize
|
||||
if: always()
|
||||
shell: bash
|
||||
env:
|
||||
PROFILE_LABEL: ${{ inputs.label }}
|
||||
run: |
|
||||
SAFE=$(printf '%s' "$PROFILE_LABEL" | sed -E 's/[^a-zA-Z0-9]+/-/g')
|
||||
echo "safe_label=$SAFE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload resource profile
|
||||
if: always() && steps.stop-profiler.outputs.profile_written == 'true'
|
||||
continue-on-error: true
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: resource-profile-${{ steps.sanitize.outputs.safe_label }}
|
||||
path: ${{ inputs.working-directory }}/resource-profile.json
|
||||
retention-days: 14
|
||||
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
|
|
@ -333,6 +333,18 @@ jobs:
|
|||
restore-keys: |
|
||||
ci-timings-baseline-
|
||||
|
||||
- name: Download resource profiles
|
||||
# Advisory — if no profiles were uploaded (e.g. sub-workflows
|
||||
# didn't run), this step finds nothing and the report still works.
|
||||
# NOTE: no merge-multiple — every artifact contains a file named
|
||||
# resource-profile.json, so merging would clobber all but one.
|
||||
# Per-artifact subdirs are exactly what load_resource_profiles walks.
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: resource-profile-*
|
||||
path: resource-profiles
|
||||
|
||||
- name: Collect timings and generate report
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
|
@ -341,7 +353,8 @@ jobs:
|
|||
--baseline ci-timings-baseline.json \
|
||||
--output ci-timings-report.html \
|
||||
--json-out ci-timings.json \
|
||||
--summary-out ci-timings-summary.md
|
||||
--summary-out ci-timings-summary.md \
|
||||
--profiles-dir resource-profiles
|
||||
|
||||
- name: Upload HTML report
|
||||
# Advisory report — artifact-service blips must not fail the job.
|
||||
|
|
@ -361,6 +374,7 @@ jobs:
|
|||
python3 scripts/ci/timings_report.py \
|
||||
--from-json ci-timings.json \
|
||||
--baseline ci-timings-baseline.json \
|
||||
--profiles-dir resource-profiles \
|
||||
--review-status-out review-status.json \
|
||||
--review-status-only
|
||||
|
||||
|
|
|
|||
14
.github/workflows/docker.yml
vendored
14
.github/workflows/docker.yml
vendored
|
|
@ -107,16 +107,10 @@ jobs:
|
|||
command: uv sync --locked --python 3.11 --extra dev
|
||||
|
||||
- name: Run docker integration tests
|
||||
env:
|
||||
# Skip rebuild; use the image already loaded by the build step.
|
||||
HERMES_TEST_IMAGE: ${{ env.IMAGE_NAME }}:test
|
||||
# Match the policy in tests.yml :: test job — no accidental
|
||||
# real-API calls from inside the harness.
|
||||
OPENROUTER_API_KEY: ""
|
||||
OPENAI_API_KEY: ""
|
||||
NOUS_API_KEY: ""
|
||||
run: |
|
||||
scripts/run_tests.sh tests/docker/ --file-timeout 600
|
||||
uses: ./.github/actions/profile
|
||||
with:
|
||||
label: docker-tests
|
||||
command: scripts/run_tests.sh tests/docker/ --file-timeout 600
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rebuild and push each architecture only after the unprivileged build/test
|
||||
|
|
|
|||
7
.github/workflows/docs-site-checks.yml
vendored
7
.github/workflows/docs-site-checks.yml
vendored
|
|
@ -45,5 +45,8 @@ jobs:
|
|||
working-directory: website
|
||||
|
||||
- name: Build Docusaurus
|
||||
run: npm run build
|
||||
working-directory: website
|
||||
uses: ./.github/actions/profile
|
||||
with:
|
||||
label: docs-site-build
|
||||
working-directory: website
|
||||
command: npm run build
|
||||
|
|
|
|||
25
.github/workflows/e2e-desktop.yml
vendored
25
.github/workflows/e2e-desktop.yml
vendored
|
|
@ -87,17 +87,20 @@ jobs:
|
|||
# `npm run test:e2e` builds dist/ as a pretest hook so the renderer
|
||||
# is always fresh — no separate build step needed.
|
||||
- name: Run Playwright E2E tests
|
||||
working-directory: apps/desktop
|
||||
run: |
|
||||
if [ "${{ github.ref_name }}" = "main" ]; then
|
||||
echo "On main — generating/updating baseline screenshots"
|
||||
npm run build && xvfb-run -a --server-args="-screen 0 1280x1024x24" \
|
||||
npx playwright test --reporter=list --update-snapshots
|
||||
else
|
||||
echo "On PR — comparing against cached baselines"
|
||||
npm run build && xvfb-run -a --server-args="-screen 0 1280x1024x24" \
|
||||
npx playwright test --reporter=list
|
||||
fi
|
||||
uses: ./.github/actions/profile
|
||||
with:
|
||||
label: e2e-desktop
|
||||
working-directory: apps/desktop
|
||||
command: |
|
||||
if [ "${{ github.ref_name }}" = "main" ]; then
|
||||
echo "On main — generating/updating baseline screenshots"
|
||||
npm run build && xvfb-run -a --server-args="-screen 0 1280x1024x24" \
|
||||
npx playwright test --reporter=list --update-snapshots
|
||||
else
|
||||
echo "On PR — comparing against cached baselines"
|
||||
npm run build && xvfb-run -a --server-args="-screen 0 1280x1024x24" \
|
||||
npx playwright test --reporter=list
|
||||
fi
|
||||
env:
|
||||
CI: "true"
|
||||
# Ensure no real API keys leak into the test env.
|
||||
|
|
|
|||
5
.github/workflows/js-tests.yml
vendored
5
.github/workflows/js-tests.yml
vendored
|
|
@ -66,4 +66,7 @@ jobs:
|
|||
- uses: ./.github/actions/retry
|
||||
with:
|
||||
command: npm ci
|
||||
- run: npm run --prefix ${{ matrix.package }} ${{ matrix.script }}
|
||||
- uses: ./.github/actions/profile
|
||||
with:
|
||||
label: js-${{ matrix.package }}-${{ matrix.script }}
|
||||
command: npm run --prefix ${{ matrix.package }} ${{ matrix.script }}
|
||||
|
|
|
|||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
|
|
@ -148,8 +148,10 @@ jobs:
|
|||
- name: ruff check .
|
||||
# No --exit-zero, no || true. Exit code propagates to the job,
|
||||
# which propagates to the required-check gate.
|
||||
run: |
|
||||
ruff check .
|
||||
uses: ./.github/actions/profile
|
||||
with:
|
||||
label: ruff-blocking
|
||||
command: ruff check .
|
||||
|
||||
windows-footguns:
|
||||
# Static guardrails on Windows-unsafe Python primitives — os.kill(pid, 0),
|
||||
|
|
|
|||
22
.github/workflows/tests.yml
vendored
22
.github/workflows/tests.yml
vendored
|
|
@ -114,11 +114,14 @@ jobs:
|
|||
#
|
||||
# File list is pre-computed by the generate job (--generate-slices)
|
||||
# which runs LPT distribution once and passes the file list to each
|
||||
# matrix job via --files. Previously each job re-discovered files and
|
||||
# re-ran LPT independently — redundant N times.
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
scripts/run_tests.sh --files '${{ matrix.slice.files }}'
|
||||
# matrix job via --files. Previously each job re-discovered files
|
||||
# and re-ran LPT independently — redundant N times.
|
||||
uses: ./.github/actions/profile
|
||||
with:
|
||||
label: tests-slice-${{ matrix.slice.index }}
|
||||
command: |
|
||||
source .venv/bin/activate
|
||||
scripts/run_tests.sh --files '${{ matrix.slice.files }}'
|
||||
env:
|
||||
# Ensure tests don't accidentally call real APIs
|
||||
OPENROUTER_API_KEY: ""
|
||||
|
|
@ -226,9 +229,12 @@ jobs:
|
|||
run: uv cache prune --ci
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
python -m pytest tests/e2e/ -v --tb=short
|
||||
uses: ./.github/actions/profile
|
||||
with:
|
||||
label: tests-e2e
|
||||
command: |
|
||||
source .venv/bin/activate
|
||||
python -m pytest tests/e2e/ -v --tb=short
|
||||
env:
|
||||
OPENROUTER_API_KEY: ""
|
||||
OPENAI_API_KEY: ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue