gitea_compliance_check_pr
Validate pull request descriptions for required sections, issue links, and formatting standards to ensure compliance with project requirements.
Instructions
Check if PR description complies with format requirements (sections, issue links, etc.).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| owner | No | Repository owner. Uses context if not provided | |
| repo | No | Repository name. Uses context if not provided | |
| pr_number | Yes | Pull request number to check | |
| config_path | No | Path to compliance config file | |
| token | No | Optional API token to override default authentication |
Implementation Reference
- src/tools/compliance.ts:366-455 (handler)Core handler implementation for 'gitea_compliance_check_pr' tool. Fetches PR details via Gitea API, checks for required sections in description, issue links, and suggests improvements based on compliance config.export async function checkPR( ctx: ComplianceToolsContext, params: CheckPRParams ): Promise<PRCheckResult> { const config = loadComplianceConfig(params.config_path); const owner = ctx.contextManager.resolveOwner(params.owner); const repo = ctx.contextManager.resolveRepo(params.repo); logger.info({ owner, repo, pr: params.pr_number }, 'Checking PR format'); // 获取 PR 信息 let pr: any; try { const response = await ctx.client.request({ method: 'GET', path: `/repos/${owner}/${repo}/pulls/${params.pr_number}`, token: params.token, }); pr = response.data; } catch (err) { logger.warn({ pr: params.pr_number, error: err }, 'Failed to fetch PR'); return { pr_number: params.pr_number, title: '', compliant: false, issues: [`无法获取 PR #${params.pr_number} 的信息`], suggestions: [], missing_sections: [], has_issue_link: false, }; } const issues: string[] = []; const suggestions: string[] = []; const body = pr.body || ''; const title = pr.title || ''; // 检查必需章节 const missingSections: string[] = []; for (const section of config.pr.required_sections) { // 检查 ## Section 或 # Section 格式 const sectionRegex = new RegExp(`^#+\\s*${section}`, 'mi'); if (!sectionRegex.test(body)) { missingSections.push(section); } } if (missingSections.length > 0) { issues.push(`缺少必需的章节: ${missingSections.join(', ')}`); suggestions.push('PR 描述应包含以下章节:'); for (const section of missingSections) { suggestions.push(` ## ${section}`); } } // 检查 Issue 链接 const issuePatterns = [ /(?:closes?|fixes?|resolves?)\s+#\d+/i, /(?:closes?|fixes?|resolves?)\s+https?:\/\/[^\s]+\/issues\/\d+/i, /#\d+/, /issue[- ]?\d+/i, ]; const hasIssueLink = issuePatterns.some((pattern) => pattern.test(body) || pattern.test(title)); if (config.pr.require_issue_link && !hasIssueLink) { issues.push('PR 未关联任何 Issue'); suggestions.push('请在 PR 描述中添加关联的 Issue,如:'); suggestions.push(' Closes #123'); suggestions.push(' Fixes #456'); } // 检查标题格式(可选,使用 Conventional Commit 格式) const titleParsed = parseConventionalCommit(title); if (!titleParsed.valid) { suggestions.push('建议 PR 标题使用 Conventional Commit 格式:'); suggestions.push(' feat(scope): add new feature'); suggestions.push(' fix(scope): fix bug'); } return { pr_number: params.pr_number, title, compliant: issues.length === 0, issues, suggestions, missing_sections: missingSections, has_issue_link: hasIssueLink, }; }
- src/tools-registry/compliance-registry.ts:65-87 (registration)Tool registration for 'gitea_compliance_check_pr' including Zod input schema, title, description, and thin wrapper handler delegating to ComplianceTools.checkPR.mcpServer.registerTool( 'gitea_compliance_check_pr', { title: '检查 PR 格式规范', description: 'Check if PR description complies with format requirements (sections, issue links, etc.).', inputSchema: z.object({ owner: z.string().optional().describe('Repository owner. Uses context if not provided'), repo: z.string().optional().describe('Repository name. Uses context if not provided'), pr_number: z.number().int().positive().describe('Pull request number to check'), config_path: z.string().optional().describe('Path to compliance config file'), token: tokenSchema, }), }, async (args) => { try { const result = await ComplianceTools.checkPR(toolsContext, args as any); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: 'text' as const, text: `Error: ${errorMessage}` }], isError: true }; } } );
- src/tools/compliance.ts:358-361 (schema)TypeScript interface defining input parameters for the checkPR handler, extending base ComplianceParams.export interface CheckPRParams extends ComplianceParams { pr_number: number; config_path?: string; }
- src/tools/compliance.ts:144-149 (schema)TypeScript interface defining the output structure of the checkPR handler, including compliance status, issues, and specific PR details.export interface PRCheckResult extends CheckResult { pr_number: number; title: string; missing_sections: string[]; has_issue_link: boolean; }
- src/tools/compliance.ts:79-101 (helper)Helper function to load compliance configuration from .gitea/compliance.yaml or use defaults, used by checkPR to determine rules for sections and issue links.export function loadComplianceConfig(configPath?: string): ComplianceConfig { const searchPaths = [ configPath, path.join(process.cwd(), '.gitea', 'compliance.yaml'), path.join(process.cwd(), '.gitea', 'compliance.yml'), ].filter(Boolean) as string[]; for (const p of searchPaths) { if (fs.existsSync(p)) { try { const content = fs.readFileSync(p, 'utf-8'); const parsed = yaml.parse(content); logger.info({ path: p }, 'Loaded compliance config'); return mergeConfig(DEFAULT_CONFIG, parsed); } catch (err) { logger.warn({ path: p, error: err }, 'Failed to parse compliance config'); } } } logger.info('Using default compliance config'); return DEFAULT_CONFIG; }