Files
pkgmgr/src/pkgmgr/core/credentials/providers/prompt.py
Kevin Veen-Birkenbach 0d652d995e
Some checks failed
Mark stable commit / test-unit (push) Has been cancelled
Mark stable commit / test-integration (push) Has been cancelled
Mark stable commit / test-env-virtual (push) Has been cancelled
Mark stable commit / test-env-nix (push) Has been cancelled
Mark stable commit / test-e2e (push) Has been cancelled
Mark stable commit / test-virgin-user (push) Has been cancelled
Mark stable commit / test-virgin-root (push) Has been cancelled
Mark stable commit / codesniffer-shellcheck (push) Has been cancelled
Mark stable commit / codesniffer-ruff (push) Has been cancelled
Mark stable commit / mark-stable (push) Has been cancelled
**feat(mirror,credentials): improve remote provisioning UX and token handling**
* Split mirror logic into atomic modules (remote check, provisioning, URL utils)
* Normalize Git remote URLs and provider host detection
* Add provider-specific token help URLs (GitHub, Gitea/Forgejo, GitLab)
* Improve keyring handling with clear warnings and install hints
* Gracefully fall back to prompt when keyring is unavailable
* Fix provider hint override logic during remote provisioning
2025-12-14 14:48:05 +01:00

69 lines
2.1 KiB
Python

# src/pkgmgr/core/credentials/providers/prompt.py
from __future__ import annotations
import sys
from dataclasses import dataclass
from getpass import getpass
from typing import Optional
from ..types import TokenRequest, TokenResult
def _token_help_url(provider_kind: str, host: str) -> Optional[str]:
"""
Return a provider-specific URL where a user can create/get an API token.
Keep this conservative and stable:
- GitHub: official token settings URL
- Gitea/Forgejo: common settings path on the given host
- GitLab: common personal access token path
"""
kind = (provider_kind or "").strip().lower()
h = (host or "").strip()
# GitHub (cloud)
if kind == "github":
return "https://github.com/settings/tokens"
# Gitea / Forgejo (self-hosted)
if kind in ("gitea", "forgejo"):
# Typical UI path: Settings -> Applications -> Access Tokens
# In many installations this is available at /user/settings/applications
base = f"https://{h}".rstrip("/")
return f"{base}/user/settings/applications"
# GitLab (cloud or self-hosted)
if kind == "gitlab":
base = "https://gitlab.com" if not h else f"https://{h}".rstrip("/")
return f"{base}/-/profile/personal_access_tokens"
return None
@dataclass(frozen=True)
class PromptTokenProvider:
"""Interactively prompt for a token.
Only used when:
- interactive mode is enabled
- stdin is a TTY
"""
source_name: str = "prompt"
def get(self, request: TokenRequest) -> Optional[TokenResult]:
if not sys.stdin.isatty():
return None
owner_info = f" (owner: {request.owner})" if request.owner else ""
help_url = _token_help_url(request.provider_kind, request.host)
if help_url:
print(f"[INFO] Create/get your token here: {help_url}")
prompt = f"Enter API token for {request.provider_kind} on {request.host}{owner_info}: "
token = (getpass(prompt) or "").strip()
if not token:
return None
return TokenResult(token=token, source=self.source_name)