Add series task branch flow
This commit is contained in:
@@ -275,7 +275,7 @@ item server proto host org repo user remote tok
|
|||||||
1 gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 r1 S e59cc...13be forever
|
1 gitea http 77.90.8.171:3001 edu-tools rv-launcher u1 r1 r1 S e59cc...13be forever
|
||||||
```
|
```
|
||||||
|
|
||||||
## Szybki start
|
## Kolejny krok: serie i karty pracy
|
||||||
|
|
||||||
Najpierw zobacz konfigurację i dostępne komendy:
|
Najpierw zobacz konfigurację i dostępne komendy:
|
||||||
|
|
||||||
@@ -290,7 +290,37 @@ Po konfiguracji tokenów najpierw sprawdź stan store:
|
|||||||
./rvctl tokens compare
|
./rvctl tokens compare
|
||||||
```
|
```
|
||||||
|
|
||||||
### Po pobraniu kart pracy
|
Następnie wylistuj dostępne serie. Jeżeli katalog `series` w workspace jeszcze
|
||||||
|
nie istnieje, `rvctl` utworzy go automatycznie:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series list
|
||||||
|
```
|
||||||
|
|
||||||
|
Przykładowy wynik:
|
||||||
|
|
||||||
|
```text
|
||||||
|
inf 3 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Po wybraniu serii `inf` wylistuj karty:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards list inf
|
||||||
|
```
|
||||||
|
|
||||||
|
Przykładowy wynik zawiera krótką nazwę karty, pełną nazwę repo, status i tytuł:
|
||||||
|
|
||||||
|
```text
|
||||||
|
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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards fetch inf bss
|
||||||
|
```
|
||||||
|
|
||||||
Karty trafiają do `series` w workspace:
|
Karty trafiają do `series` w workspace:
|
||||||
|
|
||||||
@@ -302,18 +332,48 @@ Karty trafiają do `series` w workspace:
|
|||||||
│ └── tokens.json
|
│ └── tokens.json
|
||||||
└── series
|
└── series
|
||||||
└── inf
|
└── inf
|
||||||
└── 03
|
└── lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
`rvctl` pracuje na kartach z `series_root`, czyli na katalogu
|
`rvctl` pracuje na kartach z `series_root`, czyli na katalogu
|
||||||
`~/dev/workspace/rv/series`.
|
`~/dev/workspace/rv/series`.
|
||||||
|
|
||||||
Od tego momentu możesz listować serie i karty:
|
Po pobraniu karty `rvctl` przygotowuje też remote odpowiedzi `r1a`. Remote
|
||||||
|
powstaje z tokena `r1` i wskazuje na repo pracy:
|
||||||
|
|
||||||
|
```text
|
||||||
|
answer_remote r1a
|
||||||
|
answer_org c2025-1a-inf
|
||||||
|
answer_repo lab-rv32i-strlen-bss-data-stack
|
||||||
|
answer_url http://77.90.8.171:3001/c2025-1a-inf/lab-rv32i-strlen-bss-data-stack.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Następnie wylistuj zadania w karcie:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl list-series
|
./rvctl series cards tasks list inf bss
|
||||||
./rvctl list-cards inf
|
```
|
||||||
./rvctl submission inf 03 --class 4i --nick u1
|
|
||||||
|
Przykładowy wynik:
|
||||||
|
|
||||||
|
```text
|
||||||
|
inf bss task1_bss ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
|
inf bss task2_data ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
|
inf bss task3_stack_unused ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
|
inf bss task4_stack_strlen ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
Przełącz się na wybrane zadanie. `rvctl` bierze login ucznia ze store tokenów i
|
||||||
|
tworzy branch pracy w formacie `<login>T<numer_zadania>a`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards tasks switch inf bss 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Dla użytkownika `u1` i zadania `task4_stack_strlen` branch będzie miał nazwę:
|
||||||
|
|
||||||
|
```text
|
||||||
|
u1T4a
|
||||||
```
|
```
|
||||||
|
|
||||||
### Po uruchomieniu środowiska kontenerowego
|
### Po uruchomieniu środowiska kontenerowego
|
||||||
@@ -330,7 +390,7 @@ kontenerowego. Wtedy pojawia się pod `tools`:
|
|||||||
│ └── tokens.json
|
│ └── tokens.json
|
||||||
└── series
|
└── series
|
||||||
└── inf
|
└── inf
|
||||||
└── 03
|
└── lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
Repozytoria źródłowe poza workspace, na przykład `~/dev/edu/repos/rv`, są
|
Repozytoria źródłowe poza workspace, na przykład `~/dev/edu/repos/rv`, są
|
||||||
@@ -340,8 +400,8 @@ do zwykłej pracy w workspace.
|
|||||||
Przykładowe komendy dla środowiska kontenerowego:
|
Przykładowe komendy dla środowiska kontenerowego:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tmux-container inf 03 --dry-run
|
./rvctl tmux-container inf bss --dry-run
|
||||||
./rvctl tmux-container inf 03 --session rv-inf03 --attach
|
./rvctl tmux-container inf bss --session rv-inf-bss --attach
|
||||||
```
|
```
|
||||||
|
|
||||||
`--dry-run` jest przydatny przy sprawdzaniu planu uruchomienia lub konfiguracji
|
`--dry-run` jest przydatny przy sprawdzaniu planu uruchomienia lub konfiguracji
|
||||||
|
|||||||
+24
-24
@@ -18,19 +18,19 @@ Typowy przepływ pracy:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens compare
|
./rvctl tokens compare
|
||||||
./rvctl series cards show inf 03
|
./rvctl series cards show inf bss
|
||||||
./rvctl series cards tasks list inf 03
|
./rvctl series cards tasks list inf bss
|
||||||
./rvctl containers show inf 03
|
./rvctl containers show inf bss
|
||||||
./rvctl containers start inf 03 --session rv-inf03 --attach
|
./rvctl containers start inf bss --session rv-inf-bss --attach
|
||||||
./rvctl containers list
|
./rvctl containers list
|
||||||
./rvctl containers stop rv-inf03
|
./rvctl containers stop rv-inf-bss
|
||||||
```
|
```
|
||||||
|
|
||||||
## Model
|
## Model
|
||||||
|
|
||||||
Docelowo komendy `containers` pracują na trzech warstwach:
|
Docelowo komendy `containers` pracują na trzech warstwach:
|
||||||
|
|
||||||
- karta pracy, np. `inf 03`
|
- karta pracy, np. `inf bss`
|
||||||
- opcjonalne zadanie, np. `--task task1`
|
- opcjonalne zadanie, np. `--task task1`
|
||||||
- instancja środowiska, np. `--instance shell`
|
- instancja środowiska, np. `--instance shell`
|
||||||
|
|
||||||
@@ -52,19 +52,19 @@ defaults.tmux_window
|
|||||||
Pokazuje plan uruchomienia środowiska bez tworzenia sesji `tmux`.
|
Pokazuje plan uruchomienia środowiska bez tworzenia sesji `tmux`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl containers show inf 03
|
./rvctl containers show inf bss
|
||||||
./rvctl containers show inf/03 --task task1
|
./rvctl containers show inf/bss --task task1
|
||||||
```
|
```
|
||||||
|
|
||||||
Typowe pola:
|
Typowe pola:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
selector inf/03
|
selector inf/bss
|
||||||
task task1
|
task task1
|
||||||
card_path ~/dev/workspace/rv/series/inf/03
|
card_path ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
task_path ~/dev/workspace/rv/series/inf/03/task1
|
task_path ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/task1
|
||||||
tools_root ~/dev/workspace/rv/tools/rv32i-hazard3-env
|
tools_root ~/dev/workspace/rv/tools/rv32i-hazard3-env
|
||||||
session rv-inf03
|
session rv-inf-bss
|
||||||
window rv
|
window rv
|
||||||
instance shell
|
instance shell
|
||||||
```
|
```
|
||||||
@@ -72,7 +72,7 @@ instance shell
|
|||||||
Ta komenda jest odpowiednikiem obecnego trybu:
|
Ta komenda jest odpowiednikiem obecnego trybu:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tmux-container inf 03 --dry-run
|
./rvctl tmux-container inf bss --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
### `containers start [SERIES] [CARD]`
|
### `containers start [SERIES] [CARD]`
|
||||||
@@ -80,9 +80,9 @@ Ta komenda jest odpowiednikiem obecnego trybu:
|
|||||||
Uruchamia sesję `tmux` i kontener dla wybranej karty.
|
Uruchamia sesję `tmux` i kontener dla wybranej karty.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl containers start inf 03
|
./rvctl containers start inf bss
|
||||||
./rvctl containers start inf/03 --session rv-inf03
|
./rvctl containers start inf/bss --session rv-inf-bss
|
||||||
./rvctl containers start inf 03 --task task1 --instance shell --attach
|
./rvctl containers start inf bss --task task1 --instance shell --attach
|
||||||
```
|
```
|
||||||
|
|
||||||
Przełączniki:
|
Przełączniki:
|
||||||
@@ -112,7 +112,7 @@ Przykładowy wynik:
|
|||||||
```text
|
```text
|
||||||
session selector task instance window
|
session selector task instance window
|
||||||
-------- -------- ----- -------- ------
|
-------- -------- ----- -------- ------
|
||||||
rv-inf03 inf/03 task1 shell rv
|
rv-inf-bss inf/bss task1 shell rv
|
||||||
```
|
```
|
||||||
|
|
||||||
### `containers attach SESSION`
|
### `containers attach SESSION`
|
||||||
@@ -120,13 +120,13 @@ rv-inf03 inf/03 task1 shell rv
|
|||||||
Podłącza terminal do istniejącej sesji.
|
Podłącza terminal do istniejącej sesji.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl containers attach rv-inf03
|
./rvctl containers attach rv-inf-bss
|
||||||
```
|
```
|
||||||
|
|
||||||
Technicznie odpowiada to:
|
Technicznie odpowiada to:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tmux attach -t rv-inf03
|
tmux attach -t rv-inf-bss
|
||||||
```
|
```
|
||||||
|
|
||||||
### `containers stop SESSION`
|
### `containers stop SESSION`
|
||||||
@@ -134,13 +134,13 @@ tmux attach -t rv-inf03
|
|||||||
Zamyka sesję `tmux`, a razem z nią uruchomiony w niej proces kontenera.
|
Zamyka sesję `tmux`, a razem z nią uruchomiony w niej proces kontenera.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl containers stop rv-inf03
|
./rvctl containers stop rv-inf-bss
|
||||||
```
|
```
|
||||||
|
|
||||||
Technicznie odpowiada to:
|
Technicznie odpowiada to:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tmux kill-session -t rv-inf03
|
tmux kill-session -t rv-inf-bss
|
||||||
```
|
```
|
||||||
|
|
||||||
## Aliasowanie Starej Komendy
|
## Aliasowanie Starej Komendy
|
||||||
@@ -148,9 +148,9 @@ tmux kill-session -t rv-inf03
|
|||||||
Stara komenda może zostać jako alias kompatybilności:
|
Stara komenda może zostać jako alias kompatybilności:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
tmux-container inf 03 --dry-run -> containers show inf 03
|
tmux-container inf bss --dry-run -> containers show inf bss
|
||||||
tmux-container inf 03 --session NAME -> containers start inf 03 --session NAME
|
tmux-container inf bss --session NAME -> containers start inf bss --session NAME
|
||||||
tmux-container inf/03 --attach -> containers start inf/03 --attach
|
tmux-container inf/bss --attach -> containers start inf/bss --attach
|
||||||
```
|
```
|
||||||
|
|
||||||
Dokumentacja i nowe przykłady powinny promować namespace `containers`.
|
Dokumentacja i nowe przykłady powinny promować namespace `containers`.
|
||||||
|
|||||||
+281
-55
@@ -205,6 +205,16 @@ Komendy:
|
|||||||
- `show-config`
|
- `show-config`
|
||||||
- `list-series`
|
- `list-series`
|
||||||
- `list-cards [series]`
|
- `list-cards [series]`
|
||||||
|
- `series list`
|
||||||
|
- `series show SERIES`
|
||||||
|
- `series fetch SERIES [--dry-run]`
|
||||||
|
- `series cards list SERIES`
|
||||||
|
- `series cards show SERIES CARD`
|
||||||
|
- `series cards fetch SERIES CARD`
|
||||||
|
- `series cards submission SERIES CARD --class NAME`
|
||||||
|
- `series cards tasks list SERIES CARD`
|
||||||
|
- `series cards tasks show SERIES CARD TASK`
|
||||||
|
- `series cards tasks switch SERIES CARD TASK`
|
||||||
- `tokens scan`
|
- `tokens scan`
|
||||||
- `tokens compare`
|
- `tokens compare`
|
||||||
- `tokens read`
|
- `tokens read`
|
||||||
@@ -246,25 +256,247 @@ Przykład:
|
|||||||
./rvctl show-config
|
./rvctl show-config
|
||||||
```
|
```
|
||||||
|
|
||||||
## `list-series`
|
## `series list`
|
||||||
|
|
||||||
Listuje katalogi serii znalezione w `series_root`.
|
Listuje serie dostępne w źródłach i workspace. Komenda tworzy `series_root`,
|
||||||
|
jeżeli katalog jeszcze nie istnieje.
|
||||||
|
|
||||||
Każda linia ma format:
|
Format wyjścia:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
<series_id><TAB><liczba_kart><TAB><pełna_ścieżka>
|
<series_id><TAB><karty_w_źródłach><TAB><karty_w_workspace><TAB><workspace_path>
|
||||||
```
|
```
|
||||||
|
|
||||||
Przykład:
|
Przykład:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series list
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series show SERIES`
|
||||||
|
|
||||||
|
Pokazuje szczegóły jednej serii.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
|
||||||
|
Format wyjścia:
|
||||||
|
|
||||||
|
```text
|
||||||
|
series<TAB>...
|
||||||
|
source_path<TAB>...
|
||||||
|
workspace_path<TAB>...
|
||||||
|
cards_count<TAB>...
|
||||||
|
cards<TAB>...
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series fetch SERIES`
|
||||||
|
|
||||||
|
Pobiera albo aktualizuje wszystkie karty z serii.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
|
||||||
|
Przełączniki:
|
||||||
|
|
||||||
|
- `--dry-run`
|
||||||
|
Pokazuje plan bez klonowania repo i bez zmian w token store.
|
||||||
|
|
||||||
|
Przykład:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series fetch inf --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series cards list SERIES`
|
||||||
|
|
||||||
|
Listuje karty w wybranej serii.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
|
||||||
|
Format wyjścia:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<card><TAB><repo><TAB><source|workspace><TAB><tytuł_z_README>
|
||||||
|
```
|
||||||
|
|
||||||
|
Przykład:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards list inf
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series cards show SERIES CARD`
|
||||||
|
|
||||||
|
Pokazuje szczegóły jednej karty.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
- `CARD`
|
||||||
|
Pełna nazwa repo albo unikalny fragment, na przykład `bss`.
|
||||||
|
|
||||||
|
Format wyjścia:
|
||||||
|
|
||||||
|
```text
|
||||||
|
series<TAB>...
|
||||||
|
card<TAB>...
|
||||||
|
repo<TAB>...
|
||||||
|
source_path<TAB>...
|
||||||
|
workspace_path<TAB>...
|
||||||
|
source_remote<TAB>...
|
||||||
|
source_url<TAB>...
|
||||||
|
answer_remote<TAB>...
|
||||||
|
answer_url<TAB>...
|
||||||
|
tasks<TAB>...
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series cards fetch SERIES CARD`
|
||||||
|
|
||||||
|
Pobiera kartę z repo źródłowego do `series_root`. `CARD` może być pełną nazwą
|
||||||
|
repo albo unikalnym fragmentem, na przykład `bss`.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
- `CARD`
|
||||||
|
Pełna nazwa repo albo unikalny fragment, na przykład `bss`.
|
||||||
|
|
||||||
|
Przełączniki:
|
||||||
|
|
||||||
|
- `--dry-run`
|
||||||
|
Pokazuje plan bez klonowania repo, bez zapisu tokena `r1a` i bez ustawiania
|
||||||
|
remota odpowiedzi.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards fetch inf bss
|
||||||
|
```
|
||||||
|
|
||||||
|
Po pobraniu komenda ustawia:
|
||||||
|
|
||||||
|
- `r1` na repo źródłowe w `edu-inf`
|
||||||
|
- `r1a` na repo pracy w `c2025-1a-inf`
|
||||||
|
|
||||||
|
`r1a` jest tworzony z tokena `r1` i wskazuje na repo o tej samej nazwie co
|
||||||
|
karta.
|
||||||
|
|
||||||
|
## `series cards submission SERIES CARD`
|
||||||
|
|
||||||
|
Alias funkcjonalny dla `submission`, ale z jawnym namespace kart.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
- `CARD`
|
||||||
|
Pełna nazwa repo albo unikalny fragment, na przykład `bss`.
|
||||||
|
|
||||||
|
Przełączniki:
|
||||||
|
|
||||||
|
- `--class NAME`
|
||||||
|
Id klasy albo grupy.
|
||||||
|
- `--nick NAME`
|
||||||
|
Nick ucznia. Jeżeli go nie podasz, `rvctl` bierze użytkownika ze store
|
||||||
|
tokenów.
|
||||||
|
- `--task TASK`
|
||||||
|
Zadanie używane do zbudowania domyślnej nazwy brancha. Domyślnie
|
||||||
|
`defaults.task` z `workspace.json`.
|
||||||
|
- `--branch NAME`
|
||||||
|
Nadpisuje nazwę brancha odpowiedzi. Bez tego branch ma format
|
||||||
|
`<login>T<numer_zadania>a`, na przykład `u1T1a`.
|
||||||
|
- `--date YYYY-MM-DD`
|
||||||
|
Data zajęć widoczna w planie.
|
||||||
|
- `--source-url URL`
|
||||||
|
Nadpisuje URL repo źródłowego.
|
||||||
|
- `--apply`
|
||||||
|
Zapisuje remotes `r1` i `r1a` w repo karty.
|
||||||
|
|
||||||
|
Przykład:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards submission inf bss --class c2025-1a-inf --apply
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series cards tasks list SERIES CARD`
|
||||||
|
|
||||||
|
Listuje zadania z `src/tasks` pobranej karty.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
- `CARD`
|
||||||
|
Pełna nazwa repo albo unikalny fragment, na przykład `bss`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards tasks list inf bss
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series cards tasks show SERIES CARD TASK`
|
||||||
|
|
||||||
|
Pokazuje szczegóły jednego zadania.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
- `CARD`
|
||||||
|
Pełna nazwa repo albo unikalny fragment, na przykład `bss`.
|
||||||
|
- `TASK`
|
||||||
|
Pełna nazwa zadania albo unikalny fragment, na przykład `task1`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards tasks show inf bss task1
|
||||||
|
```
|
||||||
|
|
||||||
|
## `series cards tasks switch SERIES CARD TASK`
|
||||||
|
|
||||||
|
Tworzy albo przełącza lokalny branch pracy dla zadania.
|
||||||
|
|
||||||
|
Argumenty:
|
||||||
|
|
||||||
|
- `SERIES`
|
||||||
|
Id serii, na przykład `inf`.
|
||||||
|
- `CARD`
|
||||||
|
Pełna nazwa repo albo unikalny fragment, na przykład `bss`.
|
||||||
|
- `TASK`
|
||||||
|
Pełna nazwa zadania albo unikalny fragment, na przykład `task4` albo `4`.
|
||||||
|
|
||||||
|
Przełączniki:
|
||||||
|
|
||||||
|
- `--branch NAME`
|
||||||
|
Nadpisuje automatyczną nazwę brancha.
|
||||||
|
- `--dry-run`
|
||||||
|
Pokazuje plan bez przełączania brancha.
|
||||||
|
|
||||||
|
Domyślna nazwa brancha powstaje z użytkownika w `tokens.json`, numeru zadania i
|
||||||
|
końcówki `a`. Dla użytkownika `u1` i zadania `task4_stack_strlen` będzie to
|
||||||
|
`u1T4a`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards tasks switch inf bss 4
|
||||||
|
```
|
||||||
|
|
||||||
|
## `list-series`
|
||||||
|
|
||||||
|
Alias kompatybilności dla `series list`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl list-series
|
./rvctl list-series
|
||||||
```
|
```
|
||||||
|
|
||||||
## `list-cards [series]`
|
## `list-cards [series]`
|
||||||
|
|
||||||
Listuje karty z wybranej serii.
|
Alias kompatybilności dla `series cards list SERIES`.
|
||||||
|
|
||||||
Argumenty:
|
Argumenty:
|
||||||
|
|
||||||
@@ -272,18 +504,6 @@ Argumenty:
|
|||||||
Opcjonalne id serii, na przykład `inf`.
|
Opcjonalne id serii, na przykład `inf`.
|
||||||
Jeśli go brak, brana jest domyślna seria z `workspace.json`.
|
Jeśli go brak, brana jest domyślna seria z `workspace.json`.
|
||||||
|
|
||||||
Format wyjścia:
|
|
||||||
|
|
||||||
```text
|
|
||||||
<card_no><TAB><tytuł_z_README><TAB><pełna_ścieżka>
|
|
||||||
```
|
|
||||||
|
|
||||||
Jeśli `README.md` nie ma nagłówka `#`, skrypt wypisuje:
|
|
||||||
|
|
||||||
```text
|
|
||||||
<card_no><TAB><pełna_ścieżka>
|
|
||||||
```
|
|
||||||
|
|
||||||
Przykłady:
|
Przykłady:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -317,7 +537,7 @@ Przykład:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens scan
|
./rvctl tokens scan
|
||||||
./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens scan --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tokens compare`
|
## `tokens compare`
|
||||||
@@ -358,7 +578,7 @@ Przykład:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens compare
|
./rvctl tokens compare
|
||||||
./rvctl tokens compare --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens compare --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tokens list store|remote|both`
|
## `tokens list store|remote|both`
|
||||||
@@ -466,7 +686,7 @@ Przełączniki:
|
|||||||
Przykład:
|
Przykład:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens sync store r1 --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens sync store r1 --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tokens remove store|remote|both REMOTE_ID`
|
## `tokens remove store|remote|both REMOTE_ID`
|
||||||
@@ -491,7 +711,7 @@ Przykłady:
|
|||||||
./rvctl tokens remove store r1
|
./rvctl tokens remove store r1
|
||||||
./rvctl tokens remove remote r1
|
./rvctl tokens remove remote r1
|
||||||
./rvctl tokens remove both r1
|
./rvctl tokens remove both r1
|
||||||
./rvctl tokens remove remote r1 --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens remove remote r1 --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tokens read`
|
## `tokens read`
|
||||||
@@ -559,7 +779,7 @@ in_sync<TAB>1
|
|||||||
Przykład:
|
Przykład:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tokens write`
|
## `tokens write`
|
||||||
@@ -588,7 +808,7 @@ Przełączniki:
|
|||||||
Przykład:
|
Przykład:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens write --repo ~/dev/workspace/rv/series/inf/03 --remote r1 --server http://77.90.8.171:3001
|
./rvctl tokens write --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack --remote r1 --server http://77.90.8.171:3001
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tokens update REMOTE_ID`
|
## `tokens update REMOTE_ID`
|
||||||
@@ -639,8 +859,8 @@ Przełączniki:
|
|||||||
Przykłady:
|
Przykłady:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens update --from remotes --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens update --from remotes --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
./rvctl tokens update --from store --repo ~/dev/workspace/rv/series/inf/03 --remote r1 --server http://77.90.8.171:3001
|
./rvctl tokens update --from store --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack --remote r1 --server http://77.90.8.171:3001
|
||||||
```
|
```
|
||||||
|
|
||||||
## `submission [series] [card]`
|
## `submission [series] [card]`
|
||||||
@@ -648,65 +868,71 @@ Przykłady:
|
|||||||
Wylicza przepływ oddawania rozwiązań:
|
Wylicza przepływ oddawania rozwiązań:
|
||||||
|
|
||||||
- `r1` jako repo z materiałem z `edu-inf`
|
- `r1` jako repo z materiałem z `edu-inf`
|
||||||
- `a1` jako repo odpowiedzi w `zsl-inf`
|
- `r1a` jako repo odpowiedzi w `c2025-1a-inf`
|
||||||
- branch ucznia na podstawie jego nicku
|
- branch ucznia liczony ze store tokenów i zadania
|
||||||
|
|
||||||
Nazwa repo odpowiedzi jest budowana z nazwy repo źródłowego z `edu-inf`, klasy
|
Nazwa repo odpowiedzi jest taka sama jak nazwa repo źródłowego karty:
|
||||||
i daty:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
<repo_z_edu-inf>-<klasa>-<data>
|
<repo_z_edu-inf>
|
||||||
```
|
```
|
||||||
|
|
||||||
Przykład:
|
Przykład:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
lab-rv32i-strlen-bss-data-stack-4i-2026-04-26
|
lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
Argumenty pozycyjne:
|
Argumenty pozycyjne:
|
||||||
|
|
||||||
- `series`
|
- `series`
|
||||||
Id serii albo pełny selector, na przykład `inf` albo `inf/03`.
|
Id serii albo pełny selector, na przykład `inf` albo `inf/bss`.
|
||||||
- `card`
|
- `card`
|
||||||
Numer karty, na przykład `03`.
|
Karta albo unikalny fragment nazwy repo, na przykład `bss`.
|
||||||
|
|
||||||
Przełączniki:
|
Przełączniki:
|
||||||
|
|
||||||
- `--class NAME`
|
- `--class NAME`
|
||||||
Id klasy, na przykład `4i`.
|
Id klasy albo grupy, na przykład `c2025-1a-inf`.
|
||||||
- `--nick NAME`
|
- `--nick NAME`
|
||||||
Nick ucznia. Domyślnie z niego powstaje nazwa brancha.
|
Nick ucznia. Jeżeli go nie podasz, `rvctl` bierze użytkownika ze store
|
||||||
|
tokenów.
|
||||||
|
- `--task TASK`
|
||||||
|
Zadanie używane do zbudowania domyślnej nazwy brancha. Domyślnie
|
||||||
|
`defaults.task` z `workspace.json`.
|
||||||
- `--branch NAME`
|
- `--branch NAME`
|
||||||
Nadpisuje domyślną nazwę brancha.
|
Nadpisuje domyślną nazwę brancha. Bez tego branch ma format
|
||||||
|
`<login>T<numer_zadania>a`, na przykład `u1T1a`.
|
||||||
- `--date YYYY-MM-DD`
|
- `--date YYYY-MM-DD`
|
||||||
Data zajęć używana w nazwie repo odpowiedzi. Domyślnie dzisiejsza.
|
Data zajęć. Zostaje w raporcie, ale nie zmienia nazwy repo odpowiedzi.
|
||||||
- `--source-url URL`
|
- `--source-url URL`
|
||||||
Nadpisuje URL repo źródłowego. Bez tego launcher czyta `origin` z repo karty.
|
Nadpisuje URL repo źródłowego. Bez tego launcher czyta `r1` albo `origin` z
|
||||||
|
repo karty.
|
||||||
- `--apply`
|
- `--apply`
|
||||||
Dodaje albo aktualizuje remote `r1` i `a1` w repo karty.
|
Dodaje albo aktualizuje remote `r1` i `r1a` w repo karty.
|
||||||
|
|
||||||
Typowy format wyjścia:
|
Typowy format wyjścia:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
selector<TAB>inf/03
|
selector<TAB>inf/bss
|
||||||
card_path<TAB>...
|
card_path<TAB>...
|
||||||
source_repo<TAB>edu-inf/lab-rv32i-strlen-bss-data-stack
|
source_repo<TAB>edu-inf/lab-rv32i-strlen-bss-data-stack
|
||||||
source_remote<TAB>r1
|
source_remote<TAB>r1
|
||||||
source_url<TAB>http://77.90.8.171:3001/edu-inf/lab-rv32i-strlen-bss-data-stack.git
|
source_url<TAB>http://77.90.8.171:3001/edu-inf/lab-rv32i-strlen-bss-data-stack.git
|
||||||
source_branch<TAB>deploy
|
source_branch<TAB>deploy
|
||||||
answer_remote<TAB>a1
|
answer_remote<TAB>r1a
|
||||||
answer_repo<TAB>zsl-inf/lab-rv32i-strlen-bss-data-stack-4i-2026-04-26
|
answer_repo<TAB>c2025-1a-inf/lab-rv32i-strlen-bss-data-stack
|
||||||
answer_url<TAB>http://77.90.8.171:3001/zsl-inf/lab-rv32i-strlen-bss-data-stack-4i-2026-04-26.git
|
answer_url<TAB>http://77.90.8.171:3001/c2025-1a-inf/lab-rv32i-strlen-bss-data-stack.git
|
||||||
student_branch<TAB>u1
|
student_user<TAB>u1
|
||||||
|
student_branch<TAB>u1T1a
|
||||||
```
|
```
|
||||||
|
|
||||||
Przykłady:
|
Przykłady:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl submission inf 03 --class 4i --nick u1
|
./rvctl submission inf bss --class c2025-1a-inf
|
||||||
./rvctl submission inf 03 --class 4i --nick u1 --apply
|
./rvctl submission inf bss --class c2025-1a-inf --task 4 --apply
|
||||||
./rvctl submission inf/03 --class 4i --nick u2 --date 2026-04-26
|
./rvctl series cards submission inf bss --class c2025-1a-inf --branch u1T4a
|
||||||
```
|
```
|
||||||
|
|
||||||
## `tmux-container [series] [card]`
|
## `tmux-container [series] [card]`
|
||||||
@@ -716,9 +942,9 @@ Tworzy nową sesję `tmux` i uruchamia kontener w `pane 0`.
|
|||||||
Argumenty pozycyjne:
|
Argumenty pozycyjne:
|
||||||
|
|
||||||
- `series`
|
- `series`
|
||||||
Id serii albo pełny selector, na przykład `inf` albo `inf/03`.
|
Id serii albo pełny selector, na przykład `inf` albo `inf/bss`.
|
||||||
- `card`
|
- `card`
|
||||||
Numer karty, na przykład `03`.
|
Karta albo unikalny fragment nazwy repo, na przykład `bss`.
|
||||||
|
|
||||||
Przełączniki:
|
Przełączniki:
|
||||||
|
|
||||||
@@ -735,16 +961,16 @@ Przełączniki:
|
|||||||
|
|
||||||
Reguły wyboru karty:
|
Reguły wyboru karty:
|
||||||
|
|
||||||
- `tmux-container inf 03` -> seria `inf`, karta `03`
|
- `tmux-container inf bss` -> seria `inf`, karta `bss`
|
||||||
- `tmux-container inf/03` -> pełny selector
|
- `tmux-container inf/bss` -> pełny selector
|
||||||
- `tmux-container 03` -> domyślna seria + karta `03`
|
- `tmux-container bss` -> domyślna seria + karta `bss`
|
||||||
- `tmux-container inf` -> seria `inf` + domyślna karta
|
- `tmux-container inf` -> seria `inf` + domyślna karta
|
||||||
- bez argumentów -> domyślna seria i domyślna karta
|
- bez argumentów -> domyślna seria i domyślna karta
|
||||||
|
|
||||||
Przykłady:
|
Przykłady:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tmux-container 03 --dry-run
|
./rvctl tmux-container bss --dry-run
|
||||||
./rvctl tmux-container inf 03 --session rv-inf03
|
./rvctl tmux-container inf bss --session rv-inf-bss
|
||||||
./rvctl tmux-container inf/03 --attach
|
./rvctl tmux-container inf/bss --attach
|
||||||
```
|
```
|
||||||
|
|||||||
+79
-88
@@ -1,7 +1,7 @@
|
|||||||
# Series
|
# Series
|
||||||
|
|
||||||
Ten dokument opisuje docelowy model komend `rvctl` do pracy z seriami, kartami
|
Ten dokument opisuje komendy `rvctl` do pracy z seriami, kartami pracy i
|
||||||
pracy i zadaniami w kartach.
|
zadaniami w kartach.
|
||||||
|
|
||||||
## Zasada
|
## Zasada
|
||||||
|
|
||||||
@@ -11,73 +11,51 @@ Komendy kart pracy dzielimy na trzy poziomy:
|
|||||||
- `series cards` - operacje na kartach w ramach serii
|
- `series cards` - operacje na kartach w ramach serii
|
||||||
- `series cards tasks` - operacje na zadaniach w ramach karty
|
- `series cards tasks` - operacje na zadaniach w ramach karty
|
||||||
|
|
||||||
Używamy pluralnych namespace'ów `cards` i `tasks`, bo opisują kolekcje zasobów
|
`rvctl` tworzy `series_root`, jeżeli katalog jeszcze nie istnieje. Lista serii
|
||||||
i są spójne z pluralnym `tokens`.
|
i kart może pochodzić z repo źródłowych, a `fetch` pobiera wybraną kartę do
|
||||||
|
workspace.
|
||||||
|
|
||||||
## Szybki przepływ
|
## Szybki przepływ
|
||||||
|
|
||||||
Typowy przepływ pracy:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens compare
|
./rvctl tokens compare
|
||||||
./rvctl series list
|
./rvctl series list
|
||||||
./rvctl series show inf
|
|
||||||
./rvctl series fetch inf
|
|
||||||
./rvctl series cards list inf
|
./rvctl series cards list inf
|
||||||
./rvctl series cards show inf 03
|
./rvctl series cards fetch inf bss
|
||||||
./rvctl series cards tasks list inf 03
|
./rvctl series cards tasks list inf bss
|
||||||
./rvctl series cards tasks show inf 03 task1
|
./rvctl series cards tasks switch inf bss 1
|
||||||
./rvctl series cards fetch inf 03
|
|
||||||
./rvctl series cards submission inf 03 --class 4i --nick u1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Dla karty `bss` właściwym repo jest `lab-rv32i-strlen-bss-data-stack`.
|
||||||
|
|
||||||
## Serie
|
## Serie
|
||||||
|
|
||||||
### `series list`
|
### `series list`
|
||||||
|
|
||||||
Listuje dostępne serie w workspace.
|
Listuje dostępne serie. Jeżeli `~/dev/workspace/rv/series` nie istnieje,
|
||||||
|
komenda tworzy ten katalog.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series list
|
./rvctl series list
|
||||||
```
|
```
|
||||||
|
|
||||||
Przykładowy wynik:
|
Typowy wynik:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
series cards workspace
|
inf 3 0
|
||||||
------ ----- -------------------------------
|
|
||||||
inf 12 ~/dev/workspace/rv/series/inf
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Kolumny oznaczają: seria, liczba kart w źródłach, liczba kart pobranych do
|
||||||
|
workspace.
|
||||||
|
|
||||||
### `series show SERIES`
|
### `series show SERIES`
|
||||||
|
|
||||||
Pokazuje informacje o serii jako całości.
|
Pokazuje informacje o serii.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series show inf
|
./rvctl series show inf
|
||||||
```
|
```
|
||||||
|
|
||||||
Typowe pola:
|
|
||||||
|
|
||||||
```text
|
|
||||||
series inf
|
|
||||||
workspace_path ~/dev/workspace/rv/series/inf
|
|
||||||
source_path ~/dev/edu/repos/rv/series/inf
|
|
||||||
cards_count 12
|
|
||||||
cards 01 02 03 ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### `series fetch SERIES`
|
|
||||||
|
|
||||||
Pobiera albo aktualizuje całą serię w workspace.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./rvctl series fetch inf
|
|
||||||
```
|
|
||||||
|
|
||||||
Ta komenda pracuje na wszystkich kartach w serii. Do pobrania jednej karty
|
|
||||||
służy `series cards fetch`.
|
|
||||||
|
|
||||||
## Karty
|
## Karty
|
||||||
|
|
||||||
### `series cards list SERIES`
|
### `series cards list SERIES`
|
||||||
@@ -91,38 +69,45 @@ Listuje karty w wybranej serii.
|
|||||||
Przykładowy wynik:
|
Przykładowy wynik:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
series card repo
|
bss lab-rv32i-strlen-bss-data-stack source rv32i-c / bss-data-stack
|
||||||
------ ---- ------------------------------------
|
```
|
||||||
inf 03 lab-rv32i-strlen-bss-data-stack
|
|
||||||
|
Pierwsza kolumna jest krótką nazwą karty. Pełna nazwa repo zostaje w drugiej
|
||||||
|
kolumnie.
|
||||||
|
|
||||||
|
### `series cards fetch SERIES CARD`
|
||||||
|
|
||||||
|
Pobiera albo aktualizuje jedną kartę w workspace. `CARD` może być krótką nazwą
|
||||||
|
albo unikalnym fragmentem pełnej nazwy repo.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards fetch inf bss
|
||||||
|
```
|
||||||
|
|
||||||
|
Karta trafia do:
|
||||||
|
|
||||||
|
```text
|
||||||
|
~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
|
```
|
||||||
|
|
||||||
|
Po pobraniu `rvctl` przygotowuje remotes:
|
||||||
|
|
||||||
|
- `r1` - repo źródłowe karty w `edu-inf`
|
||||||
|
- `r1a` - repo pracy w `c2025-1a-inf`
|
||||||
|
|
||||||
|
Remote `r1a` jest tworzony z tokena `r1` i wskazuje na repo o tej samej nazwie
|
||||||
|
co karta:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://77.90.8.171:3001/c2025-1a-inf/lab-rv32i-strlen-bss-data-stack.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### `series cards show SERIES CARD`
|
### `series cards show SERIES CARD`
|
||||||
|
|
||||||
Pokazuje informacje o konkretnej karcie.
|
Pokazuje informacje o karcie.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series cards show inf 03
|
./rvctl series cards show inf bss
|
||||||
```
|
|
||||||
|
|
||||||
Typowe pola:
|
|
||||||
|
|
||||||
```text
|
|
||||||
series inf
|
|
||||||
card 03
|
|
||||||
workspace_path ~/dev/workspace/rv/series/inf/03
|
|
||||||
source_path ~/dev/edu/repos/rv/series/inf/03
|
|
||||||
source_repo edu-inf/lab-rv32i-strlen-bss-data-stack
|
|
||||||
source_remote r1
|
|
||||||
answer_remote a1
|
|
||||||
tasks task1 task2 ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### `series cards fetch SERIES CARD`
|
|
||||||
|
|
||||||
Pobiera albo aktualizuje jedną kartę w workspace.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./rvctl series cards fetch inf 03
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### `series cards submission SERIES CARD`
|
### `series cards submission SERIES CARD`
|
||||||
@@ -130,14 +115,15 @@ Pobiera albo aktualizuje jedną kartę w workspace.
|
|||||||
Pokazuje albo stosuje konfigurację repo odpowiedzi dla karty.
|
Pokazuje albo stosuje konfigurację repo odpowiedzi dla karty.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series cards submission inf 03 --class 4i --nick u1
|
./rvctl series cards submission inf bss --class c2025-1a-inf
|
||||||
./rvctl series cards submission inf 03 --class 4i --nick u1 --apply
|
./rvctl series cards submission inf bss --class c2025-1a-inf --task 4 --apply
|
||||||
```
|
```
|
||||||
|
|
||||||
Bez `--apply` komenda tylko pokazuje plan. Z `--apply` ustawia remotes potrzebne
|
Bez `--apply` komenda pokazuje plan. Z `--apply` ustawia remotes potrzebne do
|
||||||
do pracy z repo odpowiedzi.
|
pracy z repo odpowiedzi. Jeżeli nie podasz `--branch`, branch jest liczony ze
|
||||||
|
store tokenów i zadania, na przykład `u1T1a`.
|
||||||
|
|
||||||
## Zadania W Karcie
|
## Zadania w karcie
|
||||||
|
|
||||||
Zadania są częścią karty, dlatego trzymamy je pod `series cards tasks`.
|
Zadania są częścią karty, dlatego trzymamy je pod `series cards tasks`.
|
||||||
|
|
||||||
@@ -146,16 +132,16 @@ Zadania są częścią karty, dlatego trzymamy je pod `series cards tasks`.
|
|||||||
Listuje zadania dostępne w konkretnej karcie.
|
Listuje zadania dostępne w konkretnej karcie.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series cards tasks list inf 03
|
./rvctl series cards tasks list inf bss
|
||||||
```
|
```
|
||||||
|
|
||||||
Przykładowy wynik:
|
Przykładowy wynik:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
series card task path
|
inf bss task1_bss ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
------ ---- ----- --------------------------------------
|
inf bss task2_data ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
inf 03 task1 ~/dev/workspace/rv/series/inf/03/task1
|
inf bss task3_stack_unused ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
inf 03 task2 ~/dev/workspace/rv/series/inf/03/task2
|
inf bss task4_stack_strlen ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack/src/tasks
|
||||||
```
|
```
|
||||||
|
|
||||||
### `series cards tasks show SERIES CARD TASK`
|
### `series cards tasks show SERIES CARD TASK`
|
||||||
@@ -163,33 +149,38 @@ inf 03 task2 ~/dev/workspace/rv/series/inf/03/task2
|
|||||||
Pokazuje informacje o konkretnym zadaniu w karcie.
|
Pokazuje informacje o konkretnym zadaniu w karcie.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl series cards tasks show inf 03 task1
|
./rvctl series cards tasks show inf bss task1
|
||||||
```
|
```
|
||||||
|
|
||||||
Typowe pola:
|
### `series cards tasks switch SERIES CARD TASK`
|
||||||
|
|
||||||
|
Tworzy albo przełącza lokalny branch pracy dla wybranego zadania.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl series cards tasks switch inf bss 4
|
||||||
|
```
|
||||||
|
|
||||||
|
Nazwa brancha jest liczona automatycznie z użytkownika w `tokens.json` i numeru
|
||||||
|
zadania. Dla użytkownika `u1` i zadania `task4_stack_strlen` powstanie:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
series inf
|
u1T4a
|
||||||
card 03
|
|
||||||
task task1
|
|
||||||
workspace_path ~/dev/workspace/rv/series/inf/03/task1
|
|
||||||
card_path ~/dev/workspace/rv/series/inf/03
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Jeżeli branch już istnieje, `rvctl` przełącza na niego repo. Jeżeli go nie ma,
|
||||||
|
tworzy go od aktualnego commita karty.
|
||||||
|
|
||||||
Na tym poziomie nie wprowadzamy osobnego `fetch`: zadania są pobierane razem z
|
Na tym poziomie nie wprowadzamy osobnego `fetch`: zadania są pobierane razem z
|
||||||
kartą przez `series cards fetch` albo razem z całą serią przez `series fetch`.
|
kartą przez `series cards fetch` albo razem z całą serią przez `series fetch`.
|
||||||
|
|
||||||
## Aliasowanie Starych Komend
|
## Aliasowanie Starych Komend
|
||||||
|
|
||||||
Stare komendy można zostawić jako aliasy kompatybilności:
|
Stare komendy zostają jako aliasy kompatybilności:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
list-series -> series list
|
list-series -> series list
|
||||||
list-cards inf -> series cards list inf
|
list-cards inf -> series cards list inf
|
||||||
submission inf 03 --class K --nick N -> series cards submission inf 03 --class K --nick N
|
submission inf bss --class K -> series cards submission inf bss --class K
|
||||||
```
|
```
|
||||||
|
|
||||||
Komendy `series cards tasks ...` nie mają starego odpowiednika i powinny być
|
|
||||||
wprowadzane tylko w nowym namespace.
|
|
||||||
|
|
||||||
Dokumentacja i nowe przykłady powinny promować namespace `series`.
|
Dokumentacja i nowe przykłady powinny promować namespace `series`.
|
||||||
|
|||||||
+4
-4
@@ -98,7 +98,7 @@ Znaczenie:
|
|||||||
Dla innego repo podaj `--repo`:
|
Dla innego repo podaj `--repo`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens list remote --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens list remote --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
## Synchronizacja
|
## Synchronizacja
|
||||||
@@ -233,9 +233,9 @@ Domyślnie komendy tokenów pracują na repo zawierającym `rvctl`. Dla kart pra
|
|||||||
albo innych repo podaj `--repo`.
|
albo innych repo podaj `--repo`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./rvctl tokens list remote --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens list remote --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
./rvctl tokens compare --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens compare --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
./rvctl tokens sync store r1 --repo ~/dev/workspace/rv/series/inf/03
|
./rvctl tokens sync store r1 --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack
|
||||||
```
|
```
|
||||||
|
|
||||||
Store tokenów nadal pozostaje jeden:
|
Store tokenów nadal pozostaje jeden:
|
||||||
|
|||||||
@@ -111,9 +111,9 @@ def load_config(config_path: Path) -> WorkspaceConfig:
|
|||||||
git = GitConfig(
|
git = GitConfig(
|
||||||
base_url=git_raw.get("base_url", "http://77.90.8.171:3001").rstrip("/"),
|
base_url=git_raw.get("base_url", "http://77.90.8.171:3001").rstrip("/"),
|
||||||
source_org=git_raw.get("source_org", "edu-inf"),
|
source_org=git_raw.get("source_org", "edu-inf"),
|
||||||
answer_org=git_raw.get("answer_org", "zsl-inf"),
|
answer_org=git_raw.get("answer_org", "c2025-1a-inf"),
|
||||||
source_remote=git_raw.get("source_remote", "r1"),
|
source_remote=git_raw.get("source_remote", "r1"),
|
||||||
answer_remote=git_raw.get("answer_remote", "a1"),
|
answer_remote=git_raw.get("answer_remote", "r1a"),
|
||||||
origin_remote=git_raw.get("origin_remote", "origin"),
|
origin_remote=git_raw.get("origin_remote", "origin"),
|
||||||
fallback_branch=git_raw.get("fallback_branch", "build"),
|
fallback_branch=git_raw.get("fallback_branch", "build"),
|
||||||
)
|
)
|
||||||
@@ -134,8 +134,7 @@ def load_config(config_path: Path) -> WorkspaceConfig:
|
|||||||
|
|
||||||
|
|
||||||
def series_directories(config: WorkspaceConfig) -> list[Path]:
|
def series_directories(config: WorkspaceConfig) -> list[Path]:
|
||||||
if not config.series_root.is_dir():
|
config.series_root.mkdir(parents=True, exist_ok=True)
|
||||||
raise SystemExit(f"Missing series root: {config.series_root}")
|
|
||||||
return sorted(path for path in config.series_root.iterdir() if path.is_dir())
|
return sorted(path for path in config.series_root.iterdir() if path.is_dir())
|
||||||
|
|
||||||
|
|
||||||
@@ -143,6 +142,51 @@ def card_directories(series_path: Path) -> list[Path]:
|
|||||||
return sorted(path for path in series_path.iterdir() if path.is_dir())
|
return sorted(path for path in series_path.iterdir() if path.is_dir())
|
||||||
|
|
||||||
|
|
||||||
|
def source_series_directories(config: WorkspaceConfig) -> list[Path]:
|
||||||
|
if not config.original_series_root.is_dir():
|
||||||
|
return []
|
||||||
|
return sorted(path for path in config.original_series_root.iterdir() if path.is_dir())
|
||||||
|
|
||||||
|
|
||||||
|
def source_card_directories(config: WorkspaceConfig, series_name: str) -> list[Path]:
|
||||||
|
source_series_path = config.original_series_root / series_name
|
||||||
|
if not source_series_path.is_dir():
|
||||||
|
return []
|
||||||
|
return sorted(path for path in source_series_path.iterdir() if path.is_dir())
|
||||||
|
|
||||||
|
|
||||||
|
def card_label_from_repo_name(repo_name: str) -> str:
|
||||||
|
if repo_name.startswith("lab-"):
|
||||||
|
repo_name = repo_name[4:]
|
||||||
|
marker = "-strlen-"
|
||||||
|
if marker in repo_name:
|
||||||
|
return repo_name.split(marker, 1)[1].split("-", 1)[0]
|
||||||
|
return repo_name
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_source_card_path(config: WorkspaceConfig, series_name: str, card_name: str) -> Path:
|
||||||
|
cards = source_card_directories(config, series_name)
|
||||||
|
if not cards:
|
||||||
|
raise SystemExit(f"Missing source series: {config.original_series_root / series_name}")
|
||||||
|
|
||||||
|
normalized = slug_value(card_name, "card")
|
||||||
|
matches = []
|
||||||
|
for card_path in cards:
|
||||||
|
repo_name = card_path.name
|
||||||
|
label = card_label_from_repo_name(repo_name)
|
||||||
|
candidates = {repo_name, label}
|
||||||
|
if normalized in candidates or any(normalized in candidate for candidate in candidates):
|
||||||
|
matches.append(card_path)
|
||||||
|
|
||||||
|
if not matches:
|
||||||
|
available = ", ".join(card_label_from_repo_name(path.name) for path in cards)
|
||||||
|
raise SystemExit(f"Missing card '{card_name}' in series '{series_name}'. Available: {available}")
|
||||||
|
if len(matches) > 1:
|
||||||
|
available = ", ".join(path.name for path in matches)
|
||||||
|
raise SystemExit(f"Ambiguous card '{card_name}' in series '{series_name}'. Matches: {available}")
|
||||||
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
def read_card_title(card_path: Path) -> str:
|
def read_card_title(card_path: Path) -> str:
|
||||||
readme_path = card_path / "README.md"
|
readme_path = card_path / "README.md"
|
||||||
if not readme_path.is_file():
|
if not readme_path.is_file():
|
||||||
@@ -170,12 +214,19 @@ def resolve_selector(
|
|||||||
return series_value, card_value
|
return series_value, card_value
|
||||||
|
|
||||||
if series_arg and not card_arg:
|
if series_arg and not card_arg:
|
||||||
if (config.series_root / series_arg).is_dir():
|
if (config.series_root / series_arg).is_dir() or (config.original_series_root / series_arg).is_dir():
|
||||||
if not default_card:
|
if not default_card:
|
||||||
raise SystemExit("Missing default card in config.")
|
raise SystemExit("Missing default card in config.")
|
||||||
return series_arg, default_card
|
return series_arg, default_card
|
||||||
if default_series and (config.series_root / default_series / series_arg).is_dir():
|
if default_series and (config.series_root / default_series / series_arg).is_dir():
|
||||||
return default_series, series_arg
|
return default_series, series_arg
|
||||||
|
if default_series:
|
||||||
|
try:
|
||||||
|
source_card_path = resolve_source_card_path(config, default_series, series_arg)
|
||||||
|
if workspace_card_path(config, default_series, source_card_path).is_dir():
|
||||||
|
return default_series, series_arg
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
|
||||||
series_value = series_arg or default_series
|
series_value = series_arg or default_series
|
||||||
card_value = card_arg or default_card
|
card_value = card_arg or default_card
|
||||||
@@ -186,15 +237,25 @@ def resolve_selector(
|
|||||||
|
|
||||||
def resolve_card_path(config: WorkspaceConfig, series_name: str, card_name: str) -> Path:
|
def resolve_card_path(config: WorkspaceConfig, series_name: str, card_name: str) -> Path:
|
||||||
card_path = config.series_root / series_name / card_name
|
card_path = config.series_root / series_name / card_name
|
||||||
if not card_path.is_dir():
|
if card_path.is_dir():
|
||||||
raise SystemExit(f"Missing card path: {card_path}")
|
return card_path
|
||||||
return card_path
|
source_card_path = resolve_source_card_path(config, series_name, card_name)
|
||||||
|
resolved_path = workspace_card_path(config, series_name, source_card_path)
|
||||||
|
if resolved_path.is_dir():
|
||||||
|
return resolved_path
|
||||||
|
raise SystemExit(f"Missing card path: {resolved_path}")
|
||||||
|
|
||||||
|
|
||||||
def print_series(config: WorkspaceConfig) -> None:
|
def print_series(config: WorkspaceConfig) -> None:
|
||||||
for series_path in series_directories(config):
|
config.series_root.mkdir(parents=True, exist_ok=True)
|
||||||
card_count = len(card_directories(series_path))
|
source_by_name = {path.name: path for path in source_series_directories(config)}
|
||||||
print(f"{series_path.name}\t{card_count}\t{series_path}")
|
workspace_by_name = {path.name: path for path in series_directories(config)}
|
||||||
|
for series_name in sorted(set(source_by_name) | set(workspace_by_name)):
|
||||||
|
source_path = source_by_name.get(series_name)
|
||||||
|
workspace_path = workspace_by_name.get(series_name)
|
||||||
|
source_count = len(source_card_directories(config, series_name)) if source_path else 0
|
||||||
|
workspace_count = len(card_directories(workspace_path)) if workspace_path else 0
|
||||||
|
print(f"{series_name}\t{source_count}\t{workspace_count}\t{workspace_path or ''}")
|
||||||
|
|
||||||
|
|
||||||
def print_cards(config: WorkspaceConfig, series_arg: str | None) -> None:
|
def print_cards(config: WorkspaceConfig, series_arg: str | None) -> None:
|
||||||
@@ -202,16 +263,23 @@ def print_cards(config: WorkspaceConfig, series_arg: str | None) -> None:
|
|||||||
if not series_name:
|
if not series_name:
|
||||||
raise SystemExit("Missing series name and no default series is configured.")
|
raise SystemExit("Missing series name and no default series is configured.")
|
||||||
|
|
||||||
series_path = config.series_root / series_name
|
config.series_root.mkdir(parents=True, exist_ok=True)
|
||||||
if not series_path.is_dir():
|
source_cards = {path.name: path for path in source_card_directories(config, series_name)}
|
||||||
raise SystemExit(f"Missing series path: {series_path}")
|
workspace_series_path = config.series_root / series_name
|
||||||
|
workspace_cards = {path.name: path for path in card_directories(workspace_series_path)} if workspace_series_path.is_dir() else {}
|
||||||
|
if not source_cards and not workspace_cards:
|
||||||
|
raise SystemExit(f"Missing series: {series_name}")
|
||||||
|
|
||||||
for card_path in card_directories(series_path):
|
for repo_name in sorted(set(source_cards) | set(workspace_cards)):
|
||||||
title = read_card_title(card_path)
|
source_path = source_cards.get(repo_name)
|
||||||
|
workspace_path = workspace_cards.get(repo_name)
|
||||||
|
title = read_card_title(workspace_path or source_path) if (workspace_path or source_path) else ""
|
||||||
|
label = card_label_from_repo_name(repo_name)
|
||||||
|
status = "workspace" if workspace_path else "source"
|
||||||
if title:
|
if title:
|
||||||
print(f"{card_path.name}\t{title}\t{card_path}")
|
print(f"{label}\t{repo_name}\t{status}\t{title}")
|
||||||
else:
|
else:
|
||||||
print(f"{card_path.name}\t{card_path}")
|
print(f"{label}\t{repo_name}\t{status}")
|
||||||
|
|
||||||
|
|
||||||
def slug_value(raw_value: str, field_name: str) -> str:
|
def slug_value(raw_value: str, field_name: str) -> str:
|
||||||
@@ -221,6 +289,13 @@ def slug_value(raw_value: str, field_name: str) -> str:
|
|||||||
return slug
|
return slug
|
||||||
|
|
||||||
|
|
||||||
|
def branch_value(raw_value: str, field_name: str = "branch") -> str:
|
||||||
|
branch_name = re.sub(r"[^A-Za-z0-9._-]+", "-", raw_value.strip()).strip("-")
|
||||||
|
if not branch_name:
|
||||||
|
raise SystemExit(f"Empty {field_name} after branch conversion: {raw_value!r}")
|
||||||
|
return branch_name
|
||||||
|
|
||||||
|
|
||||||
def git_capture(repo_path: Path, git_args: list[str]) -> str | None:
|
def git_capture(repo_path: Path, git_args: list[str]) -> str | None:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["git", "-C", str(repo_path)] + git_args,
|
["git", "-C", str(repo_path)] + git_args,
|
||||||
@@ -2388,13 +2463,17 @@ def repo_name_from_url(remote_url: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def build_answer_repo_name(source_repo_name: str, class_name: str, date_value: str) -> str:
|
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])
|
return source_repo_name
|
||||||
|
|
||||||
|
|
||||||
def build_answer_url(config: WorkspaceConfig, answer_repo_name: str) -> str:
|
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"
|
return f"{config.git.base_url}/{config.git.answer_org}/{answer_repo_name}.git"
|
||||||
|
|
||||||
|
|
||||||
|
def answer_url_for_card(config: WorkspaceConfig, repo_name: str) -> str:
|
||||||
|
return f"{config.git.base_url}/{config.git.answer_org}/{repo_name}.git"
|
||||||
|
|
||||||
|
|
||||||
def set_git_remote(repo_path: Path, remote_name: str, remote_url: str) -> str:
|
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])
|
existing_url = git_capture(repo_path, ["remote", "get-url", remote_name])
|
||||||
if existing_url == remote_url:
|
if existing_url == remote_url:
|
||||||
@@ -2413,21 +2492,393 @@ def set_git_remote(repo_path: Path, remote_name: str, remote_url: str) -> str:
|
|||||||
return "added"
|
return "added"
|
||||||
|
|
||||||
|
|
||||||
|
def source_url_for_card(config: WorkspaceConfig, series_name: str, source_card_path: Path) -> str:
|
||||||
|
source_url = git_capture(source_card_path, ["remote", "get-url", config.git.source_remote])
|
||||||
|
if source_url:
|
||||||
|
return source_url
|
||||||
|
source_url = git_capture(source_card_path, ["remote", "get-url", config.git.origin_remote])
|
||||||
|
if source_url:
|
||||||
|
return source_url
|
||||||
|
return f"{config.git.base_url}/{config.git.source_org}/{source_card_path.name}.git"
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_answer_token_from_source(
|
||||||
|
config: WorkspaceConfig,
|
||||||
|
source_remote: str,
|
||||||
|
answer_remote: str,
|
||||||
|
answer_url: str,
|
||||||
|
dry_run: bool = False,
|
||||||
|
) -> tuple[str, dict]:
|
||||||
|
token_data = load_token_store(config, write_normalized=not dry_run)
|
||||||
|
answer_server_info = server_info_from_url(config, answer_url)
|
||||||
|
if answer_server_info is None:
|
||||||
|
raise SystemExit(f"Only http/https remote URLs are supported: {answer_url}")
|
||||||
|
source_token = resolve_token_from_store(token_data, answer_server_info["endpoint"], source_remote, None)
|
||||||
|
if dry_run:
|
||||||
|
return "dry-run", source_token
|
||||||
|
status = register_token(
|
||||||
|
token_data,
|
||||||
|
answer_server_info,
|
||||||
|
answer_remote,
|
||||||
|
str(source_token.get("user", "")),
|
||||||
|
str(source_token.get("value", "")),
|
||||||
|
hydrate=False,
|
||||||
|
reset_api_metadata=True,
|
||||||
|
)
|
||||||
|
write_token_store(config, token_data)
|
||||||
|
return status, source_token
|
||||||
|
|
||||||
|
|
||||||
|
def workspace_card_path(config: WorkspaceConfig, series_name: str, source_card_path: Path) -> Path:
|
||||||
|
return config.series_root / series_name / source_card_path.name
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_workspace_card_path(config: WorkspaceConfig, series_name: str, card_name: str) -> Path:
|
||||||
|
source_card_path = resolve_source_card_path(config, series_name, card_name)
|
||||||
|
card_path = workspace_card_path(config, series_name, source_card_path)
|
||||||
|
if not card_path.is_dir():
|
||||||
|
raise SystemExit(f"Missing workspace card: {card_path}. Run: ./rvctl series cards fetch {series_name} {card_name}")
|
||||||
|
return card_path
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_card_to_workspace(config: WorkspaceConfig, series_name: str, card_name: str, dry_run: bool = False) -> dict[str, str]:
|
||||||
|
source_card_path = resolve_source_card_path(config, series_name, card_name)
|
||||||
|
repo_name = source_card_path.name
|
||||||
|
target_path = workspace_card_path(config, series_name, source_card_path)
|
||||||
|
source_url = source_url_for_card(config, series_name, source_card_path)
|
||||||
|
answer_url = answer_url_for_card(config, repo_name)
|
||||||
|
source_branch = git_current_branch(config, source_card_path)
|
||||||
|
|
||||||
|
if not dry_run:
|
||||||
|
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
if target_path.exists() and git_repo_root(target_path) is None:
|
||||||
|
raise SystemExit(f"Workspace card path exists but is not a git repo: {target_path}")
|
||||||
|
cloned = False
|
||||||
|
if not target_path.exists():
|
||||||
|
subprocess.run(["git", "clone", source_url, str(target_path)], check=True)
|
||||||
|
cloned = True
|
||||||
|
origin_url = git_capture(target_path, ["remote", "get-url", config.git.origin_remote])
|
||||||
|
if origin_url and git_capture(target_path, ["remote", "get-url", config.git.source_remote]) is None:
|
||||||
|
subprocess.run(
|
||||||
|
["git", "-C", str(target_path), "remote", "rename", config.git.origin_remote, config.git.source_remote],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
set_git_remote(target_path, config.git.source_remote, source_url)
|
||||||
|
subprocess.run(["git", "-C", str(target_path), "fetch", config.git.source_remote, source_branch], check=True)
|
||||||
|
current_branch = git_current_branch(config, target_path)
|
||||||
|
if cloned or current_branch == source_branch:
|
||||||
|
if current_branch != source_branch:
|
||||||
|
local_branch_exists = subprocess.run(
|
||||||
|
["git", "-C", str(target_path), "rev-parse", "--verify", "--quiet", source_branch],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
check=False,
|
||||||
|
).returncode == 0
|
||||||
|
if local_branch_exists:
|
||||||
|
subprocess.run(["git", "-C", str(target_path), "switch", source_branch], check=True)
|
||||||
|
else:
|
||||||
|
subprocess.run(
|
||||||
|
["git", "-C", str(target_path), "switch", "--track", "-c", source_branch, f"{config.git.source_remote}/{source_branch}"],
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
subprocess.run(["git", "-C", str(target_path), "pull", "--ff-only", config.git.source_remote, source_branch], check=True)
|
||||||
|
|
||||||
|
token_status, source_token = ensure_answer_token_from_source(
|
||||||
|
config,
|
||||||
|
config.git.source_remote,
|
||||||
|
config.git.answer_remote,
|
||||||
|
answer_url,
|
||||||
|
dry_run=dry_run,
|
||||||
|
)
|
||||||
|
answer_remote_status = "dry-run"
|
||||||
|
if not dry_run:
|
||||||
|
answer_remote_url = credentialed_remote_url(
|
||||||
|
answer_url,
|
||||||
|
str(source_token.get("user", "")),
|
||||||
|
str(source_token.get("value", "")),
|
||||||
|
)
|
||||||
|
answer_remote_status = set_git_remote(target_path, config.git.answer_remote, answer_remote_url)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"series": series_name,
|
||||||
|
"card": card_label_from_repo_name(repo_name),
|
||||||
|
"repo": repo_name,
|
||||||
|
"source": source_url,
|
||||||
|
"workspace": str(target_path),
|
||||||
|
"source_remote": config.git.source_remote,
|
||||||
|
"source_branch": source_branch,
|
||||||
|
"answer_remote": config.git.answer_remote,
|
||||||
|
"answer_org": config.git.answer_org,
|
||||||
|
"answer_repo": repo_name,
|
||||||
|
"answer_url": answer_url,
|
||||||
|
"token": config.git.answer_remote,
|
||||||
|
"token_status": token_status,
|
||||||
|
"answer_remote_status": answer_remote_status,
|
||||||
|
"status": "dry-run" if dry_run else "ready",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def print_key_values(values: dict[str, str]) -> None:
|
||||||
|
for key, value in values.items():
|
||||||
|
print(f"{key}\t{value}")
|
||||||
|
|
||||||
|
|
||||||
|
def task_names(card_path: Path) -> list[str]:
|
||||||
|
task_root = card_path / "src" / "tasks"
|
||||||
|
if not task_root.is_dir():
|
||||||
|
return []
|
||||||
|
names = {
|
||||||
|
path.stem
|
||||||
|
for path in task_root.iterdir()
|
||||||
|
if path.is_file() and path.stem.startswith("task")
|
||||||
|
}
|
||||||
|
return sorted(names)
|
||||||
|
|
||||||
|
|
||||||
|
def task_branch_part(task_name: str) -> str:
|
||||||
|
match = re.search(r"(?i)task[-_]?(\d+)", task_name)
|
||||||
|
if match is None:
|
||||||
|
match = re.fullmatch(r"(?i)t(\d+)", task_name.strip())
|
||||||
|
if match is None:
|
||||||
|
match = re.search(r"(\d+)", task_name)
|
||||||
|
if match is None:
|
||||||
|
raise SystemExit(f"Task name does not contain a task number: {task_name}")
|
||||||
|
return f"T{int(match.group(1))}"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_submission_task(config: WorkspaceConfig, card_path: Path, task_arg: str | None) -> str:
|
||||||
|
task_selector = task_arg or config.defaults.get("task")
|
||||||
|
if not task_selector:
|
||||||
|
raise SystemExit("Missing task selector. Pass --task or set defaults.task in workspace.json.")
|
||||||
|
|
||||||
|
names = task_names(card_path)
|
||||||
|
if not names:
|
||||||
|
return task_selector
|
||||||
|
|
||||||
|
matches = [name for name in names if name == task_selector]
|
||||||
|
if not matches:
|
||||||
|
try:
|
||||||
|
selector_part = task_branch_part(task_selector)
|
||||||
|
except SystemExit:
|
||||||
|
selector_part = ""
|
||||||
|
number_matches = []
|
||||||
|
for name in names:
|
||||||
|
try:
|
||||||
|
if selector_part and task_branch_part(name) == selector_part:
|
||||||
|
number_matches.append(name)
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
|
matches = number_matches
|
||||||
|
if not matches:
|
||||||
|
matches = [name for name in names if task_selector in name]
|
||||||
|
|
||||||
|
if not matches:
|
||||||
|
raise SystemExit(f"Missing task '{task_selector}' in card: {card_path}")
|
||||||
|
if len(matches) > 1:
|
||||||
|
raise SystemExit(f"Ambiguous task '{task_selector}'. Matches: {', '.join(matches)}")
|
||||||
|
return matches[0]
|
||||||
|
|
||||||
|
|
||||||
|
def store_user_for_submission(
|
||||||
|
config: WorkspaceConfig,
|
||||||
|
source_url: str,
|
||||||
|
answer_url: str,
|
||||||
|
required: bool = True,
|
||||||
|
) -> str:
|
||||||
|
token_data = load_token_store(config)
|
||||||
|
endpoints = []
|
||||||
|
for remote_url in [source_url, answer_url, config.git.base_url]:
|
||||||
|
server_info = server_info_from_url(config, remote_url)
|
||||||
|
if server_info is None:
|
||||||
|
continue
|
||||||
|
endpoint = server_info["endpoint"]
|
||||||
|
if endpoint not in endpoints:
|
||||||
|
endpoints.append(endpoint)
|
||||||
|
|
||||||
|
for endpoint in endpoints:
|
||||||
|
for remote_id in [config.git.source_remote, config.git.answer_remote]:
|
||||||
|
token_record = find_token_record(token_data, endpoint, remote_id)
|
||||||
|
if token_record and token_record.get("user"):
|
||||||
|
return slug_value(str(token_record["user"]), "token user")
|
||||||
|
|
||||||
|
users = {
|
||||||
|
str(token_record.get("user", ""))
|
||||||
|
for token_record in token_data.get("tokens", [])
|
||||||
|
if token_server_endpoint(token_record) in endpoints and token_record.get("user")
|
||||||
|
}
|
||||||
|
if len(users) == 1:
|
||||||
|
return slug_value(next(iter(users)), "token user")
|
||||||
|
|
||||||
|
if required:
|
||||||
|
raise SystemExit("Missing --nick and no unique user in tokens.json for the answer branch.")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def submission_branch_name(user_name: str, task_name: str) -> str:
|
||||||
|
return branch_value(f"{slug_value(user_name, 'student user')}{task_branch_part(task_name)}a")
|
||||||
|
|
||||||
|
|
||||||
|
def print_card_tasks(config: WorkspaceConfig, series_name: str, card_name: str) -> None:
|
||||||
|
card_path = resolve_workspace_card_path(config, series_name, card_name)
|
||||||
|
for task_name in task_names(card_path):
|
||||||
|
print(f"{series_name}\t{card_label_from_repo_name(card_path.name)}\t{task_name}\t{card_path / 'src' / 'tasks'}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_card_task(config: WorkspaceConfig, series_name: str, card_name: str, task_name: str) -> None:
|
||||||
|
card_path = resolve_workspace_card_path(config, series_name, card_name)
|
||||||
|
matches = [name for name in task_names(card_path) if name == task_name or task_name in name]
|
||||||
|
if not matches:
|
||||||
|
raise SystemExit(f"Missing task '{task_name}' in card: {card_path}")
|
||||||
|
if len(matches) > 1:
|
||||||
|
raise SystemExit(f"Ambiguous task '{task_name}'. Matches: {', '.join(matches)}")
|
||||||
|
print(f"series\t{series_name}")
|
||||||
|
print(f"card\t{card_label_from_repo_name(card_path.name)}")
|
||||||
|
print(f"task\t{matches[0]}")
|
||||||
|
print(f"card_path\t{card_path}")
|
||||||
|
print(f"task_root\t{card_path / 'src' / 'tasks'}")
|
||||||
|
|
||||||
|
|
||||||
|
def switch_card_task(
|
||||||
|
config: WorkspaceConfig,
|
||||||
|
series_name: str,
|
||||||
|
card_name: str,
|
||||||
|
task_selector: str,
|
||||||
|
branch_override: str | None = None,
|
||||||
|
dry_run: bool = False,
|
||||||
|
) -> dict[str, str]:
|
||||||
|
card_path = resolve_workspace_card_path(config, series_name, card_name)
|
||||||
|
task_name = resolve_submission_task(config, card_path, task_selector)
|
||||||
|
source_url = discover_source_url(config, card_path, series_name, card_name)
|
||||||
|
source_repo_name = repo_name_from_url(source_url)
|
||||||
|
answer_url = build_answer_url(config, source_repo_name)
|
||||||
|
student_user = store_user_for_submission(config, source_url, answer_url, required=branch_override is None)
|
||||||
|
branch_name = branch_value(branch_override) if branch_override else submission_branch_name(student_user, task_name)
|
||||||
|
current_branch = git_current_branch(config, card_path)
|
||||||
|
branch_exists = subprocess.run(
|
||||||
|
["git", "-C", str(card_path), "rev-parse", "--verify", "--quiet", branch_name],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
check=False,
|
||||||
|
).returncode == 0
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
status = "dry-run"
|
||||||
|
elif current_branch == branch_name:
|
||||||
|
status = "unchanged"
|
||||||
|
elif branch_exists:
|
||||||
|
subprocess.run(["git", "-C", str(card_path), "switch", branch_name], check=True)
|
||||||
|
status = "switched"
|
||||||
|
else:
|
||||||
|
subprocess.run(["git", "-C", str(card_path), "switch", "-c", branch_name], check=True)
|
||||||
|
status = "created"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"series": series_name,
|
||||||
|
"card": card_label_from_repo_name(card_path.name),
|
||||||
|
"task": task_name,
|
||||||
|
"student_user": student_user,
|
||||||
|
"branch": branch_name,
|
||||||
|
"workspace": str(card_path),
|
||||||
|
"status": status,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def print_series_details(config: WorkspaceConfig, series_name: str) -> None:
|
||||||
|
config.series_root.mkdir(parents=True, exist_ok=True)
|
||||||
|
source_path = config.original_series_root / series_name
|
||||||
|
workspace_path = config.series_root / series_name
|
||||||
|
cards = source_card_directories(config, series_name)
|
||||||
|
if not cards and not workspace_path.is_dir():
|
||||||
|
raise SystemExit(f"Missing series: {series_name}")
|
||||||
|
print(f"series\t{series_name}")
|
||||||
|
print(f"source_path\t{source_path if source_path.is_dir() else ''}")
|
||||||
|
print(f"workspace_path\t{workspace_path}")
|
||||||
|
print(f"cards_count\t{len(cards)}")
|
||||||
|
print(f"cards\t{' '.join(card_label_from_repo_name(path.name) for path in cards)}")
|
||||||
|
|
||||||
|
|
||||||
|
def print_card_details(config: WorkspaceConfig, series_name: str, card_name: str) -> None:
|
||||||
|
source_card_path = resolve_source_card_path(config, series_name, card_name)
|
||||||
|
target_path = workspace_card_path(config, series_name, source_card_path)
|
||||||
|
repo_name = source_card_path.name
|
||||||
|
print(f"series\t{series_name}")
|
||||||
|
print(f"card\t{card_label_from_repo_name(repo_name)}")
|
||||||
|
print(f"repo\t{repo_name}")
|
||||||
|
print(f"source_path\t{source_card_path}")
|
||||||
|
print(f"workspace_path\t{target_path}")
|
||||||
|
print(f"source_remote\t{config.git.source_remote}")
|
||||||
|
print(f"source_url\t{source_url_for_card(config, series_name, source_card_path)}")
|
||||||
|
print(f"answer_remote\t{config.git.answer_remote}")
|
||||||
|
print(f"answer_url\t{answer_url_for_card(config, repo_name)}")
|
||||||
|
if target_path.is_dir():
|
||||||
|
print(f"tasks\t{' '.join(task_names(target_path))}")
|
||||||
|
|
||||||
|
|
||||||
|
def run_series(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
|
if args.series_command == "list":
|
||||||
|
print_series(config)
|
||||||
|
return
|
||||||
|
if args.series_command == "show":
|
||||||
|
print_series_details(config, args.series)
|
||||||
|
return
|
||||||
|
if args.series_command == "fetch":
|
||||||
|
for source_card_path in source_card_directories(config, args.series):
|
||||||
|
result = fetch_card_to_workspace(config, args.series, source_card_path.name, dry_run=args.dry_run)
|
||||||
|
print_key_values(result)
|
||||||
|
print()
|
||||||
|
return
|
||||||
|
if args.series_command != "cards":
|
||||||
|
raise SystemExit(f"Unsupported series command: {args.series_command}")
|
||||||
|
|
||||||
|
if args.cards_command == "list":
|
||||||
|
print_cards(config, args.series)
|
||||||
|
return
|
||||||
|
if args.cards_command == "show":
|
||||||
|
print_card_details(config, args.series, args.card)
|
||||||
|
return
|
||||||
|
if args.cards_command == "fetch":
|
||||||
|
print_key_values(fetch_card_to_workspace(config, args.series, args.card, dry_run=args.dry_run))
|
||||||
|
return
|
||||||
|
if args.cards_command == "submission":
|
||||||
|
print_submission_plan(config, args)
|
||||||
|
return
|
||||||
|
if args.cards_command != "tasks":
|
||||||
|
raise SystemExit(f"Unsupported series cards command: {args.cards_command}")
|
||||||
|
|
||||||
|
if args.tasks_command == "list":
|
||||||
|
print_card_tasks(config, args.series, args.card)
|
||||||
|
return
|
||||||
|
if args.tasks_command == "show":
|
||||||
|
print_card_task(config, args.series, args.card, args.task)
|
||||||
|
return
|
||||||
|
if args.tasks_command == "switch":
|
||||||
|
print_key_values(switch_card_task(config, args.series, args.card, args.task, args.branch, args.dry_run))
|
||||||
|
return
|
||||||
|
raise SystemExit(f"Unsupported series cards tasks command: {args.tasks_command}")
|
||||||
|
|
||||||
|
|
||||||
def print_submission_plan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
def print_submission_plan(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
series_name, card_name = resolve_selector(config, args.series, args.card)
|
series_name, card_name = resolve_selector(config, args.series, args.card)
|
||||||
selector = f"{series_name}/{card_name}"
|
selector = f"{series_name}/{card_name}"
|
||||||
card_path = resolve_card_path(config, series_name, card_name)
|
card_path = resolve_card_path(config, series_name, card_name)
|
||||||
sync_tokens_from_repo(config, card_path)
|
sync_tokens_from_repo(config, card_path)
|
||||||
class_name = slug_value(args.class_name, "class")
|
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_url = args.source_url or discover_source_url(config, card_path, series_name, card_name)
|
||||||
source_repo_name = repo_name_from_url(source_url)
|
source_repo_name = repo_name_from_url(source_url)
|
||||||
answer_repo_name = build_answer_repo_name(source_repo_name, class_name, args.date)
|
answer_repo_name = build_answer_repo_name(source_repo_name, class_name, args.date)
|
||||||
answer_url = build_answer_url(config, answer_repo_name)
|
answer_url = build_answer_url(config, answer_repo_name)
|
||||||
source_branch = git_current_branch(config, card_path)
|
source_branch = git_current_branch(config, card_path)
|
||||||
|
task_name = resolve_submission_task(config, card_path, args.task)
|
||||||
|
student_user = (
|
||||||
|
slug_value(args.nick, "student user")
|
||||||
|
if args.nick
|
||||||
|
else store_user_for_submission(config, source_url, answer_url, required=args.branch is None)
|
||||||
|
)
|
||||||
|
branch_name = branch_value(args.branch) if args.branch else submission_branch_name(student_user, task_name)
|
||||||
|
|
||||||
print(f"selector\t{selector}")
|
print(f"selector\t{selector}")
|
||||||
print(f"card_path\t{card_path}")
|
print(f"card_path\t{card_path}")
|
||||||
|
print(f"task\t{task_name}")
|
||||||
print(f"source_repo\t{config.git.source_org}/{source_repo_name}")
|
print(f"source_repo\t{config.git.source_org}/{source_repo_name}")
|
||||||
print(f"source_remote\t{config.git.source_remote}")
|
print(f"source_remote\t{config.git.source_remote}")
|
||||||
print(f"source_url\t{source_url}")
|
print(f"source_url\t{source_url}")
|
||||||
@@ -2435,12 +2886,28 @@ def print_submission_plan(config: WorkspaceConfig, args: argparse.Namespace) ->
|
|||||||
print(f"answer_remote\t{config.git.answer_remote}")
|
print(f"answer_remote\t{config.git.answer_remote}")
|
||||||
print(f"answer_repo\t{config.git.answer_org}/{answer_repo_name}")
|
print(f"answer_repo\t{config.git.answer_org}/{answer_repo_name}")
|
||||||
print(f"answer_url\t{answer_url}")
|
print(f"answer_url\t{answer_url}")
|
||||||
|
print(f"student_user\t{student_user}")
|
||||||
print(f"student_branch\t{branch_name}")
|
print(f"student_branch\t{branch_name}")
|
||||||
|
|
||||||
if args.apply:
|
if args.apply:
|
||||||
source_status = set_git_remote(card_path, config.git.source_remote, source_url)
|
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)
|
token_status, source_token = ensure_answer_token_from_source(
|
||||||
|
config,
|
||||||
|
config.git.source_remote,
|
||||||
|
config.git.answer_remote,
|
||||||
|
answer_url,
|
||||||
|
)
|
||||||
|
answer_status = set_git_remote(
|
||||||
|
card_path,
|
||||||
|
config.git.answer_remote,
|
||||||
|
credentialed_remote_url(
|
||||||
|
answer_url,
|
||||||
|
str(source_token.get("user", "")),
|
||||||
|
str(source_token.get("value", "")),
|
||||||
|
),
|
||||||
|
)
|
||||||
print(f"apply_{config.git.source_remote}\t{source_status}")
|
print(f"apply_{config.git.source_remote}\t{source_status}")
|
||||||
|
print(f"apply_token_{config.git.answer_remote}\t{token_status}")
|
||||||
print(f"apply_{config.git.answer_remote}\t{answer_status}")
|
print(f"apply_{config.git.answer_remote}\t{answer_status}")
|
||||||
|
|
||||||
print("commands")
|
print("commands")
|
||||||
@@ -2621,10 +3088,11 @@ def print_main_overview(config_path: Path) -> None:
|
|||||||
["command", "arguments", "purpose"],
|
["command", "arguments", "purpose"],
|
||||||
[
|
[
|
||||||
["show-config", "", "show resolved paths and Git defaults"],
|
["show-config", "", "show resolved paths and Git defaults"],
|
||||||
["list-series", "", "list series from workspace"],
|
["series", "list|cards|fetch|tasks", "work with series, cards and tasks"],
|
||||||
["list-cards", "[series]", "list cards in a selected series"],
|
["list-series", "", "compat alias for series list"],
|
||||||
|
["list-cards", "[series]", "compat alias for series cards list"],
|
||||||
["tmux-container", "[series] [card]", "start tmux with container in pane 0"],
|
["tmux-container", "[series] [card]", "start tmux with container in pane 0"],
|
||||||
["submission", "[series] [card] --class K --nick N", "prepare answer repo and student branch"],
|
["submission", "[series] [card] --class K [--task T]", "prepare answer repo and student branch"],
|
||||||
["tokens", "list|compare|cmp|sync|update|remove|rm|add|read|stats|write", "manage tokens.json and Git remote credentials"],
|
["tokens", "list|compare|cmp|sync|update|remove|rm|add|read|stats|write", "manage tokens.json and Git remote credentials"],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -2634,10 +3102,10 @@ def print_main_overview(config_path: Path) -> None:
|
|||||||
["step", "command", "result"],
|
["step", "command", "result"],
|
||||||
[
|
[
|
||||||
["1", "./rvctl tokens compare", "compare repo remotes with tokens.json"],
|
["1", "./rvctl tokens compare", "compare repo remotes with tokens.json"],
|
||||||
["2", "./rvctl list-series", "choose a series"],
|
["2", "./rvctl series list", "choose a series"],
|
||||||
["3", "./rvctl list-cards inf", "choose a card"],
|
["3", "./rvctl series cards list inf", "choose a card"],
|
||||||
["4", "./rvctl tmux-container inf <card>", "start the working container"],
|
["4", "./rvctl series cards fetch inf bss", "fetch card and prepare r1a"],
|
||||||
["5", "./rvctl submission inf <card> --class 4i --nick u1", "prepare answer remote and branch"],
|
["5", "./rvctl series cards tasks list inf bss", "list card tasks"],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
print()
|
print()
|
||||||
@@ -2686,8 +3154,8 @@ def print_tokens_overview() -> None:
|
|||||||
["write store r1 to remote", "./rvctl tokens sync store r1 --repo PATH"],
|
["write store r1 to remote", "./rvctl tokens sync store r1 --repo PATH"],
|
||||||
["remove r1 from store", "./rvctl tokens rm store r1"],
|
["remove r1 from store", "./rvctl tokens rm store r1"],
|
||||||
["remove Git remote r1", "./rvctl tokens rm remote r1"],
|
["remove Git remote r1", "./rvctl tokens rm remote r1"],
|
||||||
["compare selected card", "./rvctl tokens cmp --repo ~/dev/workspace/rv/series/inf/03"],
|
["compare selected card", "./rvctl tokens cmp --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack"],
|
||||||
["stats selected card", "./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/03"],
|
["stats selected card", "./rvctl tokens stats --repo ~/dev/workspace/rv/series/inf/lab-rv32i-strlen-bss-data-stack"],
|
||||||
["write auth to r1", "./rvctl tokens write --repo PATH --remote r1 --server URL"],
|
["write auth to r1", "./rvctl tokens write --repo PATH --remote r1 --server URL"],
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@@ -2741,8 +3209,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
"tmux-container",
|
"tmux-container",
|
||||||
help="Create a tmux session with pane 0 running the container shell for a selected card.",
|
help="Create a tmux session with pane 0 running the container shell for a selected card.",
|
||||||
)
|
)
|
||||||
tmux_parser.add_argument("series", nargs="?", help="Series id or full selector like 'inf/03'.")
|
tmux_parser.add_argument("series", nargs="?", help="Series id or full selector like 'inf/bss'.")
|
||||||
tmux_parser.add_argument("card", nargs="?", help="Card id, for example '03'.")
|
tmux_parser.add_argument("card", nargs="?", help="Card id or unique fragment, for example 'bss'.")
|
||||||
tmux_parser.add_argument("--session", help="tmux session name.")
|
tmux_parser.add_argument("--session", help="tmux session name.")
|
||||||
tmux_parser.add_argument("--window", help="tmux window name.")
|
tmux_parser.add_argument("--window", help="tmux window name.")
|
||||||
tmux_parser.add_argument("--instance", help="RV_INSTANCE passed to the tool launcher.")
|
tmux_parser.add_argument("--instance", help="RV_INSTANCE passed to the tool launcher.")
|
||||||
@@ -2753,22 +3221,23 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
"submission",
|
"submission",
|
||||||
help="Print or apply source/answer git remotes for a selected card.",
|
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("series", nargs="?", help="Series id or full selector like 'inf/bss'.")
|
||||||
submission_parser.add_argument("card", nargs="?", help="Card id, for example '03'.")
|
submission_parser.add_argument("card", nargs="?", help="Card id or unique fragment, for example 'bss'.")
|
||||||
submission_parser.add_argument("--class", dest="class_name", required=True, help="Class id, for example '4i'.")
|
submission_parser.add_argument("--class", dest="class_name", required=True, help="Class id, for example 'c2025-1a-inf'.")
|
||||||
submission_parser.add_argument("--nick", required=True, help="Student nick used as the branch name.")
|
submission_parser.add_argument("--nick", help="Student nick. Defaults to the user from tokens.json.")
|
||||||
|
submission_parser.add_argument("--task", help="Task name or unique fragment. Defaults to defaults.task.")
|
||||||
submission_parser.add_argument(
|
submission_parser.add_argument(
|
||||||
"--branch",
|
"--branch",
|
||||||
help="Override the submission branch name. Defaults to the value of --nick.",
|
help="Override the submission branch name. Defaults to <store-user>T<task-number>a.",
|
||||||
)
|
)
|
||||||
submission_parser.add_argument(
|
submission_parser.add_argument(
|
||||||
"--date",
|
"--date",
|
||||||
default=dt.date.today().isoformat(),
|
default=dt.date.today().isoformat(),
|
||||||
help="Lesson date used in the answer repo name. Default: today in YYYY-MM-DD.",
|
help="Lesson date reported in the submission plan. Default: today in YYYY-MM-DD.",
|
||||||
)
|
)
|
||||||
submission_parser.add_argument(
|
submission_parser.add_argument(
|
||||||
"--source-url",
|
"--source-url",
|
||||||
help="Override the source repo URL. By default launcher reads the current origin from the card repo.",
|
help="Override the source repo URL. By default launcher reads r1 or origin from the card repo.",
|
||||||
)
|
)
|
||||||
submission_parser.add_argument(
|
submission_parser.add_argument(
|
||||||
"--apply",
|
"--apply",
|
||||||
@@ -2776,6 +3245,63 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
help="Write remotes into the selected card repo instead of only printing the plan.",
|
help="Write remotes into the selected card repo instead of only printing the plan.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
series_parser = subparsers.add_parser("series", help="Work with series, cards and tasks.")
|
||||||
|
series_subparsers = series_parser.add_subparsers(dest="series_command", required=True)
|
||||||
|
|
||||||
|
series_subparsers.add_parser("list", help="List available series.")
|
||||||
|
|
||||||
|
series_show_parser = series_subparsers.add_parser("show", help="Show one series.")
|
||||||
|
series_show_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
|
||||||
|
series_fetch_parser = series_subparsers.add_parser("fetch", help="Fetch all cards in a series.")
|
||||||
|
series_fetch_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
series_fetch_parser.add_argument("--dry-run", action="store_true", help="Print plan without cloning.")
|
||||||
|
|
||||||
|
cards_group_parser = series_subparsers.add_parser("cards", help="Work with cards in a series.")
|
||||||
|
cards_subparsers = cards_group_parser.add_subparsers(dest="cards_command", required=True)
|
||||||
|
|
||||||
|
cards_list_parser = cards_subparsers.add_parser("list", help="List cards in a series.")
|
||||||
|
cards_list_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
|
||||||
|
cards_show_parser = cards_subparsers.add_parser("show", help="Show one card.")
|
||||||
|
cards_show_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
cards_show_parser.add_argument("card", help="Card id or unique fragment, for example 'bss'.")
|
||||||
|
|
||||||
|
cards_fetch_parser = cards_subparsers.add_parser("fetch", help="Fetch one card into workspace.")
|
||||||
|
cards_fetch_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
cards_fetch_parser.add_argument("card", help="Card id or unique fragment, for example 'bss'.")
|
||||||
|
cards_fetch_parser.add_argument("--dry-run", action="store_true", help="Print plan without cloning.")
|
||||||
|
|
||||||
|
cards_submission_parser = cards_subparsers.add_parser("submission", help="Print/apply answer remotes for a card.")
|
||||||
|
cards_submission_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
cards_submission_parser.add_argument("card", help="Card id or unique fragment, for example 'bss'.")
|
||||||
|
cards_submission_parser.add_argument("--class", dest="class_name", required=True, help="Class id.")
|
||||||
|
cards_submission_parser.add_argument("--nick", help="Student nick. Defaults to the user from tokens.json.")
|
||||||
|
cards_submission_parser.add_argument("--task", help="Task name or unique fragment. Defaults to defaults.task.")
|
||||||
|
cards_submission_parser.add_argument("--branch", help="Override the submission branch name.")
|
||||||
|
cards_submission_parser.add_argument("--date", default=dt.date.today().isoformat(), help="Lesson date in YYYY-MM-DD.")
|
||||||
|
cards_submission_parser.add_argument("--source-url", help="Override source repo URL.")
|
||||||
|
cards_submission_parser.add_argument("--apply", action="store_true", help="Write remotes into the selected card repo.")
|
||||||
|
|
||||||
|
tasks_group_parser = cards_subparsers.add_parser("tasks", help="Work with tasks in a card.")
|
||||||
|
tasks_subparsers = tasks_group_parser.add_subparsers(dest="tasks_command", required=True)
|
||||||
|
|
||||||
|
tasks_list_parser = tasks_subparsers.add_parser("list", help="List tasks in a card.")
|
||||||
|
tasks_list_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
tasks_list_parser.add_argument("card", help="Card id or unique fragment, for example 'bss'.")
|
||||||
|
|
||||||
|
tasks_show_parser = tasks_subparsers.add_parser("show", help="Show one task.")
|
||||||
|
tasks_show_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
tasks_show_parser.add_argument("card", help="Card id or unique fragment, for example 'bss'.")
|
||||||
|
tasks_show_parser.add_argument("task", help="Task name or unique fragment.")
|
||||||
|
|
||||||
|
tasks_switch_parser = tasks_subparsers.add_parser("switch", help="Create or switch to the answer branch for a task.")
|
||||||
|
tasks_switch_parser.add_argument("series", help="Series id, for example 'inf'.")
|
||||||
|
tasks_switch_parser.add_argument("card", help="Card id or unique fragment, for example 'bss'.")
|
||||||
|
tasks_switch_parser.add_argument("task", help="Task name or unique fragment, for example 'task4' or '4'.")
|
||||||
|
tasks_switch_parser.add_argument("--branch", help="Override the branch name.")
|
||||||
|
tasks_switch_parser.add_argument("--dry-run", action="store_true", help="Print plan without switching branches.")
|
||||||
|
|
||||||
tokens_parser = subparsers.add_parser("tokens", help="Manage local token store and repo remotes.")
|
tokens_parser = subparsers.add_parser("tokens", help="Manage local token store and repo remotes.")
|
||||||
tokens_subparsers = tokens_parser.add_subparsers(dest="tokens_command", required=True)
|
tokens_subparsers = tokens_parser.add_subparsers(dest="tokens_command", required=True)
|
||||||
|
|
||||||
@@ -2919,6 +3445,9 @@ def main() -> int:
|
|||||||
if args.command == "submission":
|
if args.command == "submission":
|
||||||
print_submission_plan(config, args)
|
print_submission_plan(config, args)
|
||||||
return 0
|
return 0
|
||||||
|
if args.command == "series":
|
||||||
|
run_series(config, args)
|
||||||
|
return 0
|
||||||
if args.command == "tokens":
|
if args.command == "tokens":
|
||||||
if args.tokens_command in {"compare", "cmp"}:
|
if args.tokens_command in {"compare", "cmp"}:
|
||||||
run_tokens_compare(config, args)
|
run_tokens_compare(config, args)
|
||||||
|
|||||||
+3
-3
@@ -13,15 +13,15 @@
|
|||||||
"git": {
|
"git": {
|
||||||
"base_url": "http://77.90.8.171:3001",
|
"base_url": "http://77.90.8.171:3001",
|
||||||
"source_org": "edu-inf",
|
"source_org": "edu-inf",
|
||||||
"answer_org": "zsl-inf",
|
"answer_org": "c2025-1a-inf",
|
||||||
"source_remote": "r1",
|
"source_remote": "r1",
|
||||||
"answer_remote": "a1",
|
"answer_remote": "r1a",
|
||||||
"origin_remote": "origin",
|
"origin_remote": "origin",
|
||||||
"fallback_branch": "build"
|
"fallback_branch": "build"
|
||||||
},
|
},
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"series": "inf",
|
"series": "inf",
|
||||||
"card": "03",
|
"card": "bss",
|
||||||
"task": "task1",
|
"task": "task1",
|
||||||
"editor": "nvim",
|
"editor": "nvim",
|
||||||
"instance": "shell",
|
"instance": "shell",
|
||||||
|
|||||||
Reference in New Issue
Block a user