|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Contains utilities to manage Git credentials.""" |
|
import re |
|
import subprocess |
|
from typing import List, Optional |
|
|
|
from ..constants import ENDPOINT |
|
from ._subprocess import run_interactive_subprocess, run_subprocess |
|
|
|
|
|
GIT_CREDENTIAL_REGEX = re.compile( |
|
r""" |
|
^\s* # start of line |
|
credential\.helper # credential.helper value |
|
\s*=\s* # separator |
|
(\w+) # the helper name (group 1) |
|
(\s|$) # whitespace or end of line |
|
""", |
|
flags=re.MULTILINE | re.IGNORECASE | re.VERBOSE, |
|
) |
|
|
|
|
|
def list_credential_helpers(folder: Optional[str] = None) -> List[str]: |
|
"""Return the list of git credential helpers configured. |
|
|
|
See https://git-scm.com/docs/gitcredentials. |
|
|
|
Credentials are saved in all configured helpers (store, cache, macOS keychain,...). |
|
Calls "`git credential approve`" internally. See https://git-scm.com/docs/git-credential. |
|
|
|
Args: |
|
folder (`str`, *optional*): |
|
The folder in which to check the configured helpers. |
|
""" |
|
try: |
|
output = run_subprocess("git config --list", folder=folder).stdout |
|
parsed = _parse_credential_output(output) |
|
return parsed |
|
except subprocess.CalledProcessError as exc: |
|
raise EnvironmentError(exc.stderr) |
|
|
|
|
|
def set_git_credential(token: str, username: str = "hf_user", folder: Optional[str] = None) -> None: |
|
"""Save a username/token pair in git credential for HF Hub registry. |
|
|
|
Credentials are saved in all configured helpers (store, cache, macOS keychain,...). |
|
Calls "`git credential approve`" internally. See https://git-scm.com/docs/git-credential. |
|
|
|
Args: |
|
username (`str`, defaults to `"hf_user"`): |
|
A git username. Defaults to `"hf_user"`, the default user used in the Hub. |
|
token (`str`, defaults to `"hf_user"`): |
|
A git password. In practice, the User Access Token for the Hub. |
|
See https://huggingface.co/settings/tokens. |
|
folder (`str`, *optional*): |
|
The folder in which to check the configured helpers. |
|
""" |
|
with run_interactive_subprocess("git credential approve", folder=folder) as ( |
|
stdin, |
|
_, |
|
): |
|
stdin.write(f"url={ENDPOINT}\nusername={username.lower()}\npassword={token}\n\n") |
|
stdin.flush() |
|
|
|
|
|
def unset_git_credential(username: str = "hf_user", folder: Optional[str] = None) -> None: |
|
"""Erase credentials from git credential for HF Hub registry. |
|
|
|
Credentials are erased from the configured helpers (store, cache, macOS |
|
keychain,...), if any. If `username` is not provided, any credential configured for |
|
HF Hub endpoint is erased. |
|
Calls "`git credential erase`" internally. See https://git-scm.com/docs/git-credential. |
|
|
|
Args: |
|
username (`str`, defaults to `"hf_user"`): |
|
A git username. Defaults to `"hf_user"`, the default user used in the Hub. |
|
folder (`str`, *optional*): |
|
The folder in which to check the configured helpers. |
|
""" |
|
with run_interactive_subprocess("git credential reject", folder=folder) as ( |
|
stdin, |
|
_, |
|
): |
|
standard_input = f"url={ENDPOINT}\n" |
|
if username is not None: |
|
standard_input += f"username={username.lower()}\n" |
|
standard_input += "\n" |
|
|
|
stdin.write(standard_input) |
|
stdin.flush() |
|
|
|
|
|
def _parse_credential_output(output: str) -> List[str]: |
|
"""Parse the output of `git credential fill` to extract the password. |
|
|
|
Args: |
|
output (`str`): |
|
The output of `git credential fill`. |
|
""" |
|
|
|
|
|
|
|
return sorted( |
|
set( |
|
match[0] for match in GIT_CREDENTIAL_REGEX.findall(output) |
|
) |
|
) |
|
|