mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-03-25 04:59:52 +08:00
js校验
This commit is contained in:
225
.github/workflows/jsValidation.yml
vendored
Normal file
225
.github/workflows/jsValidation.yml
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
name: JS Script Validation
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened, edited]
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'repo/js/**/*.js'
|
||||
- 'repo/js/**/*.json'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
path:
|
||||
description: '要验证的路径 (repo/js下的文件或目录)'
|
||||
required: true
|
||||
default: 'repo/js'
|
||||
type: string
|
||||
auto_fix:
|
||||
description: '是否自动修复问题'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
validate-js:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install packaging semver
|
||||
pip install chardet
|
||||
|
||||
- name: Setup Git
|
||||
run: |
|
||||
git config --global user.name "GitHub Actions Bot"
|
||||
git config --global user.email "actions@github.com"
|
||||
git config --global core.quotepath false
|
||||
|
||||
# 设置upstream远程仓库指向当前仓库
|
||||
CURRENT_REPO="${{ github.repository }}"
|
||||
git remote remove upstream 2>/dev/null || true
|
||||
git remote add upstream https://github.com/${CURRENT_REPO}.git
|
||||
|
||||
# 获取upstream分支
|
||||
git fetch upstream main
|
||||
|
||||
echo "远程仓库配置:"
|
||||
git remote -v
|
||||
|
||||
- name: Get changed files
|
||||
id: changed_files
|
||||
if: ${{ github.event_name == 'pull_request_target' }}
|
||||
run: |
|
||||
# 获取修改的JS和JSON文件
|
||||
echo "当前分支: $(git branch --show-current)"
|
||||
echo "HEAD指向: $(git rev-parse HEAD)"
|
||||
echo "获取修改的文件..."
|
||||
|
||||
# 方法1:使用git diff检测变化
|
||||
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD | grep -E '^repo/js/.*\.(js|json)$' || true)
|
||||
echo "方法1找到的文件:"
|
||||
echo "$CHANGED_FILES"
|
||||
|
||||
# 方法2:如果方法1没找到,使用GitHub API
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "方法1未找到文件,尝试使用GitHub API"
|
||||
CHANGED_FILES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" | \
|
||||
jq -r '.[] | select(.filename | test("^repo/js/.*\\.(js|json)$")) | .filename' || true)
|
||||
echo "方法2找到的文件:"
|
||||
echo "$CHANGED_FILES"
|
||||
fi
|
||||
|
||||
# 回退方案:如果还是没找到,验证整个JS目录
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "未找到修改的文件,验证整个JS目录"
|
||||
CHANGED_FILES="repo/js"
|
||||
fi
|
||||
|
||||
echo "最终文件列表:"
|
||||
echo "$CHANGED_FILES"
|
||||
echo "changed_files=$(echo "$CHANGED_FILES" | base64 -w 0)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run JS validation
|
||||
env:
|
||||
GITHUB_ACTOR: ${{ github.actor }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
CHANGED_FILES_B64: ${{ steps.changed_files.outputs.changed_files }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
# 设置验证路径
|
||||
if [ "${{ github.event_name }}" = "pull_request_target" ]; then
|
||||
CHANGED_FILES=$(echo "$CHANGED_FILES_B64" | base64 --decode)
|
||||
echo "PR触发模式,验证修改的文件:"
|
||||
echo "$CHANGED_FILES"
|
||||
|
||||
# 直接处理文件列表,不创建临时文件
|
||||
if [ "$CHANGED_FILES" = "repo/js" ]; then
|
||||
# 如果回退到整个目录,直接验证目录
|
||||
python build/validate_js.py "repo/js" --fix
|
||||
else
|
||||
# 处理每个修改的文件
|
||||
echo "$CHANGED_FILES" | while read -r file_path; do
|
||||
if [ -n "$file_path" ] && [ -f "$file_path" ]; then
|
||||
echo "处理文件: $file_path"
|
||||
python build/validate_js.py "$file_path" --fix
|
||||
|
||||
# 如果是JS文件,检查对应目录的manifest.json
|
||||
if [[ "$file_path" == *.js ]]; then
|
||||
file_dir=$(dirname "$file_path")
|
||||
manifest_path="$file_dir/manifest.json"
|
||||
if [ -f "$manifest_path" ]; then
|
||||
echo "检查对应目录的manifest.json: $manifest_path"
|
||||
python build/validate_js.py "$manifest_path" --fix
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
VALIDATE_PATH="${{ github.event.inputs.path }}"
|
||||
echo "手动触发模式,验证路径: ${VALIDATE_PATH}"
|
||||
python build/validate_js.py "${VALIDATE_PATH}" --fix
|
||||
fi
|
||||
|
||||
# 检查是否有文件被修改
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "发现修改,检查修改的文件:"
|
||||
git status --porcelain
|
||||
|
||||
# 只添加manifest.json文件的修改
|
||||
MANIFEST_FILES=$(git status --porcelain | grep "manifest.json" | awk '{print $2}')
|
||||
if [ -n "$MANIFEST_FILES" ]; then
|
||||
echo "提交manifest.json版本号修改:"
|
||||
echo "$MANIFEST_FILES"
|
||||
|
||||
# 检查是否只有版本号被修改
|
||||
for manifest_file in $MANIFEST_FILES; do
|
||||
echo "检查文件修改内容: $manifest_file"
|
||||
git diff "$manifest_file" | head -20
|
||||
|
||||
# 只添加这个特定的manifest.json文件
|
||||
git add "$manifest_file"
|
||||
done
|
||||
|
||||
git commit -m "自动更新manifest.json版本号 [ci skip]"
|
||||
|
||||
# 推送到PR分支
|
||||
if [ "${{ github.event_name }}" = "pull_request_target" ] && [ -n "$HEAD_REF" ]; then
|
||||
echo "PR触发,推送到PR分支: ${HEAD_REF}"
|
||||
# 先拉取最新代码,然后推送
|
||||
git fetch origin ${HEAD_REF}
|
||||
git push origin HEAD:${HEAD_REF} --force-with-lease
|
||||
elif [ -n "$PR_NUMBER" ] && [ -n "$HEAD_REF" ]; then
|
||||
echo "提交更改到PR: #$PR_NUMBER"
|
||||
|
||||
# 确定当前分支
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
echo "当前分支: ${CURRENT_BRANCH}"
|
||||
|
||||
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
|
||||
# 如果在detached HEAD状态,使用正确的方式推送
|
||||
echo "在detached HEAD状态,使用HEAD_REF推送: ${HEAD_REF}"
|
||||
git push origin HEAD:${HEAD_REF}
|
||||
else
|
||||
# 常规推送
|
||||
echo "推送到分支: ${CURRENT_BRANCH}"
|
||||
git push origin ${CURRENT_BRANCH}
|
||||
fi
|
||||
else
|
||||
echo "未关联PR或无法确定分支,直接提交到main分支"
|
||||
git push origin HEAD
|
||||
fi
|
||||
else
|
||||
echo "没有manifest.json文件被修改,无需提交"
|
||||
fi
|
||||
else
|
||||
echo "没有文件被修改,无需提交"
|
||||
fi
|
||||
|
||||
- name: Add PR comment
|
||||
if: ${{ github.event_name == 'pull_request_target' }}
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { execSync } = require('child_process');
|
||||
let commitMessage = '';
|
||||
|
||||
try {
|
||||
const lastCommit = execSync('git log -1 --pretty=%B').toString().trim();
|
||||
if (lastCommit.includes('自动修复')) {
|
||||
commitMessage = '✅ JS脚本校验完成并自动修复了一些问题。修改已提交到PR中。';
|
||||
} else {
|
||||
commitMessage = '✅ JS脚本校验完成,没有发现需要修复的问题';
|
||||
}
|
||||
} catch (error) {
|
||||
commitMessage = '✅ JS脚本校验完成,没有发现需要修复的问题';
|
||||
}
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: commitMessage
|
||||
});
|
||||
313
build/validate_js.py
Normal file
313
build/validate_js.py
Normal file
@@ -0,0 +1,313 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
from packaging.version import parse
|
||||
from semver import VersionInfo
|
||||
|
||||
# ==================== 配置和常量 ====================
|
||||
|
||||
DEFAULT_VERSION = "1.0.0"
|
||||
|
||||
# ==================== 文件操作 ====================
|
||||
|
||||
def load_json_file(file_path):
|
||||
"""加载 JSON 文件"""
|
||||
try:
|
||||
with open(file_path, encoding='utf-8') as f:
|
||||
return json.load(f), None
|
||||
except Exception as e:
|
||||
return None, f"❌ JSON 格式错误: {str(e)}"
|
||||
|
||||
def save_json_file(file_path, old_version, new_version):
|
||||
"""保存 JSON 文件,只修改版本号,保持原有格式"""
|
||||
try:
|
||||
# 读取原文件内容
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 使用简单的字符串替换
|
||||
old_version_str = f'"{old_version}"'
|
||||
new_version_str = f'"{new_version}"'
|
||||
|
||||
if old_version_str in content:
|
||||
new_content = content.replace(old_version_str, new_version_str, 1)
|
||||
|
||||
# 写回文件
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
return True
|
||||
else:
|
||||
print(f"未找到版本号: {old_version}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"保存文件失败: {str(e)}")
|
||||
return False
|
||||
|
||||
# ==================== 版本处理 ====================
|
||||
|
||||
def get_original_manifest_version(file_path):
|
||||
"""从上游仓库获取原始manifest.json的版本号"""
|
||||
try:
|
||||
# 直接从upstream/main获取原始版本
|
||||
result = subprocess.run(['git', 'show', f'upstream/main:{file_path}'],
|
||||
capture_output=True, text=True, encoding='utf-8')
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
data = json.loads(result.stdout)
|
||||
version = data.get('version', DEFAULT_VERSION)
|
||||
print(f"🔍 获取到原始版本号: {version} (来自 upstream/main:{file_path})")
|
||||
return version
|
||||
else:
|
||||
print(f"⚠️ 无法从upstream/main获取原始版本: {file_path}")
|
||||
print(f"错误信息: {result.stderr}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 获取原始版本时出错: {str(e)}")
|
||||
|
||||
# 如果无法获取,返回None表示无法比较
|
||||
print(f"⚠️ 无法获取原始版本号,跳过版本号检查")
|
||||
return None
|
||||
|
||||
def increment_version(version_str):
|
||||
"""增加版本号,在最后一个数字上+1"""
|
||||
try:
|
||||
# 解析版本号
|
||||
version_parts = version_str.split('.')
|
||||
if len(version_parts) >= 2:
|
||||
# 将最后一个部分转换为数字并+1
|
||||
last_part = int(version_parts[-1])
|
||||
version_parts[-1] = str(last_part + 1)
|
||||
return '.'.join(version_parts)
|
||||
else:
|
||||
# 如果版本号格式不正确,返回默认版本
|
||||
return DEFAULT_VERSION
|
||||
except (ValueError, IndexError):
|
||||
return DEFAULT_VERSION
|
||||
|
||||
def process_manifest_version(manifest_path, auto_fix=False):
|
||||
"""处理manifest.json的版本号"""
|
||||
corrections = []
|
||||
|
||||
try:
|
||||
print(f"🔍 处理manifest.json版本号: {manifest_path}")
|
||||
|
||||
# 加载当前manifest.json
|
||||
data, error = load_json_file(manifest_path)
|
||||
if error:
|
||||
return error, corrections
|
||||
|
||||
current_version = data.get('version', DEFAULT_VERSION)
|
||||
print(f"📋 当前版本号: {current_version}")
|
||||
|
||||
# 获取原始版本号
|
||||
original_version = get_original_manifest_version(manifest_path)
|
||||
|
||||
# 如果无法获取原始版本号,跳过版本号检查
|
||||
if original_version is None:
|
||||
print(f"⚠️ 无法获取原始版本号,跳过版本号检查")
|
||||
return None, corrections
|
||||
|
||||
print(f"📋 原始版本号: {original_version}")
|
||||
|
||||
# 检查版本号是否增加
|
||||
if current_version == original_version:
|
||||
print(f"⚠️ 版本号未增加: {current_version} == {original_version}")
|
||||
if auto_fix:
|
||||
# 自动增加版本号
|
||||
new_version = increment_version(current_version)
|
||||
print(f"🔄 自动增加版本号: {current_version} → {new_version}")
|
||||
corrections.append(f"版本号已自动更新: {current_version} → {new_version}")
|
||||
|
||||
# 保存文件
|
||||
if save_json_file(manifest_path, current_version, new_version):
|
||||
print(f"✅ {manifest_path}: 版本号已自动更新: {current_version} → {new_version}")
|
||||
else:
|
||||
corrections.append(f"保存文件失败: {manifest_path}")
|
||||
else:
|
||||
corrections.append(f"版本号未增加: {current_version} (与原始版本相同)")
|
||||
else:
|
||||
print(f"✅ {manifest_path}: 版本号已更新: {original_version} → {current_version}")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"处理manifest.json版本号时出错: {str(e)}"
|
||||
print(f"❌ {error_msg}")
|
||||
return error_msg, corrections
|
||||
|
||||
return None, corrections
|
||||
|
||||
# ==================== JS语法校验 ====================
|
||||
|
||||
def validate_js_syntax(js_file_path):
|
||||
"""校验JS文件语法"""
|
||||
errors = []
|
||||
try:
|
||||
# 使用Node.js -c 参数进行语法检查
|
||||
result = subprocess.run(['node', '-c', js_file_path], capture_output=True, text=True, check=True)
|
||||
if result.returncode == 0:
|
||||
print(f"✅ {js_file_path}: JS语法正确")
|
||||
else:
|
||||
errors.append(f"❌ {js_file_path}: JS语法错误: {result.stderr.strip()}")
|
||||
print(f"❌ {js_file_path}: JS语法错误: {result.stderr.strip()}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
errors.append(f"❌ {js_file_path}: JS语法错误: {e.stderr.strip()}")
|
||||
print(f"❌ {js_file_path}: JS语法错误: {e.stderr.strip()}")
|
||||
except FileNotFoundError:
|
||||
errors.append(f"❌ Node.js 未安装或不在PATH中,无法校验JS语法。")
|
||||
print(f"❌ Node.js 未安装或不在PATH中,无法校验JS语法。")
|
||||
return errors
|
||||
|
||||
# ==================== 核心校验逻辑 ====================
|
||||
|
||||
def validate_js_file(file_path, auto_fix=False):
|
||||
"""校验单个JS或JSON文件"""
|
||||
errors = []
|
||||
corrections = []
|
||||
|
||||
if file_path.lower().endswith('.json'):
|
||||
if os.path.basename(file_path).lower() == 'manifest.json':
|
||||
# 处理manifest.json的版本号
|
||||
version_error, version_corrections = process_manifest_version(file_path, auto_fix)
|
||||
if version_error:
|
||||
errors.append(version_error)
|
||||
corrections.extend(version_corrections)
|
||||
|
||||
# 校验JSON格式
|
||||
data, json_error = load_json_file(file_path)
|
||||
if json_error:
|
||||
errors.append(f"❌ {file_path}: {json_error}")
|
||||
print(f"❌ {file_path}: {json_error}")
|
||||
else:
|
||||
print(f"✅ {file_path}: JSON格式正确")
|
||||
|
||||
elif file_path.lower().endswith('.js'):
|
||||
# 校验JS语法
|
||||
js_errors = validate_js_syntax(file_path)
|
||||
errors.extend(js_errors)
|
||||
|
||||
return errors, corrections
|
||||
|
||||
def validate_js_directory(dir_path, auto_fix=False):
|
||||
"""校验JS目录下的所有文件"""
|
||||
all_errors = []
|
||||
all_corrections = []
|
||||
|
||||
try:
|
||||
for root, dirs, files in os.walk(dir_path):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
file_ext = os.path.splitext(file)[1].lower()
|
||||
|
||||
# 处理所有JS和JSON文件
|
||||
if file_ext in ['.js', '.json']:
|
||||
errors, corrections = validate_js_file(file_path, auto_fix)
|
||||
all_errors.extend(errors)
|
||||
all_corrections.extend(corrections)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ 遍历目录时出错: {str(e)}"
|
||||
all_errors.append(error_msg)
|
||||
print(error_msg)
|
||||
|
||||
return all_errors, all_corrections
|
||||
|
||||
def validate_js_files_from_list(file_list_path, auto_fix=False):
|
||||
"""从文件列表校验JS文件,并处理对应目录的manifest.json"""
|
||||
all_errors = []
|
||||
all_corrections = []
|
||||
processed_manifests = set() # 避免重复处理同一个manifest.json
|
||||
|
||||
try:
|
||||
print(f"📋 读取文件列表: {file_list_path}")
|
||||
with open(file_list_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
print(f"📋 文件列表内容 ({len(lines)} 行):")
|
||||
for i, line in enumerate(lines):
|
||||
file_path = line.strip()
|
||||
print(f" {i+1}: {file_path}")
|
||||
if file_path and os.path.exists(file_path):
|
||||
print(f" ✅ 文件存在,开始校验")
|
||||
errors, corrections = validate_js_file(file_path, auto_fix)
|
||||
all_errors.extend(errors)
|
||||
all_corrections.extend(corrections)
|
||||
|
||||
# 如果是JS文件,检查对应目录的manifest.json
|
||||
if file_path.lower().endswith('.js'):
|
||||
# 获取文件所在目录
|
||||
file_dir = os.path.dirname(file_path)
|
||||
manifest_path = os.path.join(file_dir, 'manifest.json')
|
||||
|
||||
# 检查manifest.json是否存在且未被处理过
|
||||
if os.path.exists(manifest_path) and manifest_path not in processed_manifests:
|
||||
print(f" 🔍 检查对应目录的manifest.json: {manifest_path}")
|
||||
processed_manifests.add(manifest_path)
|
||||
errors, corrections = validate_js_file(manifest_path, auto_fix)
|
||||
all_errors.extend(errors)
|
||||
all_corrections.extend(corrections)
|
||||
elif not os.path.exists(manifest_path):
|
||||
print(f" ⚠️ 未找到对应的manifest.json: {manifest_path}")
|
||||
else:
|
||||
print(f" ❌ 文件不存在或为空")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ 读取文件列表时出错: {str(e)}"
|
||||
all_errors.append(error_msg)
|
||||
print(error_msg)
|
||||
|
||||
return all_errors, all_corrections
|
||||
|
||||
# ==================== 主函数 ====================
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='校验 JS 脚本文件')
|
||||
parser.add_argument('path', help='要校验的文件或目录路径')
|
||||
parser.add_argument('--fix', action='store_true', help='自动修复问题')
|
||||
args = parser.parse_args()
|
||||
|
||||
path = args.path
|
||||
auto_fix = args.fix
|
||||
|
||||
print(f"🔍 开始JS脚本校验: {path}")
|
||||
|
||||
all_errors = []
|
||||
all_corrections = []
|
||||
|
||||
if os.path.isfile(path):
|
||||
# 单个文件
|
||||
if path.endswith('.txt'):
|
||||
# 文件列表
|
||||
print(f"📋 检测到文件列表: {path}")
|
||||
errors, corrections = validate_js_files_from_list(path, auto_fix)
|
||||
else:
|
||||
# 单个JS或JSON文件
|
||||
print(f"📄 检测到单个文件: {path}")
|
||||
errors, corrections = validate_js_file(path, auto_fix)
|
||||
all_errors.extend(errors)
|
||||
all_corrections.extend(corrections)
|
||||
|
||||
elif os.path.isdir(path):
|
||||
# 目录
|
||||
print(f"📁 检测到目录: {path}")
|
||||
errors, corrections = validate_js_directory(path, auto_fix)
|
||||
all_errors.extend(errors)
|
||||
all_corrections.extend(corrections)
|
||||
|
||||
else:
|
||||
print(f"❌ 无效的路径: {path}")
|
||||
exit(1)
|
||||
|
||||
# 输出结果
|
||||
if all_errors:
|
||||
print("\n❌ 发现以下错误:")
|
||||
for error in all_errors:
|
||||
print(f"- {error}")
|
||||
exit(1)
|
||||
elif all_corrections:
|
||||
print("\n✅ JS脚本校验完成,并自动修复了以下问题:")
|
||||
for correction in all_corrections:
|
||||
print(f"- {correction}")
|
||||
else:
|
||||
print("\n✅ JS脚本校验完成,没有发现问题")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user