Add default series and card commands

This commit is contained in:
mpabi
2026-05-01 23:00:32 +02:00
parent 40cfa61f4c
commit 0505b829a1
4 changed files with 215 additions and 10 deletions
+110 -8
View File
@@ -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 <card> or ./rvctl card use <series> <card>")
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 '<card>' or '<series> <card>', 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