Skip to content

Commit 4eaa42a

Browse files
ci: add upstreaming dashboard (#460)
Based on code by Yaël Dillies and Javier Lopez-Contreras from <https://github.com/YaelDillies/LeanCamCombi/blob/cd307b92aeb4256b7a50fcebe1a51d2dc94f1675/scripts/upstreaming_dashboard.py>.
1 parent 5f21300 commit 4eaa42a

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

.github/workflows/push.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ jobs:
4343
- name: Lint project
4444
run: env LEAN_ABORT_ON_PANIC=1 ~/.elan/bin/lake exe runLinter Carleson
4545

46+
- name: Upstreaming dashboard
47+
run: python3 scripts/upstreaming_dashboard.py
48+
4649
- uses: leanprover-community/docgen-action@b210116d3e6096c0c7146f7a96a6d56b6884fef5 # 2025-06-12
4750
with:
4851
blueprint: true

scripts/upstreaming_dashboard.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2024 Yaël Dillies, Javier Lopez-Contreras. All rights reserved.
3+
# Released under Apache 2.0 license as described in the file LICENSE.
4+
5+
"""
6+
This script parse the aggregate json file and filters all PRs which touch some given files.
7+
"""
8+
9+
import json
10+
import os
11+
import pathlib
12+
import subprocess
13+
from typing import Any
14+
15+
# To customize
16+
MODULE = "Carleson"
17+
REPOSITORY = "fpvandoorn/carleson"
18+
BRANCH = "master"
19+
FOLDER_PATH = "./docs/_includes"
20+
21+
QUEUEBOARD = (
22+
"https://raw.githubusercontent.com/leanprover-community/queueboard/refs/"
23+
"heads/master/processed_data/open_pr_data.json")
24+
PR_SYMBOL = '<svg class="octicon octicon-git-pull-request open color-fg-open mr-1" title="Open" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path></svg>'
25+
26+
def main():
27+
pr_file = subprocess.run(["curl", QUEUEBOARD], capture_output=True, text=True)
28+
pr_json = json.loads(pr_file.stdout)["pr_statusses"]
29+
pr_dict = {pr["number"]: pr for pr in pr_json}
30+
31+
file_touched_pr: dict[str, list[Any]] = {}
32+
for pr in pr_dict:
33+
for file in pr_dict[pr]["files"]:
34+
pr_data = {
35+
"number" : pr,
36+
"title" : pr_dict[pr]["title"],
37+
"num_files": len(pr_dict[pr]["files"]),
38+
"is_draft": pr_dict[pr]["is_draft"]
39+
}
40+
if file not in file_touched_pr: file_touched_pr[file] = [pr_data]
41+
else: file_touched_pr[file].append(pr_data)
42+
43+
project_files: dict[str, Any] = {}
44+
for entry in pathlib.Path(MODULE).rglob("*.lean"):
45+
code = None
46+
with open(entry, 'r') as reader:
47+
code = reader.read()
48+
entry = str(entry)
49+
file = "/".join(entry.split("/")[1:])
50+
project_files[entry] = {
51+
"prs" : [] if file not in file_touched_pr else file_touched_pr[file],
52+
"num_sorries" : code.count("sorry"),
53+
"depends" : f"import {MODULE}" in code
54+
}
55+
56+
if not os.path.exists(FOLDER_PATH):
57+
os.makedirs(FOLDER_PATH)
58+
59+
with open(f"{FOLDER_PATH}/ready_to_upstream.md", 'w+') as writer:
60+
for file_path in project_files:
61+
if project_files[file_path]["num_sorries"] > 0:
62+
continue
63+
if project_files[file_path]["depends"]:
64+
continue
65+
module_name = file_path.replace('/','.')[:-5]
66+
writer.write(f"* [`{module_name}`](https://github.com/{REPOSITORY}/blob/{BRANCH}/{file_path})\n")
67+
for pr in project_files[file_path]["prs"]:
68+
if pr["title"][:4] == "perf":
69+
continue
70+
if pr["is_draft"]:
71+
continue
72+
73+
writer.write(f" * [{PR_SYMBOL} {pr['title']} #{pr['number']}]")
74+
writer.write(f"(https://github.com/leanprover-community/mathlib4/pull/{pr['number']})\n")
75+
76+
with open(f"{FOLDER_PATH}/easy_to_unlock.md", 'w+') as writer:
77+
for file_path in project_files:
78+
if project_files[file_path]["num_sorries"] == 0:
79+
continue
80+
if project_files[file_path]["depends"]:
81+
continue
82+
num_sorries = project_files[file_path]["num_sorries"]
83+
module_name = file_path.replace('/','.')[:-5]
84+
if num_sorries == 1:
85+
sorries = f"{num_sorries} sorry"
86+
else:
87+
sorries = f"{num_sorries} sorries"
88+
writer.write(f"* [`{module_name}`](https://github.com/{REPOSITORY}/blob/{BRANCH}/{file_path}) {sorries}\n")
89+
90+
main()

0 commit comments

Comments
 (0)