mirror of https://github.com/RT-Thread/rt-thread
ci: Replace the judgment file modification mechanism of bsp_building.… (#10314)
ci: Replace the judgment file modification mechanism of bsp_building.yml and incorporate the PR status show into bsp_building.yml
This commit is contained in:
parent
9e9669b47d
commit
51b6df9c16
|
@ -41,66 +41,29 @@ permissions:
|
||||||
jobs:
|
jobs:
|
||||||
generate-matrix:
|
generate-matrix:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
name: 🔍 Summary of Git Diff Changes
|
||||||
outputs:
|
outputs:
|
||||||
filtered_matrix: ${{ steps.filter.outputs.filtered_matrix }}
|
filtered_matrix: ${{ steps.filter.outputs.filtered_matrix }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@main
|
uses: actions/checkout@main
|
||||||
with:
|
with:
|
||||||
sparse-checkout: .github/ALL_BSP_COMPILE.json
|
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Read matrix config
|
fetch-depth: 0
|
||||||
id: read-config
|
|
||||||
run: |
|
|
||||||
#读取ALL_BSP_COMPILE.json文件
|
|
||||||
raw_matrix_base64=$(cat .github/ALL_BSP_COMPILE.json |egrep -v '^//'|base64 -w 0)
|
|
||||||
echo "raw_matrix=$raw_matrix_base64" >> $GITHUB_OUTPUT
|
|
||||||
- name: Get changed files
|
|
||||||
id: changed_files
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
run: |
|
|
||||||
changed_files=$(curl -s \
|
|
||||||
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" | \
|
|
||||||
jq -r '.[].filename')
|
|
||||||
prefix=$(echo "$changed_files" | cut -d'/' -f1 | sort -u )
|
|
||||||
has_bsp=true
|
|
||||||
for r in "$prefix"; do
|
|
||||||
if [[ ! "$r" == "bsp" ]]; then
|
|
||||||
has_bsp=false
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [[ $has_bsp == true ]]; then
|
|
||||||
changed_files=$(echo "${changed_files}"| cut -d'/' -f2 | sort -u| tr '\n' ',' | sed 's/,$//')
|
|
||||||
echo "CHANGED_FILES=[${changed_files}]" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
#获取修改文件的路径,如果有bsp以外的文件夹则编译全部bsp,否则获取对应文件夹名
|
|
||||||
|
|
||||||
echo "${changed_files}"
|
|
||||||
- name: Filter matrix
|
- name: Filter matrix
|
||||||
id: filter
|
id: filter
|
||||||
run: |
|
run: |
|
||||||
raw_matrix=$(echo "${{ steps.read-config.outputs.raw_matrix }}" | base64 --decode)
|
git config --global http.postBuffer 524288000
|
||||||
|
git remote -v
|
||||||
CHANGED_FILES=${{ steps.changed_files.outputs.CHANGED_FILES }}
|
git fetch origin
|
||||||
CHANGED_FILES=$(echo "$CHANGED_FILES" | sed 's/\[\|\]//g')
|
git branch
|
||||||
# 将修改的文件路径与ALL_BSP_COMPILE.json文件的SUB_RTT_BSP进行判断,判断是否包含
|
git branch -a
|
||||||
filtered_matrix=[]
|
python tools/ci/git_diff_show.py origin/master
|
||||||
echo "${CHANGED_FILES}"
|
raw_matrix=$(cat .github/ALL_BSP_COMPILE_TEMP.json)
|
||||||
if [[ -z "$CHANGED_FILES" ]]; then
|
|
||||||
FILTER_CONDITION='.legs[]'
|
|
||||||
filtered_matrix=$(jq -c "{legs: [$FILTER_CONDITION]}" <<< "$raw_matrix")
|
|
||||||
else
|
|
||||||
CONDITIONS=$(echo "$CHANGED_FILES" | awk 'BEGIN { RS="," } { printf "contains(\"%s\") or ", $1 }')
|
|
||||||
CONDITIONS=${CONDITIONS% or }
|
|
||||||
|
|
||||||
FILTER_CONDITION=".legs[] | select(any(.SUB_RTT_BSP[]; $CONDITIONS))"
|
|
||||||
|
|
||||||
filtered_matrix=$(jq -c "{legs: [$FILTER_CONDITION]}" <<< "$raw_matrix")
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
FILTER_CONDITION='.legs[]'
|
||||||
|
filtered_matrix=$(jq -c "{legs: [$FILTER_CONDITION]}" <<< "$raw_matrix")
|
||||||
echo "filtered_matrix=${filtered_matrix}" >> $GITHUB_OUTPUT
|
echo "filtered_matrix=${filtered_matrix}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
echo ${filtered_matrix}
|
echo ${filtered_matrix}
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (c) 2006-2023, RT-Thread Development Team
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# Change Logs:
|
|
||||||
# Date Author Notes
|
|
||||||
# 2025-05-15 Supper Thomas show the PR size
|
|
||||||
#
|
|
||||||
|
|
||||||
name: PR status
|
|
||||||
|
|
||||||
# 这个ci主要用来显示当前的PR对于master节点上的增加的code size有多大
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read # to fetch code (actions/checkout)
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
name: PR status show
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@main
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@main
|
|
||||||
with:
|
|
||||||
python-version: 3.8
|
|
||||||
|
|
||||||
- name: fetch origin
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
git config --global http.postBuffer 524288000
|
|
||||||
git remote -v
|
|
||||||
git fetch origin
|
|
||||||
git branch
|
|
||||||
git branch -a
|
|
||||||
|
|
||||||
- name: PR status show
|
|
||||||
if: ${{ success() }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
python tools/ci/git_diff_status_show.py origin/master
|
|
||||||
|
|
|
@ -0,0 +1,372 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2025, RT-Thread Development Team
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Change Logs:
|
||||||
|
# Date Author Notes
|
||||||
|
# 2025-05-15 supperthomas add PR status show
|
||||||
|
# 2025-05-22 hydevcode 替换bsp_building.yml的判断文件修改机制,并将PR status show合并进bsp_building.yml
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
import locale
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import List
|
||||||
|
class FileDiff:
|
||||||
|
def __init__(self, path: str, status: str, size_change: int = 0, old_size: int = 0, new_size: int = 0):
|
||||||
|
self.path = path
|
||||||
|
self.status = status # A (added), M (modified), D (deleted), R (renamed)
|
||||||
|
self.size_change = size_change
|
||||||
|
self.old_size = old_size
|
||||||
|
self.new_size = new_size
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.status == 'A':
|
||||||
|
return f"Added {self.path}: {self.size_change} bytes"
|
||||||
|
elif self.status == 'D':
|
||||||
|
return f"Deleted {self.path}: was {self.old_size} bytes"
|
||||||
|
elif self.status == 'M' or self.status == 'R':
|
||||||
|
return f"Modified {self.path}: {self.size_change} bytes change"
|
||||||
|
else:
|
||||||
|
return f"{self.status} {self.path}"
|
||||||
|
|
||||||
|
class GitDiffAnalyzer:
|
||||||
|
def __init__(self, target_branch: str):
|
||||||
|
self.target_branch = target_branch
|
||||||
|
self.encoding = locale.getpreferredencoding()
|
||||||
|
|
||||||
|
def get_diff_files(self) -> List[FileDiff]:
|
||||||
|
"""获取当前分支与目标分支之间的差异文件"""
|
||||||
|
# 找到当前分支和目标分支的最近共同祖先
|
||||||
|
merge_base = self.get_merge_base()
|
||||||
|
if not merge_base:
|
||||||
|
print("No common ancestor found between current branch and target branch")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 获取差异文件列表
|
||||||
|
diff_cmd = f"git diff --name-status {merge_base} HEAD"
|
||||||
|
print(diff_cmd)
|
||||||
|
try:
|
||||||
|
output = subprocess.check_output(diff_cmd.split(), stderr=subprocess.STDOUT)
|
||||||
|
output = output.decode(self.encoding).strip()
|
||||||
|
print(output)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error executing git diff: {e.output.decode(self.encoding)}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not output:
|
||||||
|
print("No differences between current branch and target branch")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# 处理可能的换行符问题
|
||||||
|
output = output.replace('\r\n', '\n')
|
||||||
|
lines = output.split('\n')
|
||||||
|
|
||||||
|
file_diffs = []
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = line.split('\t')
|
||||||
|
if len(parts) < 2:
|
||||||
|
# 可能是重命名文件,格式为 "RXXX\told_path\tnew_path"
|
||||||
|
match = re.match(r'R(\d+)\t(.+)\t(.+)', line)
|
||||||
|
if match:
|
||||||
|
status = 'R'
|
||||||
|
old_path = match.group(2)
|
||||||
|
new_path = match.group(3)
|
||||||
|
# 计算重命名文件的修改大小
|
||||||
|
old_size = self.get_file_size(old_path, self.target_branch)
|
||||||
|
new_size = self.get_file_size(new_path, 'HEAD')
|
||||||
|
size_change = new_size - old_size if old_size > 0 else new_size
|
||||||
|
file_diffs.append(FileDiff(new_path, status, size_change, old_size, new_size))
|
||||||
|
else:
|
||||||
|
status = parts[0][0] # 取状态码的第一个字符
|
||||||
|
path = parts[1]
|
||||||
|
|
||||||
|
if status == 'A':
|
||||||
|
# 新增文件,计算大小
|
||||||
|
size = self.get_file_size(path, 'HEAD')
|
||||||
|
file_diffs.append(FileDiff(path, status, size, 0, size))
|
||||||
|
elif status == 'D':
|
||||||
|
# 删除文件,计算原大小
|
||||||
|
size = self.get_file_size(path, self.target_branch)
|
||||||
|
file_diffs.append(FileDiff(path, status, 0, size, 0))
|
||||||
|
elif status == 'M':
|
||||||
|
# 修改文件,计算大小变化
|
||||||
|
old_size = self.get_file_size(path, self.target_branch)
|
||||||
|
new_size = self.get_file_size(path, 'HEAD')
|
||||||
|
size_change = new_size - old_size
|
||||||
|
file_diffs.append(FileDiff(path, status, size_change, old_size, new_size))
|
||||||
|
|
||||||
|
return file_diffs
|
||||||
|
|
||||||
|
def get_merge_base(self) -> str:
|
||||||
|
"""获取当前分支和目标分支的最近共同祖先"""
|
||||||
|
try:
|
||||||
|
cmd = f"git merge-base {self.target_branch} HEAD"
|
||||||
|
print(cmd)
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
|
||||||
|
return output.decode(self.encoding).strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"Error executing git merge-base: {e.output.decode(self.encoding)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_file_size(self, path: str, ref: str) -> int:
|
||||||
|
"""获取指定分支上文件的大小"""
|
||||||
|
try:
|
||||||
|
# 使用 git cat-file 来获取文件内容,然后计算其大小
|
||||||
|
cmd = f"git cat-file blob {ref}:{path}"
|
||||||
|
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
|
||||||
|
return len(output)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
# 如果文件不存在或无法获取,返回0
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def format_size(size: int) -> str:
|
||||||
|
"""将字节大小转换为人类可读的格式"""
|
||||||
|
if size >= 0:
|
||||||
|
if size < 1024:
|
||||||
|
return f"{size} bytes"
|
||||||
|
elif size < 1024 * 1024:
|
||||||
|
return f"{size / 1024:.1f} KB"
|
||||||
|
elif size < 1024 * 1024 * 1024:
|
||||||
|
return f"{size / (1024 * 1024):.1f} MB"
|
||||||
|
else:
|
||||||
|
return f"{size / (1024 * 1024 * 1024):.1f} GB"
|
||||||
|
else:
|
||||||
|
temp_size=abs(size)
|
||||||
|
if temp_size < 1024:
|
||||||
|
return f"-{temp_size} bytes"
|
||||||
|
elif temp_size < 1024 * 1024:
|
||||||
|
return f"-{temp_size / 1024:.1f} KB"
|
||||||
|
elif temp_size < 1024 * 1024 * 1024:
|
||||||
|
return f"-{temp_size / (1024 * 1024):.1f} MB"
|
||||||
|
else:
|
||||||
|
return f"-{temp_size / (1024 * 1024 * 1024):.1f} GB"
|
||||||
|
|
||||||
|
def is_bsp(path):
|
||||||
|
return os.path.isfile(os.path.join(path, "rtconfig.h"))
|
||||||
|
|
||||||
|
def filter_bsp_config(file_diffs: List[FileDiff], config_path: str):
|
||||||
|
# 读取原始JSON配置
|
||||||
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
# 获取所有修改的文件路径(统一使用Linux风格路径)
|
||||||
|
modified_paths = [diff.path.replace('\\', '/') for diff in file_diffs]
|
||||||
|
print(modified_paths)
|
||||||
|
if not modified_paths:
|
||||||
|
print("master分支运行")
|
||||||
|
return
|
||||||
|
|
||||||
|
bsp_paths = set()
|
||||||
|
bsp_in_but_not_bsp_paths = set()
|
||||||
|
all_print_paths = set()
|
||||||
|
for modified_path in modified_paths:
|
||||||
|
parts = modified_path.strip(os.sep).split('/')
|
||||||
|
if not parts:
|
||||||
|
continue
|
||||||
|
first_level = parts[0]
|
||||||
|
first_level_path = os.path.join(os.getcwd(), first_level)
|
||||||
|
|
||||||
|
#处理bsp路径的逻辑
|
||||||
|
if first_level == "bsp":
|
||||||
|
temp_path=os.path.join(os.getcwd(), modified_path)
|
||||||
|
# 判断是否是文件
|
||||||
|
if not os.path.isdir(modified_path):
|
||||||
|
temp_path = os.path.dirname(temp_path)
|
||||||
|
|
||||||
|
#循环搜索每一级是否有rtconfig.h
|
||||||
|
while temp_path !=first_level_path:
|
||||||
|
if is_bsp(temp_path):
|
||||||
|
bsp_paths.add(temp_path)
|
||||||
|
|
||||||
|
break
|
||||||
|
temp_path=os.path.dirname(temp_path)
|
||||||
|
|
||||||
|
if temp_path ==first_level_path:
|
||||||
|
bsp_in_but_not_bsp_paths.add(parts[1])
|
||||||
|
|
||||||
|
else:
|
||||||
|
#非bsp路径逻辑
|
||||||
|
all_print_paths.add(first_level_path)
|
||||||
|
|
||||||
|
# 变成相对路径
|
||||||
|
bsp_list = set()
|
||||||
|
for path in sorted(bsp_paths):
|
||||||
|
current_working_dir = os.path.join(os.getcwd(), "bsp/")
|
||||||
|
if path.startswith(current_working_dir):
|
||||||
|
bsp_list.add(path[len(current_working_dir):].lstrip(os.sep))
|
||||||
|
else:
|
||||||
|
bsp_list.add(path) # 如果 first_level_path 不以 current_working_dir 开头,则保持不变
|
||||||
|
|
||||||
|
# 处理修改了bsp外的文件的情况
|
||||||
|
filtered_bsp = [
|
||||||
|
path for path in bsp_list
|
||||||
|
if path.split('/')[0] not in bsp_in_but_not_bsp_paths
|
||||||
|
]
|
||||||
|
|
||||||
|
merged_result = filtered_bsp + list(bsp_in_but_not_bsp_paths)
|
||||||
|
|
||||||
|
filtered_legs = []
|
||||||
|
for leg in config["legs"]:
|
||||||
|
matched_paths = [
|
||||||
|
path for path in leg.get("SUB_RTT_BSP", [])
|
||||||
|
if any(keyword in path for keyword in merged_result)
|
||||||
|
]
|
||||||
|
if matched_paths:
|
||||||
|
filtered_legs.append({**leg, "SUB_RTT_BSP": matched_paths})
|
||||||
|
|
||||||
|
# 生成新的配置
|
||||||
|
new_config = {"legs": filtered_legs}
|
||||||
|
|
||||||
|
# 判断有没有修改到bsp外的文件,有的话则编译全部
|
||||||
|
if not all_print_paths:
|
||||||
|
print(new_config)
|
||||||
|
file_name = ".github/ALL_BSP_COMPILE_TEMP.json"
|
||||||
|
|
||||||
|
# 将 new_config 写入文件
|
||||||
|
with open(file_name, "w", encoding="utf-8") as file:
|
||||||
|
json.dump(new_config, file, ensure_ascii=False, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 源文件路径
|
||||||
|
source_file = ".github/ALL_BSP_COMPILE.json" # 替换为你的文件路径
|
||||||
|
|
||||||
|
# 目标文件路径
|
||||||
|
target_file = ".github/ALL_BSP_COMPILE_TEMP.json" # 替换为你的目标文件路径
|
||||||
|
|
||||||
|
# 读取源文件并过滤掉带有 // 的行
|
||||||
|
with open(source_file, "r", encoding="utf-8") as infile, open(target_file, "w", encoding="utf-8") as outfile:
|
||||||
|
for line in infile:
|
||||||
|
if not line.lstrip().startswith("//"):
|
||||||
|
outfile.write(line)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Compare current branch with target branch and show file differences.')
|
||||||
|
parser.add_argument('target_branch', help='Target branch to compare against (e.g., master)')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
analyzer = GitDiffAnalyzer(args.target_branch)
|
||||||
|
file_diffs = analyzer.get_diff_files()
|
||||||
|
|
||||||
|
# 生成报告
|
||||||
|
generate_report(file_diffs, args.target_branch)
|
||||||
|
|
||||||
|
filter_bsp_config(file_diffs,".github/ALL_BSP_COMPILE_TEMP.json")
|
||||||
|
|
||||||
|
|
||||||
|
def add_summary(text):
|
||||||
|
"""
|
||||||
|
add summary to github action.
|
||||||
|
"""
|
||||||
|
if "GITHUB_STEP_SUMMARY" in os.environ:
|
||||||
|
summary_path = os.environ["GITHUB_STEP_SUMMARY"] # 获取摘要文件路径
|
||||||
|
|
||||||
|
# 将 text 写入摘要文件(追加模式)
|
||||||
|
with open(summary_path, "a") as f: # "a" 表示追加模式
|
||||||
|
f.write(text + "\n") # 写入文本并换行
|
||||||
|
else:
|
||||||
|
print("Environment variable $GITHUB_STEP_SUMMARY is not set.")
|
||||||
|
|
||||||
|
def summarize_diff(label, count, size=None):
|
||||||
|
"""格式化输出变更摘要"""
|
||||||
|
line = f" {label:<12} {count:>3} files"
|
||||||
|
if size is not None:
|
||||||
|
line += f" ({format_size(size)})"
|
||||||
|
add_summary(line)
|
||||||
|
|
||||||
|
def generate_report(file_diffs: List[FileDiff], target_branch: str):
|
||||||
|
"""生成差异报告"""
|
||||||
|
|
||||||
|
add_summary(f"# 📊 **Comparison between {target_branch} and Current Branch**\n")
|
||||||
|
|
||||||
|
# 分类统计
|
||||||
|
added_files = [f for f in file_diffs if f.status.startswith('A')]
|
||||||
|
removed_files = [f for f in file_diffs if f.status.startswith('D')]
|
||||||
|
modified_files = [f for f in file_diffs if f.status.startswith('M')]
|
||||||
|
renamed_files = [f for f in file_diffs if f.status.startswith('R')]
|
||||||
|
copied_files = [f for f in file_diffs if f.status.startswith('C')]
|
||||||
|
unmerged_files = [f for f in file_diffs if f.status.startswith('U')]
|
||||||
|
type_changed_files = [f for f in file_diffs if f.status.startswith('T')]
|
||||||
|
|
||||||
|
# 计算总变化量
|
||||||
|
total_added = sum(f.size_change for f in added_files)
|
||||||
|
total_removed = sum(f.old_size for f in removed_files)
|
||||||
|
total_modified = sum(f.size_change for f in modified_files)
|
||||||
|
total_copied = sum(f.size_change for f in copied_files)
|
||||||
|
total_renamed = sum(f.old_size for f in renamed_files)
|
||||||
|
total_type_changed = sum(f.size_change for f in type_changed_files)
|
||||||
|
|
||||||
|
total_size_change = sum(f.size_change for f in file_diffs)
|
||||||
|
# === 汇总输出 ===
|
||||||
|
summarize_diff("Total:", len(file_diffs))
|
||||||
|
summarize_diff("Added:", len(added_files), total_added)
|
||||||
|
summarize_diff("Removed:", len(removed_files), total_removed)
|
||||||
|
summarize_diff("Modified:", len(modified_files), total_modified)
|
||||||
|
summarize_diff("Renamed:", len(renamed_files), total_renamed)
|
||||||
|
summarize_diff("Copied:", len(copied_files), total_copied)
|
||||||
|
summarize_diff("Type Changed:", len(type_changed_files), total_type_changed)
|
||||||
|
summarize_diff("Unmerged:", len(unmerged_files))
|
||||||
|
|
||||||
|
|
||||||
|
if total_size_change > 0:
|
||||||
|
change_desc = f"📈 **Increase of {format_size(total_size_change)}**"
|
||||||
|
elif total_size_change < 0:
|
||||||
|
change_desc = f"📉 **Reduction of {format_size(abs(total_size_change))}**"
|
||||||
|
else:
|
||||||
|
change_desc = "➖ **No net size change**"
|
||||||
|
|
||||||
|
add_summary(f"\n### 📦 **Total Size Change:** {change_desc} (Excluding removed files)")
|
||||||
|
|
||||||
|
|
||||||
|
# 显示详细差异文件内容
|
||||||
|
add_summary("\n## 📂 **Detailed File Changes**\n")
|
||||||
|
|
||||||
|
for diff in file_diffs:
|
||||||
|
add_summary(f"📄 {diff.path} — **[{diff.status}]**")
|
||||||
|
|
||||||
|
# 文件状态处理
|
||||||
|
if diff.status.startswith('A'):
|
||||||
|
add_summary(f"📦 Size: {format_size(diff.new_size)}")
|
||||||
|
|
||||||
|
elif diff.status.startswith(('M', 'R')): # 修改或重命名
|
||||||
|
add_summary(f"📏 Original size: {format_size(diff.old_size)}")
|
||||||
|
add_summary(f"📐 New size: {format_size(diff.new_size)}")
|
||||||
|
|
||||||
|
delta = diff.size_change
|
||||||
|
if delta > 0:
|
||||||
|
change_str = f"📈 Increased by {format_size(delta)}"
|
||||||
|
elif delta < 0:
|
||||||
|
change_str = f"📉 Reduced by {format_size(abs(delta))}"
|
||||||
|
else:
|
||||||
|
change_str = "➖ No size change"
|
||||||
|
|
||||||
|
add_summary(f"📊 Size change: {change_str}")
|
||||||
|
|
||||||
|
elif diff.status.startswith('D'):
|
||||||
|
add_summary(f"🗑️ Original size: {format_size(diff.old_size)}")
|
||||||
|
|
||||||
|
elif diff.status.startswith('C'):
|
||||||
|
add_summary(f"📎 Copied from size: {format_size(diff.old_size)} → {format_size(diff.new_size)}")
|
||||||
|
|
||||||
|
elif diff.status.startswith('T'):
|
||||||
|
add_summary("⚙️ File type changed")
|
||||||
|
|
||||||
|
elif diff.status.startswith('U'):
|
||||||
|
add_summary("⚠️ Unmerged conflict detected")
|
||||||
|
|
||||||
|
else:
|
||||||
|
add_summary("❓ Unknown change type")
|
||||||
|
|
||||||
|
add_summary("\n\n")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -1,201 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (c) 2025, RT-Thread Development Team
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# Change Logs:
|
|
||||||
# Date Author Notes
|
|
||||||
# 2025-05-15 supperthomas add PR status show
|
|
||||||
#
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import argparse
|
|
||||||
import locale
|
|
||||||
from typing import List, Dict
|
|
||||||
|
|
||||||
class FileDiff:
|
|
||||||
def __init__(self, path: str, status: str, size_change: int = 0, old_size: int = 0, new_size: int = 0):
|
|
||||||
self.path = path
|
|
||||||
self.status = status # A (added), M (modified), D (deleted), R (renamed)
|
|
||||||
self.size_change = size_change
|
|
||||||
self.old_size = old_size
|
|
||||||
self.new_size = new_size
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self.status == 'A':
|
|
||||||
return f"Added {self.path}: {self.size_change} bytes"
|
|
||||||
elif self.status == 'D':
|
|
||||||
return f"Deleted {self.path}: was {self.old_size} bytes"
|
|
||||||
elif self.status == 'M' or self.status == 'R':
|
|
||||||
return f"Modified {self.path}: {self.size_change} bytes change"
|
|
||||||
else:
|
|
||||||
return f"{self.status} {self.path}"
|
|
||||||
|
|
||||||
class GitDiffAnalyzer:
|
|
||||||
def __init__(self, target_branch: str):
|
|
||||||
self.target_branch = target_branch
|
|
||||||
self.encoding = locale.getpreferredencoding()
|
|
||||||
|
|
||||||
def get_diff_files(self) -> List[FileDiff]:
|
|
||||||
"""获取当前分支与目标分支之间的差异文件"""
|
|
||||||
# 找到当前分支和目标分支的最近共同祖先
|
|
||||||
merge_base = self.get_merge_base()
|
|
||||||
if not merge_base:
|
|
||||||
print("No common ancestor found between current branch and target branch")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# 获取差异文件列表
|
|
||||||
diff_cmd = f"git diff --name-status {merge_base} "
|
|
||||||
print(diff_cmd)
|
|
||||||
try:
|
|
||||||
output = subprocess.check_output(diff_cmd.split(), stderr=subprocess.STDOUT)
|
|
||||||
output = output.decode(self.encoding).strip()
|
|
||||||
print(output)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print(f"Error executing git diff: {e.output.decode(self.encoding)}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not output:
|
|
||||||
print("No differences between current branch and target branch")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
# 处理可能的换行符问题
|
|
||||||
output = output.replace('\r\n', '\n')
|
|
||||||
lines = output.split('\n')
|
|
||||||
|
|
||||||
file_diffs = []
|
|
||||||
for line in lines:
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
parts = line.split('\t')
|
|
||||||
if len(parts) < 2:
|
|
||||||
# 可能是重命名文件,格式为 "RXXX\told_path\tnew_path"
|
|
||||||
match = re.match(r'R(\d+)\t(.+)\t(.+)', line)
|
|
||||||
if match:
|
|
||||||
status = 'R'
|
|
||||||
old_path = match.group(2)
|
|
||||||
new_path = match.group(3)
|
|
||||||
# 计算重命名文件的修改大小
|
|
||||||
old_size = self.get_file_size(old_path, self.target_branch)
|
|
||||||
new_size = self.get_file_size(new_path, 'HEAD')
|
|
||||||
size_change = new_size - old_size if old_size > 0 else new_size
|
|
||||||
file_diffs.append(FileDiff(new_path, status, size_change, old_size, new_size))
|
|
||||||
else:
|
|
||||||
status = parts[0][0] # 取状态码的第一个字符
|
|
||||||
path = parts[1]
|
|
||||||
|
|
||||||
if status == 'A':
|
|
||||||
# 新增文件,计算大小
|
|
||||||
size = self.get_file_size(path, 'HEAD')
|
|
||||||
file_diffs.append(FileDiff(path, status, size, 0, size))
|
|
||||||
elif status == 'D':
|
|
||||||
# 删除文件,计算原大小
|
|
||||||
size = self.get_file_size(path, self.target_branch)
|
|
||||||
file_diffs.append(FileDiff(path, status, 0, size, 0))
|
|
||||||
elif status == 'M':
|
|
||||||
# 修改文件,计算大小变化
|
|
||||||
old_size = self.get_file_size(path, self.target_branch)
|
|
||||||
new_size = self.get_file_size(path, 'HEAD')
|
|
||||||
size_change = new_size - old_size
|
|
||||||
file_diffs.append(FileDiff(path, status, size_change, old_size, new_size))
|
|
||||||
|
|
||||||
return file_diffs
|
|
||||||
|
|
||||||
def get_merge_base(self) -> str:
|
|
||||||
"""获取当前分支和目标分支的最近共同祖先"""
|
|
||||||
try:
|
|
||||||
cmd = f"git merge-base {self.target_branch} HEAD"
|
|
||||||
print(cmd)
|
|
||||||
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
|
|
||||||
return output.decode(self.encoding).strip()
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print(f"Error executing git merge-base: {e.output.decode(self.encoding)}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_file_size(self, path: str, ref: str) -> int:
|
|
||||||
"""获取指定分支上文件的大小"""
|
|
||||||
try:
|
|
||||||
# 使用 git cat-file 来获取文件内容,然后计算其大小
|
|
||||||
cmd = f"git cat-file blob {ref}:{path}"
|
|
||||||
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
|
|
||||||
return len(output)
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
# 如果文件不存在或无法获取,返回0
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def format_size(size: int) -> str:
|
|
||||||
"""将字节大小转换为人类可读的格式"""
|
|
||||||
if size < 1024:
|
|
||||||
return f"{size} bytes"
|
|
||||||
elif size < 1024 * 1024:
|
|
||||||
return f"{size / 1024:.1f} KB"
|
|
||||||
elif size < 1024 * 1024 * 1024:
|
|
||||||
return f"{size / (1024 * 1024):.1f} MB"
|
|
||||||
else:
|
|
||||||
return f"{size / (1024 * 1024 * 1024):.1f} GB"
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description='Compare current branch with target branch and show file differences.')
|
|
||||||
parser.add_argument('target_branch', help='Target branch to compare against (e.g., master)')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
analyzer = GitDiffAnalyzer(args.target_branch)
|
|
||||||
file_diffs = analyzer.get_diff_files()
|
|
||||||
|
|
||||||
# 生成报告
|
|
||||||
generate_report(file_diffs, args.target_branch)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_report(file_diffs: List[FileDiff], target_branch: str):
|
|
||||||
"""生成差异报告"""
|
|
||||||
print(f"\n=== Comparison between {target_branch} and current branch ===\n")
|
|
||||||
|
|
||||||
# 分类统计
|
|
||||||
added_files = [f for f in file_diffs if f.status == 'A']
|
|
||||||
removed_files = [f for f in file_diffs if f.status == 'D']
|
|
||||||
modified_files = [f for f in file_diffs if f.status == 'M']
|
|
||||||
renamed_files = [f for f in file_diffs if f.status == 'R']
|
|
||||||
|
|
||||||
# 计算总变化量
|
|
||||||
total_added = sum(f.size_change for f in added_files)
|
|
||||||
total_removed = sum(f.old_size for f in removed_files)
|
|
||||||
total_modified = sum(abs(f.size_change) for f in modified_files)
|
|
||||||
# 计算总的大小变化
|
|
||||||
total_size_change = sum(f.size_change for f in file_diffs)
|
|
||||||
|
|
||||||
print(f"Total changes: {len(file_diffs)} files")
|
|
||||||
print(f"Added: {len(added_files)} files ({format_size(total_added)})")
|
|
||||||
print(f"Removed: {len(removed_files)} files ({format_size(total_removed)})")
|
|
||||||
print(f"Modified: {len(modified_files)} files ({format_size(total_modified)})")
|
|
||||||
print(f"Renamed: {len(renamed_files)} files")
|
|
||||||
print(f"\nTotal size change: {format_size(total_size_change)}")
|
|
||||||
print("\n" + "="*60 + "\n")
|
|
||||||
|
|
||||||
# 显示详细差异
|
|
||||||
for diff in file_diffs:
|
|
||||||
print(diff)
|
|
||||||
|
|
||||||
# 详细展示修改和新增的文件大小
|
|
||||||
if diff.status == 'A':
|
|
||||||
print(f" Size: {format_size(diff.new_size)}")
|
|
||||||
elif diff.status == 'M':
|
|
||||||
print(f" Original size: {format_size(diff.old_size)}")
|
|
||||||
print(f" New size: {format_size(diff.new_size)}")
|
|
||||||
print(f" Size change: {format_size(abs(diff.size_change))}")
|
|
||||||
elif diff.status == 'R':
|
|
||||||
print(f" Original size: {format_size(diff.old_size)}")
|
|
||||||
print(f" New size: {format_size(diff.new_size)}")
|
|
||||||
print(f" Size change: {format_size(abs(diff.size_change))}")
|
|
||||||
elif diff.status == 'D':
|
|
||||||
print(f" Original size: {format_size(diff.old_size)}")
|
|
||||||
|
|
||||||
print("-" * 50)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
Loading…
Reference in New Issue