17.3 Pre-commit hook与GitHub Actions实战
在团队协作中,将SEO和GEO质量检查左移到开发流程的最早阶段,是避免问题累积、降低修复成本的关键。本节将介绍如何通过Pre-commit hook和GitHub Actions构建自动化防线,在代码提交前和合并前自动拦截常见的SEO/GEO问题。
一、Pre-commit hook:本地防线
Pre-commit hook在开发者执行git commit时触发,可以在代码进入仓库前进行快速检查。我们推荐使用husky和lint-staged组合,配合自定义脚本。
1. 安装与配置
# 安装依赖
npm install --save-dev husky lint-staged
# 初始化 husky
npx husky install
# 添加 pre-commit hook
npx husky add .husky/pre-commit "npx lint-staged"
2. 自定义检查脚本
在package.json中配置lint-staged:
{
"lint-staged": {
"*.{html,js,ts,jsx,tsx,vue}": [
"node scripts/check-schema.js",
"node scripts/check-robots.js"
]
}
}
3. 脚本示例:检查Schema破坏
// scripts/check-schema.js
const fs = require('fs');
const path = require('path');
const files = process.argv.slice(2);
files.forEach(file => {
const content = fs.readFileSync(file, 'utf8');
// 检查JSON-LD语法
const jsonldRegex = /<script type="application\/ld\+json">([\s\S]*?)<\/script>/g;
let match;
while ((match = jsonldRegex.exec(content)) !== null) {
try {
JSON.parse(match[1]);
} catch (e) {
console.error(`❌ Schema语法错误: ${file}`);
process.exit(1);
}
}
});
console.log('✅ Schema检查通过');
4. 脚本示例:检查robots.txt变更
// scripts/check-robots.js
const fs = require('fs');
const path = require('path');
const changedFiles = process.argv.slice(2);
const robotsFile = changedFiles.find(f => f.includes('robots.txt'));
if (robotsFile) {
const content = fs.readFileSync(robotsFile, 'utf8');
// 检查是否意外屏蔽了重要爬虫
if (content.includes('Disallow: /') && !content.includes('User-agent: *')) {
console.warn('⚠️ robots.txt中检测到全局屏蔽,请确认');
}
// 检查是否屏蔽了GPTBot
if (content.includes('User-agent: GPTBot') && content.includes('Disallow: /')) {
console.warn('⚠️ 已屏蔽GPTBot,请确认这是有意的');
}
}
二、GitHub Actions:CI/CD集成
在Pull Request阶段,通过GitHub Actions进行更全面的自动化检查,确保合并到主分支的代码符合SEO/GEO标准。
1. 工作流配置
创建.github/workflows/seo-geo-check.yml:
name: SEO & GEO Quality Check
on:
pull_request:
branches: [main, develop]
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v10
with:
urls: |
https://staging.example.com/
https://staging.example.com/product/123
uploadArtifacts: true
temporaryPublicStorage: true
- name: Validate Structured Data
run: |
npx schema-validator public/**/*.html
- name: Check robots.txt
run: |
node scripts/validate-robots.js
- name: Check sitemap
run: |
node scripts/validate-sitemap.js
- name: Run Custom GEO Checks
run: |
node scripts/geo-check.js --url https://staging.example.com/
2. 自定义GEO检查脚本
// scripts/geo-check.js
const fetch = require('node-fetch');
const { JSDOM } = require('jsdom');
const url = process.argv[2];
async function checkGEOReadiness(url) {
const response = await fetch(url);
const html = await response.text();
const dom = new JSDOM(html);
const doc = dom.window.document;
const issues = [];
// 检查JSON-LD
const jsonldScripts = doc.querySelectorAll('script[type="application/ld+json"]');
if (jsonldScripts.length === 0) {
issues.push('❌ 缺少JSON-LD结构化数据');
}
// 检查FAQPage Schema
const hasFAQ = Array.from(jsonldScripts).some(script => {
try {
const data = JSON.parse(script.textContent);
return data['@type'] === 'FAQPage';
} catch {
return false;
}
});
if (!hasFAQ) {
issues.push('⚠️ 建议添加FAQPage Schema以增加被生成引擎引用的机会');
}
// 检查Speakable属性
const hasSpeakable = Array.from(jsonldScripts).some(script => {
try {
const data = JSON.parse(script.textContent);
return data.speakable;
} catch {
return false;
}
});
if (!hasSpeakable) {
issues.push('⚠️ 建议添加speakable属性以优化语音搜索');
}
// 检查内容可读性
const paragraphs = doc.querySelectorAll('p');
const avgLength = Array.from(paragraphs).reduce((sum, p) => sum + p.textContent.length, 0) / paragraphs.length;
if (avgLength < 50) {
issues.push('⚠️ 段落平均长度过短,可能影响生成引擎的语义理解');
}
// 输出结果
if (issues.length === 0) {
console.log('✅ GEO检查全部通过');
} else {
issues.forEach(issue => console.log(issue));
process.exit(issues.some(i => i.startsWith('❌')) ? 1 : 0);
}
}
checkGEOReadiness(url);
3. 自动化部署阻断
在PR中,如果检查失败,可以通过GitHub Actions的status API阻止合并:
- name: Check Status
if: failure()
run: |
echo "❌ SEO/GEO检查失败,请修复后重新提交"
exit 1
三、实战建议
- 分阶段实施:先实现Pre-commit hook的本地检查,再逐步完善GitHub Actions的CI/CD流程。
- 自定义规则:根据产品类型(电商、博客、SaaS)调整检查规则,避免过度检查导致开发效率下降。
- 缓存策略:对于大型项目,使用
actions/cache缓存依赖和构建产物,加快检查速度。 - 告警通知:将检查结果通过Slack、钉钉或邮件通知相关团队,确保问题及时处理。
通过这套自动化防线,可以将SEO/GEO质量检查从“事后补救”转变为“事前预防”,让每个代码提交都天然符合搜索优化标准。
