Unable to create new student branch #110
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: Learning Room PR Bot | |
| # Automated validation and feedback for student pull requests | |
| on: | |
| pull_request: | |
| types: [opened, edited, synchronize, reopened] | |
| paths: | |
| - 'learning-room/**' | |
| pull_request_review: | |
| types: [submitted] | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| statuses: write | |
| jobs: | |
| welcome-first-timer: | |
| name: Welcome First-Time Contributors | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' && github.event.action == 'opened' | |
| steps: | |
| - name: Check if first-time contributor | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| try { | |
| const { data: prs } = await github.rest.pulls.list({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'all', | |
| creator: context.payload.pull_request.user.login | |
| }); | |
| const isFirstPR = prs.length === 1; | |
| if (isFirstPR) { | |
| const welcomeBody = [ | |
| '## Welcome to Your First Pull Request!', | |
| '', | |
| 'Hi @' + context.payload.pull_request.user.login + '! This is your first PR in this repository. Congratulations on taking this important step in your open source journey!', | |
| '', | |
| '### What Happens Next', | |
| '', | |
| '1. **Automated Checks** \u2014 This bot will validate your PR (see checks below)', | |
| '2. **Peer Review** \u2014 A facilitator or peer will review your changes', | |
| '3. **Feedback** \u2014 You may receive suggestions for improvements', | |
| '4. **Merge** \u2014 Once approved, your changes become part of the project', | |
| '', | |
| '### While You Wait', | |
| '', | |
| '- Check the automated validation report below', | |
| '- Review the [PR guidelines](../../docs/05-working-with-pull-requests.md)', | |
| '- Look at other open PRs to learn from examples', | |
| '', | |
| '**Remember:** Every experienced contributor started exactly where you are now. Questions are welcome!', | |
| '', | |
| '---', | |
| '*This is an automated message from the Learning Room Bot. Need help? Mention @facilitator in a comment.*' | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| body: welcomeBody | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| labels: ['first-time-contributor', 'needs-review'] | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error checking first-time contributor status:', error); | |
| // Don't fail the workflow - just log and continue | |
| } | |
| validate-pr: | |
| name: Validate PR Requirements | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run PR validation | |
| id: validate | |
| run: node .github/scripts/validate-pr.js | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| PR_BODY: ${{ github.event.pull_request.body }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| - name: Post validation results | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const { buildValidationReportBody } = require('./.github/scripts/validation-report.js'); | |
| let results; | |
| try { | |
| if (!fs.existsSync('validation-results.json')) { | |
| console.log('Warning: validation-results.json not found, creating default response'); | |
| results = { | |
| passed: false, | |
| required: [{ | |
| name: 'Validation Output', | |
| passed: false, | |
| message: 'Validation results file was not created. Check workflow logs for errors.' | |
| }], | |
| suggestions: [], | |
| accessibility: [], | |
| resources: [] | |
| }; | |
| } else { | |
| results = JSON.parse(fs.readFileSync('validation-results.json', 'utf8')); | |
| } | |
| } catch (error) { | |
| console.error('Error reading validation results:', error); | |
| results = { | |
| passed: false, | |
| required: [{ | |
| name: 'Validation System Error', | |
| passed: false, | |
| message: `Error reading validation results: ${error.message}` | |
| }], | |
| suggestions: [], | |
| accessibility: [], | |
| resources: [] | |
| }; | |
| } | |
| const body = buildValidationReportBody(results); | |
| // Find and update existing bot comment, or create new one | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number | |
| }); | |
| const botComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes('PR Validation Report') | |
| ); | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| body: body | |
| }); | |
| } | |
| // Apply labels | |
| const labels = ['documentation']; | |
| if (!results.passed) { | |
| labels.push('needs-work'); | |
| } | |
| if (results.accessibility.some(a => a.type === 'error')) { | |
| labels.push('accessibility'); | |
| } | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| labels: labels | |
| }); | |
| - name: Set status check | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| if (!fs.existsSync('validation-results.json')) { | |
| console.log('validation-results.json not found, skipping status check'); | |
| return; | |
| } | |
| const results = JSON.parse(fs.readFileSync('validation-results.json', 'utf8')); | |
| const state = results.passed ? 'success' : 'failure'; | |
| const description = results.passed | |
| ? 'All validation checks passed!' | |
| : 'Some validation checks need attention'; | |
| await github.rest.repos.createCommitStatus({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: context.payload.pull_request.head.sha, | |
| state: state, | |
| target_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/pull/${context.payload.pull_request.number}#issuecomment`, | |
| description: description, | |
| context: 'Learning Room Bot / Validation' | |
| }); | |
| celebrate-review: | |
| name: Celebrate Peer Review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request_review' && github.event.review.state == 'approved' | |
| steps: | |
| - name: Congratulate reviewer | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const reviewBody = [ | |
| '## Peer Review Complete!', | |
| '', | |
| 'Great work, @' + context.payload.review.user.login + '! You\'ve completed a peer review.', | |
| '', | |
| '**Why this matters:** Code review is one of the most valuable skills in open source. You\'re helping ensure quality, sharing knowledge, and building community trust.', | |
| '', | |
| '@' + context.payload.pull_request.user.login + ' \u2014 your PR has been approved! A facilitator will merge it soon.', | |
| '', | |
| '---', | |
| '*Learning Room Bot celebrates your collaboration*' | |
| ].join('\n'); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.pull_request.number, | |
| body: reviewBody | |
| }); | |
| respond-to-questions: | |
| name: Respond to Common Questions | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'issue_comment' && github.event.issue.pull_request | |
| steps: | |
| - name: Auto-respond to keywords | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const { getAutoResponse } = require('./.github/scripts/comment-responder.js'); | |
| const comment = context.payload.comment.body.toLowerCase(); | |
| const author = context.payload.comment.user.login; | |
| // Don't respond to bots | |
| if (context.payload.comment.user.type === 'Bot') return; | |
| const response = getAutoResponse(comment, author); | |
| if (response) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.issue.number, | |
| body: response + '\n\n---\n*Auto-response from Learning Room Bot*' | |
| }); | |
| } |