Commit Graph
73 Commits
Author SHA1 Message Date
Teknium 7152bf009a fix(install): browser opt-out uses the name every PM release accepts
install.sh --skip-browser / --no-playwright and install.ps1 -SkipBrowser
now hand PM `--without agent-browser` again instead of `--without chromium`.

The downloaded installer is the newest script, but by default it installs
the stable tag, whose PM (v0.22.5) rejects `--without chromium` with
"--without accepts only optional default packages (agent-browser,
cua-driver)" and exits 1, failing the whole install. The new PM already
reads `agent-browser` as `chromium` (pm.defaults.RETIRED_DEFAULTS), so the
legacy spelling works against both the stable tag and main. Direct
`hermes pm install --without chromium` stays valid on the new PM.
2026-10-23 01:43:17 -07:00
Teknium 1ac18d2b12 refactor(pm): drop the agent-browser package; Chromium is a default package in its own right
- pm: AgentBrowser class + lock entry gone; Chromium default=True and its env
  exports only PLAYWRIGHT_BROWSERS_PATH (Playwright launches without an
  executable path still need it). A recorded or passed --without agent-browser
  maps to chromium (pm.defaults.RETIRED_DEFAULTS), and pm gc forgets the stale
  agent-browser fact so its store entry is reclaimed.
- install.sh --skip-browser/--no-playwright and install.ps1 -SkipBrowser pass
  --without chromium; undo hint is hermes pm install chromium.
- Docker bakes /etc/hermes/chromium-executable-path; stage2-hook validates it
  and exports nothing; the sandbox-desktop image drops the agent-browser npm
  install and keeps its Playwright Chromium.
2026-10-23 01:41:25 -07:00
Teknium 99cc712bf3 fix(update): a damaged .git no longer strands paused gateways
When a checkout's .git exists but git rejects it (empty HEAD, a gitdir:
file pointing nowhere), every `hermes update` failed at the fetch with a
generic "Failed to fetch updates" and, on Windows, left the paused
gateways stopped with "the next `hermes` command restarts them". No
later command could: the whole-tree gate held the set while HEAD was
unreadable, so bots stayed down until someone repaired the checkout by
hand.

- tree gate: a set whose every pause already found HEAD unreadable and
  that never bound a tree move resumes. The gateways were serving that
  exact tree and no update step wrote it. A recorded move, or a HEAD
  that was readable at the pause, still holds the set.
- fetch failure: "not a git repository" now says the checkout's git data
  is damaged and prints the installer one-liner that repairs it.
- install.sh: a dangling `.git` file is moved aside and re-cloned like a
  commit-less one, matching install.ps1 (Test-Path already matched it);
  both installers' warning names a damaged .git, not only an
  interrupted clone.
2026-10-21 16:57:37 -07:00
unsupportedpastels b65e078cdc fix(install): keep "git is required" on macOS when git is not on PATH
stage_prerequisites sent every Mac whose git_runs failed into
install_macos_git, and git_runs is also false when git is missing from
PATH. So a Mac with no git got the Command Line Tools message instead of
"git is required", contradicting the "no git on PATH: unchanged" row and
failing test_real_install_sh_ladder_leaves_only_closed_tokens on macOS.
Gate the macOS path on git being on PATH.

That ladder test was gated with a bare skipif, so the macOS lane never
imported it; give it platforms("posix") per tests/AGENTS.md.

Also from review:
- Check the developer tools' git before Homebrew, so a git that fails
  with the tools present (unaccepted Xcode license, broken ~/.gitconfig)
  reports git's own error instead of running brew install git first.
- Log why xcode-select --install failed (e.g. no GUI session over SSH)
  before the wait, instead of discarding its output.
- Parametrize the dialog-then-continue and fail-fast cases.
2026-10-16 09:05:04 -04:00
unsupportedpastels e6bfe7a5f1 fix(install): treat macOS developer tools as present only when they hold git
A macOS upgrade can leave /Library/Developer/CommandLineTools behind with its contents gone; xcode-select -p still names it, so the folder-exists check skipped the install dialog for the most common broken-git Mac. Require $dev/usr/bin/git instead, which both the CLT folder and Xcode's Contents/Developer hold.

When xcode-select --install refuses over such a leftover folder, no dialog opens: fail at once with the reinstall remedy instead of waiting 15 minutes.
2026-10-16 09:05:04 -04:00
unsupportedpastels 9b3d61732a fix(install): report git errors when macOS developer tools are already present; wait under Hermes-Setup
When xcode-select -p names an existing developer directory (e.g. Xcode installed, license never accepted), no install dialog will open: skip the dialog and the wait and fail with git's own error instead of waiting 15 minutes and suggesting the wrong fix.

Hermes-Setup drives stages with --non-interactive --json; it has no stage timeout, a Cancel button, and streams log lines as progress, so wait for the Command Line Tools there too. Only a plain headless --non-interactive run fails fast.

Run Homebrew with HOMEBREW_NO_AUTO_UPDATE=1 (auto-update runs the broken git and can take minutes) through run_logged instead of discarding its output.
2026-10-16 09:05:04 -04:00
unsupportedpastels 24204fd196 test(install): run the macOS git tests on macOS, beside the installer tests
Move the prerequisite tests to tests/scripts/install/ and drop the uname stub: macOS cases are platforms("macos"), the Linux case platforms("linux"). The probe PATH holds only system dirs so a host Homebrew never runs.
2026-10-16 09:05:04 -04:00
Maciej Lisiewski 19f6f1482c ci: fix home path issue specific to blacksmith runners 2026-10-15 05:23:51 -07:00
TekniumandLee Jackson 3b63f7b0a4 fix(install): Windows installs under a long profile path no longer fail "Clone succeeded, but checkout failed"
install.ps1 clones into %LOCALAPPDATA%\hermes\.hermes-clone-<pid>-<random>\tree
before publishing, which puts the deepest tracked file (a 159-character zh-Hans
docs path) past MAX_PATH once LOCALAPPDATA is longer than ~55 characters. Git
for Windows then refuses the file ("Filename too long"), every clone attempt
and the deferred checkout fail the same way, and the user sees "fatal: unable to
checkout working tree" from both the PowerShell one-liner and Hermes-Setup.

Clone with --config core.longpaths=true so the setting lands before the first
checkout and stays in the repo for `hermes update`; an installer rerun over an
existing checkout sets it too.

Salvages the long-path half of #72744 by @Imagineer99, written against the
pre-rewrite installer.

Co-authored-by: Lee Jackson <samleejackson0@gmail.com>
2026-10-12 16:42:41 -07:00
Teknium 3c758ec967 fix(install): git failures name git's own error, so Hermes-Setup's screen shows the cause
A failed clone, fetch, checkout or reset reported only the step ("git clone
failed; no checkout published"). Hermes-Setup shows the stage reason as-is, so a
user behind a proxy or a DNS block saw no cause and every report needed a
round-trip for bootstrap-installer.log.

run_logged / Invoke-Logged now keep the failed command's first fatal:/error:
line (else its last line, capped at 240 chars) and the git Fail sites append it:
"git clone failed; no checkout published: fatal: unable to access ...: Could not
resolve host: ...". Both output modes are covered: redirected (the Hermes-Setup
driver, CI) and the interactive status line. stderr still streams as before.

Script-only: the website Hermes-Setup.exe downloads install.ps1 / install.sh
from main on every run and on Retry, so copies already in the field show it.
2026-10-11 21:42:15 -07:00
Maciej Lisiewski ae0994cd15 ci: re-fix some linter errors after a rebase 2026-10-10 15:20:14 +02:00
Maciej Lisiewski 0006a5e229 ci: fix PLW1509, PLW1510, PLW2101 linter errors 2026-10-10 15:20:13 +02:00
kshitijk4poor dcabf76310 fix(install): a pinned fresh clone pins, with retries, before it publishes
With --no-checkout the file download moved out of the retried clone into the single unretried pin checkout, which ran after the empty clone was already published: a throttled pin left INSTALL_DIR with no index, and the next rerun stashed a 'delete every file' autostash. The pin now runs in the staged tree inside the deferred-checkout retry loop (which also stops materializing the tip first), and nothing is published until it succeeds.
2026-10-08 15:20:58 -07:00
kshitijk4poor 2f6716dd7d perf(install): a pinned install checks out only the pin, not the branch tip first 2026-10-08 15:20:58 -07:00
Teknium dbbf98371d perf(install): a pinned install.ps1 clone checks out only the pin, before it publishes
Mirrors the install.sh change from #135092 on Windows. With -Commit (what
the desktop bootstrap passes), the fresh clone runs with --no-checkout and
the pin is checked out with retries inside the staging dir, so the branch
tip is never materialized first and a refused or failed pin never publishes
a checkout. A rerun over an existing checkout keeps pinning after the fetch.
2026-10-08 15:15:33 -07:00
Maciej Lisiewski d76cce4f11 ci: apply all automatic linter fixes available 2026-10-08 17:22:54 +02:00
Teknium adeecd8638 test(install): run the treeless re-run case through install.ps1 too 2026-10-05 17:32:54 -07:00
Teknium 8627946148 test(install): start the treeless re-run fixture from a real treeless clone
The bare origin had no main HEAD, so the clone had no commits and stage_repository
re-cloned it as an interrupted install; a plain-path clone also ignores --filter.
The test passed on the unfixed installer. Use -b main and a file:// URL, and assert
the checkout was converted in place.
2026-10-05 17:32:54 -07:00
Yuan Li e7de41ca6c test(install): cover treeless checkout migration 2026-10-05 17:32:54 -07:00
Teknium 1d9bb51f6b fix(install): clone blobless, convert treeless checkouts once
Installers have cloned with --filter=tree:0 since c3e9ac87a7. A treeless
clone keeps commits only, and git requests a missing tree without telling
the server which trees it already has, so every checkout and every
path-filtered history walk re-downloads complete directory snapshots
(~0.58 MB per commit for this repo). GitHub's own guidance is that
treeless clones are for throwaway CI builds, not long-lived checkouts;
the blobless layout keeps every commit and tree local and fetches only
file contents on demand.

- install.sh / install.ps1 clone --filter=blob:none (same release tags,
  --commit pins and ancestor checks the treeless switch was made for).
- hermes update (post-update step) converts an existing tree:0 checkout once with
  `git fetch --refetch --filter=blob:none origin` (~120 MB, ~8 s
  from GitHub) and records the new filter only after it succeeds, so a
  failed refetch retries next update.
- The shallow-heal conversion unshallows as blob:none instead of tree:0.
- checkout_contains asks git with GIT_NO_LAZY_FETCH: a recorded SHA the
  checkout lacks is "not contained", never a download of that commit's
  whole history.
2026-10-05 01:30:17 -07:00
ethernet 71d120be6b fix(install): wire ~/.local/bin into ~/.bash_login for login-bash users
A login bash reads only the first of ~/.bash_profile, ~/.bash_login and
~/.profile. wire_shell_path always wrote ~/.profile and ~/.bash_profile
when present, so a user whose only login file is ~/.bash_login never saw
~/.local/bin on PATH. Treat ~/.bash_login like ~/.bash_profile: append the
guarded PATH line when the file exists, never create it (creating
~/.bash_profile would shadow it).

The test helper resolves bash by absolute path so the login-PATH tests run
on hosts without /bin/bash (NixOS).
2026-10-02 19:55:57 -04:00
kshitijk4poor 18d125cc1b test(install): give install.ps1 subprocess tests the stage budget their siblings use
test_install_ps1_json_failure_frame runs a full `install.ps1 -Stage repository
-Json` under Windows PowerShell with a 30 s timeout. On the windows-arm64 lane
that run takes 25.0 s when green and 68.8 s when slow, so it times out
intermittently (twice on PR #130298, a gateway-only change). The same file
invocation in test_install_ps1_staged_git uses 240 s; use that budget here and
for staged_git's dot-sourced Ensure-Git probe, which parses the same script under
the same 30 s limit.

Also read the preserved user file with utf-8-sig, the BOM-tolerant read the
Windows-footgun lint requires for this file.
2026-10-01 22:38:59 +05:30
kshitijk4poor 7b20b72269 fix(install): installer rerun heals a partial clone stuck on the pack-objects crash
An install whose updater dies on the git 2.53+ pack-objects BUG (#124272)
never fetches the release that heals it: the fetch runs in the installed
code. The installer rerun (install.sh / install.ps1, served fresh) is the
path that runs new code, so when origin is a promisor remote it now marks
the checkout's unmarked packs before its existing-checkout fetch. Marking is
idempotent and never rewrites objects, so it runs up front rather than as a
retry that would first print a failed-fetch report.
2026-09-29 15:24:15 +05:30
Gille ea114c3e98 fix(install): preserve apostrophe paths in Windows runtime launch 2026-09-29 13:19:27 +05:30
teknium1 b63c138d78 fix: Hermes never falls back to the user's node/npm/npx/uv
Maintainer ruling: only Hermes and only its packaged package managers
(uv/pip/node/npm) are ever used; no PATH fallback when the managed tool is
missing, no "prefer the user's if new enough".

- hermes_constants.find_node_executable: node/npm/npx resolve to PM's
  installed copy or None. Every caller already pm.ensure()s on None, so a
  missing runtime is now provisioned instead of silently borrowing the
  user's Node (native-addon ABI / npm cache mismatches).
- agent/lsp/install._install_npm: pm.ensure('npm') when PM npm is absent,
  instead of failing over to whatever npm is on PATH.
- gateway._append_node_dir_for_service: stop baking the invoker's PATH node
  dir into generated systemd/launchd units.
- main_install_repair._resolve_node_runtime_npm: drop the PATH re-scan for
  another npm.
- source_build.source_product_current: run the freshness reader only with
  PM's node.
- doctor: Node/npm rows and npm audit use PM's copies (Termux APT distro
  keeps its system Node).
- install.sh ensure_uv / install.ps1 Get-Uv: always stage the pinned uv
  artifact; delete the "uv on PATH if new enough" developer shortcut.
2026-09-27 22:04:26 -07:00
teknium1 04ea129bbf test(install): drop the dead bzip2-less tar stub from the Get-PinnedGit E2E
Get-PinnedGit no longer invokes tar at all (no $inboxTar seam exists since
the PortableGit pin), so the C# stub tar.exe and the Invoke-Native override
that bound it were never reached: the test compiled a stub for nothing and
its docstring described a mechanism the installer does not have. Keep the
real-download Get-PinnedGit run under a bzip2-less PATH and the git.exe +
bash.exe contract checks; that is what the test proves.
2026-09-28 02:04:08 +05:30
finn763 6d9ae2da6f fix(install): stage pinned git without tar/bzip2 (PortableGit SFX)
Windows 10 boxes whose System32 tar.exe cannot run the bzip2 filter die
at stage=prerequisites with "unable to run program bzip2 -d" while
extracting the pinned Git-2.53.0.3 tar.bz2 (#122512). Repin git for
both win32 targets to git-for-windows' PortableGit self-extracting 7z,
which carries its own extractor and the bundled usr/bin/bash.exe:

- pm/lock.json: new artifact urls + sha256 (the pin authority)
- scripts/install.ps1: generated fragment regenerated; Get-PinnedGit
  downloads the SFX and waits on it explicitly (the stub is a
  GUI-subsystem exe, so PowerShell's & does not wait); the System32
  tar invocation and its msys symlink excludes go away
- pm/packages.py: Git.fetch_url/Git.unpack run the self-extractor
  after the sha256-verified download
- pm/store.py: drop the now-callerless git_msys branch of extract_tar
- tests: RED->GREEN test runs the real Get-PinnedGit against a
  bzip2-less System32 tar.exe stub with no bzip2 on PATH; the three
  obsolete tar-contract tests and the install.ps1/PM msys-links parity
  test are replaced by a no-external-decompressor contract test

Closes #122512

(cherry picked from commit 4915304213495d3207ec6cd659e57cd16ef08d60)
2026-09-28 02:04:08 +05:30
Yuan Li e40637dcba test(install): drive -SkipSetup alone through the needs-input stage frame; sync zh-Hans doc
Address review on #125371: the binding test passed the script-level
-NonInteractive alongside the alias, so deleting the fold line kept it
green. The new test runs -Stage setup -Json with only -SkipSetup and
asserts the skipped/needs-user-input frame, which only the fold can
produce (without it the stage dispatches interactively and fails under
a non-interactive host). Also document the deprecated alias in the
zh-Hans windows-native guide, which still said -SkipSetup is rejected.

(cherry picked from commit b97fc4561af918910b8b2b9bf728e501b89aeb27)
2026-09-28 00:58:16 +05:30
Yuan Li 14f57ec50b test(install): regression coverage for -SkipSetup binding and ffmpeg pin liveness
- tests/scripts/install/test_install_ps1_skipsetup_alias.py: Windows-lane
  test driving the real installer with -SkipSetup -ShowResolvedPaths —
  the side-effect-free contract that still exercises parameter binding.
  Red on main (NamedParameterNotFound), green with the alias restored.
- tests/pm/test_ffmpeg_pin_liveness.py: walks every BtbN URL in the
  lockfile against the live upstream (HEAD); catches the next pin rot
  before a fresh install 404s. Skips without network.

(cherry picked from commit 736b7ff4c1; ffmpeg lock/liveness hunks dropped, superseded by #125468)
2026-09-28 00:58:16 +05:30
teknium1 f5c3c7d70a test: trim musl coverage to two invariants
Keep one PM invariant (a musl ELF userland resolves to linux-<arch>-musl
and the default closure is satisfiable with musl-native artifacts) and
one installer invariant (uv_bootstrap_target picks musl uv from a musl
ELF interpreter even when ldd says glibc). Drop the lock-content and
URL-shape change-detectors.
2026-09-27 03:24:39 -07:00
JoaoMarcos44 87d1243623 fix(installer): detect musl uv when ldd is unavailable
Fall back to the musl loader if ldd cannot identify libc, while honoring
an explicit GNU libc report on hosts with a secondary musl toolchain.
Cover both paths against the pinned uv URL and digest.

Refs: #123682
2026-09-27 03:24:39 -07:00
JoaoMarcos44 24487f3db6 fix(pm): keep musl installs on compatible runtime artifacts
Skip glibc-linked FFmpeg on musl in both default install and update roots.
Select musl uv during the standalone shell bootstrap, and let the native
userland resolve libc before bootstrap Python build metadata.

Add regression checks for the closure, target precedence, and installer pins.
2026-09-27 03:24:39 -07:00
ymat19 5cd9dbc261 fix(install): recognize bare PATH= assignments and keep the appended line idempotent
wire_shell_path's existing-setup regex required a character before PATH=, so it missed bare assignments such as Fedora's ~/.bashrc (    PATH="$HOME/.local/bin:$HOME/bin:$PATH") and Debian's ~/.profile. The installer then appended its own line to .bashrc, .profile and .bash_profile, and because Fedora's .bash_profile sources .bashrc, login shells got ~/.local/bin on PATH several times.

Match bare assignments too, and make the appended line a no-op when PATH already contains ~/.local/bin.
2026-09-27 02:50:10 -07:00
Hermes Agent 980318e689 fix(install): detect arch-suffixed desktop builds on repair/upgrade reruns (#94703)
electron-builder names the unpacked output <os>-unpacked on x64 and
<os>-<arch>-unpacked elsewhere (linux-arm64-unpacked, win-arm64-unpacked).
install.sh desktop_product_present and install.ps1
Test-DesktopProductPresent only listed the x64 names, so a rerun on an
ARM64 desktop install skipped the desktop rebuild and left a bundle built
from the previous code. List every unpacked dir main_desktop.py already
resolves, in both installers.
2026-09-26 20:29:47 -05:00
ethernet 90963a6f74 install: --skip-browser / -SkipBrowser become PM's persisted opt-out
The flag used to exit 1 as retired. Now that PM installs the browser tools
by default, it maps to `pm.cli install --without agent-browser`, which later
installs and `hermes update` honour.
2026-09-24 17:27:45 -04:00
ethernet 50de548e49 fix(install.sh): arrow-style output with child noise collapsed on a terminal
The pm-era installer printed "[hermes]" lines between raw git, uv and pm
output. Restore the pre-pm installer's look (→ ✓ ⚠ ✗, banner on the full
ladder) and route every child command through run_logged: on a terminal it
shows one status line rewritten with the command's newest output line
(git clone phases via --progress), appends everything to
$HERMES_HOME/logs/install.log, and on failure prints the last 20 lines plus
the log path. CI, --verbose, HERMES_INSTALL_VERBOSE and a non-terminal
stdout (the Hermes-Setup --json driver, E2E transcripts) keep the full
stream, so their parsers see what they saw before. Errors stay on stderr.
2026-09-24 14:23:48 -04:00
ethernet e8d1fc76ec fix(install): run the installed hermes under the Restricted policy
`irm | iex` runs install.ps1 as text, which execution policy never
checks, but Invoke-InstalledHermes then dot-sourced runtime.ps1 from
disk. That is a file load, and the default Restricted policy (Windows
Sandbox, fresh machines) refused it right after "hermes command
installed". Load the helper from its text instead.

That failure hid a second one on the same path: the `$command` local
was shadowed inside Invoke-Native by its case-insensitive `$Command`
parameter, so `& $command[0]` invoked the scriptblock itself until the
call depth overflowed. Rename the local.
2026-09-24 11:56:34 -04:00
ethernet 4aadff7bd6 test(install): expect the arch-qualified bootstrap Python request
419a3cbe00 made install.ps1 ask uv for cpython-<minor>-windows-<arch>-none,
but this test still pinned the bare version.
2026-09-24 11:31:09 -04:00
ethernet bcabc5b881 fix(install): keep the PowerShell session open when an iex install fails
The documented one-liner, iex (irm .../install.ps1), runs the installer
inside the user's own session. Fail ended with exit 1, so any failed
stage closed the user's PowerShell window.

Fail now throws. The two entry points own reporting and the exit code:
-Stage prints the reason, emits the -Json frame and exits 1, as before;
the full install exits 1 only when it runs from a script file, and under
iex it prints the reason, sets LASTEXITCODE=1 and returns. A scriptblock
literal's File tells the two apart: $MyInvocation.MyCommand.Path names the
caller's script under iex.
2026-09-24 11:31:08 -04:00
ethernet 48c78669b1 test(install-e2e): capture PM desktop launches on macOS through the isolated launcher
PM launchers run python -I, which ignores PYTHONPATH, so the macOS
hermes-desktop-app-update route never loaded the sitecustomize capture hook.
Route PM launchers through pm-launch.py as the Linux driver already does, and
cover the packaged .app shape the macOS route launches.
2026-09-24 11:12:27 -04:00
ethernet ff14aaecc0 fix: reject incomplete source installer arguments before install work 2026-09-24 09:20:50 -04:00
ethernet 0fa8dd08cb test(e2e): distinguish absent and unrelated native DMG receipts 2026-09-24 07:07:08 -04:00
ethernet 7289bff2c3 test(e2e): wait for native DMG bootstrap completion across old releases 2026-09-24 07:06:42 -04:00
ethernet dcfe8f2d75 fix(install): refuse occupied Windows checkout before Git provisioning 2026-09-24 06:09:17 -04:00
ethernet 1a495c4033 fix(e2e): wait for macOS setup completion before source verification 2026-09-24 05:57:28 -04:00
ethernet ebf5f3bdfb test: seed real Git in Linux pwsh installer fixture 2026-09-24 05:45:13 -04:00
ethernet 4ffba2c882 test(ci): prepare pinned Git before Windows installer stages 2026-09-24 05:45:13 -04:00
ethernet f74ae0d862 test: capture PM desktop launches under isolated interpreter 2026-09-24 04:48:34 -04:00
ethernet 46e9f8746f fix: recognize PM source launcher in DMG install driver 2026-09-24 04:48:34 -04:00
ethernet fb7fe862ef fix: restore pinned Git in each Windows bootstrap stage 2026-09-24 04:42:28 -04:00