Files
hermes-agent/.github/workflows/install-e2e-macos-run.yml
T
yoniebans 2f25c07a2c perf(install-e2e): 60-minute job caps; 35-minute updater wait
The slowest green leg ever recorded is 29 minutes; every cap hit in the
suite's history was a hang, never work. Caps were linux 75 / macos 120 /
windows 240, so a wedged leg burned up to 4 hours of runner time to
report what its log showed in the first minutes. 60 minutes covers the
slowest leg plus cold-cache variance, and every driver-internal bound
(dmg install 45m, AHK 50m, updater wait) still fires before the job cap
in any single-hang scenario, keeping failure diagnostics specific.

The detached-updater wait drops 90m -> 35m on the same evidence: a
working updater finishes far inside 35m; a wedged one never finishes at
any bound, and the longer wait only delayed the report by an hour.
2026-09-01 17:34:13 +02:00

163 lines
6.7 KiB
YAML

# Reusable runner for ONE macOS install/update combination.
#
# Two driver arms, one runs per dispatch (the other natively skips):
#
# e2e (script arms) tests/install/installer-script-e2e.sh - the
# OS-agnostic git-redirect driver shared with
# linux. installer-script(+desktop) installs,
# script/updater/hermes-desktop-app-update
# updates.
# gui-e2e (desktop arm) tests/install/macos-desktop-e2e.sh - the
# published Hermes-Setup.dmg, mounted and run,
# then the app driven by Playwright for the
# app-update methods.
#
# Method pairs without a driver arm yet NATIVELY SKIP (grey check, no
# runner): the capability knowledge lives here, next to the drivers.
name: install-e2e macos leg
on:
workflow_call:
inputs:
install-method:
description: 'How OLD gets installed. Supported: installer-script, installer-script+desktop (curl | bash one-liner, optionally with --include-desktop) and desktop-installer@latest (the published Hermes-Setup.dmg).'
required: true
type: string
update-method:
description: 'How the install updates to HEAD. Script installs support hermes-update / installer-script / installer-script+desktop / hermes-desktop-app-update; dmg installs support open-app-update / hermes-desktop-app-update.'
required: true
type: string
install-ref:
description: 'What to install before updating: a branch, a tag, or a SHA reachable from main.'
required: false
type: string
default: refs/heads/main
tag-has-desktop:
description: "Whether install-ref ships the desktop app (apps/desktop). The caller annotates this from the tag's own tree; desktop-method legs from pre-desktop releases natively skip."
required: false
type: boolean
default: true
leg-id:
description: 'Artifact-safe matrix leg id (from generate-e2e-matrix.mjs legId). Names this leg''s logs + player artifacts so the report job can link a row to its zip.'
required: true
type: string
dmg-url:
description: 'Bootstrap dmg to install OLD with. Default: the latest published one — what a user downloads today.'
required: false
type: string
default: https://hermes-assets.nousresearch.com/Hermes-Setup.dmg
timeout-minutes:
description: 'Job timeout. App-update legs do a full Electron build.'
required: false
type: number
default: 60
permissions:
contents: read
jobs:
# ---- arm 1: script installs (the shared OS-agnostic driver) --------------
e2e:
name: install & update
if: >-
(inputs.install-method == 'installer-script'
|| (inputs.install-method == 'installer-script+desktop' && inputs.tag-has-desktop))
&& (contains(fromJSON('["hermes-update", "installer-script"]'), inputs.update-method)
|| (contains(fromJSON('["installer-script+desktop", "hermes-desktop-app-update"]'), inputs.update-method) && inputs.tag-has-desktop))
uses: ./.github/workflows/install-e2e-run.yml
with:
install-method: ${{ inputs.install-method }}
update-method: ${{ inputs.update-method }}
install-ref: ${{ inputs.install-ref }}
tag-has-desktop: ${{ inputs.tag-has-desktop }}
leg-id: ${{ inputs.leg-id }}
runner: macos-latest
timeout-minutes: ${{ inputs.timeout-minutes }}
# ---- arm 2: the published dmg, then Playwright drives the app ------------
gui-e2e:
# Short static name on purpose: name expressions render UNEXPANDED on
# skipped jobs.
name: Hermes-Setup.dmg
if: >-
inputs.install-method == 'desktop-installer@latest' && inputs.tag-has-desktop
&& contains(fromJSON('["open-app-update", "hermes-desktop-app-update", "hermes-update", "installer-script", "installer-script+desktop"]'), inputs.update-method)
runs-on: macos-latest
timeout-minutes: ${{ inputs.timeout-minutes }}
steps:
# Full history: the driver bare-clones this checkout as the repo the
# installer/updater talk to.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Start screen recording
uses: ./.github/actions/e2e-screen-record
with:
mode: start
output: ${{ runner.temp }}/e2e-logs/recording.mkv
- name: Stage serve repo (main -> ${{ inputs.install-ref }})
run: |
set -euo pipefail
tests/install/macos-desktop-e2e.sh --phase stage \
--update-method '${{ inputs.update-method }}' \
--install-ref '${{ inputs.install-ref }}' \
--dmg-url '${{ inputs.dmg-url }}'
env:
HERMES_E2E_LOG_DIR: ${{ runner.temp }}/e2e-logs
- name: Install ${{ inputs.install-ref }} via Hermes-Setup.dmg
run: |
set -euo pipefail
tests/install/macos-desktop-e2e.sh --phase install \
--update-method '${{ inputs.update-method }}' \
--install-ref '${{ inputs.install-ref }}' \
--dmg-url '${{ inputs.dmg-url }}'
env:
HERMES_E2E_LOG_DIR: ${{ runner.temp }}/e2e-logs
- name: Update ${{ inputs.install-ref }} -> HEAD (${{ inputs.update-method }})
run: |
set -euo pipefail
tests/install/macos-desktop-e2e.sh --phase update \
--update-method '${{ inputs.update-method }}' \
--install-ref '${{ inputs.install-ref }}' \
--dmg-url '${{ inputs.dmg-url }}'
env:
HERMES_E2E_LOG_DIR: ${{ runner.temp }}/e2e-logs
- name: Stop screen recording
if: always()
uses: ./.github/actions/e2e-screen-record
with:
mode: stop
output: ${{ runner.temp }}/e2e-logs/recording.mkv
- name: Remux recording for browser playback
if: always()
run: |
set -euo pipefail
if [ -f "${{ runner.temp }}/e2e-logs/recording.mkv" ]; then
ffmpeg -y -hide_banner -loglevel error -i "${{ runner.temp }}/e2e-logs/recording.mkv" \
-c copy "${{ runner.temp }}/e2e-logs/recording.mp4"
fi
# Artifact names cannot contain '/'; install-ref may be a full ref.
- name: Build artifact name
id: artifact
if: always()
run: |
echo "name=install-e2e-logs-${{ inputs.leg-id }}" >> "$GITHUB_OUTPUT"
- name: Upload logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.artifact.outputs.name }}
path: ${{ runner.temp }}/e2e-logs
retention-days: 14
if-no-files-found: ignore