Skip to main content
Glama
issue-creation-hygiene.ymlβ€’8.94 kB
name: Issue Creation Hygiene on: issues: types: - opened - edited - reopened permissions: contents: read issues: write jobs: enforce-labels: runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Apply required labels from form selections uses: actions/github-script@v7 with: script: | const issue = context.payload.issue; const owner = context.repo.owner; const repo = context.repo.repo; const body = (issue.body || ""); // Parse form selections and existing content // GitHub forms output "P0 (Critical - System down, data loss)" format const priorityMatch = body.match(/\b(P[0-5])\s*\([^)]*\)/i); const priority = priorityMatch ? priorityMatch[1] : ""; // For type, check both direct labels and form dropdowns let type = ""; const directTypeMatch = body.match(/\btype:(bug|feature|enhancement|documentation|test|refactor|chore|ci|dependencies)\b/i); if (directTypeMatch) { type = directTypeMatch[0].toLowerCase(); } else { // Check issue template labels field const labelsMatch = body.match(/labels:\s*\[([^\]]*)\]/); if (labelsMatch) { const labels = labelsMatch[1].split(',').map(l => l.trim().replace(/['"]/g, '')); const typeLabel = labels.find(l => l.match(/^type:/i)); if (typeLabel) type = typeLabel.toLowerCase(); } } // For status, check form output and direct labels const statusMatch = body.match(/\bstatus:(untriaged|ready|in-progress|blocked|needs-info|review)\b/i); const status = statusMatch ? statusMatch[0].toLowerCase() : ""; // For areas, handle both form output and direct labels let areas = []; // First try direct area: labels const directAreas = body.match(/\barea:[a-z0-9:-]+\b/gi); if (directAreas) { areas.push(...directAreas.map(s => s.toLowerCase())); } // Also check issue template labels field for area labels const labelsMatch = body.match(/labels:\s*\[([^\]]*)\]/); if (labelsMatch) { const labels = labelsMatch[1].split(',').map(l => l.trim().replace(/['"]/g, '')); const areaLabels = labels.filter(l => l.match(/^area:/i)); areas.push(...areaLabels.map(l => l.toLowerCase())); } // Deduplicate areas areas = Array.from(new Set(areas)); console.log(`Parsed from issue body: Priority: ${priority} Type: ${type} Status: ${status} Areas: ${areas.join(', ')}`); // Get existing labels (GitHub template labels are already applied) const current = (issue.labels || []).map(l => l.name.toLowerCase()); const add = new Set(current); // If we didn't find type in body, check if it's already in labels (from template) if (!type) { const existingType = current.find(l => /^type:/.test(l)); if (existingType) type = existingType; } // Ensure exactly one P* (Priority) const existingP = current.find(l => /^p[0-5]$/.test(l)); if (existingP && priority) { // Replace existing priority with parsed one add.delete(existingP); add.add(priority.toLowerCase()); } else if (priority) { // Add new priority add.add(priority.toLowerCase()); } else if (existingP) { // Keep existing priority if no new one found in body add.add(existingP); } // Ensure exactly one type:* const existingType = current.find(l => /^type:/.test(l)); if (existingType && type) { // Replace existing type with parsed one add.delete(existingType); add.add(type); } else if (type) { // Add new type add.add(type); } else if (existingType) { // Keep existing type if no new one found in body add.add(existingType); } // Ensure exactly one status:* const existingStatus = current.find(l => /^status:/.test(l)); if (existingStatus && status) { // Replace existing status with parsed one add.delete(existingStatus); add.add(status); } else if (status) { // Add new status add.add(status); } else if (existingStatus) { // Keep existing status if no new one found in body add.add(existingStatus); } else { // Default status if none found add.add("status:untriaged"); } // Ensure at least one area:* const hasArea = Array.from(add).some(l => /^area:/.test(l)); if (!hasArea && areas.length) { areas.forEach(a => add.add(a)); } // Validate required categories const labelsArr = Array.from(add); const okPriority = labelsArr.some(l => /^p[0-5]$/.test(l)); const okType = labelsArr.some(l => /^type:/.test(l)); const okStatus = labelsArr.some(l => /^status:/.test(l)); const okArea = labelsArr.some(l => /^area:/.test(l)); console.log(`Label validation: Priority: ${okPriority} Type: ${okType} Status: ${okStatus} Area: ${okArea} Final labels: ${labelsArr.join(', ')}`); // Apply labels if we have any if (labelsArr.length && labelsArr.join(',') !== current.join(',')) { await github.rest.issues.setLabels({ owner, repo, issue_number: issue.number, labels: labelsArr }); console.log(`Applied labels: ${labelsArr.join(', ')}`); } // Check for missing categories and provide helpful feedback const missing = []; if (!okPriority) missing.push("Priority (P0–P5)"); if (!okType) missing.push("Type (type:bug|feature|enhancement|documentation|test|refactor|chore|ci|dependencies)"); if (!okStatus) missing.push("Status (status:untriaged|ready|in-progress|blocked|needs-info|review)"); if (!okArea) missing.push("Area (area:core|api|build|documentation|testing|etc.)"); if (missing.length) { const msg = [ '## ⚠️ Issue Hygiene Check', '', `Missing required label categories: **${missing.join(", ")}**`, '', 'Please add the missing labels manually or edit this issue to include them in the description.', '', '**Required label format:**', '- **Priority**: P0 (Critical), P1 (High), P2 (Medium), P3 (Low), P4 (Minor), P5 (Trivial)', '- **Type**: type:bug, type:feature, type:enhancement, type:documentation, type:test, type:refactor, type:chore, type:ci, type:dependencies', '- **Status**: status:untriaged, status:ready, status:in-progress, status:blocked, status:needs-info, status:review', '- **Area**: area:core, area:api, area:build, area:documentation, area:testing, area:performance, area:security, etc.', '', `See [Label Guide](https://github.com/${owner}/${repo}/blob/main/docs/tools/github-cli/issues.md) for complete list.` ].join('\n'); // Check if we already commented on this issue const comments = await github.rest.issues.listComments({ owner, repo, issue_number: issue.number }); const existingHygieneComment = comments.data.find(comment => comment.user.type === 'Bot' && comment.body.includes('Issue Hygiene Check') ); if (existingHygieneComment) { await github.rest.issues.updateComment({ owner, repo, comment_id: existingHygieneComment.id, body: msg }); } else { await github.rest.issues.createComment({ owner, repo, issue_number: issue.number, body: msg }); } core.setFailed(`Issue hygiene: missing required label categories: ${missing.join(', ')}`); } else { console.log("βœ… All required label categories present"); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server