fix(update): stop stash/restore from clobbering desktop source on managed clones (#38542)

The stash/restore cycle in the update path was observed to clobber
freshly-pulled source files (apps/desktop/ deletion -> Vite
'[UNRESOLVED_ENTRY] Cannot resolve entry module index.html'). On a
managed clone the user never edits the source tree, so any 'dirty' state
is pure git artifact (CRLF renormalization, npm lockfile churn, files
left behind when a directory was deleted upstream such as
apps/bootstrap-installer/). Stashing that and re-applying it after a pull
is fragile and unnecessary.

- hermes update (hermes_cli/main.py): on a non-fork (managed) clone,
  discard working-tree dirt via reset --hard HEAD + clean -fd instead of
  stash/apply. Forks keep the stash machinery so intentional edits
  survive. Also pin core.autocrlf=false on Windows so the dirt is never
  created (mirrors install.ps1 #38239).
- install.sh: replace the update-path stash/restore dance with a hard
  reset to origin/<branch>; the installer is a managed-only entry point.
- install.sh + install.ps1 desktop stage: prefer 'npm ci' (wipes and
  reinstalls node_modules from the lockfile) over bare 'npm install',
  which can report 'up to date' against a stale marker while node_modules
  is empty -- leaving tsc unresolved so 'npm run pack' fails.

Tests: managed clone cleans instead of stashing; fork still stashes;
existing stash tests force the stash path explicitly.
This commit is contained in:
Teknium
2026-06-03 16:40:13 -07:00
committed by GitHub
parent 1927ff217e
commit 8a19884bf3
4 changed files with 208 additions and 47 deletions

View File

@ -1093,50 +1093,24 @@ clone_repo() {
log_info "Existing installation found, updating..."
cd "$INSTALL_DIR"
local autostash_ref=""
if [ -n "$(git status --porcelain)" ]; then
local stash_name
stash_name="hermes-install-autostash-$(date -u +%Y%m%d-%H%M%S)"
log_info "Local changes detected, stashing before update..."
git stash push --include-untracked -m "$stash_name"
autostash_ref="stash@{0}"
fi
# This is a managed clone the user never edits, so any working-tree
# dirt is git artifact (CRLF renormalization, npm lockfile churn,
# files left behind when a directory was deleted upstream such as
# apps/bootstrap-installer/). The old path stashed that dirt and
# re-applied it after the pull, but the stash/restore cycle has
# clobbered freshly-pulled source files (apps/desktop/ →
# "[UNRESOLVED_ENTRY] Cannot resolve entry module index.html").
# Discard the dirt with a hard reset instead — mirrors install.ps1's
# update path. Fork users customize via `hermes update`, which keeps
# the stash machinery; the installer is a managed-only entry point.
git fetch origin
git checkout "$BRANCH"
git pull --ff-only origin "$BRANCH"
if [ -n "$autostash_ref" ]; then
local restore_now="yes"
if [ -t 0 ] && [ -t 1 ]; then
echo
log_warn "Local changes were stashed before updating."
log_warn "Restoring them may reapply local customizations onto the updated codebase."
printf "Restore local changes now? [Y/n] "
read -r restore_answer
case "$restore_answer" in
""|y|Y|yes|YES|Yes) restore_now="yes" ;;
*) restore_now="no" ;;
esac
fi
if [ "$restore_now" = "yes" ]; then
log_info "Restoring local changes..."
if git stash apply "$autostash_ref"; then
git stash drop "$autostash_ref" >/dev/null
log_warn "Local changes were restored on top of the updated codebase."
log_warn "Review git diff / git status if Hermes behaves unexpectedly."
else
log_error "Update succeeded, but restoring local changes failed. Your changes are still preserved in git stash."
log_info "Resolve manually with: git stash apply $autostash_ref"
exit 1
fi
else
log_info "Skipped restoring local changes."
log_info "Your changes are still preserved in git stash."
log_info "Restore manually with: git stash apply $autostash_ref"
fi
if [ -n "$(git status --porcelain)" ]; then
log_info "Discarding working-tree changes on managed clone before update..."
git reset --hard HEAD >/dev/null 2>&1 || true
git clean -fd >/dev/null 2>&1 || true
fi
git checkout "$BRANCH"
git reset --hard "origin/$BRANCH"
else
log_error "Directory exists but is not a git repository: $INSTALL_DIR"
log_info "Remove it or choose a different directory with --dir"
@ -2271,8 +2245,16 @@ install_desktop() {
# 1. Root workspace install so apps/desktop's deps (Electron, Vite,
# node-pty prebuilds) resolve. The browser-tools install runs in the
# repo-root package workspace, which does not pull apps/* deps.
#
# Prefer `npm ci`: it deletes node_modules and reinstalls from the
# lockfile, so it always produces a complete tree. Bare `npm install`
# can report "up to date" against a stale node_modules/.package-lock.json
# marker while node_modules is actually empty (Windows workspace-hoisting
# flake) — leaving tsc/typescript unresolved and `npm run pack`'s
# `tsc -b` failing with no obvious cause. Fall back to `npm install`
# only if `npm ci` is unavailable or the lockfile is out of sync.
log_info "Installing desktop workspace dependencies (includes Electron ~150MB, 1-3min)..."
( cd "$INSTALL_DIR" && npm install ) || {
( cd "$INSTALL_DIR" && npm ci ) || ( cd "$INSTALL_DIR" && npm install ) || {
log_error "Desktop workspace npm install failed"
return 1
}