Awesome MCP Servers持续集成:GitHub Actions自动化工作流
·
Awesome MCP Servers持续集成:GitHub Actions自动化工作流
概述
Awesome MCP Servers 是一个精心策划的 Model Context Protocol (MCP) 服务器集合,为AI助手提供与各种服务和资源的标准化交互能力。随着项目规模的不断扩大,建立一个健壮的持续集成(CI)工作流变得至关重要。本文将深入探讨如何为Awesome MCP Servers项目设计和实现基于GitHub Actions的自动化工作流。
为什么需要持续集成?
当前挑战
- 手动验证困难:项目包含数百个MCP服务器链接,手动验证每个链接的有效性和准确性极其耗时
- 格式一致性:贡献者需要遵循严格的格式规范,包括emoji标识、分类排序和描述标准
- 多语言维护:项目支持8种语言的README文件,需要保持同步更新
- 链接有效性:确保所有GitHub仓库链接有效且可访问
CI工作流的价值
GitHub Actions工作流设计
核心工作流组件
1. 格式验证工作流
name: Format Validation
on:
pull_request:
paths:
- 'README*.md'
- 'CONTRIBUTING.md'
jobs:
validate-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install regex requests beautifulsoup4
- name: Run format validation
run: python scripts/validate_format.py
2. 链接检查工作流
name: Link Validation
on:
schedule:
- cron: '0 0 * * 0' # 每周日运行
workflow_dispatch: # 手动触发
jobs:
check-links:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Link Checker
uses: lycheeverse/lychee-action@v1
with:
args: --verbose --no-progress **/*.md
验证脚本实现
格式验证脚本 (validate_format.py)
import re
import requests
from pathlib import Path
class READMEValidator:
def __init__(self):
self.patterns = {
'server_line': r'^- \[.*?\]\(https://github\.com/.*?\) .*? - .*$',
'emoji_pattern': r'[🐍📇🏎️🦀#️⃣☕🌊💎☁️🏠📟🍎🪟🐧]',
'category_header': r'^### .* <a name=".*"></a>.*$'
}
def validate_readme(self, file_path):
errors = []
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
current_category = None
servers_in_category = []
for i, line in enumerate(lines, 1):
line = line.strip()
# 检查分类标题
if line.startswith('### '):
current_category = line
servers_in_category = []
if not re.match(self.patterns['category_header'], line):
errors.append(f"Line {i}: Invalid category format: {line}")
# 检查服务器行格式
elif line.startswith('- ['):
if not re.match(self.patterns['server_line'], line):
errors.append(f"Line {i}: Invalid server format: {line}")
# 检查emoji标识
emojis = re.findall(self.patterns['emoji_pattern'], line)
if not emojis:
errors.append(f"Line {i}: Missing emoji identifiers: {line}")
servers_in_category.append(line)
return errors
def main():
validator = READMEValidator()
readme_files = ['README.md', 'README-zh.md', 'README-ja.md', 'README-ko.md']
all_errors = []
for file in readme_files:
if Path(file).exists():
errors = validator.validate_readme(file)
if errors:
all_errors.extend([f"{file}: {error}" for error in errors])
if all_errors:
print("Validation errors found:")
for error in all_errors:
print(error)
exit(1)
else:
print("All README files passed validation!")
if __name__ == "__main__":
main()
链接验证增强脚本
import requests
import re
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.parse import urlparse
class LinkValidator:
def __init__(self):
self.session = requests.Session()
self.session.headers.update({'User-Agent': 'Awesome-MCP-Servers-CI'})
def check_link(self, url):
try:
response = self.session.head(url, timeout=10, allow_redirects=True)
return url, response.status_code == 200
except requests.RequestException:
return url, False
def extract_github_links(self, content):
pattern = r'https://github\.com/[a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+'
return re.findall(pattern, content)
def validate_links():
validator = LinkValidator()
broken_links = []
# 读取所有README文件
readme_files = ['README.md', 'README-zh.md', 'README-ja.md', 'README-ko.md']
all_links = []
for file in readme_files:
try:
with open(file, 'r', encoding='utf-8') as f:
content = f.read()
links = validator.extract_github_links(content)
all_links.extend(links)
except FileNotFoundError:
continue
# 并发检查链接
with ThreadPoolExecutor(max_workers=10) as executor:
future_to_url = {
executor.submit(validator.check_link, url): url
for url in set(all_links)
}
for future in as_completed(future_to_url):
url, is_valid = future.result()
if not is_valid:
broken_links.append(url)
return broken_links
完整的工作流配置
.github/workflows/ci.yml
name: Awesome MCP Servers CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
paths:
- 'README*.md'
- 'CONTRIBUTING.md'
- '.github/workflows/**'
- 'scripts/**'
jobs:
format-validation:
name: Format Validation
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install regex requests beautifulsoup4
- name: Run format validation
run: python scripts/validate_format.py
spell-check:
name: Spell Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install cspell
run: npm install -g cspell
- name: Run spell check
run: cspell "README*.md" "CONTRIBUTING.md"
link-validation:
name: Link Validation
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install requests
- name: Run link validation
run: python scripts/validate_links.py
multi-language-sync:
name: Multi-language Sync Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: pip install deepdiff
- name: Run sync validation
run: python scripts/validate_sync.py
验证工具详细实现
多语言同步验证脚本
from deepdiff import DeepDiff
import re
def extract_server_entries(content):
"""从README内容中提取服务器条目"""
pattern = r'- \[(.*?)\]\((.*?)\) (.*?) - (.*)'
entries = re.findall(pattern, content)
return {entry[1]: (entry[0], entry[2], entry[3]) for entry in entries}
def validate_sync():
main_readme = open('README.md', 'r', encoding='utf-8').read()
main_entries = extract_server_entries(main_readme)
translation_files = {
'zh': 'README-zh.md',
'ja': 'README-ja.md',
'ko': 'README-ko.md'
}
errors = []
for lang, filename in translation_files.items():
try:
content = open(filename, 'r', encoding='utf-8').read()
lang_entries = extract_server_entries(content)
# 检查缺失的条目
missing_in_main = set(lang_entries.keys()) - set(main_entries.keys())
missing_in_lang = set(main_entries.keys()) - set(lang_entries.keys())
if missing_in_main:
errors.append(f"{filename}: Contains entries not in main README: {list(missing_in_main)}")
if missing_in_lang:
errors.append(f"{filename}: Missing entries from main README: {list(missing_in_lang)}")
except FileNotFoundError:
errors.append(f"{filename}: Translation file not found")
return errors
高级格式验证增强
import ast
from collections import defaultdict
class AdvancedValidator:
def __init__(self):
self.categories = defaultdict(list)
def validate_category_structure(self, content):
"""验证分类结构完整性"""
lines = content.split('\n')
current_category = None
for line in lines:
line = line.strip()
if line.startswith('### '):
current_category = line
elif line.startswith('- [') and current_category:
self.categories[current_category].append(line)
# 检查每个分类是否有足够数量的服务器
errors = []
for category, servers in self.categories.items():
if len(servers) < 3: # 假设每个分类至少应有3个服务器
errors.append(f"Category '{category}' has only {len(servers)} servers")
return errors
def validate_emoji_consistency(self):
"""验证emoji使用的一致性"""
emoji_patterns = {
'🐍': 'Python',
'📇': 'TypeScript/JavaScript',
'🏎️': 'Go',
'🦀': 'Rust',
'#️⃣': 'C#',
'☕': 'Java',
'🌊': 'C/C++',
'💎': 'Ruby'
}
errors = []
for category, servers in self.categories.items():
for server in servers:
used_emojis = re.findall(r'[🐍📇🏎️🦀#️⃣☕🌊💎]', server)
if not used_emojis:
errors.append(f"Server missing language emoji: {server}")
return errors
自动化测试矩阵
测试覆盖率统计
性能优化策略
class PerformanceOptimizer:
def __init__(self):
self.cache = {}
def cached_link_check(self, url):
"""带缓存的链接检查"""
if url in self.cache:
return self.cache[url]
result = self.check_link(url)
self.cache[url] = result
return result
def batch_processing(self, urls, batch_size=50):
"""批量处理链接检查"""
results = []
for i in range(0, len(urls), batch_size):
batch = urls[i:i + batch_size]
batch_results = self.process_batch(batch)
results.extend(batch_results)
return results
错误处理和报告机制
详细的错误报告生成
class ErrorReporter:
def generate_report(self, errors, context=None):
"""生成详细的错误报告"""
report = {
'timestamp': datetime.now().isoformat(),
'total_errors': len(errors),
'errors_by_type': self._categorize_errors(errors),
'detailed_errors': errors,
'context': context
}
# 生成Markdown格式的报告
md_report = self._generate_markdown_report(report)
return md_report
def _categorize_errors(self, errors):
categories = {
'format': 0,
'link': 0,
'spelling': 0,
'sync': 0,
'other': 0
}
for error in errors:
if 'format' in error.lower():
categories['format'] += 1
elif 'link' in error.lower():
categories['link'] += 1
elif 'spell' in error.lower():
categories['spelling'] += 1
elif 'sync' in error.lower():
categories['sync'] += 1
else:
categories['other'] += 1
return categories
部署和监控
工作流监控配置
name: Workflow Monitoring
on:
workflow_run:
workflows: ["Awesome MCP Servers CI"]
types: [completed]
jobs:
monitor:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion != 'success' }}
steps:
- name: Send notification
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.payload.workflow_run.pull_requests[0].number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `CI workflow failed. Please check the details: ${{ github.event.workflow_run.html_url }}`
})
最佳实践总结
表格:CI工作流检查项总结
| 检查类型 | 工具/技术 | 频率 | 严重级别 | 自动修复 |
|---|---|---|---|---|
| 格式验证 | 自定义Python脚本 | PR时 | 高 | 部分支持 |
| 链接检查 | lychee + 自定义 | 每日+PR时 | 中 | 否 |
| 拼写检查 | cspell | PR时 | 低 | 是 |
| 多语言同步 | 深度差异比较 | PR时 | 高 | 否 |
| 性能测试 | 基准测试 | 每周 | 低 | 否 |
关键成功因素
- 渐进式实施:从基本的格式验证开始,逐步添加更复杂的检查
- 清晰的错误消息:为贡献者提供具体、可操作的错误信息
- 合理的超时设置:为网络请求设置适当的超时时间
- 缓存策略:对重复的检查结果进行缓存以提高性能
- 定期维护:定期更新依赖和调整检查规则
通过实施这个全面的GitHub Actions工作流,Awesome MCP Servers项目能够确保代码质量、维护多语言一致性,并为贡献者提供更好的开发体验。这个自动化工作流不仅提高了项目的可靠性,还为未来的扩展奠定了坚实的基础。
更多推荐
所有评论(0)