Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/source/en/guides/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ You can also install the CLI using [Homebrew](https://brew.sh/):

Check out the Homebrew huggingface page [here](https://formulae.brew.sh/formula/hf) for more details.

### Updating

To upgrade to the latest version, run:

```bash
>>> hf update
```

This detects how `hf` was installed (Homebrew, standalone installer, or pip) and runs the matching update command.

By default, the CLI also prints a one-line yellow warning to stderr when a newer version is available on PyPI. To silence it (e.g. in offline CI), set `HF_HUB_DISABLE_UPDATE_CHECK=1`.

## hf auth login

In many cases, you must be logged in to a Hugging Face account to interact with the Hub (download private repos, upload files, create PRs, etc.). To do so, you need a [User Access Token](https://huggingface.co/docs/hub/security-tokens) from your [Settings page](https://huggingface.co/settings/tokens). The User Access Token is used to authenticate your identity to the Hub. Make sure to set a token with write access if you want to upload or modify content.
Expand Down
2 changes: 2 additions & 0 deletions docs/source/en/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ On Windows:
powershell -ExecutionPolicy ByPass -c "irm https://hf.co/cli/install.ps1 | iex"
```

To upgrade an existing install, run `hf upgrade` — it detects how `hf` was installed (standalone installer, Homebrew, or pip) and runs the matching command.

## Install with conda

If you are more familiar with it, you can install `huggingface_hub` using the [conda-forge channel](https://anaconda.org/conda-forge/huggingface_hub):
Expand Down
15 changes: 15 additions & 0 deletions docs/source/en/package_reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ $ hf [OPTIONS] COMMAND [ARGS]...
* `skills`: Manage skills for AI assistants.
* `spaces`: Interact with spaces on the Hub.
* `sync`: Sync files between local directory and a...
* `update`: Update the `hf` CLI to the latest version.
* `upload`: Upload a file or a folder to the Hub.
* `upload-large-folder`: Upload a large folder to the Hub.
* `version`: Print information about the hf version.
Expand Down Expand Up @@ -3763,6 +3764,20 @@ $ hf sync [OPTIONS] [SOURCE] [DEST]
* `--token TEXT`: A User Access Token generated from https://huggingface.co/settings/tokens.
* `--help`: Show this message and exit.

## `hf update`

Update the `hf` CLI to the latest version.

**Usage**:

```console
$ hf update [OPTIONS]
```

**Options**:

* `--help`: Show this message and exit.

## `hf upload`

Upload a file or a folder to the Hub. Recommended for single-commit uploads.
Expand Down
6 changes: 6 additions & 0 deletions docs/source/en/package_reference/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ Each library defines its own policy (i.e. which usage to monitor) but the core i

You can set `HF_HUB_DISABLE_TELEMETRY=1` as environment variable to globally disable telemetry.

### HF_HUB_DISABLE_UPDATE_CHECK

By default, the `hf` CLI checks PyPI for a newer release at startup (at most once every 24 hours) and prints a one-line yellow warning to stderr when one is available, suggesting `hf update`. The check is already a no-op on dev and pre-release versions.

Set `HF_HUB_DISABLE_UPDATE_CHECK=1` to skip the PyPI request and silence the warning entirely. Useful in offline CI environments or when you prefer a quieter shell output.

### HF_HUB_DISABLE_XET

Set to disable using `hf-xet`, even if it is available in your Python environment. This is since `hf-xet` will be used automatically if it is found, this allows explicitly disabling its usage. If you are disabling Xet, please consider [filing an issue and including the diagnostics](https://github.com/huggingface/xet-core?tab=readme-ov-file#issues-diagnostics--debugging) information to help us understand why Xet is not working for you.
Expand Down
95 changes: 22 additions & 73 deletions src/huggingface_hub/cli/_cli_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
from huggingface_hub import Volume, __version__, constants
from huggingface_hub.errors import CLIError
from huggingface_hub.utils import (
ANSI,
disable_progress_bars,
get_session,
hf_raise_for_status,
Expand Down Expand Up @@ -953,10 +952,10 @@ def check_cli_update(library: Literal["huggingface_hub", "transformers"]) -> Non
"""
Check whether a newer version of a library is available on PyPI.

If a newer version is found and stdin/stderr are attached to a TTY, prompt the user to update interactively.
Otherwise (non-TTY or update command cannot be determined), print a warning to stderr.
If a newer version is found, print a hint pointing at `hf update`.

If current version is a pre-release (e.g. `1.0.0.rc1`), or a dev version (e.g. `1.0.0.dev1`), no check is performed.
If `HF_HUB_DISABLE_UPDATE_CHECK` is set, the check is skipped entirely.

This function is called at the entry point of the CLI. It only performs the check once every 24 hours, and any error
during the check is caught and logged, to avoid breaking the CLI.
Expand All @@ -972,6 +971,9 @@ def check_cli_update(library: Literal["huggingface_hub", "transformers"]) -> Non


def _check_cli_update(library: Literal["huggingface_hub", "transformers"]) -> None:
if constants.HF_HUB_DISABLE_UPDATE_CHECK:
return

current_version = importlib.metadata.version(library)

# Skip if current version is a pre-release or dev version
Expand Down Expand Up @@ -1002,81 +1004,28 @@ def _check_cli_update(library: Literal["huggingface_hub", "transformers"]) -> No
else:
update_command = _get_transformers_update_command()

if sys.stdin.isatty() and sys.stderr.isatty() and update_command is not None:
_prompt_autoupdate(library, current_version, latest_version, update_command)
else:
display_cmd = " ".join(update_command) if update_command else None
update_hint = f"To update, run: {ANSI.bold(display_cmd)}" if display_cmd else ""
click.echo(
ANSI.yellow(
f"A new version of {library} ({latest_version}) is available! "
f"You are using version {current_version}." + (f"\n{update_hint}" if update_hint else "") + "\n"
),
file=sys.stderr,
)
message = f"A new version of {library} ({latest_version}) is available! You are using version {current_version}."
if update_command is not None:
match library:
case "huggingface_hub":
message += "\nTo update, run: hf update"
case _:
message += f"\nTo update, run: {' '.join(update_command)}"
out.hint(message)
Comment thread
cursor[bot] marked this conversation as resolved.


def _prompt_autoupdate(
library: str,
current_version: str,
latest_version: str,
update_command: list[str],
) -> None:
"""Interactively ask the user if they want to update, and run the update command if accepted.
def run_update() -> int:
"""Run the install-method-appropriate update command for the `hf` CLI.

After a successful update the CLI exits so the user can re-run their command with the new version.
All output goes to stderr to keep stdout clean for command output.
Raises CLIError if the installation method can't be determined.
Returns the subprocess exit code on success/failure of the update itself.
"""
display_cmd = " ".join(update_command)

click.echo("", file=sys.stderr)
click.echo(
ANSI.yellow(f" A new version of {library} is available: {current_version} → {latest_version}"),
file=sys.stderr,
)
click.echo("", file=sys.stderr)

click.echo(
ANSI.yellow(" Do you want to update now? [Y/n] ") + ANSI.gray(f"({display_cmd})") + " ",
file=sys.stderr,
nl=False,
)
try:
raw_answer = sys.stdin.readline()
except (EOFError, KeyboardInterrupt):
click.echo("", file=sys.stderr)
return

if raw_answer == "":
# EOF (e.g. Ctrl+D) — treat as cancellation, not acceptance
click.echo("", file=sys.stderr)
return

answer = raw_answer.strip().lower() # Note: if user press 'Enter', raw_answer is `\n`
if answer in ("", "y", "yes"):
click.echo("", file=sys.stderr)
click.echo(ANSI.gray(f" Running: {display_cmd}"), file=sys.stderr)
click.echo("", file=sys.stderr)
returncode = subprocess.call(update_command)
if returncode == 0:
click.echo("", file=sys.stderr)
click.echo(
ANSI.green(f" ✓ Successfully updated {library} to {latest_version}. Please re-run your command."),
file=sys.stderr,
)
raise SystemExit(0)
else:
click.echo("", file=sys.stderr)
click.echo(
ANSI.red(f" ✗ Update failed (exit code {returncode}). Please update manually."),
file=sys.stderr,
)
else:
click.echo(
ANSI.gray(f" Skipped. You can update later with: {display_cmd}"),
file=sys.stderr,
cmd = _get_huggingface_hub_update_command()
if cmd is None:
raise CLIError(
"Cannot determine how to update huggingface_hub (unknown installation method). Please update manually."
)
click.echo("", file=sys.stderr)
return subprocess.call(cmd)


def _get_huggingface_hub_update_command() -> list[str] | None:
Expand Down
3 changes: 2 additions & 1 deletion src/huggingface_hub/cli/hf.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from huggingface_hub.cli.repos import repos_cli
from huggingface_hub.cli.skills import skills_cli
from huggingface_hub.cli.spaces import spaces_cli
from huggingface_hub.cli.system import env, version
from huggingface_hub.cli.system import env, update, version
from huggingface_hub.cli.upload import UPLOAD_EXAMPLES, upload
from huggingface_hub.cli.upload_large_folder import UPLOAD_LARGE_FOLDER_EXAMPLES, upload_large_folder
from huggingface_hub.cli.webhooks import webhooks_cli
Expand Down Expand Up @@ -80,6 +80,7 @@ def app_callback(
app.command(examples=UPLOAD_LARGE_FOLDER_EXAMPLES)(upload_large_folder)

app.command(topic="help")(env)
app.command(topic="help")(update)
app.command(topic="help")(version)

app.command(hidden=True)(lfs_enable_largefiles)
Expand Down
1 change: 1 addition & 0 deletions src/huggingface_hub/cli/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@

- Use `hf <command> --help` for full options, descriptions, usage, and real-world examples
- Authenticate with `HF_TOKEN` env var (recommended) or with `--token`
- Update the CLI with `hf update` (uses the correct command for the detected install method)
"""

CENTRAL_LOCAL = Path(".agents/skills")
Expand Down
15 changes: 15 additions & 0 deletions src/huggingface_hub/cli/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@
# limitations under the License.
"""Contains commands to print information about the environment and version."""

import typer

from huggingface_hub import __version__

from ..utils import dump_environment_info
from ._cli_utils import run_update
from ._output import out


def env() -> None:
Expand All @@ -26,3 +30,14 @@ def env() -> None:
def version() -> None:
"""Print information about the hf version."""
print(__version__)


def update() -> None:
"""Update the `hf` CLI to the latest version."""
returncode = run_update()
if returncode != 0:
raise typer.Exit(code=returncode)
out.hint(
"You may also want to run `hf skills upgrade` to refresh any installed skills "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe should be update for consistency

"so your AI agent sees the latest command surface."
)
3 changes: 3 additions & 0 deletions src/huggingface_hub/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ def list_files(repo_id: str):
# Check is performed once per 24 hours at most.
CHECK_FOR_UPDATE_DONE_PATH = os.path.join(HF_HOME, ".check_for_update_done")

# Set to skip the CLI update check (PyPI query + "new version available" warning at startup).
HF_HUB_DISABLE_UPDATE_CHECK = _is_true(os.environ.get("HF_HUB_DISABLE_UPDATE_CHECK"))

# If set, log level will be set to DEBUG and all requests made to the Hub will be logged
# as curl commands for reproducibility.
HF_DEBUG = _is_true(os.environ.get("HF_DEBUG"))
Expand Down
Loading