Skip to content

Commit 48655f2

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

File tree

2 files changed

+250
-0
lines changed

2 files changed

+250
-0
lines changed

.github/ci/release_checker.py

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

0 commit comments

Comments
 (0)