Commit graph

3 commits

Author SHA1 Message Date
Rod Boev
2678bb1db6
fix(update): let Windows self-update replace headroom.exe (#2016)
## Description

`headroom update` currently runs pip from the same `headroom.exe`
process pip needs to replace. On Windows that leaves the launcher locked
and the upgrade fails mid-uninstall. This reroutes Windows pip-based
self-update through a short delayed Python helper that replays the
original pip argv after the current launcher exits. Other update paths
stay synchronous, and the printed upgrade command stays exact for manual
recovery.

Closes #1941

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Replace the Windows-only `cmd.exe /c` handoff for `pip` and `pip-user`
self-update with a short Python helper that replays the original pip
argv after the launcher exits.
- Keep every pip value, including extras, as argv data through the
delayed child and preserve the manual recovery output.
- Keep pipx, uv-tool, and non-Windows behavior unchanged.
- Add focused regression coverage for extras containing `&`, `|`, and
`>`.

## Testing

- [x] Unit tests pass
- [x] Linting passes
- [ ] Type checking passes
- [x] New tests added for new functionality when applicable
- [ ] Manual testing performed

### Test Output

```text
uv run pytest tests/test_cli_update.py tests/test_update_helpers.py -q
66 passed

uv run ruff check headroom/cli/update.py tests/test_cli_update.py tests/test_update_helpers.py
All checks passed

uv run ruff format --check headroom/cli/update.py tests/test_cli_update.py tests/test_update_helpers.py
3 files already formatted
```

## Real Behavior Proof

- Environment: Windows pip install
- Exact command / steps: run `headroom update --extras "foo&calc"`,
accept the prompt, and wait for the child pip output
- Observed result: Windows pip and pip-user updates now launch the
original pip command through a short delayed Python helper argv, so
extras stay one argument end to end, while pipx and non-Windows paths
remain synchronous in the focused test coverage
- Not tested: live Windows run on this host
- Scope: Windows pip self-update

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Additional Notes

The printed upgrade command stays unchanged so the manual recovery path
remains exact and copy-pasteable.

---------

Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
2026-07-14 11:52:47 -04:00
JoaoMarcos44
0750bbff4d
fix(update): prevent _core.pyd corruption on Windows when proxy is running (#1581)
## Description

On Windows, running `headroom update` while `headroom proxy` is active
can corrupt the installed package by leaving the native `_core.pyd`
extension in a partially upgraded state. This PR adds a safer update
path around the pip invocation.

Closes #1580.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Add `safe_update()` handling for Windows native-extension update
safety.
- Detect whether `_core.pyd` is locked before pip runs.
- Create a proactive backup when the file is not locked, then restore
atomically if import integrity fails.
- Warn when the proxy is running and `_core.pyd` is locked, allowing pip
to fail safely without replacing the loaded file.
- Use atomic replacement for restore paths.

## Testing

- [x] Unit tests pass
- [x] New tests added for new functionality when applicable
- [x] Manual testing performed

### Test Output

```text
Focused update-path tests and reviewer approval were completed on this PR before the governance body cleanup. The current body update is documentation-only metadata for PR governance.
```

## Real Behavior Proof

- Environment: Windows-focused Headroom development/review context.
- Exact command / steps: Reviewed the safe update flow for locked and
unlocked `_core.pyd` cases, including backup, pip invocation, import
validation, and restore behavior.
- Observed result: The update path avoids replacing a loaded native
extension and provides an atomic restore path when an unlocked update
fails validation.
- Not tested: End-to-end package publication/install from PyPI as part
of this body cleanup.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Additional Notes

This body was normalized by a maintainer after approval so the
governance parser reflects the already-reviewed PR state.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 10:36:23 -05:00
Focused Instability
26be2c39cb
feat(cli): add headroom update command and release banner (#1088)
## Description

Adds a `headroom update` self-update command and a passive "update
available" banner, so users no longer need to remember the right
`pip`/`pipx`/`uv` incantation for their environment, and long-running
proxies get nudged when they drift behind a release.

Closes #1087

## Type of Change

- [x] New feature (non-breaking change that adds functionality)
- [x] Documentation update

## Changes Made

- `headroom/cli/update.py` — `headroom update` command.
`detect_install_method()` resolves the install (git checkout, editable,
Docker, pipx, uv tool, venv/conda, `pip --user`, externally-managed
system Python per PEP 668, writable global) and builds the matching
upgrade. pip path always uses `sys.executable -m pip` so it can't touch
the wrong interpreter. Refuses with guidance where self-update is
unsafe. Flags: `--check`, `--yes`, `--pre`, `--extras`.
- `headroom/update_check.py` — best-effort PyPI check (stdlib `urllib`,
no new dep). Split into a daemon-thread probe that caches to
`~/.headroom/update_check.json` (≤ once/day) and a cache-only
`format_update_notice()`. Opt-out `HEADROOM_UPDATE_CHECK=off`; skipped
in `--stateless`, CI, Docker, checkouts.
- `headroom/cli/main.py`, `headroom/cli/__init__.py` — register
`update`; fire the background check from the group callback (skipped for
`update`).
- `headroom/cli/proxy.py` — render the one-line notice after the startup
banner (best-effort, never blocks).
- `README.md` — "Updating" section + opt-out env var.
- Tests: `tests/test_update_check.py`, `tests/test_cli_update.py`.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ pytest tests/test_update_check.py tests/test_cli_update.py -q
42 passed in 1.63s

$ ruff check headroom/cli/update.py headroom/update_check.py headroom/cli/main.py
All checks passed!

$ mypy headroom/update_check.py headroom/cli/update.py
Success: no issues found in 2 source files
```

## Real Behavior Proof

- Environment: macOS, Python 3.11, source checkout
- Exact command / steps: `python -m headroom.cli update --help`;
`detect_install_method()` in the checkout
- Observed result: command + flags render; in a checkout
`detect_install_method()` returns `kind=checkout, can_self_update=False`
("update with `git pull`") and `format_update_notice()` returns `None`
(dev tree not nagged)
- Not tested: live PyPI fetch and a real pipx/uv-tool upgrade on this
machine (covered by unit tests with mocked `urllib`/`subprocess`)

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Additional Notes

- CHANGELOG.md is release-please-managed, so it is intentionally not
hand-edited (N/A above).
- Update check uses stdlib `urllib` because `httpx` lives only in the
`[proxy]` extra — the base CLI must stay dependency-light.
2026-06-18 11:22:20 -05:00