Add tabular rvctl command overview

This commit is contained in:
mpabi
2026-04-26 15:26:24 +02:00
parent 2b672d2137
commit f13b09c317
3 changed files with 176 additions and 0 deletions
+9
View File
@@ -71,6 +71,13 @@ rvctl.py
`rvctl` jest jedynym publicznym entrypointem. `rvctl.py` jest implementacja `rvctl` jest jedynym publicznym entrypointem. `rvctl.py` jest implementacja
uruchamiana przez wrapper i nie wymaga osobnego wywolywania przez ucznia. uruchamiana przez wrapper i nie wymaga osobnego wywolywania przez ucznia.
Uruchomienie bez argumentow pokazuje tabelaryczny skrot komend:
```bash
./rvctl
./rvctl tokens
```
## Autoryzacja ## Autoryzacja
Masz dwie drogi. Masz dwie drogi.
@@ -253,9 +260,11 @@ cd rv-launcher
Przyklady: Przyklady:
```bash ```bash
./rvctl
./rvctl show-config ./rvctl show-config
./rvctl list-series ./rvctl list-series
./rvctl list-cards inf ./rvctl list-cards inf
./rvctl tokens
./rvctl submission inf 03 --class 4i --nick u1 ./rvctl submission inf 03 --class 4i --nick u1
./rvctl tmux-container inf 03 --dry-run ./rvctl tmux-container inf 03 --dry-run
./rvctl tmux-container inf 03 --session rv-inf03 --attach ./rvctl tmux-container inf 03 --session rv-inf03 --attach
+25
View File
@@ -65,6 +65,14 @@ rvctl.py
`rvctl` jest jedynym publicznym entrypointem. `rvctl.py` jest implementacja `rvctl` jest jedynym publicznym entrypointem. `rvctl.py` jest implementacja
uruchamiana przez wrapper i nie wymaga osobnego wywolywania przez ucznia. uruchamiana przez wrapper i nie wymaga osobnego wywolywania przez ucznia.
Uruchomienie bez argumentow pokazuje tabelaryczny skrot komend:
```bash
./rvctl
./rvctl tokens
./rvctl help tokens
```
### Autoryzacja ### Autoryzacja
Masz dwie drogi. Masz dwie drogi.
@@ -174,6 +182,23 @@ Globalne przelaczniki:
- `--config PATH` - `--config PATH`
Uzywa innego pliku `workspace.json`. Uzywa innego pliku `workspace.json`.
Pomoc tabelaryczna:
```bash
./rvctl
./rvctl help
./rvctl tokens
./rvctl help tokens
```
Szczegolowy help parsera:
```bash
./rvctl --help
./rvctl <komenda> --help
./rvctl tokens <komenda> --help
```
Komendy: Komendy:
- `show-config` - `show-config`
+142
View File
@@ -1108,6 +1108,140 @@ def add_store_selection_options(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--token-name", help="Token name inside the selected user entry, for example 't1'.") parser.add_argument("--token-name", help="Token name inside the selected user entry, for example 't1'.")
def print_table(headers: list[str], rows: list[list[str]]) -> None:
widths = [len(header) for header in headers]
for row in rows:
for index, value in enumerate(row):
widths[index] = max(widths[index], len(value))
print(" ".join(header.ljust(widths[index]) for index, header in enumerate(headers)))
print(" ".join("-" * width for width in widths))
for row in rows:
print(" ".join(value.ljust(widths[index]) for index, value in enumerate(row)))
def print_config_summary(config_path: Path) -> None:
try:
config = load_config(config_path)
except (OSError, json.JSONDecodeError, KeyError, TypeError, ValueError) as error:
print("Config:")
print_table(
["field", "value"],
[
["config", str(config_path)],
["status", f"unavailable: {error}"],
],
)
return
print("Config:")
print_table(
["field", "value"],
[
["config", str(config.config_path)],
["workspace", str(config.workspace_root)],
["series", str(config.series_root)],
["tokens", str(config.token_path)],
],
)
def print_main_overview(config_path: Path) -> None:
print("RVCTL - launcher workspace RV")
print()
print("Usage:")
print(" ./rvctl <command> [options]")
print(" ./rvctl tokens <command> [options]")
print(" ./rvctl <command> --help")
print()
print("Commands:")
print_table(
["command", "arguments", "purpose"],
[
["show-config", "", "show resolved paths and Git defaults"],
["list-series", "", "list series from workspace"],
["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"],
],
)
print()
print("Typical flow:")
print_table(
["step", "command", "result"],
[
["1", "./rvctl tokens scan", "read credentials from remotes if present"],
["2", "./rvctl list-series", "choose a series"],
["3", "./rvctl list-cards inf", "choose a card"],
["4", "./rvctl tmux-container inf <card>", "start the working container"],
["5", "./rvctl submission inf <card> --class 4i --nick u1", "prepare answer remote and branch"],
],
)
print()
print_config_summary(config_path)
print()
print("Docs:")
print(" doc/rvctl.md")
print(" doc/tokens.md")
print(" doc/workspace.md")
def print_tokens_overview() -> None:
print("RVCTL tokens - token store and Git remote credentials")
print()
print("Usage:")
print(" ./rvctl tokens <command> [options]")
print(" ./rvctl tokens <command> --help")
print()
print("Commands:")
print_table(
["command", "common options", "direction", "purpose"],
[
["scan", "[--repo PATH]", "repo -> tokens.json", "scan remotes and store credentials found in URLs"],
["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"],
],
)
print()
print("Examples:")
print_table(
["case", "command"],
[
["scan current repo", "./rvctl tokens scan"],
["scan selected card", "./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/03"],
["compare state", "./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/03"],
["write auth to r1", "./rvctl tokens write --repo PATH --remote r1 --server URL --user u1 --token-name t1"],
],
)
def extract_config_path(argv: list[str]) -> tuple[Path, list[str]]:
config_path = Path(__file__).with_name("workspace.json")
remaining: list[str] = []
index = 0
while index < len(argv):
arg = argv[index]
if arg == "--config":
if index + 1 >= len(argv):
remaining.append(arg)
index += 1
continue
config_path = Path(argv[index + 1])
index += 2
continue
if arg.startswith("--config="):
config_path = Path(arg.split("=", 1)[1])
index += 1
continue
remaining.append(arg)
index += 1
return config_path, remaining
def build_parser() -> argparse.ArgumentParser: def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
prog="rvctl", prog="rvctl",
@@ -1223,6 +1357,14 @@ def build_parser() -> argparse.ArgumentParser:
def main() -> int: def main() -> int:
config_path, command_args = extract_config_path(sys.argv[1:])
if not command_args or command_args == ["help"]:
print_main_overview(config_path)
return 0
if command_args in (["tokens"], ["help", "tokens"], ["tokens", "help"]):
print_tokens_overview()
return 0
parser = build_parser() parser = build_parser()
args = parser.parse_args() args = parser.parse_args()
config = load_config(args.config) config = load_config(args.config)