Make token scan read-only
This commit is contained in:
@@ -156,11 +156,13 @@ Podstawowe komendy tokenow:
|
||||
./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/03
|
||||
```
|
||||
|
||||
`tokens scan` wypisuje tabele `tokens`: jeden wiersz na logiczny token.
|
||||
`tokens scan` wypisuje tabele `tokens` i niczego nie zapisuje: jeden wiersz na
|
||||
logiczny remote tokena.
|
||||
`token_ref` laczy nazwe tokena z markerem po prawej stronie: `*` oznacza, ze
|
||||
remote i `tokens.json` sa zgodne oraz uprawnienia zostaly wczytane, `R` oznacza
|
||||
token tylko w remote, a `S` token tylko w `tokens.json`. Kolumny `scope`, `org`
|
||||
i `repo` sa maskami uprawnien; bez `*` maja wartosc `?????` albo `????`.
|
||||
Zapis z remote do `tokens.json` robi dopiero `tokens update --from remotes`.
|
||||
Kolumna `valid` oznacza, czy token zostal zaakceptowany przez API teraz.
|
||||
Jesli przy tokenie w `tokens.json` zapiszesz `expires_at`, `valid` pokaze date
|
||||
wygasniecia; bez daty poprawny token pokazuje `forever`.
|
||||
|
||||
+25
-11
@@ -10,7 +10,8 @@ Sa dwa miejsca, w ktorych moga byc zapisane tokeny:
|
||||
- lokalny plik `~/dev/workspace/rv/tokens/tokens.json`
|
||||
|
||||
Jesli remote URL zawiera `LOGIN:TOKEN@...`, to remote jest zrodlem prawdy.
|
||||
Launcher moze wtedy zeskanowac repo i zapisac ten token do `tokens.json`.
|
||||
Launcher moze wtedy pokazac stan przez `tokens scan` albo jawnie zapisac token
|
||||
do `tokens.json` przez `tokens update --from remotes`.
|
||||
|
||||
Jesli remote nie ma tokena, `tokens.json` moze byc uzyty jako lokalny store
|
||||
przy operacjach `tokens write` i `tokens update --from store`.
|
||||
@@ -34,6 +35,9 @@ Tokeny sa trzymane per endpoint serwera, a dopiero pod nim per user i token:
|
||||
"t1": "SECRET",
|
||||
"t2": {
|
||||
"value": "SECRET",
|
||||
"remote": "r1",
|
||||
"org": "edu-tools",
|
||||
"repo": "rv-launcher",
|
||||
"expires_at": "2026-05-01T12:00:00"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +54,8 @@ To pozwala odroznic:
|
||||
- endpoint, czyli `scheme + host + port`
|
||||
- uzytkownikow na danym serwerze
|
||||
- wiele tokenow dla jednego usera
|
||||
- nazwe remota, ktora jest identyfikatorem parowania z repo
|
||||
- opcjonalne `org` i `repo` zapamietane z remote URL-a
|
||||
- opcjonalna date wygasniecia `expires_at` dla tokena
|
||||
|
||||
## Skanowanie remota
|
||||
@@ -58,10 +64,10 @@ Przy `tokens scan` launcher:
|
||||
|
||||
- czyta wszystkie remote URL-e w repo
|
||||
- czyta `tokens.json`
|
||||
- laczy remote i store w pary po endpoincie serwera
|
||||
- laczy remote i store w pary po endpoincie serwera i nazwie remota
|
||||
- wybiera tylko `http` i `https`
|
||||
- jesli URL ma `LOGIN:TOKEN@...`, wyciaga login i token
|
||||
- zapisuje je pod odpowiednim endpointem w `tokens.json`
|
||||
- niczego nie zapisuje do `tokens.json`
|
||||
|
||||
Endpoint jest liczony z:
|
||||
|
||||
@@ -69,7 +75,7 @@ Endpoint jest liczony z:
|
||||
- host
|
||||
- port
|
||||
|
||||
Przy skanowaniu launcher zapisuje tez metadane serwera:
|
||||
Przy `tokens update --from remotes` launcher zapisuje tez metadane serwera:
|
||||
|
||||
- `type`
|
||||
- `scheme`
|
||||
@@ -90,17 +96,15 @@ Dzialanie:
|
||||
|
||||
- skanuje remote URL-e w repo
|
||||
- czyta wpisy z `tokens.json`
|
||||
- laczy oba zrodla po endpoincie serwera
|
||||
- zapisuje znalezione tokeny do `tokens.json`
|
||||
- zapisuje je per endpoint serwera
|
||||
- laczy oba zrodla po endpoincie serwera i nazwie remota
|
||||
- wypisuje tabele `tokens`
|
||||
|
||||
Tabela `tokens` pokazuje jeden logiczny wiersz na token. Remote i `tokens.json`
|
||||
sa laczone po endpoincie, userze i wartosci tokena.
|
||||
Tabela `tokens` pokazuje jeden logiczny wiersz na remote tokena. Remote i
|
||||
`tokens.json` sa laczone po endpoincie serwera oraz kolumnie `remote`.
|
||||
|
||||
Znacznik w `token_ref`:
|
||||
|
||||
- `*` - token jest w remote i `tokens.json`, uprawnienia zostaly wczytane
|
||||
- `*` - token jest w remote i `tokens.json`, remote/user/token sa zgodne, uprawnienia zostaly wczytane
|
||||
- `R` - token jest tylko w remote
|
||||
- `S` - token jest tylko w `tokens.json`
|
||||
- `!` - blad wczytania uprawnien dla sparowanego tokena
|
||||
@@ -140,6 +144,10 @@ przyklad `e59cc...13be`.
|
||||
- `?` - token nie jest sparowany jako `*`, wiec nie sprawdzamy uprawnien
|
||||
- `!` - blad sprawdzania API
|
||||
|
||||
`tokens scan` jest read-only. Jezeli token jest tylko w remote, tabela pokaze
|
||||
`R` i maski `?????`/`????`. Dopiero jawne `tokens update --from remotes`
|
||||
zapisuje token oraz metadane `remote`, `org` i `repo` do `tokens.json`.
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
@@ -160,6 +168,7 @@ Wynik zawiera:
|
||||
- port
|
||||
- users
|
||||
- tokens
|
||||
- remote/org/repo przy tokenie, jezeli sa zapisane
|
||||
|
||||
Oraz liste userow i nazw tokenow dla kazdego endpointu.
|
||||
|
||||
@@ -226,6 +235,10 @@ Dozwolone kierunki:
|
||||
- `tokens update --from remotes`
|
||||
- `tokens update --from store`
|
||||
|
||||
`tokens update --from remotes` kopiuje tokeny z remote URL-i repo do
|
||||
`tokens.json`. `tokens update --from store` kopiuje wybrany token z
|
||||
`tokens.json` do remote URL-a repo.
|
||||
|
||||
Przyklad:
|
||||
|
||||
```bash
|
||||
@@ -240,7 +253,7 @@ Domyslnie launcher nie zgaduje przy konflikcie.
|
||||
Jesli:
|
||||
|
||||
- token jest w repo, ale nie ma go w `tokens.json`
|
||||
uzyj `tokens scan`
|
||||
uzyj `tokens update --from remotes`
|
||||
- token jest w `tokens.json`, ale nie ma go w repo
|
||||
uzyj `tokens write`
|
||||
- token jest i tu, i tu, ale wartosci sa rozne
|
||||
@@ -256,6 +269,7 @@ Przy `tokens write` i `tokens update --from store` mozna uzyc:
|
||||
Najbezpieczniejszy model pracy:
|
||||
|
||||
- `tokens scan` do zczytywania danych z remote'ow
|
||||
- `tokens update --from remotes` do jawnego zapisania tokenow z remote'ow w `tokens.json`
|
||||
- `tokens read` do podgladu store
|
||||
- `tokens stats` do zbiorczego przegladu per endpoint
|
||||
- `tokens write` do jawnego wpisania auth do remota
|
||||
|
||||
@@ -303,6 +303,10 @@ def normalize_token_entry(raw_token_entry) -> str | dict | None:
|
||||
expires_at = raw_token_entry.get("expires_at")
|
||||
if isinstance(expires_at, str) and expires_at:
|
||||
token_entry["expires_at"] = expires_at
|
||||
for field_name in ["remote", "org", "repo"]:
|
||||
field_value = raw_token_entry.get(field_name)
|
||||
if isinstance(field_value, str) and field_value:
|
||||
token_entry[field_name] = field_value
|
||||
return token_entry
|
||||
|
||||
|
||||
@@ -320,6 +324,12 @@ def token_entry_expires_at(token_entry) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def token_entry_field(token_entry, field_name: str) -> str:
|
||||
if isinstance(token_entry, dict) and isinstance(token_entry.get(field_name), str):
|
||||
return token_entry[field_name]
|
||||
return ""
|
||||
|
||||
|
||||
def token_value_exists(tokens: dict, token_value: str) -> bool:
|
||||
return any(token_entry_value(existing_token) == token_value for existing_token in tokens.values())
|
||||
|
||||
@@ -431,7 +441,7 @@ def normalize_token_store(config: WorkspaceConfig, raw_data: dict) -> dict:
|
||||
return token_data
|
||||
|
||||
|
||||
def load_token_store(config: WorkspaceConfig) -> dict:
|
||||
def load_token_store(config: WorkspaceConfig, write_normalized: bool = True) -> dict:
|
||||
if not config.token_path.exists():
|
||||
return empty_token_store()
|
||||
|
||||
@@ -439,7 +449,7 @@ def load_token_store(config: WorkspaceConfig) -> dict:
|
||||
if not isinstance(raw_data, dict):
|
||||
raise SystemExit(f"Invalid token store format: {config.token_path}")
|
||||
normalized_data = normalize_token_store(config, raw_data)
|
||||
if raw_data != normalized_data:
|
||||
if write_normalized and raw_data != normalized_data:
|
||||
write_token_store(config, normalized_data)
|
||||
return normalized_data
|
||||
|
||||
@@ -455,17 +465,58 @@ def write_token_store(config: WorkspaceConfig, token_data: dict) -> None:
|
||||
os.chmod(config.token_path, 0o600)
|
||||
|
||||
|
||||
def register_token(token_data: dict, server_info: dict, user_name: str, token_value: str) -> bool:
|
||||
def token_entry_with_metadata(token_value: str, server_info: dict, remote_name: str) -> dict[str, str]:
|
||||
token_entry = {"value": token_value}
|
||||
for source_name, target_name in [("org", "org"), ("repo", "repo")]:
|
||||
source_value = server_info.get(source_name)
|
||||
if isinstance(source_value, str) and source_value:
|
||||
token_entry[target_name] = source_value
|
||||
if remote_name:
|
||||
token_entry["remote"] = remote_name
|
||||
return token_entry
|
||||
|
||||
|
||||
def update_token_entry_metadata(token_entry, token_value: str, server_info: dict, remote_name: str) -> tuple[dict, bool]:
|
||||
if isinstance(token_entry, dict):
|
||||
updated_entry = dict(token_entry)
|
||||
else:
|
||||
updated_entry = {"value": token_value}
|
||||
|
||||
changed = updated_entry.get("value") != token_value
|
||||
updated_entry["value"] = token_value
|
||||
for source_name, target_name in [("org", "org"), ("repo", "repo")]:
|
||||
source_value = server_info.get(source_name)
|
||||
if isinstance(source_value, str) and source_value and updated_entry.get(target_name) != source_value:
|
||||
updated_entry[target_name] = source_value
|
||||
changed = True
|
||||
if remote_name and updated_entry.get("remote") != remote_name:
|
||||
updated_entry["remote"] = remote_name
|
||||
changed = True
|
||||
return updated_entry, changed
|
||||
|
||||
|
||||
def register_token(token_data: dict, server_info: dict, remote_name: str, user_name: str, token_value: str) -> str:
|
||||
server_entry = ensure_server_entry(token_data, server_info)
|
||||
users = server_entry.setdefault("users", {})
|
||||
user_entry = users.setdefault(user_name, {"tokens": {}})
|
||||
tokens = user_entry.setdefault("tokens", {})
|
||||
|
||||
if token_value_exists(tokens, token_value):
|
||||
return False
|
||||
for token_name, token_entry in list(tokens.items()):
|
||||
if token_entry_field(token_entry, "remote") != remote_name:
|
||||
continue
|
||||
updated_entry, changed = update_token_entry_metadata(token_entry, token_value, server_info, remote_name)
|
||||
tokens[token_name] = updated_entry
|
||||
return "updated" if changed else "existing"
|
||||
|
||||
tokens[next_token_name(tokens)] = token_value
|
||||
return True
|
||||
for token_name, token_entry in list(tokens.items()):
|
||||
if token_entry_value(token_entry) != token_value:
|
||||
continue
|
||||
updated_entry, changed = update_token_entry_metadata(token_entry, token_value, server_info, remote_name)
|
||||
tokens[token_name] = updated_entry
|
||||
return "updated" if changed else "existing"
|
||||
|
||||
tokens[next_token_name(tokens)] = token_entry_with_metadata(token_value, server_info, remote_name)
|
||||
return "added"
|
||||
|
||||
|
||||
def remote_urls(repo_path: Path) -> list[str]:
|
||||
@@ -502,7 +553,7 @@ def remote_credentials(remote_url: str) -> tuple[str, str] | None:
|
||||
return unquote(split_url.username), unquote(split_url.password)
|
||||
|
||||
|
||||
def sync_tokens_from_repo(config: WorkspaceConfig, repo_path: Path) -> dict:
|
||||
def scan_repo_remotes(config: WorkspaceConfig, repo_path: Path) -> dict:
|
||||
repo_root = git_repo_root(repo_path)
|
||||
if repo_root is None:
|
||||
return {
|
||||
@@ -515,12 +566,14 @@ def sync_tokens_from_repo(config: WorkspaceConfig, repo_path: Path) -> dict:
|
||||
"unsupported_urls": 0,
|
||||
"found": 0,
|
||||
"added": 0,
|
||||
"updated": 0,
|
||||
"servers": 0,
|
||||
"remote_rows": [],
|
||||
"found_credentials": [],
|
||||
"changed": False,
|
||||
}
|
||||
|
||||
found_credentials: list[tuple[int, dict, str, str]] = []
|
||||
found_credentials: list[tuple[int, dict, str, str, str]] = []
|
||||
remote_entries = remote_name_urls(repo_root)
|
||||
remote_names = {remote_name for remote_name, _ in remote_entries}
|
||||
touched_servers: set[str] = set()
|
||||
@@ -582,19 +635,7 @@ def sync_tokens_from_repo(config: WorkspaceConfig, repo_path: Path) -> dict:
|
||||
"result": "found",
|
||||
}
|
||||
)
|
||||
found_credentials.append((row_index, server_info, user_name, token_value))
|
||||
|
||||
token_data = load_token_store(config)
|
||||
added = 0
|
||||
for row_index, server_info, user_name, token_value in found_credentials:
|
||||
if register_token(token_data, server_info, user_name, token_value):
|
||||
added += 1
|
||||
remote_rows[row_index]["result"] = "added"
|
||||
else:
|
||||
remote_rows[row_index]["result"] = "existing"
|
||||
|
||||
if found_credentials and (added or not config.token_path.exists()):
|
||||
write_token_store(config, token_data)
|
||||
found_credentials.append((row_index, server_info, remote_name, user_name, token_value))
|
||||
|
||||
return {
|
||||
"repo_root": repo_root,
|
||||
@@ -605,13 +646,42 @@ def sync_tokens_from_repo(config: WorkspaceConfig, repo_path: Path) -> dict:
|
||||
"plain_urls": plain_urls,
|
||||
"unsupported_urls": unsupported_urls,
|
||||
"found": len(found_credentials),
|
||||
"added": added,
|
||||
"added": 0,
|
||||
"updated": 0,
|
||||
"servers": len(touched_servers),
|
||||
"remote_rows": remote_rows,
|
||||
"changed": added > 0,
|
||||
"found_credentials": found_credentials,
|
||||
"changed": False,
|
||||
}
|
||||
|
||||
|
||||
def sync_tokens_from_repo(config: WorkspaceConfig, repo_path: Path) -> dict:
|
||||
report = scan_repo_remotes(config, repo_path)
|
||||
found_credentials = report.get("found_credentials", [])
|
||||
if not found_credentials:
|
||||
return report
|
||||
|
||||
token_data = load_token_store(config)
|
||||
added = 0
|
||||
updated = 0
|
||||
for row_index, server_info, remote_name, user_name, token_value in found_credentials:
|
||||
result = register_token(token_data, server_info, remote_name, user_name, token_value)
|
||||
if result == "added":
|
||||
added += 1
|
||||
elif result == "updated":
|
||||
updated += 1
|
||||
report["remote_rows"][row_index]["result"] = result
|
||||
|
||||
changed = added > 0 or updated > 0
|
||||
if changed or not config.token_path.exists():
|
||||
write_token_store(config, token_data)
|
||||
|
||||
report["added"] = added
|
||||
report["updated"] = updated
|
||||
report["changed"] = changed
|
||||
return report
|
||||
|
||||
|
||||
def count_server_tokens(server_entry: dict) -> int:
|
||||
return sum(len(user_entry.get("tokens", {})) for user_entry in server_entry.get("users", {}).values())
|
||||
|
||||
@@ -627,15 +697,15 @@ def token_store_servers(token_data: dict) -> list[tuple[str, dict]]:
|
||||
return sorted(servers.items())
|
||||
|
||||
|
||||
def token_pairs_from_users(users_data: dict) -> set[tuple[str, str]]:
|
||||
token_pairs: set[tuple[str, str]] = set()
|
||||
def token_pairs_from_users(users_data: dict) -> set[tuple[str, str, str]]:
|
||||
token_pairs: set[tuple[str, str, str]] = set()
|
||||
for user_name, user_entry in users_data.items():
|
||||
if not isinstance(user_entry, dict):
|
||||
continue
|
||||
for token_entry in user_entry.get("tokens", {}).values():
|
||||
token_value = token_entry_value(token_entry)
|
||||
if token_value:
|
||||
token_pairs.add((str(user_name), token_value))
|
||||
token_pairs.add((token_entry_field(token_entry, "remote"), str(user_name), token_value))
|
||||
return token_pairs
|
||||
|
||||
|
||||
@@ -703,7 +773,7 @@ def collect_repo_server_entries(config: WorkspaceConfig, repo_path: Path) -> tup
|
||||
user_entry = entry["users"].setdefault(user_name, {"tokens": {}})
|
||||
tokens = user_entry.setdefault("tokens", {})
|
||||
if not token_value_exists(tokens, token_value):
|
||||
tokens[next_token_name(tokens)] = token_value
|
||||
tokens[next_token_name(tokens)] = token_entry_with_metadata(token_value, server_info, remote_name)
|
||||
|
||||
return repo_root, repo_servers
|
||||
|
||||
@@ -878,6 +948,9 @@ def token_store_rows(store_servers: dict[str, dict]) -> list[dict[str, str]]:
|
||||
"endpoint": endpoint,
|
||||
"user": str(user_name),
|
||||
"token_name": str(token_name),
|
||||
"remote": token_entry_field(token_entry, "remote"),
|
||||
"owner": token_entry_field(token_entry, "org"),
|
||||
"repo_name": token_entry_field(token_entry, "repo"),
|
||||
"token_value": token_value,
|
||||
"expires_at": token_entry_expires_at(token_entry),
|
||||
}
|
||||
@@ -1078,9 +1151,10 @@ def print_token_item_rows(
|
||||
status_counts[status_name] += 1
|
||||
|
||||
store_rows = token_store_rows(store_servers)
|
||||
store_by_value: dict[tuple[str, str, str], dict[str, str]] = {}
|
||||
store_by_remote: dict[tuple[str, str], dict[str, str]] = {}
|
||||
for row in store_rows:
|
||||
store_by_value.setdefault((row["endpoint"], row["user"], row["token_value"]), row)
|
||||
if row["remote"]:
|
||||
store_by_remote.setdefault((row["endpoint"], row["remote"]), row)
|
||||
|
||||
endpoint_values_by_name = {
|
||||
endpoint: endpoint_column_values(endpoint, repo_servers.get(endpoint), store_servers.get(endpoint))
|
||||
@@ -1125,13 +1199,14 @@ def print_token_item_rows(
|
||||
for row in repo_token_rows(endpoint_names, repo_servers, scan_rows):
|
||||
endpoint = row["endpoint"]
|
||||
token_value = row["token_value"]
|
||||
store_row = store_by_value.get((endpoint, row["user"], token_value))
|
||||
store_row = store_by_remote.get((endpoint, row["remote"]))
|
||||
token_name = store_row["token_name"] if store_row else ""
|
||||
expires_at = store_row.get("expires_at", "") if store_row else ""
|
||||
marker = "R"
|
||||
authz = None
|
||||
if store_row:
|
||||
used_store_keys.add((endpoint, store_row["user"], store_row["token_name"], token_value))
|
||||
used_store_keys.add((endpoint, store_row["user"], store_row["token_name"], store_row["token_value"]))
|
||||
if store_row["user"] == row["user"] and store_row["token_value"] == token_value:
|
||||
authz_key = (endpoint, row["user"], token_value, expires_at, row["owner"], row["repo_name"])
|
||||
authz = authz_cache.get(authz_key)
|
||||
if authz is None:
|
||||
@@ -1166,12 +1241,12 @@ def print_token_item_rows(
|
||||
append_token_row(
|
||||
row["endpoint"],
|
||||
row["user"],
|
||||
"",
|
||||
row["remote"],
|
||||
row["token_name"],
|
||||
row["token_value"],
|
||||
"S",
|
||||
fallback.get("owner", ""),
|
||||
fallback.get("repo_name", ""),
|
||||
row["owner"] or fallback.get("owner", ""),
|
||||
row["repo_name"] or fallback.get("repo_name", ""),
|
||||
)
|
||||
|
||||
print_fixed_table("tokens", TOKEN_COLUMNS, token_rows)
|
||||
@@ -1268,16 +1343,16 @@ def resolve_repo_argument(repo_arg: str | None) -> Path:
|
||||
|
||||
def run_tokens_scan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
repo_path = resolve_repo_argument(args.repo)
|
||||
report = sync_tokens_from_repo(config, repo_path)
|
||||
repo_root, repo_servers = collect_repo_server_entries(config, repo_path)
|
||||
token_data = load_token_store(config)
|
||||
token_data = load_token_store(config, write_normalized=False)
|
||||
store_servers = token_data.get("servers", {})
|
||||
report = scan_repo_remotes(config, repo_path)
|
||||
repo_root, repo_servers = collect_repo_server_entries(config, repo_path)
|
||||
endpoint_names = sorted(set(repo_servers) | set(store_servers))
|
||||
print_token_item_rows(endpoint_names, repo_servers, store_servers, report["remote_rows"])
|
||||
|
||||
|
||||
def run_tokens_read(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
token_data = load_token_store(config)
|
||||
token_data = load_token_store(config, write_normalized=False)
|
||||
servers = token_store_servers(token_data)
|
||||
if args.server:
|
||||
server_entry = token_data.get("servers", {}).get(args.server)
|
||||
@@ -1309,6 +1384,10 @@ def run_tokens_read(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
token_value = token_entry_value(token_entry)
|
||||
secret_value = token_value if args.show_secrets else mask_secret(token_value)
|
||||
print(f"token\t{token_name}\t{secret_value}")
|
||||
for field_name in ["remote", "org", "repo"]:
|
||||
field_value = token_entry_field(token_entry, field_name)
|
||||
if field_value:
|
||||
print(f"{field_name}\t{token_name}\t{field_value}")
|
||||
expires_at = token_entry_expires_at(token_entry)
|
||||
if expires_at:
|
||||
print(f"expires_at\t{token_name}\t{expires_at}")
|
||||
@@ -1316,7 +1395,7 @@ def run_tokens_read(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
|
||||
def run_tokens_stats(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
repo_root, repo_servers = collect_repo_server_entries(config, resolve_repo_argument(args.repo))
|
||||
token_data = load_token_store(config)
|
||||
token_data = load_token_store(config, write_normalized=False)
|
||||
store_servers = token_data.get("servers", {})
|
||||
endpoint_names = sorted(set(repo_servers) | set(store_servers))
|
||||
if args.server:
|
||||
@@ -1381,7 +1460,13 @@ def run_tokens_write(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
|
||||
def run_tokens_update(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||
if args.from_source == "remotes":
|
||||
run_tokens_scan(config, args)
|
||||
repo_path = resolve_repo_argument(args.repo)
|
||||
report = sync_tokens_from_repo(config, repo_path)
|
||||
repo_root, repo_servers = collect_repo_server_entries(config, repo_path)
|
||||
token_data = load_token_store(config, write_normalized=False)
|
||||
store_servers = token_data.get("servers", {})
|
||||
endpoint_names = sorted(set(repo_servers) | set(store_servers))
|
||||
print_token_item_rows(endpoint_names, repo_servers, store_servers, report["remote_rows"])
|
||||
return
|
||||
if args.from_source == "store":
|
||||
run_tokens_write(config, args)
|
||||
@@ -1702,11 +1787,11 @@ def print_tokens_overview() -> None:
|
||||
print_table(
|
||||
["command", "common options", "direction", "purpose"],
|
||||
[
|
||||
["scan", "[--repo PATH]", "repo + tokens.json", "print token table with remote/store marker and auth masks"],
|
||||
["scan", "[--repo PATH]", "read-only", "print token table with remote/store marker and auth masks"],
|
||||
["read", "[--server ENDPOINT]", "tokens.json", "show servers, users and token names"],
|
||||
["stats", "[--repo PATH]", "repo + tokens.json", "compare remote URLs with local token store"],
|
||||
["write", "--remote R [--replace]", "tokens.json -> repo", "write selected token into a remote URL"],
|
||||
["update", "--from remotes|store", "selected", "run scan or write with explicit direction"],
|
||||
["update", "--from remotes|store", "selected", "copy token data in an explicit direction"],
|
||||
],
|
||||
)
|
||||
print()
|
||||
@@ -1810,7 +1895,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
|
||||
tokens_scan_parser = tokens_subparsers.add_parser(
|
||||
"scan",
|
||||
help="Scan repo remotes and tokens.json, then print paired endpoint rows.",
|
||||
help="Read repo remotes and tokens.json, then print paired token rows.",
|
||||
)
|
||||
add_repo_option(tokens_scan_parser)
|
||||
|
||||
@@ -1872,7 +1957,6 @@ def main() -> int:
|
||||
parser = build_parser()
|
||||
args = parser.parse_args()
|
||||
config = load_config(args.config)
|
||||
sync_tokens_from_repo(config, Path.cwd())
|
||||
|
||||
if args.command == "show-config":
|
||||
print_config(config)
|
||||
|
||||
Reference in New Issue
Block a user