Add token list command
This commit is contained in:
@@ -156,6 +156,8 @@ Podstawowe komendy tokenow:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens scan
|
./rvctl tokens scan
|
||||||
|
./rvctl tokens list store
|
||||||
|
./rvctl tokens list remote
|
||||||
./rvctl tokens sync remote r1
|
./rvctl tokens sync remote r1
|
||||||
./rvctl tokens update r1
|
./rvctl tokens update r1
|
||||||
./rvctl tokens sync store r1
|
./rvctl tokens sync store r1
|
||||||
@@ -168,6 +170,7 @@ Podstawowe komendy tokenow:
|
|||||||
|
|
||||||
`tokens scan` wypisuje tabele `tokens` i niczego nie zapisuje: jeden wiersz na
|
`tokens scan` wypisuje tabele `tokens` i niczego nie zapisuje: jeden wiersz na
|
||||||
logiczny remote tokena.
|
logiczny remote tokena.
|
||||||
|
`tokens list store|remote|both` wypisuje jedno zrodlo bez porownywania.
|
||||||
`tokens.json` synchronizujemy z repo `rv-launcher`; remotes kart pracy i
|
`tokens.json` synchronizujemy z repo `rv-launcher`; remotes kart pracy i
|
||||||
odpowiedzi sa generowane jako pochodne tego ustawienia.
|
odpowiedzi sa generowane jako pochodne tego ustawienia.
|
||||||
`token_ref` laczy nazwe tokena z markerem po prawej stronie: `*` oznacza, ze
|
`token_ref` laczy nazwe tokena z markerem po prawej stronie: `*` oznacza, ze
|
||||||
|
|||||||
@@ -331,6 +331,37 @@ Przyklad:
|
|||||||
./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/03
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `tokens list store|remote|both`
|
||||||
|
|
||||||
|
Wypisuje jedno zrodlo bez porownywania go z drugim. `list` jest read-only:
|
||||||
|
pokazuje co jest w `tokens.json`, co jest w git remote albo oba zrodla jako
|
||||||
|
osobne wiersze. `scan` sluzy do porownania zgodnosci.
|
||||||
|
|
||||||
|
Przelaczniki:
|
||||||
|
|
||||||
|
- `--repo PATH`
|
||||||
|
Repo, z ktorego listowane sa git remotes. Domyslnie repo zawierajace `rvctl`.
|
||||||
|
- `--server ENDPOINT`
|
||||||
|
Ogranicza wynik do jednego endpointu.
|
||||||
|
|
||||||
|
Typowy wynik:
|
||||||
|
|
||||||
|
```text
|
||||||
|
tokens
|
||||||
|
item source kind server proto host org repo user remote token valid scope org repo
|
||||||
|
---- ------ ----- ------ ----- ------------------ --------- ----------- ---- ------ ------------ ------------------- aAimnopru oawrc- oawr--
|
||||||
|
1 store auth gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 e59cc...13be forever --------- +++++ ++++
|
||||||
|
2 remote auth gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 e59cc...13be
|
||||||
|
```
|
||||||
|
|
||||||
|
Przyklady:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl tokens list store
|
||||||
|
./rvctl tokens list remote
|
||||||
|
./rvctl tokens list both
|
||||||
|
```
|
||||||
|
|
||||||
## `tokens add REMOTE_ID`
|
## `tokens add REMOTE_ID`
|
||||||
|
|
||||||
Dodaje pusty szkielet tokena do `tokens.json`. `REMOTE_ID` musi byc taki sam
|
Dodaje pusty szkielet tokena do `tokens.json`. `REMOTE_ID` musi byc taki sam
|
||||||
|
|||||||
+113
-47
@@ -1,31 +1,41 @@
|
|||||||
# Tokens
|
# Tokens
|
||||||
|
|
||||||
Plik opisuje model pracy z tokenami w launcherze.
|
Ten dokument opisuje model tokenow uzywany przez `rvctl`.
|
||||||
|
|
||||||
## Zrodlo prawdy
|
## Zasada
|
||||||
|
|
||||||
Sa dwa miejsca, w ktorych moga byc zapisane dane dostepowe:
|
`tokens.json` synchronizujemy z repo `rv-launcher`. Remoty kart pracy i repo
|
||||||
|
odpowiedzi sa generowane jako pochodne konfiguracji launchera, a nie jako osobne
|
||||||
|
zrodla prawdy dla tokenow.
|
||||||
|
|
||||||
- remote URL w repo, na przyklad `http://u1:SECRET@host/org/repo.git`
|
W praktyce oznacza to:
|
||||||
|
|
||||||
|
- `r1` w repo `rv-launcher` jest miejscem startowym do pobrania tokena z remote
|
||||||
|
- `tokens.json` jest lokalnym store sekretow i metadanych tokenow
|
||||||
|
- karty pracy dostaja remote na podstawie ustawien launchera i komend typu `submission`
|
||||||
|
- `tokens scan` jest read-only i niczego nie zapisuje
|
||||||
|
|
||||||
|
## Zrodlo Prawdy
|
||||||
|
|
||||||
|
Sa dwa miejsca, ktore `rvctl` potrafi porownac:
|
||||||
|
|
||||||
|
- git remote w repo `rv-launcher`, np. `http://u1:SECRET@host/org/repo.git`
|
||||||
- lokalny plik `~/dev/workspace/rv/tokens/tokens.json`
|
- lokalny plik `~/dev/workspace/rv/tokens/tokens.json`
|
||||||
|
|
||||||
`tokens scan` tylko porownuje oba miejsca i niczego nie zapisuje.
|
Kierunek jest jawny:
|
||||||
`tokens sync remote r1` czyta remote `r1` i zapisuje jego dane w
|
|
||||||
`tokens.json`.
|
|
||||||
`tokens sync store r1` zapisuje rekord `r1` z `tokens.json` do git remote
|
|
||||||
`r1`.
|
|
||||||
`tokens update r1` nie synchronizuje sekretu; tylko pobiera z API metadane
|
|
||||||
tokenu: `valid`, `scope`, `org_perm` i `repo_perm`.
|
|
||||||
|
|
||||||
W podstawowym flow `tokens.json` synchronizujemy z repo `rv-launcher`. Remoty
|
- `tokens list store` pokazuje tylko rekordy w `tokens.json`
|
||||||
kart pracy i repo odpowiedzi sa generowane jako pochodne konfiguracji launchera,
|
- `tokens list remote` pokazuje tylko git remotes
|
||||||
nie jako osobne zrodla prawdy dla tokenow.
|
- `tokens sync remote r1` czyta git remote `r1` i zapisuje rekord do `tokens.json`
|
||||||
|
- `tokens sync store r1` czyta `tokens.json` i zapisuje auth do git remote `r1`
|
||||||
|
- `tokens update r1` nie synchronizuje sekretu, tylko pobiera metadane z API
|
||||||
|
- `tokens remove ... r1` usuwa rekord ze store, git remote albo oba miejsca
|
||||||
|
|
||||||
## Format `tokens.json`
|
## Format `tokens.json`
|
||||||
|
|
||||||
Aktualny format to `version: 3`. Glownym rekordem jest remote-token. Pole
|
Aktualny format to `version: 3`. Glownym rekordem jest jeden remote-token.
|
||||||
`id` jest obowiazkowe i musi byc takie samo jak nazwa git remote, np. `r1`
|
Pole `id` jest obowiazkowe i musi byc takie samo jak nazwa git remote, np.
|
||||||
albo `r1a`.
|
`r1` albo `r1a`.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -74,30 +84,30 @@ albo `r1a`.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Schemat JSON jest w pliku `doc/tokens.schema.json`.
|
Pelny schemat jest w `doc/tokens.schema.json`.
|
||||||
|
|
||||||
## Pola
|
## Pola
|
||||||
|
|
||||||
Pola synchronizowane z remote URL-a:
|
Pola synchronizowane z git remote:
|
||||||
|
|
||||||
- `id` - nazwa git remote, np. `r1`; to jest klucz rekordu w `tokens.json`
|
- `id` - nazwa git remote, np. `r1`; to jest klucz rekordu
|
||||||
- `server.endpoint` - endpoint serwera, na przyklad `http://77.90.8.171:3001`
|
- `server.endpoint` - endpoint serwera, np. `http://77.90.8.171:3001`
|
||||||
- `server.type` - typ serwera, na przyklad `gitea`, `github`, `gitlab`, `unknown`
|
- `server.type` - typ serwera, np. `gitea`, `github`, `gitlab`, `unknown`
|
||||||
- `user` - login z URL-a, czyli lewa strona `http://u1:SECRET@...`
|
- `user` - login z URL-a, czyli lewa strona `http://u1:SECRET@...`
|
||||||
- `value` - sekret tokena
|
- `value` - sekret tokena
|
||||||
- `org` - organizacja z URL-a
|
- `org` - organizacja z URL-a
|
||||||
- `repo` - repo z URL-a
|
- `repo` - repo z URL-a
|
||||||
|
|
||||||
Pola wzbogacane przez API przy `tokens update r1`:
|
Pola pobierane z API przez `tokens update r1`:
|
||||||
|
|
||||||
- `valid` - `forever`, data `expires_at`, `invalid`, `?` albo `!`
|
- `valid` - `forever`, data wygasniecia, `invalid`, `?` albo `!`
|
||||||
- `scope` - mapa scope tokena
|
- `scope` - mapa scope tokena
|
||||||
- `org_perm` - mapa praw w organizacji
|
- `org_perm` - mapa praw uzytkownika w organizacji
|
||||||
- `repo_perm` - mapa praw w repo
|
- `repo_perm` - mapa praw uzytkownika w repo
|
||||||
|
|
||||||
## Porownanie
|
## Porownanie
|
||||||
|
|
||||||
Porownanie z `git remote -v` jest robione po:
|
`tokens scan` porownuje git remote i `tokens.json` po:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
server.endpoint + id + user + value + org + repo
|
server.endpoint + id + user + value + org + repo
|
||||||
@@ -106,17 +116,17 @@ server.endpoint + id + user + value + org + repo
|
|||||||
Znacznik w kolumnie `token_ref`:
|
Znacznik w kolumnie `token_ref`:
|
||||||
|
|
||||||
- `*` - remote i `tokens.json` sa zgodne
|
- `*` - remote i `tokens.json` sa zgodne
|
||||||
- `R` - wpis istnieje tylko w remote URL-u
|
- `R` - wpis istnieje tylko w git remote
|
||||||
- `S` - wpis istnieje tylko w `tokens.json`
|
- `S` - wpis istnieje tylko w `tokens.json`
|
||||||
- `!` - remote i `tokens.json` sa zgodne, ale zapisany token jest `invalid` albo ma blad walidacji
|
- `!` - wpis jest sparowany, ale token jest `invalid` albo ma blad sprawdzania
|
||||||
|
|
||||||
Bez zgodnego wpisu kolumny `valid`, `scope`, `org` i `repo` w raporcie maja
|
Dla niesparowanych wpisow `rvctl` pokazuje `?` w kolumnach `valid`, `scope`,
|
||||||
wartosc `?`, bo launcher nie pokazuje metadanych API dla niesparowanych wpisow.
|
`org` i `repo`, bo metadane API sa wiarygodne dopiero dla rekordu ze store.
|
||||||
|
|
||||||
## Uprawnienia
|
## Uprawnienia
|
||||||
|
|
||||||
W `tokens.json` uprawnienia sa zapisane jako mapy klucz-wartosc. W tabeli CLI sa
|
W `tokens.json` uprawnienia sa zapisane jako mapy klucz-wartosc. W tabeli CLI sa
|
||||||
pokazywane skrocone maski.
|
pokazywane jako zwarte maski.
|
||||||
|
|
||||||
Naglowki masek:
|
Naglowki masek:
|
||||||
|
|
||||||
@@ -136,7 +146,7 @@ Kategorie `scope`:
|
|||||||
- `r` - repository
|
- `r` - repository
|
||||||
- `u` - user
|
- `u` - user
|
||||||
|
|
||||||
Znaki w `scope`:
|
Wartosci w `scope`:
|
||||||
|
|
||||||
- `w` - read/write
|
- `w` - read/write
|
||||||
- `r` - read
|
- `r` - read
|
||||||
@@ -159,25 +169,80 @@ Kategorie `repo_perm`:
|
|||||||
- `w` - write
|
- `w` - write
|
||||||
- `r` - read
|
- `r` - read
|
||||||
|
|
||||||
Znaki w `org_perm` i `repo_perm`:
|
Wartosci w `org_perm` i `repo_perm`:
|
||||||
|
|
||||||
- `+` - flaga wlaczona
|
- `+` - flaga wlaczona
|
||||||
- `-` - flaga wylaczona
|
- `-` - flaga wylaczona
|
||||||
- `?` - nie wczytano
|
- `?` - nie wczytano
|
||||||
- `!` - blad wczytania
|
- `!` - blad wczytania
|
||||||
|
|
||||||
|
## Typowy Flow
|
||||||
|
|
||||||
|
W repo `rv-launcher`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote add r1 http://u1:TOKEN@77.90.8.171:3001/edu-tools/rv-launcher.git
|
||||||
|
./rvctl tokens scan
|
||||||
|
./rvctl tokens sync remote r1
|
||||||
|
./rvctl tokens update r1
|
||||||
|
./rvctl tokens scan
|
||||||
|
```
|
||||||
|
|
||||||
|
Po `sync remote` sekret jest w `tokens.json`. Po `update` `rvctl` dopisuje
|
||||||
|
`valid`, `scope`, `org_perm` i `repo_perm`.
|
||||||
|
|
||||||
|
Jesli chcesz tylko zobaczyc jedno zrodlo, bez porownywania:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl tokens list remote
|
||||||
|
./rvctl tokens list store
|
||||||
|
./rvctl tokens list both
|
||||||
|
```
|
||||||
|
|
||||||
|
Jesli remote ma byc bez sekretu, a token ma zostac tylko w `tokens.json`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl tokens remove remote r1
|
||||||
|
git remote add r1 http://77.90.8.171:3001/edu-tools/rv-launcher.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Jesli trzeba ponownie wpisac sekret ze store do remota:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl tokens sync store r1
|
||||||
|
```
|
||||||
|
|
||||||
## Komendy
|
## Komendy
|
||||||
|
|
||||||
### `tokens scan`
|
### `tokens scan`
|
||||||
|
|
||||||
Read-only. Czyta remote URL-e i `tokens.json`, a potem wypisuje tabele
|
Read-only. Czyta git remote i `tokens.json`, laczy wpisy w pary i wypisuje
|
||||||
`tokens`. Nie tworzy i nie modyfikuje `tokens.json`.
|
tabele `tokens`. Nie tworzy i nie modyfikuje `tokens.json`. Bez `--repo`
|
||||||
|
czyta repo zawierajace `rvctl`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens scan
|
./rvctl tokens scan
|
||||||
./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens scan --repo ~/dev/workspace/rv/tools/rv-launcher
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `tokens list store|remote|both`
|
||||||
|
|
||||||
|
Read-only. Wypisuje jedno zrodlo bez porownywania go z drugim. To odroznia
|
||||||
|
`list` od `scan`: `list` odpowiada na pytanie "co jest zapisane tutaj", a
|
||||||
|
`scan` odpowiada na pytanie "czy store i remote sa zgodne".
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl tokens list store
|
||||||
|
./rvctl tokens list remote
|
||||||
|
./rvctl tokens list both
|
||||||
|
./rvctl tokens list remote --repo ~/dev/workspace/rv/tools/rv-launcher
|
||||||
|
```
|
||||||
|
|
||||||
|
Przelaczniki:
|
||||||
|
|
||||||
|
- `--repo PATH` - repo, z ktorego listowane sa git remotes; bez tej opcji uzywane jest repo zawierajace `rvctl`
|
||||||
|
- `--server ENDPOINT` - ogranicza wynik do jednego endpointu
|
||||||
|
|
||||||
### `tokens sync remote REMOTE_ID`
|
### `tokens sync remote REMOTE_ID`
|
||||||
|
|
||||||
Kopiuje dane dostepowe z git remote do `tokens.json`. Nie pobiera metadanych z
|
Kopiuje dane dostepowe z git remote do `tokens.json`. Nie pobiera metadanych z
|
||||||
@@ -190,7 +255,7 @@ API. Bez `--repo` czyta repo zawierajace `rvctl`.
|
|||||||
|
|
||||||
### `tokens update REMOTE_ID`
|
### `tokens update REMOTE_ID`
|
||||||
|
|
||||||
Pobiera z API metadane dla rekordu `REMOTE_ID` juz zapisanego w `tokens.json`.
|
Pobiera z API metadane dla rekordu `REMOTE_ID` zapisanego w `tokens.json`.
|
||||||
Nie zmienia git remote URL-a i nie kopiuje sekretu z remote.
|
Nie zmienia git remote URL-a i nie kopiuje sekretu z remote.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -204,21 +269,19 @@ Zapisuje dane z rekordu `REMOTE_ID` w `tokens.json` do git remote o tej samej
|
|||||||
nazwie. Bez `--repo` zapisuje do repo zawierajacego `rvctl`.
|
nazwie. Bez `--repo` zapisuje do repo zawierajacego `rvctl`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens sync store r1 --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens sync store r1
|
||||||
./rvctl tokens sync store r1 --repo PATH --replace
|
./rvctl tokens sync store r1 --replace
|
||||||
```
|
```
|
||||||
|
|
||||||
### `tokens remove store|remote|both REMOTE_ID`
|
### `tokens remove store|remote|both REMOTE_ID`
|
||||||
|
|
||||||
Usuwa rekord `REMOTE_ID` z `tokens.json`, git remote albo oba miejsca.
|
Usuwa rekord ze store, git remote albo oba miejsca. Bez `--repo` operacje
|
||||||
Domyslnym repo dla `remote` i `both` jest repo, w ktorym lezy `rvctl`. Inne
|
`remote` i `both` dzialaja na repo zawierajacym `rvctl`.
|
||||||
repo mozna wskazac przez `--repo PATH`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens remove store r1
|
./rvctl tokens remove store r1
|
||||||
./rvctl tokens remove remote r1
|
./rvctl tokens remove remote r1
|
||||||
./rvctl tokens remove both r1
|
./rvctl tokens remove both r1
|
||||||
./rvctl tokens remove remote r1 --repo ~/dev/workspace/rv/series/inf/03
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Przelaczniki:
|
Przelaczniki:
|
||||||
@@ -252,15 +315,18 @@ nazwie git remote.
|
|||||||
|
|
||||||
### `tokens stats`
|
### `tokens stats`
|
||||||
|
|
||||||
Pokazuje kontekst, tabele `tokens` i podsumowanie statusow endpointow.
|
Pokazuje kontekst, tabele `tokens` i podsumowanie statusow endpointow. Bez
|
||||||
|
`--repo` czyta repo zawierajace `rvctl`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens stats
|
||||||
|
./rvctl tokens stats --repo ~/dev/workspace/rv/tools/rv-launcher
|
||||||
```
|
```
|
||||||
|
|
||||||
### Komendy zgodnosci
|
### Komendy zgodnosci
|
||||||
|
|
||||||
Stare komendy nadal dzialaja, ale sa mniej jednoznaczne:
|
Stare komendy nadal dzialaja, ale w nowych instrukcjach preferujemy jawne
|
||||||
|
`sync remote`, `sync store` i `update REMOTE_ID`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens update --from remotes --repo PATH
|
./rvctl tokens update --from remotes --repo PATH
|
||||||
|
|||||||
@@ -1277,6 +1277,24 @@ TOKEN_COLUMNS = [
|
|||||||
("repo_mask", "repo", 6),
|
("repo_mask", "repo", 6),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
TOKEN_LIST_COLUMNS = [
|
||||||
|
("item", "item", 4),
|
||||||
|
("source", "source", 6),
|
||||||
|
("kind", "kind", 5),
|
||||||
|
("server", "server", 6),
|
||||||
|
("proto", "proto", 5),
|
||||||
|
("host", "host", 18),
|
||||||
|
("owner", "org", 9),
|
||||||
|
("repo_name", "repo", 11),
|
||||||
|
("user", "user", 4),
|
||||||
|
("remote", "remote", 6),
|
||||||
|
("token", "token", 12),
|
||||||
|
("valid", "valid", 19),
|
||||||
|
("scope_mask", "scope", 9),
|
||||||
|
("org_mask", "org", 6),
|
||||||
|
("repo_mask", "repo", 6),
|
||||||
|
]
|
||||||
|
|
||||||
TOKEN_SEPARATOR_OVERRIDES = {
|
TOKEN_SEPARATOR_OVERRIDES = {
|
||||||
"scope_mask": "aAimnopru",
|
"scope_mask": "aAimnopru",
|
||||||
"org_mask": "oawrc-",
|
"org_mask": "oawrc-",
|
||||||
@@ -1409,6 +1427,94 @@ def repo_token_rows(
|
|||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def list_store_rows(store_servers: dict[str, dict], server_filter: str | None = None) -> list[dict[str, str]]:
|
||||||
|
rows: list[dict[str, str]] = []
|
||||||
|
endpoints = sorted(store_servers)
|
||||||
|
for endpoint in endpoints:
|
||||||
|
if server_filter and endpoint != server_filter:
|
||||||
|
continue
|
||||||
|
server_entry = store_servers[endpoint]
|
||||||
|
endpoint_values = endpoint_column_values(endpoint, None, server_entry)
|
||||||
|
for row in sorted(server_entry.get("tokens", []), key=lambda item: item.get("token_id", "")):
|
||||||
|
token_value = str(row.get("token_value", ""))
|
||||||
|
rows.append(
|
||||||
|
{
|
||||||
|
"item": str(len(rows) + 1),
|
||||||
|
"source": "store",
|
||||||
|
"kind": "auth" if token_value else "empty",
|
||||||
|
"server": endpoint_values["server"],
|
||||||
|
"proto": endpoint_values["scheme"],
|
||||||
|
"host": endpoint_values["host"],
|
||||||
|
"owner": str(row.get("owner", "")),
|
||||||
|
"repo_name": str(row.get("repo_name", "")),
|
||||||
|
"user": str(row.get("user", "")),
|
||||||
|
"remote": str(row.get("remote", "")),
|
||||||
|
"token": short_secret(token_value) if token_value else "",
|
||||||
|
"valid": str(row.get("valid", "")),
|
||||||
|
"scope_mask": str(row.get("scope_mask", "")),
|
||||||
|
"org_mask": str(row.get("org_mask", "")),
|
||||||
|
"repo_mask": str(row.get("repo_mask", "")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def list_remote_rows(repo_servers: dict[str, dict], server_filter: str | None = None) -> list[dict[str, str]]:
|
||||||
|
rows: list[dict[str, str]] = []
|
||||||
|
endpoints = sorted(repo_servers)
|
||||||
|
for endpoint in endpoints:
|
||||||
|
if server_filter and endpoint != server_filter:
|
||||||
|
continue
|
||||||
|
server_entry = repo_servers[endpoint]
|
||||||
|
endpoint_values = endpoint_column_values(endpoint, server_entry, None)
|
||||||
|
remote_rows = []
|
||||||
|
for row in server_entry.get("tokens", []):
|
||||||
|
remote_rows.append(
|
||||||
|
{
|
||||||
|
"kind": "auth",
|
||||||
|
"remote": row.get("remote", ""),
|
||||||
|
"owner": row.get("org", ""),
|
||||||
|
"repo_name": row.get("repo", ""),
|
||||||
|
"user": row.get("user", ""),
|
||||||
|
"token_value": row.get("token_value", ""),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for row in server_entry.get("plain_remotes", []):
|
||||||
|
remote_rows.append(
|
||||||
|
{
|
||||||
|
"kind": "plain",
|
||||||
|
"remote": row.get("remote", ""),
|
||||||
|
"owner": row.get("org", ""),
|
||||||
|
"repo_name": row.get("repo", ""),
|
||||||
|
"user": "",
|
||||||
|
"token_value": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in sorted(remote_rows, key=lambda item: (item.get("remote", ""), item.get("kind", ""))):
|
||||||
|
token_value = str(row.get("token_value", ""))
|
||||||
|
rows.append(
|
||||||
|
{
|
||||||
|
"item": str(len(rows) + 1),
|
||||||
|
"source": "remote",
|
||||||
|
"kind": str(row.get("kind", "")),
|
||||||
|
"server": endpoint_values["server"],
|
||||||
|
"proto": endpoint_values["scheme"],
|
||||||
|
"host": endpoint_values["host"],
|
||||||
|
"owner": str(row.get("owner", "")),
|
||||||
|
"repo_name": str(row.get("repo_name", "")),
|
||||||
|
"user": str(row.get("user", "")),
|
||||||
|
"remote": str(row.get("remote", "")),
|
||||||
|
"token": short_secret(token_value) if token_value else "",
|
||||||
|
"valid": "",
|
||||||
|
"scope_mask": "",
|
||||||
|
"org_mask": "",
|
||||||
|
"repo_mask": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def fallback_project_by_endpoint(
|
def fallback_project_by_endpoint(
|
||||||
endpoint_names: list[str],
|
endpoint_names: list[str],
|
||||||
repo_servers: dict[str, dict],
|
repo_servers: dict[str, dict],
|
||||||
@@ -1750,7 +1856,7 @@ def safe_remote_url_label(remote_url: str | None) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def run_tokens_scan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
def run_tokens_scan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
repo_path = resolve_repo_argument(args.repo)
|
repo_path = resolve_launcher_repo_argument(args.repo)
|
||||||
token_data = load_token_store(config, write_normalized=False)
|
token_data = load_token_store(config, write_normalized=False)
|
||||||
store_servers = store_servers_from_tokens(token_data)
|
store_servers = store_servers_from_tokens(token_data)
|
||||||
report = scan_repo_remotes(config, repo_path)
|
report = scan_repo_remotes(config, repo_path)
|
||||||
@@ -1759,6 +1865,24 @@ def run_tokens_scan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
|||||||
print_token_item_rows(endpoint_names, repo_servers, store_servers, report["remote_rows"])
|
print_token_item_rows(endpoint_names, repo_servers, store_servers, report["remote_rows"])
|
||||||
|
|
||||||
|
|
||||||
|
def run_tokens_list(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
|
rows: list[dict[str, str]] = []
|
||||||
|
if args.target in {"store", "both"}:
|
||||||
|
token_data = load_token_store(config, write_normalized=False)
|
||||||
|
store_servers = store_servers_from_tokens(token_data)
|
||||||
|
rows.extend(list_store_rows(store_servers, args.server))
|
||||||
|
|
||||||
|
if args.target in {"remote", "both"}:
|
||||||
|
repo_root, repo_servers = collect_repo_server_entries(config, resolve_launcher_repo_argument(args.repo))
|
||||||
|
if repo_root is None:
|
||||||
|
raise SystemExit(f"Missing git repo at path: {resolve_launcher_repo_argument(args.repo)}")
|
||||||
|
rows.extend(list_remote_rows(repo_servers, args.server))
|
||||||
|
|
||||||
|
for index, row in enumerate(rows, start=1):
|
||||||
|
row["item"] = str(index)
|
||||||
|
print_fixed_table("tokens", TOKEN_LIST_COLUMNS, rows)
|
||||||
|
|
||||||
|
|
||||||
def run_tokens_read(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
def run_tokens_read(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
token_data = load_token_store(config, write_normalized=False)
|
token_data = load_token_store(config, write_normalized=False)
|
||||||
servers = token_store_servers(token_data)
|
servers = token_store_servers(token_data)
|
||||||
@@ -1804,7 +1928,7 @@ def run_tokens_read(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def run_tokens_stats(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))
|
repo_root, repo_servers = collect_repo_server_entries(config, resolve_launcher_repo_argument(args.repo))
|
||||||
token_data = load_token_store(config, write_normalized=False)
|
token_data = load_token_store(config, write_normalized=False)
|
||||||
store_servers = store_servers_from_tokens(token_data)
|
store_servers = store_servers_from_tokens(token_data)
|
||||||
endpoint_names = sorted(set(repo_servers) | set(store_servers))
|
endpoint_names = sorted(set(repo_servers) | set(store_servers))
|
||||||
@@ -2345,7 +2469,7 @@ def print_main_overview(config_path: Path) -> None:
|
|||||||
["list-cards", "[series]", "list cards in a selected series"],
|
["list-cards", "[series]", "list cards in a selected series"],
|
||||||
["tmux-container", "[series] [card]", "start tmux with container in pane 0"],
|
["tmux-container", "[series] [card]", "start tmux with container in pane 0"],
|
||||||
["submission", "[series] [card] --class K --nick N", "prepare answer repo and student branch"],
|
["submission", "[series] [card] --class K --nick N", "prepare answer repo and student branch"],
|
||||||
["tokens", "scan|sync|update|remove|add|read|stats|write", "manage tokens.json and Git remote credentials"],
|
["tokens", "scan|list|sync|update|remove|add|read|stats|write", "manage tokens.json and Git remote credentials"],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
print()
|
print()
|
||||||
@@ -2382,6 +2506,7 @@ def print_tokens_overview() -> None:
|
|||||||
["command", "common options", "direction", "purpose"],
|
["command", "common options", "direction", "purpose"],
|
||||||
[
|
[
|
||||||
["scan", "[--repo PATH]", "read-only", "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"],
|
||||||
|
["list", "store|remote|both", "read-only", "list one source without comparing it"],
|
||||||
["add", "REMOTE_ID", "tokens.json", "add an empty token skeleton for manual editing"],
|
["add", "REMOTE_ID", "tokens.json", "add an empty token skeleton for manual editing"],
|
||||||
["read", "[--server ENDPOINT]", "tokens.json", "show servers, remote ids and tokens"],
|
["read", "[--server ENDPOINT]", "tokens.json", "show servers, remote ids and tokens"],
|
||||||
["stats", "[--repo PATH]", "repo + tokens.json", "compare remote URLs with local token store"],
|
["stats", "[--repo PATH]", "repo + tokens.json", "compare remote URLs with local token store"],
|
||||||
@@ -2397,7 +2522,9 @@ def print_tokens_overview() -> None:
|
|||||||
print_table(
|
print_table(
|
||||||
["case", "command"],
|
["case", "command"],
|
||||||
[
|
[
|
||||||
["scan current repo", "./rvctl tokens scan"],
|
["scan launcher repo", "./rvctl tokens scan"],
|
||||||
|
["list token store", "./rvctl tokens list store"],
|
||||||
|
["list launcher remotes", "./rvctl tokens list remote"],
|
||||||
["read remote r1 into store", "./rvctl tokens sync remote r1"],
|
["read remote r1 into store", "./rvctl tokens sync remote r1"],
|
||||||
["refresh r1 metadata", "./rvctl tokens update r1"],
|
["refresh r1 metadata", "./rvctl tokens update r1"],
|
||||||
["write store r1 to remote", "./rvctl tokens sync store r1 --repo PATH"],
|
["write store r1 to remote", "./rvctl tokens sync store r1 --repo PATH"],
|
||||||
@@ -2500,7 +2627,21 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
"scan",
|
"scan",
|
||||||
help="Read repo remotes and tokens.json, then print paired token rows.",
|
help="Read repo remotes and tokens.json, then print paired token rows.",
|
||||||
)
|
)
|
||||||
add_repo_option(tokens_scan_parser)
|
tokens_scan_parser.add_argument(
|
||||||
|
"--repo",
|
||||||
|
help="Path inside a target git repo. Default: repo containing rvctl.",
|
||||||
|
)
|
||||||
|
|
||||||
|
tokens_list_parser = tokens_subparsers.add_parser(
|
||||||
|
"list",
|
||||||
|
help="List token store records, Git remotes, or both without comparing them.",
|
||||||
|
)
|
||||||
|
tokens_list_parser.add_argument("target", choices=["store", "remote", "both"], help="Source to list.")
|
||||||
|
tokens_list_parser.add_argument(
|
||||||
|
"--repo",
|
||||||
|
help="Path inside a target git repo for target remote/both. Default: repo containing rvctl.",
|
||||||
|
)
|
||||||
|
tokens_list_parser.add_argument("--server", help="Filter output to one server endpoint.")
|
||||||
|
|
||||||
tokens_read_parser = tokens_subparsers.add_parser(
|
tokens_read_parser = tokens_subparsers.add_parser(
|
||||||
"read",
|
"read",
|
||||||
@@ -2526,7 +2667,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
"stats",
|
"stats",
|
||||||
help="Print per-server token statistics from tokens.json.",
|
help="Print per-server token statistics from tokens.json.",
|
||||||
)
|
)
|
||||||
add_repo_option(tokens_stats_parser)
|
tokens_stats_parser.add_argument(
|
||||||
|
"--repo",
|
||||||
|
help="Path inside a target git repo. Default: repo containing rvctl.",
|
||||||
|
)
|
||||||
tokens_stats_parser.add_argument("--server", help="Filter output to one server endpoint.")
|
tokens_stats_parser.add_argument("--server", help="Filter output to one server endpoint.")
|
||||||
|
|
||||||
tokens_sync_parser = tokens_subparsers.add_parser(
|
tokens_sync_parser = tokens_subparsers.add_parser(
|
||||||
@@ -2621,6 +2765,9 @@ def main() -> int:
|
|||||||
if args.tokens_command == "scan":
|
if args.tokens_command == "scan":
|
||||||
run_tokens_scan(config, args)
|
run_tokens_scan(config, args)
|
||||||
return 0
|
return 0
|
||||||
|
if args.tokens_command == "list":
|
||||||
|
run_tokens_list(config, args)
|
||||||
|
return 0
|
||||||
if args.tokens_command == "add":
|
if args.tokens_command == "add":
|
||||||
run_tokens_add(config, args)
|
run_tokens_add(config, args)
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user