diff --git a/.github/workflows/install-e2e-skip.yml b/.github/workflows/install-e2e-skip.yml index 7b355b8816..0241500740 100644 --- a/.github/workflows/install-e2e-skip.yml +++ b/.github/workflows/install-e2e-skip.yml @@ -1,15 +1,18 @@ name: Install & Update E2E — unsupported combo (reusable) -# A declared-but-unimplemented {os, install-method, update-method} -# combination from scripts/sandbox/generate-e2e-matrix.mjs. The caller fans -# out one of these per unsupported combo so every coverage gap is its own -# named, NATIVELY SKIPPED check (grey, conclusion=skipped, no runner) in the -# Checks tab. +# A declared {os, install-method, update-method} combination whose OS has +# no driving E2E workflow at all (today: every macOS combo). The per-tag +# caller (install-e2e-tag.yml) fans out one of these per such combo so the +# coverage gap is its own named, NATIVELY SKIPPED check (grey, +# conclusion=skipped, no runner) in the Checks tab. # -# `implemented` stays at its false default until the combo has a real -# driver; flipping a combo to implemented happens in the generator (move it -# into IMPLEMENTED and point it at a real reusable workflow), never by -# setting this input. +# OSes WITH a driving workflow don't come here: their unimplemented method +# pairs natively skip inside that workflow, next to the driver that will +# implement them (see install-e2e-windows-run.yml). +# +# `implemented` stays at its false default until the OS has a real driving +# workflow; flipping happens by routing the combo to that workflow in +# generate-e2e-matrix.mjs, never by setting this input. on: workflow_call: diff --git a/.github/workflows/install-e2e-tag.yml b/.github/workflows/install-e2e-tag.yml new file mode 100644 index 0000000000..ee56019753 --- /dev/null +++ b/.github/workflows/install-e2e-tag.yml @@ -0,0 +1,100 @@ +name: Install & Update E2E — one starting tag (reusable) + +# Everything a user starting on ONE released version could do: for each +# {os, install-method, update-method} combination the support matrix +# (scripts/sandbox/generate-e2e-matrix.mjs) declares, one job that installs +# this tag and updates to HEAD. +# +# Skips happen where the knowledge lives: +# * macOS combos have no workflow at all -- they natively skip right +# here via install-e2e-skip.yml; +# * windows combos ALL dispatch to install-e2e-windows-run.yml, which +# natively skips the method pairs its driver cannot run yet; +# * linux combos are all implemented. +# +# Call it: +# +# jobs: +# tag: +# strategy: +# matrix: { install-ref: [v2026.8.3, v2026.3.12] } +# uses: ./.github/workflows/install-e2e-tag.yml +# with: +# install-ref: ${{ matrix.install-ref }} + +on: + workflow_call: + inputs: + install-ref: + description: 'The released version to start from: installed first, then updated to HEAD.' + required: true + type: string + route: + description: 'Combination filter, mirroring the dispatch route choice: all, both, update, installer, windows-desktop.' + required: false + type: string + default: all + +permissions: + contents: read + +jobs: + generate-matrix: + name: expand combinations + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + linux: ${{ steps.gen.outputs.linux }} + windows: ${{ steps.gen.outputs.windows }} + skipped: ${{ steps.gen.outputs.skipped }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + sparse-checkout: scripts/sandbox/generate-e2e-matrix.mjs + sparse-checkout-cone-mode: false + - id: gen + run: | + set -euo pipefail + matrices="$(node scripts/sandbox/generate-e2e-matrix.mjs \ + --tags '["${{ inputs.install-ref }}"]' \ + --route '${{ inputs.route }}')" + echo "$matrices" + for key in linux windows skipped; do + echo "$key=$(echo "$matrices" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>console.log(JSON.stringify(JSON.parse(d)[process.argv[1]])))' "$key")" >> "$GITHUB_OUTPUT" + done + + linux: + name: "linux: ${{ matrix.name }}" + if: fromJSON(needs.generate-matrix.outputs.linux).include[0] != null + needs: generate-matrix + strategy: + # One combo breaking is worth knowing about even if another already + # failed, so let every leg report. + fail-fast: false + matrix: ${{ fromJSON(needs.generate-matrix.outputs.linux) }} + uses: ./.github/workflows/install-e2e-run.yml + with: + route: ${{ matrix.route }} + install-ref: ${{ matrix.install_ref }} + + windows: + name: "windows: ${{ matrix.name }}" + if: fromJSON(needs.generate-matrix.outputs.windows).include[0] != null + needs: generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.generate-matrix.outputs.windows) }} + uses: ./.github/workflows/install-e2e-windows-run.yml + with: + install-method: ${{ matrix.install_method }} + update-method: ${{ matrix.update_method }} + install-ref: ${{ matrix.install_ref }} + + skipped: + name: "${{ matrix.os }}: ${{ matrix.install }} -> ${{ matrix.update }}" + if: fromJSON(needs.generate-matrix.outputs.skipped).include[0] != null + needs: generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.generate-matrix.outputs.skipped) }} + uses: ./.github/workflows/install-e2e-skip.yml diff --git a/.github/workflows/install-e2e-windows-run.yml b/.github/workflows/install-e2e-windows-run.yml index ad7dfbb27c..f4ac6e4435 100644 --- a/.github/workflows/install-e2e-windows-run.yml +++ b/.github/workflows/install-e2e-windows-run.yml @@ -15,31 +15,43 @@ name: Install & Update E2E — Windows desktop (reusable) # update leg — an update becomes available exactly the way it does for a # real user. # -# Routes (the update mechanism under test): -# desktop the app's own Update button: the installed Hermes.exe runs -# under Playwright's Electron driver, which clicks Settings -> -# About -> "Update now"; the production hand-off chain runs -# untouched (marker, app quit, detached updater, hermes -# update, desktop rebuild, relaunch). -# update TODO: `hermes update` from the installed venv. -# installer TODO: re-run the bootstrap exe over the existing install. +# Routes (from the update-method input): +# desktop-app the app's own Update button: the installed +# Hermes.exe runs under Playwright's Electron +# driver, which clicks Settings -> About -> +# "Update now"; the production hand-off chain runs +# untouched (marker, app quit, detached updater, +# hermes update, desktop rebuild, relaunch). +# hermes-update TODO: `hermes update` from the installed venv. +# desktop-installer-rerun@latest +# TODO: re-run the bootstrap exe over the install. +# irm-iex TODO: re-run the irm | iex one-liner. +# +# Method pairs without a driver yet NATIVELY SKIP (grey check, no runner): +# the capability knowledge lives here, next to the driver, so the caller +# can dispatch every declared combination without knowing which ones work. # # Call it: # # jobs: -# windows-desktop: +# windows: # uses: ./.github/workflows/install-e2e-windows-run.yml # with: -# route: desktop +# install-method: desktop-installer@latest +# update-method: desktop-app +# install-ref: v2026.8.3 on: workflow_call: inputs: - route: - description: 'Update mechanism to exercise: desktop (Update button; implemented), update (hermes update; TODO), installer (re-run bootstrap exe; TODO).' - required: false + install-method: + description: 'How OLD gets installed. Supported: desktop-installer@latest (website exe, AHK-clicked). Declared-but-TODO methods skip.' + required: true + type: string + update-method: + description: 'How the install updates to HEAD. Supported: desktop-app (Update button under Playwright). Declared-but-TODO methods skip.' + required: true type: string - default: desktop install-ref: description: 'Ref to install as OLD (served as main while the installer runs). auto = the newest release tag in the checkout.' required: false @@ -61,7 +73,10 @@ permissions: jobs: e2e: - name: "${{ inputs.route }} route from website installer" + name: "${{ inputs.install-method }} -> ${{ inputs.update-method }}" + # The one pair the driver can run today. Anything else is a declared + # TODO: native skip, so the coverage gap is a grey check on every run. + if: inputs.install-method == 'desktop-installer@latest' && inputs.update-method == 'desktop-app' runs-on: windows-latest timeout-minutes: ${{ inputs.timeout-minutes }} @@ -110,15 +125,15 @@ jobs: - name: Stage serve repo (main -> OLD) shell: powershell - run: powershell -NoProfile -ExecutionPolicy Bypass -File tests\install\windows-desktop-gui-e2e.ps1 -Phase stage -Route ${{ inputs.route }} -InstallRef "${{ inputs.install-ref }}" -SetupExeUrl ${{ inputs.setup-exe-url }} + run: powershell -NoProfile -ExecutionPolicy Bypass -File tests\install\windows-desktop-gui-e2e.ps1 -Phase stage -Route "${{ inputs.update-method }}" -InstallRef "${{ inputs.install-ref }}" -SetupExeUrl ${{ inputs.setup-exe-url }} - name: Install OLD via website Hermes-Setup.exe (headed, AHK-clicked) shell: powershell - run: powershell -NoProfile -ExecutionPolicy Bypass -File tests\install\windows-desktop-gui-e2e.ps1 -Phase install-gui -Route ${{ inputs.route }} -InstallRef "${{ inputs.install-ref }}" -SetupExeUrl ${{ inputs.setup-exe-url }} + run: powershell -NoProfile -ExecutionPolicy Bypass -File tests\install\windows-desktop-gui-e2e.ps1 -Phase install-gui -Route "${{ inputs.update-method }}" -InstallRef "${{ inputs.install-ref }}" -SetupExeUrl ${{ inputs.setup-exe-url }} - - name: Update OLD -> HEAD (${{ inputs.route }} route) + - name: Update OLD -> HEAD (${{ inputs.update-method }}) shell: powershell - run: powershell -NoProfile -ExecutionPolicy Bypass -File tests\install\windows-desktop-gui-e2e.ps1 -Phase update-gui -Route ${{ inputs.route }} -InstallRef "${{ inputs.install-ref }}" -SetupExeUrl ${{ inputs.setup-exe-url }} + run: powershell -NoProfile -ExecutionPolicy Bypass -File tests\install\windows-desktop-gui-e2e.ps1 -Phase update-gui -Route "${{ inputs.update-method }}" -InstallRef "${{ inputs.install-ref }}" -SetupExeUrl ${{ inputs.setup-exe-url }} - name: Collect proof + logs if: always() @@ -141,7 +156,7 @@ jobs: if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: install-e2e-windows-${{ inputs.route }}-${{ github.sha }} + name: install-e2e-windows-${{ inputs.update-method }}-${{ inputs.install-ref }}-${{ github.sha }} path: gui-e2e-proof retention-days: 14 if-no-files-found: ignore diff --git a/.github/workflows/install-e2e.yml b/.github/workflows/install-e2e.yml index 7b0e131467..259fa322da 100644 --- a/.github/workflows/install-e2e.yml +++ b/.github/workflows/install-e2e.yml @@ -4,27 +4,34 @@ name: Install & Update E2E # # The support matrix -- every {os, install-method, update-method} combination # a user could be on -- lives in scripts/sandbox/generate-e2e-matrix.mjs. -# The generate-matrix job expands it into ONE JOB PER COMBINATION: +# The job graph nests one sub-graph per starting release tag: # -# * linux legs install a sampled release through the real -# `curl | install.sh` one-liner (uv, a managed Python, Node, the venv) -# inside scripts/dev-sandbox.sh, then apply the combo's update route and -# require the checkout to land on this commit with a working `hermes`; -# * windows legs run the real desktop user flow (install-e2e-windows-run -# .yml): the website's Hermes-Setup.exe clicked by AutoHotkey, then the -# update driven through the app itself, Playwright clicking "Update now"; -# * combinations the spec declares but CI can't drive yet (macOS, the -# remaining windows install/update methods) fan out too, one NATIVELY -# SKIPPED check per combo, so every coverage gap is enumerable from the -# Checks tab. +# from (install-e2e-tag.yml) +# -> linux: -> one job per combination, a real +# curl|bash install in bubblewrap +# (install-e2e-run.yml) +# -> windows: -> 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: -> natively skipped until a macos +# workflow exists +# (install-e2e-skip.yml) # -# The linux starting versions are chosen at runtime from the repo's release -# tags (scripts/sandbox/pick-release-tags.sh): newest, oldest, and a spread +# 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 -# windows starting version is the newest release tag (the published -# installer has no commit pin -- it installs what `main` serves, which the -# harness stages to that tag). +# 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) @@ -68,8 +75,8 @@ concurrency: cancel-in-progress: true jobs: - # Which released versions do we test updating FROM? Resolved once and shared - # by every linux leg, so all routes cover the same set. + # 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 @@ -93,79 +100,24 @@ jobs: echo "Testing updates from: $tags" echo "tags=$tags" >> "$GITHUB_OUTPUT" - # Expand the {os, install-method, update-method} support matrix - # (scripts/sandbox/generate-e2e-matrix.mjs) into one job per combination. - # Implemented combos land in the linux/windows matrices below; everything - # the spec declares but CI can't drive yet lands in `skipped`, so the TODO - # surface is visible in every run instead of buried in comments. - generate-matrix: - name: Generate combination matrix + # 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 - runs-on: ubuntu-latest - timeout-minutes: 5 - outputs: - linux: ${{ steps.gen.outputs.linux }} - windows: ${{ steps.gen.outputs.windows }} - skipped: ${{ steps.gen.outputs.skipped }} - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - sparse-checkout: scripts/sandbox/generate-e2e-matrix.mjs - sparse-checkout-cone-mode: false - - id: gen - run: | - set -euo pipefail - matrices="$(node scripts/sandbox/generate-e2e-matrix.mjs \ - --tags '${{ needs.pick-releases.outputs.tags }}' \ - --route '${{ inputs.route || 'all' }}')" - echo "$matrices" - for key in linux windows skipped; do - echo "$key=$(echo "$matrices" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>console.log(JSON.stringify(JSON.parse(d)[process.argv[1]])))' "$key")" >> "$GITHUB_OUTPUT" - done - - # Linux sandbox legs: one job per {install-method -> update-method, release - # tag} combination, each a real curl|bash install in bubblewrap followed by - # the combo's update route. - linux: - name: "linux: ${{ matrix.name }}" - if: fromJSON(needs.generate-matrix.outputs.linux).include[0] != null - needs: generate-matrix strategy: - # One combo breaking is worth knowing about even if another already - # failed, so let every leg report. + # One tag breaking is worth knowing about even if another already + # failed, so let every sub-graph report. fail-fast: false - max-parallel: 4 - matrix: ${{ fromJSON(needs.generate-matrix.outputs.linux) }} - uses: ./.github/workflows/install-e2e-run.yml + matrix: + install-ref: ${{ fromJSON(needs.pick-releases.outputs.tags) }} + uses: ./.github/workflows/install-e2e-tag.yml with: - route: ${{ matrix.route }} - install-ref: ${{ matrix.install_ref }} - - # Windows GUI legs: one job per combination of the real desktop user flow - # (website installer clicked by AutoHotkey, update driven through the app). - windows: - name: "windows: ${{ matrix.name }}" - if: fromJSON(needs.generate-matrix.outputs.windows).include[0] != null - needs: generate-matrix - strategy: - fail-fast: false - matrix: ${{ fromJSON(needs.generate-matrix.outputs.windows) }} - uses: ./.github/workflows/install-e2e-windows-run.yml - with: - route: ${{ matrix.route }} - install-ref: ${{ matrix.install_ref }} - - # Declared-but-unimplemented combinations: one job per combo, each a - # NATIVE skip (grey, conclusion=skipped, no runner). Matrix context is not - # available in job-level `if`, so the native skip lives in the called - # workflow instead: its inner job is gated on an input that defaults to - # false and is never passed. Implementing a combo moves it into - # IMPLEMENTED in the generator. - skipped: - name: "todo: ${{ matrix.os }}: ${{ matrix.install }} -> ${{ matrix.update }}" - if: fromJSON(needs.generate-matrix.outputs.skipped).include[0] != null - needs: generate-matrix - strategy: - fail-fast: false - matrix: ${{ fromJSON(needs.generate-matrix.outputs.skipped) }} - uses: ./.github/workflows/install-e2e-skip.yml + install-ref: ${{ matrix.install-ref }} + route: ${{ inputs.route || 'all' }} diff --git a/scripts/sandbox/generate-e2e-matrix.mjs b/scripts/sandbox/generate-e2e-matrix.mjs index 21b257c5e3..5522cbcc75 100644 --- a/scripts/sandbox/generate-e2e-matrix.mjs +++ b/scripts/sandbox/generate-e2e-matrix.mjs @@ -7,15 +7,19 @@ * per combination -- and everything else lands in a "skipped" matrix so the * TODO surface stays visible in every run instead of buried in comments. * - * Used by .github/workflows/install-e2e.yml: + * Used by .github/workflows/install-e2e-tag.yml, which runs it once per + * starting release tag: * * node scripts/sandbox/generate-e2e-matrix.mjs \ - * --tags '["v2026.8.3","v2026.3.12"]' --route all + * --tags '["v2026.8.3"]' --route all * * Prints JSON: { linux: {include:[...]}, windows: {include:[...]}, - * skipped: {include:[...]} }. The linux include entries carry - * {name, route, install_ref} for install-e2e-run.yml; windows entries carry - * {name, route, install_ref} for install-e2e-windows-run.yml. + * skipped: {include:[...]} }. linux entries carry {name, route, + * install_ref} for install-e2e-run.yml; windows entries carry {name, + * install_method, update_method, install_ref} for + * install-e2e-windows-run.yml (which itself natively skips method pairs it + * cannot drive yet); skipped entries are the macOS combos (no workflow + * exists at all). */ import path from 'node:path'; @@ -84,18 +88,20 @@ const KNOWN_METHODS = new Set([ const ALLOWED_VERSIONS = new Set(['latest']); /** - * The combinations CI actually runs today, and which reusable workflow + - * route input drives each. Everything in SPEC but not here is emitted as a - * skipped combo. Install ids include the expanded @version suffix. + * How each implemented combination maps onto a reusable workflow. + * + * linux: every combo is implemented; `route` is install-e2e-run.yml's input. + * windows: ALL combos dispatch to install-e2e-windows-run.yml -- the run + * workflow itself natively skips (grey) any {install, update} method pair + * it cannot drive yet, so "which windows methods work" lives THERE, next + * to the driver, not here. + * macos: nothing is implemented; combos surface as native skips in the + * caller via install-e2e-skip.yml without burning a tag fanout. */ -export const IMPLEMENTED = [ - // scripts/dev-sandbox.sh legs (install-e2e-run.yml, bubblewrap sandbox). - { os: 'linux', install: 'curl-bash', update: 'hermes-update', workflow: 'linux', route: 'update' }, - { os: 'linux', install: 'curl-bash', update: 'curl-bash', workflow: 'linux', route: 'installer' }, - // Real Windows GUI flow (install-e2e-windows-run.yml): website exe clicked - // by AutoHotkey, Update now clicked by Playwright. - { os: 'windows', install: 'desktop-installer@latest', update: 'desktop-app', workflow: 'windows', route: 'desktop' }, -]; +export const LINUX_ROUTES = { + 'hermes-update': 'update', + 'curl-bash': 'installer', +}; function validateEntry(os, kind, entry) { if (typeof entry.method !== 'string' || !KNOWN_METHODS.has(entry.method)) { @@ -150,25 +156,19 @@ export function generateEnvironments(spec) { return envs; } -function findImplementation(env) { - return IMPLEMENTED.find( - (m) => m.os === env.os && m.install === env.install && m.update === env.update, - ); -} - /** Mirror of install-e2e.yml's dispatch `route` choice. */ -function routeWants(route, env, impl) { +function routeWants(route, env) { switch (route) { case 'all': return true; case 'both': - return env.os === 'linux' && Boolean(impl); + return env.os === 'linux'; case 'update': - return impl?.route === 'update'; + return env.os === 'linux' && env.update === 'hermes-update'; case 'installer': - return impl?.route === 'installer'; + return env.os === 'linux' && env.update === 'curl-bash'; case 'windows-desktop': - return env.os === 'windows' && Boolean(impl); + return env.os === 'windows'; default: throw new Error(`unknown route filter: ${JSON.stringify(route)}`); } @@ -176,39 +176,63 @@ function routeWants(route, env, impl) { /** * Split the combinations into per-workflow matrices. - * Linux combos fan out further over `tags` (which released version the leg - * installs first); Windows install versions come from the method id instead - * (desktop-installer@latest = whatever the website serves). + * + * `tags` (the released versions we test updating FROM) is the OUTER axis, + * applied to every OS with a driving workflow: for each tag, for each + * combo, one job that installs the tag and updates to HEAD. Linux legs + * pass the tag as install-ref for the sandbox to install; Windows legs + * pass it as the ref the staged serve.git parks `main` at -- the published + * installer has no commit pin, so that IS the installed version. + * + * Skip placement follows where the knowledge lives: macOS combos are known + * unimplementable HERE (no workflow exists), so they go to the `skipped` + * matrix -- one native grey check per combo, not multiplied by tags, since + * no tag would change the outcome. Windows combos ALL dispatch to the run + * workflow, which natively skips the method pairs it cannot drive. */ export function buildMatrices(envs, { tags = [], route = 'all' } = {}) { const linux = []; const windows = []; const skipped = []; + const needTags = () => { + if (tags.length === 0) { + throw new Error('a combo with a driving workflow was selected but no --tags given'); + } + }; for (const env of envs) { - const impl = findImplementation(env); - if (!routeWants(route, env, impl)) continue; - if (!impl) { - skipped.push({ ...env, reason: 'not implemented yet' }); - } else if (impl.workflow === 'linux') { - if (tags.length === 0) { - throw new Error(`linux combo ${env.install} -> ${env.update} selected but no --tags given`); + if (!routeWants(route, env)) continue; + if (env.os === 'macos') { + skipped.push({ ...env, reason: 'no macos workflow yet' }); + continue; + } + if (env.os === 'linux') { + const linuxRoute = LINUX_ROUTES[env.update]; + if (!linuxRoute) { + throw new Error(`linux update method ${JSON.stringify(env.update)} has no route mapping`); } + needTags(); for (const tag of tags) { linux.push({ - name: `${env.install} @ ${tag} -> ${env.update}`, - route: impl.route, + name: `${env.install} -> ${env.update}`, + route: linuxRoute, install_ref: tag, }); } - } else if (impl.workflow === 'windows') { - windows.push({ - name: `${env.install} -> ${env.update}`, - route: impl.route, - install_ref: 'auto', - }); - } else { - throw new Error(`unknown workflow ${JSON.stringify(impl.workflow)} for ${env.os}`); + continue; } + if (env.os === 'windows') { + needTags(); + for (const tag of tags) { + windows.push({ + name: `${env.install} -> ${env.update}`, + install_method: env.install, + update_method: env.update, + install_ref: tag, + }); + } + continue; + } + throw new Error(`no workflow routing for os ${JSON.stringify(env.os)}`); } return { linux: { include: linux }, diff --git a/tests/install/windows-desktop-gui-e2e.ps1 b/tests/install/windows-desktop-gui-e2e.ps1 index 06e87f0a11..e95aa783d0 100644 --- a/tests/install/windows-desktop-gui-e2e.ps1 +++ b/tests/install/windows-desktop-gui-e2e.ps1 @@ -71,12 +71,13 @@ param( [ValidateSet("stage", "install-gui", "update-gui", "all")] [string]$Phase = "all", - # Update route to exercise in the update-gui phase. Only "desktop" (the - # app's own Update button) is implemented; "update" (CLI `hermes - # update`) and "installer" (re-run the bootstrap exe) are declared arms - # so the workflow surface is stable when they land. - [ValidateSet("desktop", "update", "installer")] - [string]$Route = "desktop", + # Update method to exercise in the update-gui phase, named by the same + # ids the combination generator (scripts/sandbox/generate-e2e-matrix + # .mjs) uses. Only "desktop-app" (the app's own Update button) is + # implemented; the others are declared arms so the surface is stable + # when they land. + [ValidateSet("desktop-app", "hermes-update", "desktop-installer-rerun@latest", "irm-iex")] + [string]$Route = "desktop-app", # The OLD version: the ref served as `main` while the installer runs, # i.e. what the user starts on. The published Hermes-Setup.exe carries @@ -657,20 +658,25 @@ function Invoke-GuiUpdateDesktopRoute([string]$TargetSha) { function Invoke-PhaseUpdateGui { $state = Read-State switch ($Route) { - "desktop" { + "desktop-app" { Invoke-GuiUpdateDesktopRoute $state.current } - "update" { + "hermes-update" { # TODO: run `hermes update` from the installed venv -- the CLI - # route. Needs the same completion/sha/relaunch asserts minus - # the app-quit dance. - throw "route 'update' (CLI hermes update) is not implemented yet" + # route. Needs the same completion/sha asserts minus the + # app-quit dance. + throw "update method 'hermes-update' is not implemented yet" } - "installer" { + "desktop-installer-rerun@latest" { # TODO: re-run the bootstrap Hermes-Setup.exe over the existing # install (its --update flow jumps straight to progress and # runs unattended). - throw "route 'installer' (re-run bootstrap exe) is not implemented yet" + throw "update method 'desktop-installer-rerun@latest' is not implemented yet" + } + "irm-iex" { + # TODO: re-run the irm | iex one-liner over the existing + # install. + throw "update method 'irm-iex' is not implemented yet" } } }