Files
hermes-agent/.github/workflows/install-e2e-run.yml
T
ethernet ea4cd375f8 ci(install-e2e): retire the bubblewrap sandbox - git redirect everywhere, macos legs live
The fake Internet (bubblewrap + slirp4netns + MITM proxy +
upload-pack shim, 883 lines across dev-sandbox.sh, stage2-run.sh,
proxy.py, ssh-shim.sh, openssl.cnf, install-update-e2e.sh) existed to
isolate install.sh's network. The GIT_CONFIG_GLOBAL insteadOf redirect
the windows driver introduced does the same job with a gitconfig file
and works on any OS, so:

* install-e2e-run.yml now runs tests/install/installer-script-e2e.sh
  directly on the bare runner - no sandbox deps, no userns sysctls -
  and takes a runner input;
* the macos matrix calls the SAME workflow on macos-latest, deleting
  install-e2e-macos-run.yml: installer-script -> installer-script /
  hermes-update flip from grey to live, app-update pairs stay TODO
  inside the shared gate;
* install.sh is no longer curl'd through a fake CA - each leg runs
  the copy from the ref a user of that version actually executed;
* scripts/dev-sandbox.sh becomes the minimal isolation sandbox from
  ab6b9492f (separate HERMES_HOME / Electron userData / app name,
  same CLI surface: --persistent, --from, --delete), keeping its
  .hermes-sandbox dir name so gitignore and docs hold;
* nix/sandbox.nix drops the bwrap/proxy closure and keeps only the
  Electron runtime LD_LIBRARY_PATH the desktop app needs.

Verified: nix build .#sandbox + smoke run (isolated HERMES_HOME
created, ephemeral cleanup), shellcheck/bash -n on both scripts,
actionlint on all three workflows, and the new driver ran the full
v0.20.2 -> HEAD hermes-update pass locally before this commit.
2026-08-11 21:20:42 -04:00

124 lines
5.0 KiB
YAML

name: Install & Update E2E (reusable)
# Runs ONE {install-method, update-method} combination against ONE starting
# commit, with a real install (uv, a managed Python, Node, the venv) behind
# it.
#
# Reusable so callers can fan out over the combinations that matter --
# update from the tip vs. from an older release, `hermes update` vs.
# re-running the installer -- without duplicating the runner setup. Each leg
# is independent: its own isolated HOME, its own install, nothing rewound
# or shared.
#
# No sandbox: tests/install/installer-script-e2e.sh points every git
# process at a local bare clone (url.<file://serve.git>.insteadOf in a
# driver-owned GIT_CONFIG_GLOBAL) and isolates HOME, so the installer and
# updater run byte-for-byte against their real URLs on the bare runner --
# which is disposable, and therefore IS the sandbox. That also makes this
# workflow OS-agnostic: the same driver runs on ubuntu and macos runners.
#
# Method ids come from scripts/sandbox/generate-e2e-matrix.mjs. Supported
# today: install via installer-script, update via hermes-update or
# installer-script (re-run the one-liner).
# Anything else NATIVELY SKIPS (grey check, no runner): capability
# knowledge lives here, next to the driver, so the caller can dispatch
# every declared combination without knowing which ones work.
#
# Call it:
#
# jobs:
# tip:
# uses: ./.github/workflows/install-e2e-run.yml
# with:
# install-method: installer-script
# update-method: hermes-update
# install-ref: refs/heads/main
on:
workflow_call:
inputs:
install-method:
description: 'How the starting version gets installed. Supported: installer-script (the real curl | install.sh one-liner).'
required: true
type: string
update-method:
description: 'How the install updates to HEAD. Supported: hermes-update (the updater) or installer-script (re-run the one-liner). Declared-but-TODO methods skip.'
required: true
type: string
install-ref:
description: 'What to install before updating: a branch, a tag (v2026.7.7), or a SHA reachable from main.'
required: false
type: string
default: refs/heads/main
runner:
description: 'Runner label.'
required: false
type: string
default: ubuntu-latest
timeout-minutes:
description: 'Job timeout. A cold run installs real toolchains twice.'
required: false
type: number
default: 45
permissions:
contents: read
jobs:
e2e:
# Static name on purpose: the caller's job name already carries the
# method pair, and GitHub renders name expressions UNEXPANDED (literal
# "${{ inputs... }}") on natively skipped jobs. Short because it is
# only a rendered tail ("<combo> / e2e").
name: e2e
# The pairs the driver can run today; anything else is a declared TODO
# and natively skips.
if: inputs.install-method == 'installer-script' && contains(fromJSON('["hermes-update", "installer-script"]'), inputs.update-method)
runs-on: ${{ inputs.runner }}
timeout-minutes: ${{ inputs.timeout-minutes }}
steps:
# Full history: the driver bare-clones this checkout as the repo the
# installer/updater talk to, and both OLD and HEAD must be reachable
# in that clone. A shallow clone cannot serve either need.
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Run install + update E2E
run: |
set -euo pipefail
tests/install/installer-script-e2e.sh \
--update-method '${{ inputs.update-method }}' \
--install-ref '${{ inputs.install-ref }}'
env:
# Outside the workspace on purpose: logs written into the repo
# would trip the driver's own dirty-tree guard.
HERMES_E2E_LOG_DIR: ${{ runner.temp }}/e2e-logs
# Artifact names cannot contain '/', and install-ref may be a full ref
# like refs/heads/main. GitHub Actions expressions have no string-replace
# function, so build the safe name here. Runs even on failure -- that is
# exactly when the logs are wanted.
- name: Build artifact name
if: always()
id: artifact
run: |
set -euo pipefail
safe_ref='${{ inputs.install-ref }}'
safe_ref="${safe_ref//\//-}"
echo "name=install-e2e-${{ runner.os }}-${{ inputs.update-method }}-${safe_ref}" >> "$GITHUB_OUTPUT"
# The installer's own transcripts say far more than the assertion that
# tripped when a real install breaks.
- name: Upload installer logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
# Unique per leg: a matrix over releases runs this workflow several
# times per route, and same-named artifacts collide.
name: ${{ steps.artifact.outputs.name }}-${{ github.sha }}
path: ${{ runner.temp }}/e2e-logs
retention-days: 14
if-no-files-found: ignore