Add token skeleton command

This commit is contained in:
mpabi
2026-04-26 21:56:07 +02:00
parent e9933d7385
commit d2d200257f
5 changed files with 123 additions and 17 deletions
+1
View File
@@ -159,6 +159,7 @@ Podstawowe komendy tokenow:
```bash
./rvctl tokens scan
./rvctl tokens add t1
./rvctl tokens read
./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/03
```
+41 -11
View File
@@ -291,9 +291,9 @@ Przyklady:
## `tokens scan`
Skanuje remote URL-e w repo oraz lokalny `tokens.json`, laczy wpisy w pary po
endpoincie, userze i wartosci tokena, a potem pokazuje jeden logiczny wiersz
na token.
Czyta remote URL-e w repo oraz lokalny `tokens.json`, laczy wpisy w pary po
endpoincie, nazwie remota, token id, wartosci tokena, org i repo, a potem
pokazuje jeden logiczny wiersz na token. Komenda jest read-only.
Przelaczniki:
@@ -304,9 +304,9 @@ Typowy wynik:
```text
tokens
item server proto host org repo user remote token_ref token valid scope org repo
---- ------ ----- ------------------ --------- ----------- ---- ------ --------- ------------ ----- awrop- oawrc- oawr--
1 gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 t1 * e59cc...13be forever +---+ +++++ ++++
item server proto host org repo user remote token_ref token valid scope org repo
---- ------ ----- ------------------ --------- ----------- ---- ------ --------- ------------ ------------------- aAimnopru oawrc- oawr--
1 gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 t1 * e59cc...13be forever -----w--- +++++ ++++
```
`token_ref` jest komorka stalej szerokosci: nazwa tokena jest po lewej, a marker
@@ -316,7 +316,8 @@ oraz uprawnienia zostaly wczytane. Marker `R` oznacza token tylko w remote, a
Maski uprawnien:
- `scope` ma pozycje `awrop`: `all`, `write:repository`, `read:repository`, organization scope, `public-only`
- `scope` ma pozycje `aAimnopru`: activitypub, admin, issue, misc, notification, organization, package, repository, user
- w `scope`: `w` oznacza read/write, `r` read, `-` brak dostepu
- `org` ma pozycje `oawrc`: owner, admin, write, read, create repo
- `repo` ma pozycje `oawr`: owner, admin, write, read
- `+` oznacza wlaczone, `-` wylaczone, `?` nie wczytano, `!` blad wczytania
@@ -329,6 +330,35 @@ Przyklad:
./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/03
```
## `tokens add TOKEN_ID`
Dodaje pusty szkielet tokena do `tokens.json`. Pole `value` jest puste i trzeba
je uzupelnic recznie przed uzyciem tokena.
Przelaczniki:
- `--server ENDPOINT`
Endpoint serwera. Domyslnie `git.base_url` z `workspace.json`.
- `--value TOKEN`
Opcjonalna wartosc tokena. Domyslnie pusta.
- `--user NAME`
Opcjonalny opis usera API.
- `--remote NAME`
Opcjonalny remote powiazany z tokenem.
- `--org NAME`
Opcjonalna organizacja dla remota.
- `--repo NAME`
Opcjonalne repo dla remota.
- `--dry-run`
Pokazuje plan bez zapisu.
Przyklady:
```bash
./rvctl tokens add r6
./rvctl tokens add t1 --remote r1 --org edu-tools --repo rv-launcher
```
## `tokens read`
Pokazuje zawartosc `tokens/tokens.json` w podziale na endpointy serwerow.
@@ -383,9 +413,9 @@ repo_root<TAB>...
token_path<TAB>...
tokens
item server proto host org repo user remote token_ref token valid scope org repo
---- ------ ----- ------------------ --------- ----------- ---- ------ --------- ------------ ----- awrop- oawrc- oawr--
1 gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 t1 * e59cc...13be forever +---+ +++++ ++++
item server proto host org repo user remote token_ref token valid scope org repo
---- ------ ----- ------------------ --------- ----------- ---- ------ --------- ------------ ------------------- aAimnopru oawrc- oawr--
1 gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 t1 * e59cc...13be forever -----w--- +++++ ++++
status
item<TAB>value
@@ -452,7 +482,7 @@ Przelaczniki:
- `--replace`
Nadpisuje inne auth przy `--from store`.
- `--dry-run`
Pokazuje plan bez zapisu przy `--from store`.
Pokazuje plan bez zapisu.
Przyklady:
+13
View File
@@ -161,6 +161,19 @@ Pokazuje zawartosc `tokens.json`.
./rvctl tokens read --show-secrets
```
### `tokens add`
Dodaje pusty szkielet tokena do `tokens.json`. Pole `value` zostaje puste, zeby
mozna bylo recznie wkleic sekret.
```bash
./rvctl tokens add t1
./rvctl tokens add r6 --server http://77.90.8.171:3001
./rvctl tokens add t1 --remote r1 --org edu-tools --repo rv-launcher
```
`tokens write` nie uzyje pustego tokena. Najpierw trzeba uzupelnic `value`.
### `tokens stats`
Pokazuje kontekst, tabele `tokens` i podsumowanie statusow endpointow.
+1 -2
View File
@@ -77,8 +77,7 @@
"minLength": 1
},
"value": {
"type": "string",
"minLength": 1
"type": "string"
},
"server": {
"$ref": "#/$defs/server"
+67 -4
View File
@@ -397,10 +397,12 @@ def normalize_v3_token(config: WorkspaceConfig, raw_token: dict) -> dict | None:
return None
token_id = raw_token.get("token_id") or raw_token.get("id") or raw_token.get("token_ref")
token_value = raw_token.get("value") or raw_token.get("token")
token_value = raw_token.get("value")
if token_value is None:
token_value = raw_token.get("token")
if not isinstance(token_id, str) or not token_id:
return None
if not isinstance(token_value, str) or not token_value:
if not isinstance(token_value, str):
return None
server_record = normalize_server_record(config, raw_token.get("server", {}))
@@ -1154,7 +1156,7 @@ def token_store_rows(store_servers: dict[str, dict]) -> list[dict[str, str]]:
for row in server_entry.get("tokens", []):
token_value = row.get("token_value", "")
token_id = row.get("token_id", "")
if token_value and token_id:
if token_id:
rows.append(
{
"endpoint": endpoint,
@@ -1620,6 +1622,48 @@ def run_tokens_stats(config: WorkspaceConfig, args: argparse.Namespace) -> None:
print_status_counts(status_counts)
def run_tokens_add(config: WorkspaceConfig, args: argparse.Namespace) -> None:
server_url = (args.server or config.git.base_url).rstrip("/")
server_info = server_info_from_url(config, server_url)
if server_info is None:
raise SystemExit(f"Only http/https server URLs are supported: {server_url}")
token_data = load_token_store(config)
existing_token = find_token_record(token_data, server_info["endpoint"], args.token_id)
if existing_token is not None:
raise SystemExit(f"Token already exists in tokens.json: {server_info['endpoint']}:{args.token_id}")
token_record = {
"token_id": args.token_id,
"value": args.value or "",
"server": server_record_from_info(server_info),
"remotes": [],
}
if args.user:
token_record["user"] = args.user
if args.remote:
token_record["remotes"].append(
{
"name": args.remote,
"org": args.org or "",
"repo": args.repo_name or "",
}
)
print(f"token_path\t{config.token_path}")
print(f"server\t{server_info['endpoint']}")
print(f"token_id\t{args.token_id}")
print(f"value\t{'set' if token_record['value'] else 'empty'}")
print(f"remote\t{args.remote or ''}")
print(f"status\t{'dry-run' if args.dry_run else 'added'}")
if args.dry_run:
return
token_data.setdefault("tokens", []).append(token_record)
write_token_store(config, token_data)
def run_tokens_write(config: WorkspaceConfig, args: argparse.Namespace) -> None:
repo_path = resolve_repo_argument(args.repo)
repo_root = git_repo_root(repo_path)
@@ -1648,6 +1692,8 @@ def run_tokens_write(config: WorkspaceConfig, args: argparse.Namespace) -> None:
server_endpoint = token_server_endpoint(token_record)
token_id = str(token_record.get("token_id", ""))
token_value = str(token_record.get("value", ""))
if not token_value:
raise SystemExit(f"Token value is empty in tokens.json: {server_endpoint}:{token_id}")
final_url = credentialed_remote_url(target_url, token_id, token_value)
status = "added" if existing_remote_url is None else "updated"
@@ -1971,7 +2017,7 @@ def print_main_overview(config_path: Path) -> None:
["list-cards", "[series]", "list cards in a selected series"],
["tmux-container", "[series] [card]", "start tmux with container in pane 0"],
["submission", "[series] [card] --class K --nick N", "prepare answer repo and student branch"],
["tokens", "scan|read|stats|write|update", "manage tokens.json and Git remote credentials"],
["tokens", "scan|add|read|stats|write|update", "manage tokens.json and Git remote credentials"],
],
)
print()
@@ -2008,6 +2054,7 @@ def print_tokens_overview() -> None:
["command", "common options", "direction", "purpose"],
[
["scan", "[--repo PATH]", "read-only", "print token table with remote/store marker and auth masks"],
["add", "TOKEN_ID", "tokens.json", "add an empty token skeleton for manual editing"],
["read", "[--server ENDPOINT]", "tokens.json", "show servers, token ids and remotes"],
["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"],
@@ -2126,6 +2173,19 @@ def build_parser() -> argparse.ArgumentParser:
tokens_read_parser.add_argument("--server", help="Filter output to one server endpoint.")
tokens_read_parser.add_argument("--show-secrets", action="store_true", help="Print full token values.")
tokens_add_parser = tokens_subparsers.add_parser(
"add",
help="Add an empty token skeleton to tokens.json.",
)
tokens_add_parser.add_argument("token_id", help="Token id, for example 't1' or 'r6'.")
tokens_add_parser.add_argument("--server", help="Server endpoint. Default: git.base_url from workspace.json.")
tokens_add_parser.add_argument("--value", default="", help="Optional token value. Default: empty for manual editing.")
tokens_add_parser.add_argument("--user", help="Optional API user label.")
tokens_add_parser.add_argument("--remote", help="Optional remote name to attach to this token.")
tokens_add_parser.add_argument("--org", help="Optional remote organization.")
tokens_add_parser.add_argument("--repo", dest="repo_name", help="Optional remote repository.")
tokens_add_parser.add_argument("--dry-run", action="store_true", help="Print the planned token without writing it.")
tokens_stats_parser = tokens_subparsers.add_parser(
"stats",
help="Print per-server token statistics from tokens.json.",
@@ -2197,6 +2257,9 @@ def main() -> int:
if args.tokens_command == "scan":
run_tokens_scan(config, args)
return 0
if args.tokens_command == "add":
run_tokens_add(config, args)
return 0
if args.tokens_command == "read":
run_tokens_read(config, args)
return 0