From 0505b829a1e7a11b28739fc947d43a3646987c24 Mon Sep 17 00:00:00 2001 From: mpabi Date: Fri, 1 May 2026 23:00:32 +0200 Subject: [PATCH] Add default series and card commands --- README.md | 12 ++++- doc/rvctl.md | 59 +++++++++++++++++++++++++ doc/series.md | 36 +++++++++++++++ rvctl.py | 118 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 215 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 75c0f0d..c32dc66 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,7 @@ inf 3 0 Po wybraniu serii `inf` wylistuj karty: ```bash +./rvctl series use inf ./rvctl series cards list inf ``` @@ -335,12 +336,19 @@ bss lab-rv32i-strlen-bss-data-stack source rv32i-c / bss-data-stack ``` Przykładowa karta `bss` odpowiada repo `lab-rv32i-strlen-bss-data-stack`. -Pobierz ją do workspace: +Ustaw ją jako domyślną i pobierz do workspace: ```bash +./rvctl card use bss ./rvctl series cards fetch inf bss ``` +Jeżeli chcesz jedną komendą ustawić serię i kartę, użyj: + +```bash +./rvctl card use inf bss +``` + Karty trafiają do `series` w workspace: ```text @@ -370,7 +378,7 @@ answer_url http://77.90.8.171:3001/c2025-1a-inf/lab-rv32i-strlen-bss-data-st ``` Następnie wylistuj zadania w karcie. Skrót `tasks` działa na domyślnej serii i -karcie z `workspace.json`, czyli tutaj na `inf bss`: +karcie ustawionej w `workspace.json`, czyli tutaj na `inf bss`: ```bash ./rvctl tasks list diff --git a/doc/rvctl.md b/doc/rvctl.md index 04f1fbd..62865e4 100644 --- a/doc/rvctl.md +++ b/doc/rvctl.md @@ -211,8 +211,11 @@ Komendy: - `list-series` - `list-cards [series]` - `series list` +- `series use SERIES` - `series show SERIES` - `series fetch SERIES [--dry-run]` +- `card use CARD` +- `card use SERIES CARD` - `series cards list SERIES` - `series cards show SERIES CARD` - `series cards fetch SERIES CARD` @@ -313,6 +316,30 @@ Przykład: ./rvctl series list ``` +## `series use SERIES` + +Ustawia `defaults.series` w `workspace.json`. + +Argumenty: + +- `SERIES` + Id serii, na przykład `inf`. + +Przykład: + +```bash +./rvctl series use inf +``` + +Typowy wynik: + +```text +old_series... +seriesinf +statusupdated|unchanged +config... +``` + ## `series show SERIES` Pokazuje szczegóły jednej serii. @@ -373,6 +400,38 @@ Przykład: ./rvctl series cards list inf ``` +## `card use CARD` + +Ustawia `defaults.card` w `workspace.json` dla aktualnie domyślnej serii. + +Argumenty: + +- `CARD` + Pełna nazwa repo albo unikalny fragment, na przykład `bss`. + +Przykład: + +```bash +./rvctl card use bss +``` + +## `card use SERIES CARD` + +Ustawia jednocześnie `defaults.series` i `defaults.card` w `workspace.json`. + +Argumenty: + +- `SERIES` + Id serii, na przykład `inf`. +- `CARD` + Pełna nazwa repo albo unikalny fragment, na przykład `bss`. + +Przykład: + +```bash +./rvctl card use inf bss +``` + ## `series cards show SERIES CARD` Pokazuje szczegóły jednej karty. diff --git a/doc/series.md b/doc/series.md index 9eceec2..b9f56b0 100644 --- a/doc/series.md +++ b/doc/series.md @@ -23,7 +23,9 @@ używa starego fallbacku przez `original_series_root`. ```bash ./rvctl tokens compare ./rvctl series list +./rvctl series use inf ./rvctl series cards list inf +./rvctl card use bss ./rvctl series cards fetch inf bss ./rvctl tasks list ./rvctl tasks switch 1 @@ -62,6 +64,15 @@ Pokazuje informacje o serii. ./rvctl series show inf ``` +### `series use SERIES` + +Ustawia domyślną serię w `workspace.json`. Po tej komendzie skróty kart i +zadań mogą korzystać z tej serii bez podawania jej za każdym razem. + +```bash +./rvctl series use inf +``` + ## Karty ### `series cards list SERIES` @@ -81,6 +92,31 @@ bss lab-rv32i-strlen-bss-data-stack source rv32i-c / bss-data-stack Pierwsza kolumna jest krótką nazwą karty. Pełna nazwa repo zostaje w drugiej kolumnie. +### `card use CARD` + +Ustawia domyślną kartę w aktualnie domyślnej serii. Dla serii `inf`: + +```bash +./rvctl card use bss +``` + +Komenda zapisuje `defaults.card` w `workspace.json`. + +### `card use SERIES CARD` + +Ustawia jednocześnie domyślną serię i domyślną kartę: + +```bash +./rvctl card use inf bss +``` + +To jest odpowiednik: + +```bash +./rvctl series use inf +./rvctl card use bss +``` + ### `series cards fetch SERIES CARD` Pobiera albo aktualizuje jedną kartę w workspace. `CARD` może być krótką nazwą diff --git a/rvctl.py b/rvctl.py index 2c271a9..582cbed 100755 --- a/rvctl.py +++ b/rvctl.py @@ -185,6 +185,40 @@ def load_config(config_path: Path) -> WorkspaceConfig: ) +def write_workspace_config(config: WorkspaceConfig, raw_config: dict) -> None: + config.config_path.parent.mkdir(parents=True, exist_ok=True) + temp_path = config.config_path.with_suffix(config.config_path.suffix + ".tmp") + temp_path.write_text(json.dumps(raw_config, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + temp_path.replace(config.config_path) + + +def update_config_defaults(config: WorkspaceConfig, updates: dict[str, str]) -> dict[str, str]: + raw_config = json.loads(config.config_path.read_text(encoding="utf-8")) + defaults = raw_config.get("defaults") + if not isinstance(defaults, dict): + defaults = {} + raw_config["defaults"] = defaults + + result: dict[str, str] = {} + changed = False + for key, value in updates.items(): + old_value = str(defaults.get(key, "")) + if old_value != value: + defaults[key] = value + changed = True + config.defaults[key] = value + result[f"old_{key}"] = old_value + result[key] = value + + if changed: + write_workspace_config(config, raw_config) + result["status"] = "updated" + else: + result["status"] = "unchanged" + result["config"] = str(config.config_path) + return result + + def workspace_info_root(config: WorkspaceConfig) -> Path: return config.workspace_info_path.parent @@ -3100,10 +3134,57 @@ def print_card_details(config: WorkspaceConfig, series_name: str, card_name: str print(f"tasks\t{' '.join(task_names(target_path))}") +def available_series_hint(config: WorkspaceConfig) -> str: + names = sorted( + set(workspace_series_manifests(config)) + | {path.name for path in source_series_directories(config)} + | {path.name for path in series_directories(config)} + ) + return ", ".join(names) + + +def use_series_default(config: WorkspaceConfig, series_name: str) -> dict[str, str]: + series_id = slug_value(series_name, "series") + if not series_exists(config, series_id): + hint = available_series_hint(config) + suffix = f" Available: {hint}" if hint else "" + raise SystemExit(f"Missing series '{series_name}'.{suffix}") + return update_config_defaults(config, {"series": series_id}) + + +def use_card_default(config: WorkspaceConfig, selector: list[str]) -> dict[str, str]: + if len(selector) == 1: + series_name = config.defaults.get("series") + if not series_name: + raise SystemExit("Missing default series. Use: ./rvctl series use inf") + card_name = selector[0] + updates_series = False + elif len(selector) == 2: + series_name = slug_value(selector[0], "series") + card_name = selector[1] + updates_series = True + else: + raise SystemExit("Use: ./rvctl card use or ./rvctl card use ") + + if not series_exists(config, series_name): + hint = available_series_hint(config) + suffix = f" Available: {hint}" if hint else "" + raise SystemExit(f"Missing series '{series_name}'.{suffix}") + + card_info = resolve_card_info(config, series_name, card_name) + updates = {"card": card_info.card_id} + if updates_series: + updates = {"series": series_name, "card": card_info.card_id} + return update_config_defaults(config, updates) + + def run_series(config: WorkspaceConfig, args: argparse.Namespace) -> None: if args.series_command == "list": print_series(config) return + if args.series_command == "use": + print_key_values(use_series_default(config, args.series)) + return if args.series_command == "show": print_series_details(config, args.series) return @@ -3143,6 +3224,13 @@ def run_series(config: WorkspaceConfig, args: argparse.Namespace) -> None: raise SystemExit(f"Unsupported series cards tasks command: {args.tasks_command}") +def run_card(config: WorkspaceConfig, args: argparse.Namespace) -> None: + if args.card_command == "use": + print_key_values(use_card_default(config, args.selector)) + return + raise SystemExit(f"Unsupported card command: {args.card_command}") + + def resolve_task_command_selector( config: WorkspaceConfig, values: list[str], @@ -3736,7 +3824,8 @@ def print_main_overview(config_path: Path) -> None: [ ["show-config", "", "show resolved paths and Git defaults"], ["workspace", "show|sync", "show or update meta/workspace-info"], - ["series", "list|cards|fetch|tasks", "work with series, cards and tasks"], + ["series", "list|use|cards|fetch|tasks", "work with series, cards and tasks"], + ["card", "use [series] card", "set the default card, optionally with the default series"], ["tasks", "list|show|switch", "shortcut for tasks in the default card"], ["env", "list|sync|build", "manage rv32i/host container environment"], ["debug", "rv32i|host [selector]", "open nvim + gdb in the selected container profile"], @@ -3757,12 +3846,14 @@ def print_main_overview(config_path: Path) -> None: ["1", "./rvctl tokens compare", "compare repo remotes with tokens.json"], ["2", "./rvctl workspace sync", "clone or update workspace-info"], ["3", "./rvctl series list", "choose a series"], - ["4", "./rvctl series cards list inf", "choose a card"], - ["5", "./rvctl series cards fetch inf bss", "fetch card and prepare r1a"], - ["6", "./rvctl tasks list", "list tasks from the default card"], - ["7", "./rvctl tasks switch 4", "switch to the answer branch for task 4"], - ["8", "./rvctl debug rv32i 4", "debug task 4 in RV32I/Hazard3"], - ["9", "./rvctl debug host 4", "debug task 4 natively in a host container"], + ["4", "./rvctl series use inf", "set the default series"], + ["5", "./rvctl series cards list inf", "choose a card"], + ["6", "./rvctl card use bss", "set the default card"], + ["7", "./rvctl series cards fetch inf bss", "fetch card and prepare r1a"], + ["8", "./rvctl tasks list", "list tasks from the default card"], + ["9", "./rvctl tasks switch 4", "switch to the answer branch for task 4"], + ["10", "./rvctl debug rv32i 4", "debug task 4 in RV32I/Hazard3"], + ["11", "./rvctl debug host 4", "debug task 4 natively in a host container"], ], ) print() @@ -3914,6 +4005,11 @@ def build_parser() -> argparse.ArgumentParser: tasks_short_switch_parser.add_argument("--branch", help="Override the branch name.") tasks_short_switch_parser.add_argument("--dry-run", action="store_true", help="Print plan without switching branches.") + card_parser = subparsers.add_parser("card", help="Manage the default card selector.") + card_subparsers = card_parser.add_subparsers(dest="card_command", required=True) + card_use_parser = card_subparsers.add_parser("use", help="Set the default card, optionally with the default series.") + card_use_parser.add_argument("selector", nargs="+", help="Use '' or ' ', for example 'bss' or 'inf bss'.") + tmux_parser = subparsers.add_parser( "tmux-container", help="Create a tmux session with pane 0 running the container shell for a selected card.", @@ -3959,6 +4055,9 @@ def build_parser() -> argparse.ArgumentParser: series_subparsers.add_parser("list", help="List available series.") + series_use_parser = series_subparsers.add_parser("use", help="Set the default series.") + series_use_parser.add_argument("series", help="Series id, for example 'inf'.") + series_show_parser = series_subparsers.add_parser("show", help="Show one series.") series_show_parser.add_argument("series", help="Series id, for example 'inf'.") @@ -4138,7 +4237,7 @@ def main() -> int: parser = build_parser() args = parser.parse_args() config = load_config(args.config) - if args.command in {"list-series", "list-cards", "series", "tasks", "tmux-container", "submission", "debug", "run", "shell"}: + if args.command in {"list-series", "list-cards", "series", "card", "tasks", "tmux-container", "submission", "debug", "run", "shell"}: ensure_workspace_info(config) if args.command == "show-config": @@ -4168,6 +4267,9 @@ def main() -> int: if args.command == "tasks": run_tasks(config, args) return 0 + if args.command == "card": + run_card(config, args) + return 0 if args.command == "tmux-container": run_tmux_container(config, args) return 0