[REGISTER] GIT Going with GitHub - March 2026 #4
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: Registration - Welcome & CSV Export | |
| on: | |
| issues: | |
| types: [opened] | |
| workflow_dispatch: | |
| permissions: | |
| issues: write | |
| jobs: | |
| welcome: | |
| name: Welcome New Registrant | |
| runs-on: ubuntu-latest | |
| if: >- | |
| github.event_name == 'issues' && | |
| github.event.action == 'opened' && | |
| contains(github.event.issue.title, '[REGISTER]') | |
| steps: | |
| - name: Check for duplicate registration | |
| id: dup-check | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const username = context.payload.issue.user.login; | |
| const currentIssueNumber = context.issue.number; | |
| // Find existing registration issues by this user | |
| const existingIssues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'registration', | |
| state: 'all', | |
| per_page: 100 | |
| }); | |
| const priorRegistration = existingIssues.find( | |
| issue => issue.user.login === username && issue.number !== currentIssueNumber | |
| ); | |
| if (priorRegistration) { | |
| core.setOutput('is_duplicate', 'true'); | |
| core.setOutput('original_issue', priorRegistration.number); | |
| } else { | |
| core.setOutput('is_duplicate', 'false'); | |
| } | |
| - name: Handle duplicate registration | |
| if: steps.dup-check.outputs.is_duplicate == 'true' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const originalIssue = ${{ steps.dup-check.outputs.original_issue }}; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: `## You are already registered! | |
| Hi @${context.payload.issue.user.login}, it looks like you already registered in issue #${originalIssue}. No need to register again — your spot is saved! | |
| We have closed this issue since your original registration is already on file. If you need to update your details, please comment on your original issue #${originalIssue}. | |
| Questions? Email [support@bits-acb.org](mailto:support@bits-acb.org).` | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| labels: ['duplicate'] | |
| }); | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| state: 'closed', | |
| state_reason: 'not_planned' | |
| }); | |
| - name: Check capacity | |
| id: capacity-check | |
| if: steps.dup-check.outputs.is_duplicate == 'false' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const MAX_CAPACITY = 75; | |
| // Count unique registered users (excluding this issue) | |
| const existingIssues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'registration', | |
| state: 'all', | |
| per_page: 100 | |
| }); | |
| const currentIssueNumber = context.issue.number; | |
| const uniqueUsers = new Set(); | |
| for (const issue of existingIssues) { | |
| if (issue.number !== currentIssueNumber) { | |
| uniqueUsers.add(issue.user.login); | |
| } | |
| } | |
| const currentCount = uniqueUsers.size; | |
| core.setOutput('count', currentCount); | |
| if (currentCount >= MAX_CAPACITY) { | |
| core.setOutput('is_full', 'true'); | |
| } else { | |
| core.setOutput('is_full', 'false'); | |
| } | |
| - name: Handle registration full | |
| if: steps.dup-check.outputs.is_duplicate == 'false' && steps.capacity-check.outputs.is_full == 'true' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: `## Registration is currently full | |
| Hi @${context.payload.issue.user.login}, thank you for your interest in GIT Going with GitHub! Unfortunately, all 75 spots have been filled. | |
| We have added you to the waitlist. If a spot opens up, we will let you know right here on this issue. | |
| Questions? Email [support@bits-acb.org](mailto:support@bits-acb.org).` | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| labels: ['waitlist'] | |
| }); | |
| - name: Post welcome comment | |
| if: steps.dup-check.outputs.is_duplicate == 'false' && steps.capacity-check.outputs.is_full == 'false' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: `## Welcome to GIT Going with GitHub! | |
| Hi @${context.payload.issue.user.login}, thank you for registering! We are excited to have you join us. | |
| Your registration is confirmed. Please register for the Zoom session here: [Register on Zoom](https://us06web.zoom.us/meeting/register/YdAAvwzAQUCYpPpNtAlG3g) | |
| In the meantime, you can get a head start with the [Pre-Workshop Setup Guide](https://bits-acb.github.io/git-going-with-github/docs/00-pre-workshop-setup.html). | |
| If you have any questions, email us at [support@bits-acb.org](mailto:support@bits-acb.org). | |
| See you on March 7!` | |
| }); | |
| - name: Add registration label | |
| if: steps.dup-check.outputs.is_duplicate == 'false' && steps.capacity-check.outputs.is_full == 'false' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| try { | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| labels: ['registration'] | |
| }); | |
| } catch (e) { | |
| console.log('Label may already exist:', e.message); | |
| } | |
| export-csv: | |
| name: Export Registrations to CSV | |
| runs-on: ubuntu-latest | |
| if: >- | |
| (github.event_name == 'workflow_dispatch') || | |
| (github.event_name == 'issues' && | |
| github.event.action == 'opened' && | |
| contains(github.event.issue.title, '[REGISTER]')) | |
| steps: | |
| - name: Generate CSV from registration issues | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| // Fetch all issues with the registration label | |
| const issues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'registration', | |
| state: 'all', | |
| per_page: 100 | |
| }); | |
| // Parse issue body fields (GitHub YAML forms use ### heading + content pattern) | |
| function parseField(body, fieldName) { | |
| if (!body) return ''; | |
| // Match the pattern: ### Field Name\n\nValue | |
| // Also handle ### Field Name\r\n\r\nValue | |
| const regex = new RegExp(`### ${fieldName}\\s*\\n+([\\s\\S]*?)(?=\\n### |$)`, 'i'); | |
| const match = body.match(regex); | |
| if (!match) return ''; | |
| let value = match[1].trim(); | |
| // Remove _No response_ placeholder | |
| if (value === '_No response_') return ''; | |
| // Escape CSV: quote fields containing commas, quotes, or newlines | |
| if (value.includes(',') || value.includes('"') || value.includes('\n')) { | |
| value = '"' + value.replace(/"/g, '""') + '"'; | |
| } | |
| return value; | |
| } | |
| // Build CSV | |
| const headers = [ | |
| 'Registration Date', | |
| 'GitHub Username', | |
| 'First Name', | |
| 'Last Name', | |
| 'Email Address', | |
| 'GitHub Proficiency Level', | |
| 'Primary Screen Reader', | |
| 'Questions or Accommodations', | |
| 'Issue Number', | |
| 'Issue URL' | |
| ]; | |
| let csv = headers.join(',') + '\n'; | |
| // Deduplicate by GitHub username, keeping only the earliest registration | |
| const seen = new Set(); | |
| const sortedIssues = issues.sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); | |
| for (const issue of sortedIssues) { | |
| const username = issue.user.login; | |
| if (seen.has(username)) continue; | |
| seen.add(username); | |
| const row = [ | |
| issue.created_at.split('T')[0], | |
| username, | |
| parseField(issue.body, 'First Name'), | |
| parseField(issue.body, 'Last Name'), | |
| parseField(issue.body, 'Email Address'), | |
| parseField(issue.body, 'GitHub Proficiency Level'), | |
| parseField(issue.body, 'Primary Screen Reader'), | |
| parseField(issue.body, 'Questions or Accommodations'), | |
| issue.number, | |
| issue.html_url | |
| ]; | |
| csv += row.join(',') + '\n'; | |
| } | |
| // Write CSV file | |
| fs.mkdirSync('.github/data', { recursive: true }); | |
| fs.writeFileSync('.github/data/registrations.csv', csv, 'utf-8'); | |
| console.log(`Exported ${seen.size} unique registration(s) from ${issues.length} total issue(s) to .github/data/registrations.csv`); | |
| - name: Upload CSV as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: registrations | |
| path: .github/data/registrations.csv | |
| retention-days: 90 |