Skip to content

Commit c6bf62b

Browse files
committed
ci(release_checker): Script for check changes after last release for all components and BSPs
1 parent 836697f commit c6bf62b

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed

.github/ci/release_checker.py

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
import os
4+
import yaml
5+
import subprocess
6+
from tabulate import tabulate
7+
import re
8+
from datetime import datetime
9+
import wcwidth
10+
from pathlib import Path
11+
12+
repo_path = Path(".")
13+
14+
target_dirs = [
15+
os.path.join(repo_path, "bsp"),
16+
os.path.join(repo_path, "components"),
17+
]
18+
19+
deprecated = [
20+
"esp-box",
21+
"esp-box-lite",
22+
"esp32_azure_iot_kit",
23+
"esp32_s2_kaluga_kit",
24+
"hts221",
25+
26+
]
27+
28+
priority_order = {
29+
"⛔ Yes": 0,
30+
"⚠️ MD ": 1,
31+
"✔️ No ": 2
32+
}
33+
34+
results = []
35+
36+
release_commits = {}
37+
component_paths = {}
38+
39+
40+
def run_git_command(args, cwd):
41+
result = subprocess.run(["git"] + args, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
42+
return result.stdout.strip()
43+
44+
45+
for base_dir in target_dirs:
46+
if os.path.exists(base_dir):
47+
for root, dirs, files in os.walk(base_dir):
48+
if "idf_component.yml" in files:
49+
yml_path = os.path.join(root, "idf_component.yml")
50+
component_name = os.path.basename(root)
51+
version = "N/A"
52+
release_date = "?"
53+
changes_since_version = "N/A"
54+
commit_count = "?"
55+
56+
if component_name in deprecated:
57+
continue
58+
59+
try:
60+
with open(yml_path, "r") as f:
61+
yml_data = yaml.safe_load(f)
62+
version = yml_data.get("version", "N/A")
63+
except Exception as e:
64+
print(f"Chyba: {e}")
65+
66+
if version != "N/A":
67+
try:
68+
rel_yml_path = os.path.relpath(yml_path, repo_path).replace("\\", "/")
69+
log_output = run_git_command(["log", "-p", "--", rel_yml_path], cwd=repo_path)
70+
71+
current_commit = None
72+
current_date = None
73+
old_version = None
74+
new_version = None
75+
commit_hash = None
76+
77+
lines = log_output.splitlines()
78+
for i, line in enumerate(lines):
79+
if line.startswith("commit "):
80+
current_commit = line.split()[1]
81+
old_version = None
82+
new_version = None
83+
elif line.startswith("Date:"):
84+
raw_date = line.replace("Date:", "").strip()
85+
try:
86+
dt = datetime.strptime(raw_date, "%a %b %d %H:%M:%S %Y %z")
87+
current_date = dt.strftime("%d.%m.%Y")
88+
except Exception as e:
89+
print(f"Chyba: {e}")
90+
current_date = raw_date
91+
elif line.startswith("-version:"):
92+
match = re.match(r"-version:\s*['\"]?([\w\.\-~]+)['\"]?", line)
93+
if match:
94+
old_version = match.group(1)
95+
elif line.startswith("+version:"):
96+
match = re.match(r"\+version:\s*['\"]?([\w\.\-~]+)['\"]?", line)
97+
if match:
98+
new_version = match.group(1)
99+
100+
if old_version and new_version and old_version != new_version:
101+
commit_hash = current_commit
102+
release_date = current_date
103+
break
104+
105+
if not commit_hash:
106+
first_commit = run_git_command(["log", "--diff-filter=A", "--format=%H %aD", "--", rel_yml_path], cwd=repo_path)
107+
if first_commit:
108+
parts = first_commit.split()
109+
commit_hash = parts[0]
110+
try:
111+
dt = datetime.strptime(" ".join(parts[1:]), "%a, %d %b %Y %H:%M:%S %z")
112+
release_date = dt.strftime("%d.%m.%Y")
113+
except Exception as e:
114+
print(f"Chyba: {e}")
115+
release_date = "?"
116+
117+
if commit_hash:
118+
rel_component_path = os.path.relpath(root, repo_path).replace("\\", "/")
119+
120+
# Save
121+
release_commits[component_name] = commit_hash
122+
component_paths[component_name] = rel_component_path
123+
124+
diff_output = run_git_command(["diff", "--name-only", f"{commit_hash}..HEAD", "--", rel_component_path], cwd=repo_path)
125+
126+
extensions = {}
127+
if diff_output:
128+
extensions = {os.path.splitext(path)[1] for path in diff_output.splitlines()}
129+
130+
if extensions == {'.md'}:
131+
changes_since_version = "⚠️ MD "
132+
elif extensions:
133+
changes_since_version = "⛔ Yes"
134+
else:
135+
changes_since_version = "✔️ No "
136+
137+
count_output = run_git_command(["rev-list", f"{commit_hash}..HEAD", "--count", rel_component_path], cwd=repo_path)
138+
commit_count = count_output if count_output.isdigit() else "?"
139+
140+
except Exception as e:
141+
print(f"Chyba: {e}")
142+
143+
if release_date != "?":
144+
extension_str = ", ".join(sorted(extensions)) if extensions else "-"
145+
results.append([component_name, version, release_date, changes_since_version + f" ({commit_count})", extension_str])
146+
147+
148+
def show_diff_for_component(component_name):
149+
commit_hash = release_commits.get(component_name)
150+
rel_path = component_paths.get(component_name)
151+
152+
if not commit_hash or not rel_path:
153+
print("Commit path not found.")
154+
return
155+
156+
# Získání seznamu změněných souborů
157+
changed_files = run_git_command(["diff", "--name-only", f"{commit_hash}..HEAD", "--", rel_path], cwd=repo_path)
158+
changed_files = [f for f in changed_files.splitlines() if not f.endswith(".md")]
159+
160+
if not changed_files:
161+
print("No changes except *.md files.")
162+
return
163+
164+
print(f"Changes for component '{component_name}' from last release:\n")
165+
subprocess.run(["git", "diff", "--color=always", f"{commit_hash}..HEAD", "--"] + changed_files, cwd=repo_path)
166+
167+
168+
# Funkce pro výpočet vizuální šířky textu
169+
def visual_width(text):
170+
return sum(wcwidth.wcwidth(c) for c in text)
171+
172+
173+
# Funkce pro zarovnání textu na požadovanou vizuální šířku
174+
def pad_visual(text, target_width):
175+
current_width = visual_width(text)
176+
padding = max(0, target_width - current_width)
177+
return text + " " * padding
178+
179+
180+
def get_change_key(row):
181+
change = row[3].strip()
182+
extensions = row[4].split(", ")
183+
has_code_change = any(ext in ['.c', '.h'] for ext in extensions)
184+
185+
if change.startswith("⛔") and has_code_change:
186+
return 0
187+
elif change.startswith("⛔"):
188+
return 1
189+
elif change.startswith("⚠️"):
190+
return 2
191+
elif change.startswith("✔️"):
192+
return 3
193+
return 99
194+
195+
196+
# Seřaď results podle priority
197+
results.sort(key=get_change_key)
198+
199+
# Zarovnáme sloupec „Změny“ na vizuální šířku 8 znaků
200+
for row in results:
201+
row[3] = pad_visual(row[3], 8)
202+
203+
# Výpis jako Markdown tabulka
204+
headers = ["Component", "Version", "Released", "Changed", "Files"]
205+
markdown_table = tabulate(results, headers=headers, tablefmt="github")
206+
print("# Component/BSP release version checker")
207+
print("This page show all components in BSP repository with its latest versions and how many changes was not released.")
208+
print(markdown_table)
209+
210+
if os.getenv("CI") != "true":
211+
while True:
212+
component_name = input("Input the component name for diff (or type 'exit' to quit): ")
213+
if component_name.lower() == 'exit':
214+
break
215+
show_diff_for_component(component_name)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
name: Daily Release Check
3+
4+
on:
5+
pull_request:
6+
types: [opened, reopened, synchronize, labeled]
7+
schedule:
8+
- cron: '0 0 * * *' # every day at midnight
9+
workflow_dispatch: # can be run manually
10+
11+
12+
jobs:
13+
build-and-deploy:
14+
runs-on: ubuntu-latest
15+
16+
permissions:
17+
pages: write
18+
id-token: write
19+
20+
environment:
21+
name: github-pages
22+
url: ${{ steps.deployment.outputs.page_url }}
23+
24+
steps:
25+
- name: Checkout repo
26+
uses: actions/checkout@v4
27+
28+
- name: Set up Python
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: '3.11'
32+
33+
- name: Run release_checker.py
34+
run: |
35+
sudo apt-get update && sudo apt-get install -y pandoc
36+
pip install requests pyyaml tabulate wcwidth
37+
python .github/ci/release_checker.py > output.md
38+
cat output.md
39+
mkdir site
40+
pandoc output.md -o site/release_checker.html
41+
42+
- name: Upload file to gh pages
43+
uses: actions/upload-pages-artifact@v3
44+
with:
45+
path: site
46+
47+
- name: Deploy to GitHub Pages
48+
id: deployment
49+
uses: actions/deploy-pages@v4

0 commit comments

Comments
 (0)