name: vouch-check-issue on: issues: types: [opened] permissions: contents: read issues: write jobs: check: runs-on: ubuntu-latest steps: - name: Check if issue author is denounced uses: actions/github-script@v7 with: script: | const author = context.payload.issue.user.login; const issueNumber = context.payload.issue.number; // Skip bots if (author.endsWith('[bot]')) { core.info(`Skipping bot: ${author}`); return; } // Read the VOUCHED.td file via API (no checkout needed) let content; try { const response = await github.rest.repos.getContent({ owner: context.repo.owner, repo: context.repo.repo, path: '.github/VOUCHED.td', }); content = Buffer.from(response.data.content, 'base64').toString('utf-8'); } catch (error) { if (error.status === 404) { core.info('No .github/VOUCHED.td file found, skipping check.'); return; } throw error; } // Parse the .td file for denounced users const denounced = new Map(); for (const line of content.split('\n')) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; if (!trimmed.startsWith('-')) continue; const rest = trimmed.slice(1).trim(); if (!rest) continue; const spaceIdx = rest.indexOf(' '); const handle = spaceIdx === -1 ? rest : rest.slice(0, spaceIdx); const reason = spaceIdx === -1 ? null : rest.slice(spaceIdx + 1).trim(); // Handle platform:username or bare username // Only match bare usernames or github: prefix (skip other platforms) const colonIdx = handle.indexOf(':'); if (colonIdx !== -1) { const platform = handle.slice(0, colonIdx).toLowerCase(); if (platform !== 'github') continue; } const username = colonIdx === -1 ? handle : handle.slice(colonIdx + 1); if (!username) continue; denounced.set(username.toLowerCase(), reason); } // Check if the author is denounced const reason = denounced.get(author.toLowerCase()); if (reason === undefined) { core.info(`User ${author} is not denounced. Allowing issue.`); return; } // Author is denounced — close the issue const body = 'This issue has been automatically closed.'; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, body, }); await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, state: 'closed', state_reason: 'not_planned', }); core.info(`Closed issue #${issueNumber} from denounced user ${author}`);