预发布检查清单:确保测试通过、变更日志已更新、版本号已提升、无调试代码、依赖干净、文档最新、无敏感信息泄露。
释放准备状态检查器是一项面向实际任务的技能,主要用于在剪切发布前进行全面的释放前审核;检查代码质量、 文件、 依赖性、 CI 状态和常见的释放阻塞器;
该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;
若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。
在正式发布前运行一次全面的预发布审计。检查代码质量、文档完整性、依赖项状态、CI 构建状态以及常见发布阻碍项,并生成“可发布 / 不可发布”结论报告。
适用场景:当团队提出“我们是否已准备好发布?”、“预发布检查”、“发布审计”、“能否上线此版本?”、“发布核对清单”,或在打版本标签(tag)之前。
check — 全量发布就绪审计执行全部检查项,并输出最终“可发布 / 不可发布”判定结果。
# 检查当前版本
if [ -f "package.json" ]; then
CURRENT=$(python3 -c "import json; print(json.load(open('package.json')).get('version','none'))" 2>/dev/null)
echo "Current version: $CURRENT"
# 与最新 Git tag 比较
LATEST_TAG=$(git tag --sort=-version:refname 2>/dev/null | head -1)
echo "Latest tag: ${LATEST_TAG:-none}"
if [ "$CURRENT" = "${LATEST_TAG#v}" ] || [ "v$CURRENT" = "$LATEST_TAG" ]; then
echo "⚠️ Version matches latest tag — did you forget to bump?"
fi
fi
# 在其他文件中检查版本字段
for f in pyproject.toml Cargo.toml setup.py setup.cfg version.txt VERSION; do
if [ -f "$f" ]; then
grep -i "version" "$f" | head -3
fi
done
# 检查 CHANGELOG 文件是否存在,且包含近期条目
for f in CHANGELOG.md CHANGELOG CHANGES.md HISTORY.md; do
if [ -f "$f" ]; then
echo "Found: $f"
# 检查顶部条目是否匹配当前版本或为 Unreleased
head -20 "$f"
# 检查 Unreleased 区域是否有内容
UNRELEASED=$(sed -n '/[Unreleased]/,/[/p' "$f" 2>/dev/null | wc -l)
if [ "$UNRELEASED" -le 2 ]; then
echo "⚠️ Unreleased section appears empty"
fi
break
fi
done
# 若未找到任何 changelog 文件
if [ ! -f "CHANGELOG.md" ] && [ ! -f "CHANGELOG" ] && [ ! -f "CHANGES.md" ]; then
echo "⚠️ No CHANGELOG file found"
fi
# 常见调试痕迹
echo "=== Debug Code Check ==="
rg -n "console.log|console.debug|console.warn|debugger;"
-g '!node_modules' -g '!vendor' -g '!dist' -g '!build' -g '!*.test.*' -g '!*.spec.*'
-g '*.{js,ts,jsx,tsx}' --stats 2>&1 | tail -5
rg -n "print(|breakpoint()|pdb.set_trace|import pdb|import ipdb"
-g '!vendor' -g '!dist' -g '*.py' -g '!*test*' --stats 2>&1 | tail -5
rg -n "fmt.Print|log.Print"
-g '*.go' -g '!*_test.go' --stats 2>&1 | tail -5
# 关键路径(非测试文件)中的 TODO/FIXME(不包括测试)
CRITICAL_TODOS=$(rg -c "TODO|FIXME|HACK|XXX"
-g '!node_modules' -g '!vendor' -g '!dist' -g '!*.test.*' -g '!*.spec.*'
--type-not binary 2>/dev/null | awk -F: '{s+=$2} END {print s+0}')
echo "TODO/FIXME count (non-test): $CRITICAL_TODOS"
echo "=== Test Check ==="
# 自动识别测试运行器
if [ -f "package.json" ]; then
HAS_TEST=$(python3 -c "import json; d=json.load(open('package.json')); print('yes' if d.get('scripts',{}).get('test','') not in ['','echo "Error: no test specified" && exit 1'] else 'no')" 2>/dev/null)
if [ "$HAS_TEST" = "yes" ]; then
echo "Test command: npm test"
echo "(Run 'npm test' to verify — not running automatically to avoid side effects)"
else
echo "⚠️ No test script configured in package.json"
fi
fi
if [ -f "pytest.ini" ] || [ -f "setup.cfg" ] || [ -f "pyproject.toml" ]; then
if python3 -c "import pytest" 2>/dev/null; then
echo "Test runner: pytest detected"
fi
fi
# 检查是否存在测试文件
TEST_COUNT=$(find . -type f ( -name "*.test.*" -o -name "*.spec.*" -o -name "test_*" -o -name "*_test.*" )
-not -path '*/node_modules/*' -not -path '*/vendor/*' 2>/dev/null | wc -l)
echo "Test files found: $TEST_COUNT"
if [ "$TEST_COUNT" -eq 0 ]; then
echo "❌ No test files found"
fi
echo "=== Dependency Check ==="
# 检查过时依赖(主版本)
if [ -f "package-lock.json" ] || [ -f "yarn.lock" ] || [ -f "pnpm-lock.yaml" ]; then
npm outdated 2>/dev/null | head -15 || true
# 检查已知漏洞
npm audit --json 2>/dev/null | python3 -c "
import json, sys
try:
d = json.load(sys.stdin)
vulns = d.get('metadata', {}).get('vulnerabilities', {})
crit = vulns.get('critical', 0)
high = vulns.get('high', 0)
if crit > 0: print(f'❌ {crit} critical vulnerabilities')
elif high > 0: print(f'⚠️ {high} high vulnerabilities')
else: print('✅ No critical/high vulnerabilities')
except: print('Could not parse npm audit output')
" 2>/dev/null
fi
# Lockfile 新鲜度检查
if [ -f "package-lock.json" ]; then
LOCK_AGE=$(git log -1 --format="%ar" -- package-lock.json 2>/dev/null)
echo "Lock file last updated: ${LOCK_AGE:-unknown}"
fi
echo "=== Secrets Check ==="
# 常见密钥模式匹配
rg -n "(PRIVATE_KEY|SECRET_KEY|API_KEY|ACCESS_TOKEN|passwords*=s*['"][^'"]+['"])"
-g '!node_modules' -g '!vendor' -g '!dist' -g '!*.lock' -g '!*.test.*'
--type-not binary -i 2>/dev/null |
grep -v "process.env|os.environ|os.getenv|.env|example|sample|template|test|mock|fake|dummy" | head -10
# 检查 .env 是否被 gitignore
if [ -f ".env" ]; then
if git check-ignore .env >/dev/null 2>&1; then
echo "✅ .env is gitignored"
else
echo "❌ .env is NOT gitignored — potential secret exposure"
fi
fi
# 检查是否误提交了 .env 类文件
git ls-files '*.env' '.env*' 2>/dev/null | grep -v '.env.example|.env.sample|.env.template' | while read f; do
echo "⚠️ Committed env file: $f"
done
echo "=== CI Check ==="
# 检查 GitHub Actions 当前分支构建状态
BRANCH=$(git branch --show-current 2>/dev/null)
if command -v gh &>/dev/null; then
gh run list --branch "$BRANCH" --limit 3 2>/dev/null || echo "Could not fetch CI status (gh not configured)"
else
echo "gh CLI not available — check CI manually"
fi
# 检查 CI 配置文件是否存在
for f in .github/workflows/*.yml .github/workflows/*.yaml .gitlab-ci.yml Jenkinsfile .circleci/config.yml; do
if ls $f 2>/dev/null | head -1 >/dev/null; then
echo "CI config found: $f"
fi
done 2>/dev/null
echo "=== Documentation Check ==="
# README 存在且内容充分
if [ -f "README.md" ]; then
LINES=$(wc -l < README.md)
echo "README.md: $LINES lines"
if [ "$LINES" -lt 10 ]; then
echo "⚠️ README.md seems sparse"
fi
else
echo "⚠️ No README.md found"
fi
# 如存在 API 文档目录或文件
for f in docs/ doc/ api-docs/ API.md; do
if [ -e "$f" ]; then
echo "Docs found: $f"
fi
done
# 检查 README 是否引用了当前版本
if [ -f "README.md" ] && [ -n "$CURRENT" ]; then
if grep -q "$CURRENT" README.md 2>/dev/null; then
echo "✅ README references current version ($CURRENT)"
fi
fi
echo "=== Git Status ===" UNCOMMITTED=$(git status --porcelain 2>/dev/null | wc -l) if [ "$UNCOMMITTED" -gt 0 ]; then echo "⚠️ $UNCOMMITTED uncommitted changes" git status --short 2>/dev/null | head -10 else echo "✅ Working tree clean" fi # 检查当前分支是否落后于远程 BRANCH=$(git branch --show-current 2>/dev/null) AHEAD=$(git rev-list --count origin/$BRANCH..$BRANCH 2>/dev/null || echo "?") BEHIND=$(git rev-list --count $BRANCH..origin/$BRANCH 2>/dev/null || echo "?") echo "Branch: $BRANCH (ahead: $AHEAD, behind: $BEHIND)" if [ "$BEHIND" != "0" ] && [ "$BEHIND" != "?" ]; then echo "⚠️ Branch is behind remote — pull before releasing" fi
verdict — “可发布 / 不可发布”汇总结论完成全部检查后,输出结构化判定结果:
# Release Readiness Report Project: [name] | Version: [version] | Date: [date] ## Verdict: 🟢 GO / 🟡 CAUTION / 🔴 NO-GO ### Blockers (must fix before release) - ❌ 3 critical vulnerabilities in dependencies - ❌ .env file committed to repository ### Warnings (should fix, won't block) - ⚠️ Version not bumped from last tag - ⚠️ 12 TODO/FIXME comments in source - ⚠️ 5 uncommitted changes ### Passing - ✅ Changelog updated - ✅ Tests exist (47 test files) - ✅ No debug code detected - ✅ README current (89 lines) - ✅ CI config present
判定规则:
checklist — 交互式核对清单生成可用于人工复核的 Markdown 格式清单:
## Pre-Release Checklist ### Automated (run `check` to verify) - [ ] Version bumped - [ ] Changelog updated - [ ] No debug code (console.log, debugger, etc.) - [ ] Tests pass - [ ] Dependencies clean (no critical vulns) - [ ] No secrets committed - [ ] CI green - [ ] Docs updated - [ ] Git status clean ### Manual Review - [ ] Breaking changes documented - [ ] Migration guide written (if needed) - [ ] API deprecation notices sent - [ ] Performance regression check - [ ] Security review completed - [ ] Stakeholders notified - [ ] Release notes drafted - [ ] Rollback plan documented
compare — 与上一发布版本对比展示自上次打 tag 版本以来的全部变更:
LATEST_TAG=$(git tag --sort=-version:refname 2>/dev/null | head -1) if [ -n "$LATEST_TAG" ]; then echo "Changes since $LATEST_TAG:" echo "" echo "=== Commits ===" git log "$LATEST_TAG"..HEAD --oneline 2>/dev/null | head -30 echo "" echo "=== File Stats ===" git diff --stat "$LATEST_TAG"..HEAD 2>/dev/null | tail -5 echo "" echo "=== New Files ===" git diff --name-only --diff-filter=A "$LATEST_TAG"..HEAD 2>/dev/null | head -20 echo "" echo "=== Deleted Files ===" git diff --name-only --diff-filter=D "$LATEST_TAG"..HEAD 2>/dev/null | head -20 echo "" echo "=== Contributors ===" git log "$LATEST_TAG"..HEAD --format="%an" 2>/dev/null | sort -u fi
{version, verdict, blockers: [], warnings: [], passing: [], checks: {name: {status, details}}}退出码语义:
# 在失败时阻止发布
- name: Release readiness check
run: |
# Agent runs: release-readiness-checker check
# Exits 1 if blockers found
# 作为 PR 评论发布报告
- name: Post readiness report
run: |
# Agent runs: release-readiness-checker verdict --format markdown > report.md
gh pr comment $PR_NUMBER --body-file report.md
gh CLI 工具.release-readiness.json 可调整阈值(如最大允许 TODO 数量、必需文档项等)相关专题
热门下载
相关下载
精品课程
共6课时 | 54.7万人学习
共89课时 | 134.1万人学习
共49课时 | 82.6万人学习
最新文章