Files
hermes-agent/.github/workflows/install-e2e-macos-run.yml

286 lines
12 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
update-ref:
description: 'What to update TO: HEAD (the commit under test), or NEXT -- a synthetic child of install-ref that the driver mints, so a leg that installs HEAD still has an update to take (HEAD''s own updater).'
required: false
type: string
default: HEAD
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
bundle-manifest-url:
description: 'URL of the JSON schema-1 bundle manifest for the packaged-app arm. Empty (default) means the bundled arm does not run — native packaged coverage only runs when the caller pins a real signed OLD/NEW pair.'
required: false
type: string
default: ''
bundle-manifest-sha256:
description: Pinned transition manifest digest for stable acceptance
type: string
default: ''
bundle-arch:
description: 'Architecture of the bundled pair: arm64 (macos-15) or x64 (macos-15-intel).'
required: false
type: string
default: arm64
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 }}
update-ref: ${{ inputs.update-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@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 0
- name: Set up driver tools only
uses: ./.github/actions/setup-pm
with:
toolchain: all
packages: ffmpeg
cache: false
cache-python: false
cache-node: false
- name: Install locked chat driver dependencies
shell: bash
run: npm ci --workspace tests-js --include-workspace-root --omit=dev --ignore-scripts --no-audit --no-fund
- 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 }}' \
--update-ref '${{ inputs.update-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 }} -> ${{ inputs.update-ref }} (${{ 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
# ---- arm 3: the real signed bundle pair, in-app update ------------------
# Runs ONLY when the caller passes a bundle-manifest-url: the parent's
# matrix job owns resolving + pinning the OLD/NEW signed release pair.
# The driver itself re-guards on Darwin + GITHUB_ACTIONS and refuses to
# run anywhere native coverage would be fake.
bundled-e2e:
name: packaged bundle (open-app-update)
if: inputs.install-method == 'packaged-app' && inputs.update-method == 'open-app-update'
runs-on: ${{ inputs.bundle-arch == 'x64' && 'macos-15-intel' || 'macos-15' }}
timeout-minutes: ${{ inputs.timeout-minutes }}
steps:
- uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7.0.1
with:
ref: ${{ github.sha }}
- name: Set up driver tools only
uses: ./.github/actions/setup-pm
with:
toolchain: all
packages: ffmpeg
cache: false
cache-python: false
cache-node: false
- name: Install locked chat driver dependencies
shell: bash
run: npm ci --workspace tests-js --include-workspace-root --omit=dev --ignore-scripts --no-audit --no-fund
- name: Start screen recording
uses: ./.github/actions/e2e-screen-record
with:
mode: start
output: ${{ runner.temp }}/e2e-logs/recording.mkv
- name: Install OLD signed bundle (${{ inputs.install-ref }})
run: |
set -euo pipefail
bash tests/install/macos-bundled-e2e.sh --phase install \
--manifest-url "$BUNDLE_MANIFEST" --arch "$BUNDLE_ARCH"
env:
BUNDLE_MANIFEST: ${{ inputs.bundle-manifest-url }}
BUNDLE_MANIFEST_SHA256: ${{ inputs.bundle-manifest-sha256 }}
BUNDLE_ARCH: ${{ inputs.bundle-arch }}
HERMES_E2E_LOG_DIR: ${{ runner.temp }}/e2e-logs
- name: Update to NEW via About -> Update now (${{ inputs.bundle-arch }})
run: |
set -euo pipefail
bash tests/install/macos-bundled-e2e.sh --phase update \
--manifest-url "$BUNDLE_MANIFEST" --arch "$BUNDLE_ARCH"
env:
BUNDLE_MANIFEST: ${{ inputs.bundle-manifest-url }}
BUNDLE_MANIFEST_SHA256: ${{ inputs.bundle-manifest-sha256 }}
BUNDLE_ARCH: ${{ inputs.bundle-arch }}
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
- name: Build artifact name
id: artifact
if: always()
run: |
echo "name=install-e2e-logs-${{ inputs.leg-id }}-bundle-${{ inputs.bundle-arch }}" >> "$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