Source challenge status from GitLab instead of Nextcloud
Read the README.md of every repo in the GitLab challenges group via the REST API and rework the HedgeDoc note for project tracking: challenges ready for infra testing first, then status by category and a category x difficulty grid. Free-text status/difficulty/category values are normalised; untouched templates and repos scheduled for deletion are left out of the counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
# ctf_status.py
|
||||
|
||||
Reads the per-author challenge folders straight off the Nextcloud share (WebDAV,
|
||||
no sync, no mount) and tells you who to scream at.
|
||||
Reads the `README.md` of every repo in the GitLab challenges group (REST API, no
|
||||
clone) and shows which categories have challenges in which state, at which
|
||||
difficulty — in the terminal and as a HedgeDoc note.
|
||||
|
||||
## Setup
|
||||
|
||||
```sh
|
||||
cp ctf_status.env.example ctf_status.env && chmod 600 ctf_status.env
|
||||
$EDITOR ctf_status.env # app password from Nextcloud → Settings → Security
|
||||
$EDITOR ctf_status.env # GitLab group URL + token, HedgeDoc note
|
||||
```
|
||||
|
||||
## Use
|
||||
@@ -20,6 +21,10 @@ $EDITOR ctf_status.env # app password from Nextcloud → Settings → Secur
|
||||
./ctf_status.py --update <note-url> # ... or an explicit one
|
||||
```
|
||||
|
||||
The token is a fine-grained personal access token on the challenges group with
|
||||
**Project: Read** (to list the repos) and **Repository: Read** (to download the
|
||||
READMEs). A plain `.env` with `GL_PAT=` / `GITLAB_URL=` is read as well.
|
||||
|
||||
## Keeping a HedgeDoc note up to date
|
||||
|
||||
`--update` overwrites an **existing** note in place, so the URL you share stays the
|
||||
@@ -78,33 +83,51 @@ changed or something broke. Overlapping runs are prevented by a lock file, and t
|
||||
HedgeDoc write is retried (`--retries`, default 3) before the run gives up with a
|
||||
one-line error and a non-zero exit.
|
||||
|
||||
## What the note shows
|
||||
|
||||
1. **✅ Ready for infra testing** — first thing in the note: every challenge at
|
||||
Testing Ready, Tested or Ready, linked to its repo, so infra knows what it
|
||||
can deploy and test against right now.
|
||||
2. **Status by category** — how many challenges each category has per stage
|
||||
(⚪ Planning → 🟠 PoC Draft → 🟡 Testing Ready → 🔵 Tested → 🟢 Ready).
|
||||
3. **Category × difficulty** — one dot per challenge, coloured by its status, so
|
||||
gaps and stragglers show up at a glance.
|
||||
4. **Challenges** — one table per category, linked to the repo, with event,
|
||||
author, tester and the repo's last activity.
|
||||
5. **Needs attention** — see below.
|
||||
|
||||
## Reading the README
|
||||
|
||||
Only the template's header block is parsed:
|
||||
|
||||
```
|
||||
### Challenge Name
|
||||
- Category: crypto
|
||||
- Difficulty: medium
|
||||
- Event: TUM student CTF
|
||||
- Author: ...
|
||||
- Tester: ...
|
||||
- Status: Testing Ready
|
||||
```
|
||||
|
||||
People fill it in loosely, so values are normalised:
|
||||
|
||||
- **Status** is matched against the five stages ("Tested with …" → Tested,
|
||||
"PoC" → PoC Draft). If it names several ("Planning to PoC Draft", a half-deleted
|
||||
option list) the *least* advanced one counts and it is flagged as unclear.
|
||||
- **Difficulty** ranges written as `medium-hard`, `medium/hard` or
|
||||
`easy to medium` become their own column; otherwise the first level named wins.
|
||||
- **Category**: the first one named counts ("rev, misc" → rev); the full value
|
||||
is shown next to the challenge. Categories outside the usual list (e.g. `ai`)
|
||||
get their own row.
|
||||
|
||||
## What it flags
|
||||
|
||||
| verdict | meaning |
|
||||
| --- | --- |
|
||||
| 🔴 SCREAM | nothing submitted, **or** they filled out `template.md` in place |
|
||||
| 🟡 NAG | submitted, but placeholders / empty sections remain |
|
||||
| 🟢 OK | every challenge fully filled out |
|
||||
- challenge at Testing Ready or beyond with no tester (`[Tester Name]`, `??`,
|
||||
`None`, `not found yet` all count as none)
|
||||
- status / difficulty / category missing or unclear
|
||||
- no challenge name in the README
|
||||
- repos whose README is missing or still the untouched template — these are left
|
||||
out of the counts
|
||||
|
||||
A `template.md` that is still pristine is ignored. One that has real content in it
|
||||
is counted as a challenge *and* flagged — so nobody's work is lost, they just get
|
||||
yelled at.
|
||||
|
||||
Also flagged per challenge: unnamed challenge, `[placeholder]` values left in any
|
||||
of Category/Difficulty/Event/Author/Tester/Status, empty prose sections, and
|
||||
category/difficulty values outside the allowed set.
|
||||
|
||||
## Layout it expects
|
||||
|
||||
```
|
||||
<NC_ROOT>/
|
||||
alice/
|
||||
README.md # ignored
|
||||
template.md # ignored while pristine
|
||||
baby-rsa.md # ← a challenge, any filename
|
||||
bob/
|
||||
```
|
||||
|
||||
Any `.md` that isn't `README.md` is parsed as a challenge, so the file naming
|
||||
convention doesn't have to be enforced by the script. If you want it enforced,
|
||||
add the check in `Challenge.problems`.
|
||||
Repos GitLab has scheduled for deletion, and archived repos, are skipped.
|
||||
|
||||
Reference in New Issue
Block a user