Skip to content

Commit efbc040

Browse files
shaypal5claude
andcommitted
feat(publish): read HF token from ~/.config/leadforge/credentials
Add _resolve_token() and _read_token_from_credentials_file() helpers to scripts/publish_hf.py. Token lookup priority: 1. --token CLI arg 2. HF_TOKEN / HUGGING_FACE_HUB_TOKEN env var 3. ~/.config/leadforge/credentials (KEY=VALUE file, new fallback) 4. huggingface_hub stored login cache Placeholder values (lines starting with 'REPLACE_WITH') are ignored. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent a34c9f2 commit efbc040

1 file changed

Lines changed: 57 additions & 2 deletions

File tree

scripts/publish_hf.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,65 @@
5454
from __future__ import annotations
5555

5656
import argparse
57+
import os
5758
import sys
5859
from collections.abc import Sequence
5960
from pathlib import Path
6061
from typing import Final
6162

63+
_CREDENTIALS_FILE: Final[Path] = Path.home() / ".config" / "leadforge" / "credentials"
64+
65+
66+
def _read_token_from_credentials_file() -> str | None:
67+
"""Read HF_TOKEN from ``~/.config/leadforge/credentials`` if present.
68+
69+
The file uses ``KEY=VALUE`` lines; blank lines and lines starting with
70+
``#`` are ignored. Returns the first value for ``HF_TOKEN`` found, or
71+
``None`` if the file doesn't exist or the key isn't set.
72+
"""
73+
if not _CREDENTIALS_FILE.exists():
74+
return None
75+
try:
76+
for raw_line in _CREDENTIALS_FILE.read_text(encoding="utf-8").splitlines():
77+
line = raw_line.strip()
78+
if not line or line.startswith("#"):
79+
continue
80+
if "=" in line:
81+
key, _, value = line.partition("=")
82+
if key.strip() == "HF_TOKEN":
83+
token = value.strip()
84+
if token and not token.startswith("REPLACE_WITH"):
85+
return token
86+
except OSError:
87+
pass
88+
return None
89+
90+
91+
def _resolve_token(cli_token: str | None) -> str | None:
92+
"""Return the best available HF token.
93+
94+
Priority order (highest first):
95+
1. ``--token`` CLI argument
96+
2. ``HF_TOKEN`` / ``HUGGING_FACE_HUB_TOKEN`` env var
97+
3. ``~/.config/leadforge/credentials`` file
98+
4. ``None`` — falls through to ``huggingface_hub``'s own credential cache
99+
"""
100+
if cli_token:
101+
return cli_token
102+
for env_key in ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN"):
103+
value = os.environ.get(env_key)
104+
if value:
105+
return value
106+
file_token = _read_token_from_credentials_file()
107+
if file_token:
108+
print(
109+
f" token: read from {_CREDENTIALS_FILE}",
110+
file=sys.stderr,
111+
)
112+
return file_token
113+
return None
114+
115+
62116
# Make ``scripts/`` importable regardless of invocation style.
63117
sys.path.insert(0, str(Path(__file__).resolve().parent))
64118

@@ -306,10 +360,11 @@ def main(argv: Sequence[str] | None = None) -> int:
306360
args = _parse_args(argv)
307361
release_dir: Path = args.release_dir.resolve()
308362
variant: str = args.variant
363+
token: str | None = _resolve_token(args.token)
309364

310365
# --- Go-public shortcut -------------------------------------------------
311366
if args.go_public:
312-
_go_public(variant, token=args.token)
367+
_go_public(variant, token=token)
313368
return 0
314369

315370
# --- Pre-flight ---------------------------------------------------------
@@ -342,7 +397,7 @@ def main(argv: Sequence[str] | None = None) -> int:
342397
_upload(
343398
upload_dir,
344399
variant,
345-
token=args.token,
400+
token=token,
346401
private=args.private,
347402
commit_message=args.commit_message,
348403
)

0 commit comments

Comments
 (0)