Add submission repo planning for card repos
This commit is contained in:
@@ -5,6 +5,7 @@ To repo zawiera launcher do workspace `rv`:
|
|||||||
- `workspace.json` trzyma rooty workspace, socketow i toolingu
|
- `workspace.json` trzyma rooty workspace, socketow i toolingu
|
||||||
- `workspace.py` listuje serie i karty z dysku
|
- `workspace.py` listuje serie i karty z dysku
|
||||||
- `workspace.py tmux-container ...` stawia sesje `tmux` z kontenerem w `pane 0`
|
- `workspace.py tmux-container ...` stawia sesje `tmux` z kontenerem w `pane 0`
|
||||||
|
- `workspace.py submission ...` wylicza repo odpowiedzi w `zsl-inf` i branch ucznia
|
||||||
- `doc/usage.md` opisuje przelaczniki CLI i format listowania
|
- `doc/usage.md` opisuje przelaczniki CLI i format listowania
|
||||||
|
|
||||||
## Przygotowanie katalogu
|
## Przygotowanie katalogu
|
||||||
@@ -41,14 +42,6 @@ git fetch r1 main
|
|||||||
git switch --track -c main r1/main
|
git switch --track -c main r1/main
|
||||||
```
|
```
|
||||||
|
|
||||||
Jesli po zajeciach uzytkownik bedzie chcial wyslac projekt na swoje konto
|
|
||||||
Gitea, lepiej dodac osobny remote `r2`, zamiast przepinac `r1`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git remote add r2 http://LOGIN:TOKEN@77.90.8.171:3001/LOGIN/rv-launcher.git
|
|
||||||
git push -u r2 main
|
|
||||||
```
|
|
||||||
|
|
||||||
### Droga 2: lokalny `tokens/gitea_tokens.json`
|
### Droga 2: lokalny `tokens/gitea_tokens.json`
|
||||||
|
|
||||||
To jest wariant alternatywny, wygodniejszy wtedy, gdy launcher ma sam wykonywac
|
To jest wariant alternatywny, wygodniejszy wtedy, gdy launcher ma sam wykonywac
|
||||||
@@ -127,6 +120,55 @@ git fetch origin feat/x
|
|||||||
git switch --track -c feat/x origin/feat/x
|
git switch --track -c feat/x origin/feat/x
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Repo odpowiedzi kart pracy
|
||||||
|
|
||||||
|
Model pracy kart jest taki:
|
||||||
|
|
||||||
|
- `r1` wskazuje repo z materialem z `edu-inf`
|
||||||
|
- `a1` wskazuje wspolne repo odpowiedzi w `zsl-inf`
|
||||||
|
- kazdy uczen wysyla swoja prace na branch o nazwie swojego nicku
|
||||||
|
|
||||||
|
Repo odpowiedzi nie zawiera nicku w nazwie. Launcher buduje je w formacie:
|
||||||
|
|
||||||
|
```text
|
||||||
|
zsl-inf/<repo_z_edu-inf>-<klasa>-<data>
|
||||||
|
```
|
||||||
|
|
||||||
|
Przyklad:
|
||||||
|
|
||||||
|
```text
|
||||||
|
zsl-inf/lab-rv32i-strlen-bss-data-stack-4i-2026-04-26
|
||||||
|
```
|
||||||
|
|
||||||
|
Przyklad planu dla ucznia `u1`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 workspace.py submission inf 03 --class 4i --nick u1
|
||||||
|
```
|
||||||
|
|
||||||
|
Przyklad konfiguracji remote'ow w repo karty:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 workspace.py submission inf 03 --class 4i --nick u1 --apply
|
||||||
|
```
|
||||||
|
|
||||||
|
Launcher wtedy:
|
||||||
|
|
||||||
|
- czyta URL zrodlowego repo z `origin` aktualnej karty i ustawia go jako `r1`
|
||||||
|
- bierze basename tego repo z `edu-inf` i z niego buduje nazwe repo odpowiedzi
|
||||||
|
- wylicza repo odpowiedzi w `zsl-inf` i ustawia je jako `a1`
|
||||||
|
- proponuje branch ucznia, na przyklad `u1`
|
||||||
|
|
||||||
|
Typowy wynik to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
source_repo edu-inf/lab-rv32i-strlen-bss-data-stack
|
||||||
|
source_remote r1
|
||||||
|
answer_remote a1
|
||||||
|
answer_repo zsl-inf/lab-rv32i-strlen-bss-data-stack-4i-2026-04-26
|
||||||
|
student_branch u1
|
||||||
|
```
|
||||||
|
|
||||||
## Wariant alternatywny: `git clone`
|
## Wariant alternatywny: `git clone`
|
||||||
|
|
||||||
Jesli celem nie jest cwiczenie `remote add` i `fetch`, mozna tez uzyc:
|
Jesli celem nie jest cwiczenie `remote add` i `fetch`, mozna tez uzyc:
|
||||||
@@ -146,6 +188,7 @@ Przyklady:
|
|||||||
python3 workspace.py show-config
|
python3 workspace.py show-config
|
||||||
python3 workspace.py list-series
|
python3 workspace.py list-series
|
||||||
python3 workspace.py list-cards inf
|
python3 workspace.py list-cards inf
|
||||||
|
python3 workspace.py submission inf 03 --class 4i --nick u1
|
||||||
python3 workspace.py tmux-container inf 03 --dry-run
|
python3 workspace.py tmux-container inf 03 --dry-run
|
||||||
python3 workspace.py tmux-container inf 03 --session rv-inf03 --attach
|
python3 workspace.py tmux-container inf 03 --session rv-inf03 --attach
|
||||||
```
|
```
|
||||||
|
|||||||
+76
-10
@@ -39,15 +39,7 @@ Przyklad:
|
|||||||
```bash
|
```bash
|
||||||
git remote add r1 http://u1:TOKEN@77.90.8.171:3001/edu-tools/rv-launcher.git
|
git remote add r1 http://u1:TOKEN@77.90.8.171:3001/edu-tools/rv-launcher.git
|
||||||
git fetch r1 main
|
git fetch r1 main
|
||||||
git switch main
|
git switch --track -c main r1/main
|
||||||
git pull --ff-only r1 main
|
|
||||||
```
|
|
||||||
|
|
||||||
Jesli po zajeciach uzytkownik bedzie chcial wysylac projekty na swoje konto
|
|
||||||
Gitea, moze przepiac remote `r1` na wlasne repo:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git remote set-url r1 http://LOGIN:TOKEN@77.90.8.171:3001/LOGIN/rv-launcher.git
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Droga 2: lokalny `tokens/gitea_tokens.json`
|
#### Droga 2: lokalny `tokens/gitea_tokens.json`
|
||||||
@@ -103,7 +95,7 @@ Wariant przez `r1`:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
git fetch r1 main
|
git fetch r1 main
|
||||||
git switch main
|
git switch --track -c main r1/main
|
||||||
git pull --ff-only r1 main
|
git pull --ff-only r1 main
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -137,6 +129,7 @@ Komendy:
|
|||||||
- `show-config`
|
- `show-config`
|
||||||
- `list-series`
|
- `list-series`
|
||||||
- `list-cards [series]`
|
- `list-cards [series]`
|
||||||
|
- `submission [series] [card]`
|
||||||
- `tmux-container [series] [card]`
|
- `tmux-container [series] [card]`
|
||||||
|
|
||||||
## `show-config`
|
## `show-config`
|
||||||
@@ -151,6 +144,13 @@ workspace_root<TAB>...
|
|||||||
series_root<TAB>...
|
series_root<TAB>...
|
||||||
socket_root<TAB>...
|
socket_root<TAB>...
|
||||||
tools_root<TAB>...
|
tools_root<TAB>...
|
||||||
|
git_base_url<TAB>...
|
||||||
|
git_source_org<TAB>...
|
||||||
|
git_answer_org<TAB>...
|
||||||
|
git_source_remote<TAB>...
|
||||||
|
git_answer_remote<TAB>...
|
||||||
|
git_origin_remote<TAB>...
|
||||||
|
git_fallback_branch<TAB>...
|
||||||
tools_root_candidates
|
tools_root_candidates
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
@@ -206,6 +206,72 @@ python3 workspace.py list-cards
|
|||||||
python3 workspace.py list-cards inf
|
python3 workspace.py list-cards inf
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `submission [series] [card]`
|
||||||
|
|
||||||
|
Wylicza flow oddawania rozwiazan:
|
||||||
|
|
||||||
|
- `r1` jako repo z materialem z `edu-inf`
|
||||||
|
- `a1` jako repo odpowiedzi w `zsl-inf`
|
||||||
|
- branch ucznia na podstawie jego nicku
|
||||||
|
|
||||||
|
Nazwa repo odpowiedzi jest budowana z nazwy repo zrodlowego z `edu-inf`, klasy
|
||||||
|
i daty:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<repo_z_edu-inf>-<klasa>-<data>
|
||||||
|
```
|
||||||
|
|
||||||
|
Przyklad:
|
||||||
|
|
||||||
|
```text
|
||||||
|
lab-rv32i-strlen-bss-data-stack-4i-2026-04-26
|
||||||
|
```
|
||||||
|
|
||||||
|
Argumenty pozycyjne:
|
||||||
|
|
||||||
|
- `series`
|
||||||
|
Id serii albo pelny selector, na przyklad `inf` albo `inf/03`.
|
||||||
|
- `card`
|
||||||
|
Numer karty, na przyklad `03`.
|
||||||
|
|
||||||
|
Przelaczniki:
|
||||||
|
|
||||||
|
- `--class NAME`
|
||||||
|
Id klasy, na przyklad `4i`.
|
||||||
|
- `--nick NAME`
|
||||||
|
Nick ucznia. Domyslnie z niego powstaje nazwa brancha.
|
||||||
|
- `--branch NAME`
|
||||||
|
Nadpisuje domyslna nazwe brancha.
|
||||||
|
- `--date YYYY-MM-DD`
|
||||||
|
Data zajec uzywana w nazwie repo odpowiedzi. Domyslnie dzisiejsza.
|
||||||
|
- `--source-url URL`
|
||||||
|
Nadpisuje URL repo zrodlowego. Bez tego launcher czyta `origin` z repo karty.
|
||||||
|
- `--apply`
|
||||||
|
Dodaje albo aktualizuje remote `r1` i `a1` w repo karty.
|
||||||
|
|
||||||
|
Typowy format wyjscia:
|
||||||
|
|
||||||
|
```text
|
||||||
|
selector<TAB>inf/03
|
||||||
|
card_path<TAB>...
|
||||||
|
source_repo<TAB>edu-inf/lab-rv32i-strlen-bss-data-stack
|
||||||
|
source_remote<TAB>r1
|
||||||
|
source_url<TAB>http://77.90.8.171:3001/edu-inf/lab-rv32i-strlen-bss-data-stack.git
|
||||||
|
source_branch<TAB>deploy
|
||||||
|
answer_remote<TAB>a1
|
||||||
|
answer_repo<TAB>zsl-inf/lab-rv32i-strlen-bss-data-stack-4i-2026-04-26
|
||||||
|
answer_url<TAB>http://77.90.8.171:3001/zsl-inf/lab-rv32i-strlen-bss-data-stack-4i-2026-04-26.git
|
||||||
|
student_branch<TAB>u1
|
||||||
|
```
|
||||||
|
|
||||||
|
Przyklady:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 workspace.py submission inf 03 --class 4i --nick u1
|
||||||
|
python3 workspace.py submission inf 03 --class 4i --nick u1 --apply
|
||||||
|
python3 workspace.py submission inf/03 --class 4i --nick u2 --date 2026-04-26
|
||||||
|
```
|
||||||
|
|
||||||
## `tmux-container [series] [card]`
|
## `tmux-container [series] [card]`
|
||||||
|
|
||||||
Tworzy nowa sesje `tmux` i uruchamia kontener w `pane 0`.
|
Tworzy nowa sesje `tmux` i uruchamia kontener w `pane 0`.
|
||||||
|
|||||||
@@ -7,6 +7,15 @@
|
|||||||
"~/dev/workspace/tools/rv32i-hazard3-env",
|
"~/dev/workspace/tools/rv32i-hazard3-env",
|
||||||
"~/dev/workspace/rv/tools/rv32i-hazard3-env"
|
"~/dev/workspace/rv/tools/rv32i-hazard3-env"
|
||||||
],
|
],
|
||||||
|
"git": {
|
||||||
|
"base_url": "http://77.90.8.171:3001",
|
||||||
|
"source_org": "edu-inf",
|
||||||
|
"answer_org": "zsl-inf",
|
||||||
|
"source_remote": "r1",
|
||||||
|
"answer_remote": "a1",
|
||||||
|
"origin_remote": "origin",
|
||||||
|
"fallback_branch": "main"
|
||||||
|
},
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"series": "inf",
|
"series": "inf",
|
||||||
"card": "03",
|
"card": "03",
|
||||||
|
|||||||
+185
@@ -2,8 +2,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import datetime as dt
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -19,6 +21,17 @@ def expand_path(raw_path: str, base_dir: Path) -> Path:
|
|||||||
return path.resolve()
|
return path.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GitConfig:
|
||||||
|
base_url: str
|
||||||
|
source_org: str
|
||||||
|
answer_org: str
|
||||||
|
source_remote: str
|
||||||
|
answer_remote: str
|
||||||
|
origin_remote: str
|
||||||
|
fallback_branch: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WorkspaceConfig:
|
class WorkspaceConfig:
|
||||||
config_path: Path
|
config_path: Path
|
||||||
@@ -28,6 +41,7 @@ class WorkspaceConfig:
|
|||||||
tools_root_candidates: list[Path]
|
tools_root_candidates: list[Path]
|
||||||
tools_root: Path
|
tools_root: Path
|
||||||
defaults: dict[str, str]
|
defaults: dict[str, str]
|
||||||
|
git: GitConfig
|
||||||
|
|
||||||
|
|
||||||
def load_config(config_path: Path) -> WorkspaceConfig:
|
def load_config(config_path: Path) -> WorkspaceConfig:
|
||||||
@@ -45,6 +59,16 @@ def load_config(config_path: Path) -> WorkspaceConfig:
|
|||||||
|
|
||||||
tools_root = next((path for path in tools_root_candidates if path.exists()), tools_root_candidates[0])
|
tools_root = next((path for path in tools_root_candidates if path.exists()), tools_root_candidates[0])
|
||||||
defaults = raw.get("defaults", {})
|
defaults = raw.get("defaults", {})
|
||||||
|
git_raw = raw.get("git", {})
|
||||||
|
git = GitConfig(
|
||||||
|
base_url=git_raw.get("base_url", "http://77.90.8.171:3001").rstrip("/"),
|
||||||
|
source_org=git_raw.get("source_org", "edu-inf"),
|
||||||
|
answer_org=git_raw.get("answer_org", "zsl-inf"),
|
||||||
|
source_remote=git_raw.get("source_remote", "r1"),
|
||||||
|
answer_remote=git_raw.get("answer_remote", "a1"),
|
||||||
|
origin_remote=git_raw.get("origin_remote", "origin"),
|
||||||
|
fallback_branch=git_raw.get("fallback_branch", "main"),
|
||||||
|
)
|
||||||
|
|
||||||
return WorkspaceConfig(
|
return WorkspaceConfig(
|
||||||
config_path=config_path,
|
config_path=config_path,
|
||||||
@@ -54,6 +78,7 @@ def load_config(config_path: Path) -> WorkspaceConfig:
|
|||||||
tools_root_candidates=tools_root_candidates,
|
tools_root_candidates=tools_root_candidates,
|
||||||
tools_root=tools_root,
|
tools_root=tools_root,
|
||||||
defaults=defaults,
|
defaults=defaults,
|
||||||
|
git=git,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -138,6 +163,129 @@ def print_cards(config: WorkspaceConfig, series_arg: str | None) -> None:
|
|||||||
print(f"{card_path.name}\t{card_path}")
|
print(f"{card_path.name}\t{card_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def slug_value(raw_value: str, field_name: str) -> str:
|
||||||
|
slug = re.sub(r"[^a-z0-9._-]+", "-", raw_value.strip().lower()).strip("-")
|
||||||
|
if not slug:
|
||||||
|
raise SystemExit(f"Empty {field_name} after slug conversion: {raw_value!r}")
|
||||||
|
return slug
|
||||||
|
|
||||||
|
|
||||||
|
def git_capture(repo_path: Path, git_args: list[str]) -> str | None:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "-C", str(repo_path)] + git_args,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return None
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def git_current_branch(config: WorkspaceConfig, repo_path: Path) -> str:
|
||||||
|
branch_name = git_capture(repo_path, ["branch", "--show-current"])
|
||||||
|
if branch_name:
|
||||||
|
return branch_name
|
||||||
|
|
||||||
|
head_ref = git_capture(repo_path, ["symbolic-ref", "--short", f"refs/remotes/{config.git.origin_remote}/HEAD"])
|
||||||
|
if head_ref and "/" in head_ref:
|
||||||
|
return head_ref.split("/", 1)[1]
|
||||||
|
|
||||||
|
return config.git.fallback_branch
|
||||||
|
|
||||||
|
|
||||||
|
def fallback_source_repo_name(series_name: str, card_name: str) -> str:
|
||||||
|
return "-".join([slug_value(series_name, "series"), slug_value(card_name, "card")])
|
||||||
|
|
||||||
|
|
||||||
|
def fallback_source_url(config: WorkspaceConfig, series_name: str, card_name: str) -> str:
|
||||||
|
repo_name = fallback_source_repo_name(series_name, card_name)
|
||||||
|
return f"{config.git.base_url}/{config.git.source_org}/{repo_name}.git"
|
||||||
|
|
||||||
|
|
||||||
|
def discover_source_url(config: WorkspaceConfig, repo_path: Path, series_name: str, card_name: str) -> str:
|
||||||
|
source_url = git_capture(repo_path, ["remote", "get-url", config.git.origin_remote])
|
||||||
|
if source_url:
|
||||||
|
return source_url
|
||||||
|
|
||||||
|
source_url = git_capture(repo_path, ["remote", "get-url", config.git.source_remote])
|
||||||
|
if source_url:
|
||||||
|
return source_url
|
||||||
|
|
||||||
|
return fallback_source_url(config, series_name, card_name)
|
||||||
|
|
||||||
|
|
||||||
|
def repo_name_from_url(remote_url: str) -> str:
|
||||||
|
repo_name = remote_url.rstrip("/").rsplit("/", 1)[-1]
|
||||||
|
if repo_name.endswith(".git"):
|
||||||
|
repo_name = repo_name[:-4]
|
||||||
|
return slug_value(repo_name, "source repo")
|
||||||
|
|
||||||
|
|
||||||
|
def build_answer_repo_name(source_repo_name: str, class_name: str, date_value: str) -> str:
|
||||||
|
return "-".join([source_repo_name, slug_value(class_name, "class"), date_value])
|
||||||
|
|
||||||
|
|
||||||
|
def build_answer_url(config: WorkspaceConfig, answer_repo_name: str) -> str:
|
||||||
|
return f"{config.git.base_url}/{config.git.answer_org}/{answer_repo_name}.git"
|
||||||
|
|
||||||
|
|
||||||
|
def set_git_remote(repo_path: Path, remote_name: str, remote_url: str) -> str:
|
||||||
|
existing_url = git_capture(repo_path, ["remote", "get-url", remote_name])
|
||||||
|
if existing_url == remote_url:
|
||||||
|
return "unchanged"
|
||||||
|
if existing_url:
|
||||||
|
subprocess.run(
|
||||||
|
["git", "-C", str(repo_path), "remote", "set-url", remote_name, remote_url],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
return "updated"
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
["git", "-C", str(repo_path), "remote", "add", remote_name, remote_url],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
return "added"
|
||||||
|
|
||||||
|
|
||||||
|
def print_submission_plan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
|
series_name, card_name = resolve_selector(config, args.series, args.card)
|
||||||
|
selector = f"{series_name}/{card_name}"
|
||||||
|
card_path = resolve_card_path(config, series_name, card_name)
|
||||||
|
class_name = slug_value(args.class_name, "class")
|
||||||
|
branch_name = slug_value(args.branch or args.nick, "branch")
|
||||||
|
source_url = args.source_url or discover_source_url(config, card_path, series_name, card_name)
|
||||||
|
source_repo_name = repo_name_from_url(source_url)
|
||||||
|
answer_repo_name = build_answer_repo_name(source_repo_name, class_name, args.date)
|
||||||
|
answer_url = build_answer_url(config, answer_repo_name)
|
||||||
|
source_branch = git_current_branch(config, card_path)
|
||||||
|
|
||||||
|
print(f"selector\t{selector}")
|
||||||
|
print(f"card_path\t{card_path}")
|
||||||
|
print(f"source_repo\t{config.git.source_org}/{source_repo_name}")
|
||||||
|
print(f"source_remote\t{config.git.source_remote}")
|
||||||
|
print(f"source_url\t{source_url}")
|
||||||
|
print(f"source_branch\t{source_branch}")
|
||||||
|
print(f"answer_remote\t{config.git.answer_remote}")
|
||||||
|
print(f"answer_repo\t{config.git.answer_org}/{answer_repo_name}")
|
||||||
|
print(f"answer_url\t{answer_url}")
|
||||||
|
print(f"student_branch\t{branch_name}")
|
||||||
|
|
||||||
|
if args.apply:
|
||||||
|
source_status = set_git_remote(card_path, config.git.source_remote, source_url)
|
||||||
|
answer_status = set_git_remote(card_path, config.git.answer_remote, answer_url)
|
||||||
|
print(f"apply_{config.git.source_remote}\t{source_status}")
|
||||||
|
print(f"apply_{config.git.answer_remote}\t{answer_status}")
|
||||||
|
|
||||||
|
print("commands")
|
||||||
|
print(f" git -C {shlex.quote(str(card_path))} remote add {config.git.source_remote} {shlex.quote(source_url)}")
|
||||||
|
print(f" git -C {shlex.quote(str(card_path))} remote add {config.git.answer_remote} {shlex.quote(answer_url)}")
|
||||||
|
print(f" git -C {shlex.quote(str(card_path))} fetch {config.git.source_remote} {shlex.quote(source_branch)}")
|
||||||
|
print(f" git -C {shlex.quote(str(card_path))} switch -c {shlex.quote(branch_name)}")
|
||||||
|
print(f" git -C {shlex.quote(str(card_path))} push -u {config.git.answer_remote} {shlex.quote(branch_name)}")
|
||||||
|
|
||||||
|
|
||||||
def tmux_target_exists(session_name: str) -> bool:
|
def tmux_target_exists(session_name: str) -> bool:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["tmux", "has-session", "-t", session_name],
|
["tmux", "has-session", "-t", session_name],
|
||||||
@@ -229,6 +377,13 @@ def print_config(config: WorkspaceConfig) -> None:
|
|||||||
print(f"series_root\t{config.series_root}")
|
print(f"series_root\t{config.series_root}")
|
||||||
print(f"socket_root\t{config.socket_root}")
|
print(f"socket_root\t{config.socket_root}")
|
||||||
print(f"tools_root\t{config.tools_root}")
|
print(f"tools_root\t{config.tools_root}")
|
||||||
|
print(f"git_base_url\t{config.git.base_url}")
|
||||||
|
print(f"git_source_org\t{config.git.source_org}")
|
||||||
|
print(f"git_answer_org\t{config.git.answer_org}")
|
||||||
|
print(f"git_source_remote\t{config.git.source_remote}")
|
||||||
|
print(f"git_answer_remote\t{config.git.answer_remote}")
|
||||||
|
print(f"git_origin_remote\t{config.git.origin_remote}")
|
||||||
|
print(f"git_fallback_branch\t{config.git.fallback_branch}")
|
||||||
print("tools_root_candidates")
|
print("tools_root_candidates")
|
||||||
for path in config.tools_root_candidates:
|
for path in config.tools_root_candidates:
|
||||||
print(f" {path}")
|
print(f" {path}")
|
||||||
@@ -263,6 +418,33 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
tmux_parser.add_argument("--attach", action="store_true", help="Attach to the session after creation.")
|
tmux_parser.add_argument("--attach", action="store_true", help="Attach to the session after creation.")
|
||||||
tmux_parser.add_argument("--dry-run", action="store_true", help="Print commands instead of running tmux.")
|
tmux_parser.add_argument("--dry-run", action="store_true", help="Print commands instead of running tmux.")
|
||||||
|
|
||||||
|
submission_parser = subparsers.add_parser(
|
||||||
|
"submission",
|
||||||
|
help="Print or apply source/answer git remotes for a selected card.",
|
||||||
|
)
|
||||||
|
submission_parser.add_argument("series", nargs="?", help="Series id or full selector like 'inf/03'.")
|
||||||
|
submission_parser.add_argument("card", nargs="?", help="Card id, for example '03'.")
|
||||||
|
submission_parser.add_argument("--class", dest="class_name", required=True, help="Class id, for example '4i'.")
|
||||||
|
submission_parser.add_argument("--nick", required=True, help="Student nick used as the branch name.")
|
||||||
|
submission_parser.add_argument(
|
||||||
|
"--branch",
|
||||||
|
help="Override the submission branch name. Defaults to the value of --nick.",
|
||||||
|
)
|
||||||
|
submission_parser.add_argument(
|
||||||
|
"--date",
|
||||||
|
default=dt.date.today().isoformat(),
|
||||||
|
help="Lesson date used in the answer repo name. Default: today in YYYY-MM-DD.",
|
||||||
|
)
|
||||||
|
submission_parser.add_argument(
|
||||||
|
"--source-url",
|
||||||
|
help="Override the source repo URL. By default launcher reads the current origin from the card repo.",
|
||||||
|
)
|
||||||
|
submission_parser.add_argument(
|
||||||
|
"--apply",
|
||||||
|
action="store_true",
|
||||||
|
help="Write remotes into the selected card repo instead of only printing the plan.",
|
||||||
|
)
|
||||||
|
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
@@ -283,6 +465,9 @@ def main() -> int:
|
|||||||
if args.command == "tmux-container":
|
if args.command == "tmux-container":
|
||||||
run_tmux_container(config, args)
|
run_tmux_container(config, args)
|
||||||
return 0
|
return 0
|
||||||
|
if args.command == "submission":
|
||||||
|
print_submission_plan(config, args)
|
||||||
|
return 0
|
||||||
|
|
||||||
parser.error(f"Unsupported command: {args.command}")
|
parser.error(f"Unsupported command: {args.command}")
|
||||||
return 2
|
return 2
|
||||||
|
|||||||
Reference in New Issue
Block a user