Download .github/workflows/issue-gate.yml from SaylorTwift/pi: direct link, hf CLI and curl.
- Browser
- Download file 5.23 kB
-
https://huggingface.co/SaylorTwift/pi/resolve/main/.github/workflows/issue-gate.yml
- Command line
-
hf download hf://SaylorTwift/pi/.github/workflows/issue-gate.yml
-
curl -L -o issue-gate.yml https://huggingface.co/SaylorTwift/pi/resolve/main/.github/workflows/issue-gate.yml
5.23 kB
| name: Issue Gate | |
| on: | |
| issues: | |
| types: [opened] | |
| jobs: | |
| check-contributor: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - name: Check issue author | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const APPROVED_FILE = '.github/APPROVED_CONTRIBUTORS'; | |
| const VALID_CAPABILITIES = new Set(['issue', 'pr']); | |
| const TRUSTED_BOT_AUTHORS = new Set(['dependabot[bot]', 'sentry[bot]', 'claude[bot]']); | |
| const issueAuthor = context.payload.issue.user.login; | |
| const defaultBranch = context.payload.repository.default_branch; | |
| const isBotAuthor = issueAuthor.endsWith('[bot]'); | |
| if (TRUSTED_BOT_AUTHORS.has(issueAuthor)) { | |
| console.log(`Skipping trusted bot: ${issueAuthor}`); | |
| return; | |
| } | |
| async function getPermission(username) { | |
| try { | |
| const { data: permissionLevel } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| username, | |
| }); | |
| return permissionLevel.permission; | |
| } catch { | |
| return null; | |
| } | |
| } | |
| async function getTextFile(path) { | |
| const { data: fileContent } = await github.rest.repos.getContent({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| path, | |
| ref: defaultBranch, | |
| }); | |
| if (!('content' in fileContent) || typeof fileContent.content !== 'string') { | |
| throw new Error(`Expected file content for ${path}`); | |
| } | |
| return Buffer.from(fileContent.content, 'base64').toString('utf8'); | |
| } | |
| function parseApprovedUsers(content) { | |
| const users = new Map(); | |
| for (const rawLine of content.split('\n')) { | |
| const line = rawLine.trim(); | |
| if (!line || line.startsWith('#')) continue; | |
| const parts = line.split(/\s+/); | |
| if (parts.length !== 2) { | |
| console.log(`Skipping malformed line: ${rawLine}`); | |
| continue; | |
| } | |
| const [username, capability] = parts; | |
| const normalizedCapability = capability.toLowerCase(); | |
| if (!VALID_CAPABILITIES.has(normalizedCapability)) { | |
| console.log(`Skipping line with invalid capability: ${rawLine}`); | |
| continue; | |
| } | |
| users.set(username.toLowerCase(), normalizedCapability); | |
| } | |
| return users; | |
| } | |
| const permission = await getPermission(issueAuthor); | |
| if (!isBotAuthor && ['admin', 'maintain', 'write'].includes(permission)) { | |
| console.log(`${issueAuthor} is a collaborator with ${permission} access`); | |
| return; | |
| } | |
| const approvedContent = await getTextFile(APPROVED_FILE); | |
| const approvedUsers = parseApprovedUsers(approvedContent); | |
| const capability = approvedUsers.get(issueAuthor.toLowerCase()); | |
| if (!isBotAuthor && (capability === 'issue' || capability === 'pr')) { | |
| console.log(`${issueAuthor} is approved for ${capability}`); | |
| return; | |
| } | |
| const message = [ | |
| 'This issue was auto-closed. All issues from new contributors are auto-closed by default.', | |
| '', | |
| `Maintainers review auto-closed issues daily and reopen worthwhile ones. Issues that do not meet the quality bar in [CONTRIBUTING.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${defaultBranch}/CONTRIBUTING.md) will not be reopened or receive a reply.`, | |
| '', | |
| 'If a maintainer replies `lgtmi` on one of your issues, your future issues will stay open. If a maintainer replies `lgtm`, your future issues and PRs will stay open. The command must be at the start of the reply (optionally after one or more `@username` mentions) or at the end.', | |
| '', | |
| `See [CONTRIBUTING.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${defaultBranch}/CONTRIBUTING.md).`, | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: message, | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| labels: ['untriaged'], | |
| }); | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| state: 'closed', | |
| state_reason: 'not_planned', | |
| }); | |