Skip to content

feat(update): add --release to pin tagged releases (#34514) - #34528

Open
hustshawn wants to merge 1 commit into
NousResearch:mainfrom
hustshawn:feat/update-release-channel
Open

feat(update): add --release to pin tagged releases (#34514)#34528
hustshawn wants to merge 1 commit into
NousResearch:mainfrom
hustshawn:feat/update-release-channel

Conversation

@hustshawn

Copy link
Copy Markdown

Summary

Adds hermes update --release [TAG] to pin an install to a tagged release instead of tracking a branch tip.

  • hermes update --release / --release latest → install the newest vYYYY.M.D tag
  • hermes update --release v2026.5.29 → pin to that exact tag
  • hermes update --check --release [TAG] → report without installing
  • The tag is checked out as a detached HEAD, so the pin survives across runs

Fixes the core complaint in #34514 that a tag-pinned (detached-HEAD) install gets forced back onto main by hermes update: when --release is used we skip the branch-switch entirely and land on the tag, and we never drag a pin forward to upstream/main.

Relationship to #24938

This is complementary to @Sunwo0u's #24938 (opt-in --channel {main,release}), not a replacement. That PR adds a "track the latest release" channel; this PR adds specific-tag pinning and the respect-the-pin / detached-HEAD behavior the issue asks for. The two can land independently or together — there's no overlap in flags (--channel vs --release).

Behavior & safety

  • Non-breaking: with no --release flag the code path is byte-for-byte the historical branch-tracking behavior. Default is unchanged.
  • Reuses the existing post-pull syntax guard + auto-rollback (the PR feat(kanban): stale detection for running tasks in dispatcher (#23790) #28452 incident guard) on the release path — a bad tag still rolls back cleanly.
  • Pre-update snapshot/backup, dependency reinstall, skills sync, config migration and gateway restart all run unchanged after the checkout.
  • --release and --branch are mutually exclusive (clear error rather than silent precedence).
  • The Windows ZIP fallback refuses --release (it can't honor a tag pin reliably), mirroring how it already refuses --branch=<non-main>.

Implementation

  • Helpers: _resolve_release_request, _latest_release_tag (version-sorted), _tag_exists, _head_is_at_or_after.
  • _cmd_update_impl: resolve the target tag after a --tags fetch, skip branch-switch for pins, git checkout --detach <tag>, reuse the guard/rollback.
  • _cmd_update_check_release for the --check --release reporting path.

Test plan

  • python -m pytest tests/hermes_cli/test_cmd_update.py -q30 passed (21 pre-existing + 9 new)
  • Related suites green: test_update_autostash.py, test_update_yes_flag.py, test_cmd_update_docker.py, test_update_concurrent_quarantine.py
  • End-to-end smoke against this repo's real tags:
    • update --check --release → "Already on release v2026.5.29"
    • update --check --release v9999.1.1 → "Release tag 'v9999.1.1' not found"
  • New tests cover: flag parsing (absent/bare/blank/explicit), latest-pin detached checkout, specific-tag pin, already-installed short-circuit, unknown-tag exit, and --release/--branch mutual exclusion.

Refs #34514. Complements #24938.

hermes update --release [TAG] pins the install to a release tag instead
of tracking a branch tip. Bare --release (or --release latest) installs
the newest vYYYY.M.D tag; --release <tag> pins an exact version.

The tag is checked out as a detached HEAD so the pin survives across
runs — fixing NousResearch#34514's complaint that pinned installs get forced back
onto main. Reuses the existing post-pull syntax guard + rollback, skips
the fork->upstream sync, and reports via --check --release. --release and
--branch are mutually exclusive; the Windows ZIP fallback refuses it.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard labels May 29, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing the explicit release-pin path. The underlying problem is still present on current main: a detached checkout is switched to the default update branch at hermes_cli/main.py:9633-9647, and tests/hermes_cli/test_update_autostash.py:625-639 covers that behavior.

Problems

  • hermes_cli/main.py:9232 treats any descendant of the requested tag as already pinned. On main after vX, merge-base --is-ancestor vX HEAD succeeds and returns before the proposed detached checkout, so --release vX cannot downgrade/pin exactly as documented.
  • The implementation targets an older update layout. Current main registers the parser in hermes_cli/subcommands/update.py:12-76 and the live flow has newer scoped-fetch, Windows, venv-health, and interrupted-install handling at hermes_cli/main.py:9584-9978.
  • The public update docs would need the release behavior documented; current pages only document --branch (website/docs/getting-started/updating.md:34-43, website/docs/reference/cli-commands.md:1542-1558).

Suggested changes

  • Use exact HEAD/tag identity, not ancestry, for the already-pinned fast path.
  • Rework the feature onto the current parser and update flow, then add descendant-of-tag and current-path coverage.

Automated hermes-sweeper review.

Comment thread hermes_cli/main.py
# Already on (or ahead of) the requested release? A pinned
# checkout is its own ancestor, so re-running `--release <tag>`
# is correctly reported as up to date without touching the tree.
if _head_is_at_or_after(git_cmd, PROJECT_ROOT, target_tag):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ancestry check makes --release vX a no-op whenever HEAD is a descendant of vX (for example, main after that release). Exact pinning needs an identity comparison against the peeled tag target; otherwise the later detached checkout is skipped.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/install-update Installer, updater, packaging, wheels, doctor labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants