feat: redesign LearnHub landing page #3
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Integrity Checks | |
| on: | |
| pull_request_target: | |
| types: [opened, edited, synchronize] | |
| permissions: | |
| pull-requests: write | |
| issues: write | |
| contents: read | |
| jobs: | |
| integrity-check: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout PR code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| fetch-depth: 0 | |
| - name: Run checks | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const author = pr.user.login; | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const prNumber = pr.number; | |
| // Check maintainer role | |
| let isMaintainer = false; | |
| try { | |
| const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner, | |
| repo, | |
| username: author | |
| }); | |
| isMaintainer = ['admin', 'write'].includes(permission.permission); | |
| } catch (e) { | |
| // Not collaborator | |
| } | |
| // 1. Security: Auto-close PRs from forks modifying workflow files unless from a maintainer | |
| const isFork = pr.head.repo.fork; | |
| if (isFork && !isMaintainer) { | |
| // Get changed files | |
| const { data: files } = await github.rest.pulls.listFiles({ | |
| owner, | |
| repo, | |
| pull_number: prNumber | |
| }); | |
| const modifiesWorkflows = files.some(file => file.filename.startsWith('.github/workflows/')); | |
| if (modifiesWorkflows) { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| body: `🚨 @${author}, pull requests modifying GitHub actions/workflows from forks are automatically closed for repository security. Maintainers will review and manually implement updates if needed.` | |
| }); | |
| await github.rest.pulls.update({ | |
| owner, | |
| repo, | |
| pull_number: prNumber, | |
| state: 'closed' | |
| }); | |
| return; | |
| } | |
| } | |
| // 2. Auto-label and comment PRs without linked issues | |
| const prBody = pr.body || ''; | |
| const issueLinkRegex = /(?:close|closes|fix|fixes|resolve|resolves)\s+#\d+/i; | |
| const hasIssueLink = issueLinkRegex.test(prBody); | |
| if (!hasIssueLink) { | |
| // Label the PR | |
| await github.rest.issues.addLabels({ | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| labels: ['needs-issue-link'] | |
| }); | |
| // Comment requesting issue link | |
| const comments = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number: prNumber | |
| }); | |
| const hasReqComment = comments.data.some(c => c.body.includes('please link an open issue')); | |
| if (!hasReqComment) { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| body: `🔍 @${author}, please link an open issue in your pull request description (e.g. \`Closes #123\`). This is required before we can review your PR.` | |
| }); | |
| } | |
| } else { | |
| // Try to remove label if issue link was added later | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| name: 'needs-issue-link' | |
| }); | |
| } catch (e) { | |
| // Label might not exist | |
| } | |
| // 3. Warning: If author is not assignee of linked issue | |
| const issueMatch = prBody.match(/(?:close|closes|fix|fixes|resolve|resolves)\s+#(\d+)/i); | |
| if (issueMatch && issueMatch[1]) { | |
| const linkedIssueNumber = parseInt(issueMatch[1], 10); | |
| try { | |
| const { data: issue } = await github.rest.issues.get({ | |
| owner, | |
| repo, | |
| issue_number: linkedIssueNumber | |
| }); | |
| const assignees = issue.assignees.map(a => a.login); | |
| if (!assignees.includes(author)) { | |
| const comments = await github.rest.issues.listComments({ | |
| owner, | |
| repo, | |
| issue_number: prNumber | |
| }); | |
| const hasWarning = comments.data.some(c => c.body.includes('Warning: You are submitting a PR for an issue')); | |
| if (!hasWarning) { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| body: `⚠️ Warning: You are submitting a PR for issue #${linkedIssueNumber}, but you are not the assigned contributor. Please ensure the assignee is okay with this or wait to be assigned before contributing.` | |
| }); | |
| } | |
| } | |
| } catch (err) { | |
| console.error('Failed to inspect linked issue:', err); | |
| } | |
| } | |
| } |