diff --git a/src/roadmap/github.py b/src/roadmap/github.py new file mode 100644 index 0000000..bbb1e6d --- /dev/null +++ b/src/roadmap/github.py @@ -0,0 +1,36 @@ +import httpx + +from src.utils.cache import cache + +GITHUB_REPO = "ColinMaudry/decp.info" +_LABEL_KEYS = {"en cours": "en_cours", "mis au vote": "au_vote"} + + +@cache.memoize(timeout=3600) +def fetch_roadmap_issues() -> dict[str, list[dict]]: + """Issues ouvertes du dépôt portant un label de roadmap, regroupées par label. + + Appel anonyme à l'API GitHub publique. Mis en cache 1 h. + """ + resp = httpx.get( + f"https://api.github.com/repos/{GITHUB_REPO}/issues", + params={"state": "open", "per_page": 100}, + headers={"Accept": "application/vnd.github+json"}, + timeout=10, + ) + resp.raise_for_status() + result: dict[str, list[dict]] = {"en_cours": [], "au_vote": []} + for issue in resp.json(): + if "pull_request" in issue: + continue # l'endpoint /issues inclut les PR + labels = {lbl["name"] for lbl in issue.get("labels", [])} + for label, key in _LABEL_KEYS.items(): + if label in labels: + result[key].append( + { + "number": issue["number"], + "title": issue["title"], + "html_url": issue["html_url"], + } + ) + return result diff --git a/tests/roadmap/test_github.py b/tests/roadmap/test_github.py new file mode 100644 index 0000000..172ccde --- /dev/null +++ b/tests/roadmap/test_github.py @@ -0,0 +1,53 @@ +from src.roadmap import github + + +class _FakeResp: + def __init__(self, payload): + self._payload = payload + + def raise_for_status(self): + pass + + def json(self): + return self._payload + + +_ISSUES = [ + { + "number": 10, + "title": "Feature en cours", + "html_url": "https://github.com/ColinMaudry/decp.info/issues/10", + "labels": [{"name": "en cours"}], + }, + { + "number": 20, + "title": "Feature au vote", + "html_url": "https://github.com/ColinMaudry/decp.info/issues/20", + "labels": [{"name": "mis au vote"}], + }, + { + "number": 30, + "title": "Issue sans label pertinent", + "html_url": "https://github.com/ColinMaudry/decp.info/issues/30", + "labels": [{"name": "bug"}], + }, + { + "number": 40, + "title": "Une PR déguisée en issue", + "html_url": "https://github.com/ColinMaudry/decp.info/pull/40", + "labels": [{"name": "mis au vote"}], + "pull_request": {"url": "..."}, + }, +] + + +def test_fetch_roadmap_issues_filters_by_label(monkeypatch): + monkeypatch.setattr(github.httpx, "get", lambda *a, **k: _FakeResp(_ISSUES)) + result = github.fetch_roadmap_issues.uncached() + assert [i["number"] for i in result["en_cours"]] == [10] + assert [i["number"] for i in result["au_vote"]] == [20] # PR #40 exclue + assert result["en_cours"][0] == { + "number": 10, + "title": "Feature en cours", + "html_url": "https://github.com/ColinMaudry/decp.info/issues/10", + }