Files
hermes-agent/.github/workflows/install-e2e.yml
T
ethernet 83d009ae82 ci(install-e2e): nest the graph per starting tag; skips live where the knowledge lives
Two structural changes to the combination fanout:

1. Tags become the OUTER axis, as a sub-graph per starting version:
   install-e2e.yml fans a plain matrix over the picked tags into a new
   per-tag reusable workflow (install-e2e-tag.yml), which runs the
   combination generator for that one tag and fans out one job per
   {os, install-method, update-method}. The Actions graph now reads
   'from vX -> windows: install -> update' per leg. Nothing is
   hardcoded in the workflows: the tag workflow calls the generator
   itself.

2. Native skips move to the point that owns the capability knowledge:
   macOS combos (no driving workflow exists) grey out in the tag
   workflow via install-e2e-skip.yml, untouched by the tag axis; ALL
   windows combos dispatch to install-e2e-windows-run.yml, which takes
   install-method/update-method inputs and natively skips the pairs
   its driver cannot run yet - so implementing a windows method is a
   change in the run workflow + driver only. The driver's -Route ids
   now match the generator's method ids verbatim.

Generator output shape, route filters, and all error paths re-verified
locally; all four workflows pass actionlint; driver re-parses clean
pure-ASCII.
2026-08-11 16:01:36 -04:00

124 lines
5.3 KiB
YAML

name: Install & Update E2E
# Can a user on a released version get to this commit?
#
# The support matrix -- every {os, install-method, update-method} combination
# a user could be on -- lives in scripts/sandbox/generate-e2e-matrix.mjs.
# The job graph nests one sub-graph per starting release tag:
#
# from <tag> (install-e2e-tag.yml)
# -> linux: <install> -> <update> one job per combination, a real
# curl|bash install in bubblewrap
# (install-e2e-run.yml)
# -> windows: <install> -> <update> one job per combination, the real
# desktop user flow: website exe
# clicked by AutoHotkey, update via
# the app, Playwright clicking
# "Update now"
# (install-e2e-windows-run.yml)
# -> macos: <install> -> <update> natively skipped until a macos
# workflow exists
# (install-e2e-skip.yml)
#
# Every combination is dispatched; unimplemented ones natively skip (grey)
# at the point that owns the knowledge -- macOS combos in the tag workflow
# (no driver exists), windows method pairs inside the windows run workflow
# (next to the driver that will implement them).
#
# The starting versions are chosen at runtime from the repo's release tags
# (scripts/sandbox/pick-release-tags.sh): newest, oldest, and a spread
# between. A hardcoded list would stop covering the newest release the day
# after it ships, and would pin an "oldest" that nobody still runs. The tag
# axis is applied to EVERY os: linux legs install the tag in the sandbox;
# windows legs stage serve.git's `main` at it, which is what the published
# installer (no commit pin) then installs.
#
# Triggers:
# * every 12 hours, so upstream drift (a new uv, a Node bump, a PyPI change)
# surfaces on a schedule rather than in someone's review cycle;
# * when a release tag is created -- the moment the set of versions users can
# update FROM changes, and the moment a broken updater would strand them;
# * manually, where you can pick the route and how many releases to sample.
#
# Deliberately NOT on pull_request: a leg takes ~11 minutes of real toolchain
# installation, and the matrix multiplies that. Updating is release-shaped work,
# so it is gated on releases and the clock instead.
on:
workflow_dispatch:
inputs:
route:
description: 'Which update route to exercise. all/both include the Windows desktop leg.'
required: false
type: choice
default: all
options: [all, both, update, installer, windows-desktop]
tag-count:
description: 'How many release tags to sample (newest, oldest, and a spread between).'
required: false
type: string
default: '5'
schedule:
# Every 12 hours, off the hour to avoid the top-of-hour runner crunch.
- cron: '20 7,19 * * *'
push:
tags:
# Release tags only: the repo also carries backup/* and one-off tags.
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+.[0-9]+'
permissions:
contents: read
concurrency:
group: install-e2e-${{ github.ref }}
cancel-in-progress: true
jobs:
# Which released versions do we test updating FROM? Resolved once; the
# generator applies the tag axis to every OS's legs.
pick-releases:
name: Pick release tags
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
tags: ${{ steps.pick.outputs.tags }}
steps:
# This job only reads tag names and runs one script, so take the cheap
# checkout: no blobs (filter), no other files (sparse), but DO fetch tags
# -- they are the whole input, and the default shallow checkout has none.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
filter: blob:none
fetch-tags: true
sparse-checkout: scripts/sandbox/pick-release-tags.sh
sparse-checkout-cone-mode: false
- id: pick
run: |
set -euo pipefail
tags="$(scripts/sandbox/pick-release-tags.sh --count '${{ inputs.tag-count || 5 }}')"
echo "Testing updates from: $tags"
echo "tags=$tags" >> "$GITHUB_OUTPUT"
# One sub-graph per starting tag: install-e2e-tag.yml expands the
# {os, install-method, update-method} support matrix for that tag and
# fans out one job per combination, so the graph reads
# "from vX -> windows: install -> update" per leg. Skips happen where
# the knowledge lives: macOS combos natively skip inside the tag
# workflow (no workflow exists for them at all); unimplemented windows
# method pairs natively skip inside install-e2e-windows-run.yml, next
# to the driver that will implement them.
from-tag:
name: "from ${{ matrix.install-ref }}"
needs: pick-releases
strategy:
# One tag breaking is worth knowing about even if another already
# failed, so let every sub-graph report.
fail-fast: false
matrix:
install-ref: ${{ fromJSON(needs.pick-releases.outputs.tags) }}
uses: ./.github/workflows/install-e2e-tag.yml
with:
install-ref: ${{ matrix.install-ref }}
route: ${{ inputs.route || 'all' }}