feat: sysext hub #663
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: Vote Monitor and Onboarding Automation | |
| on: | |
| issues: | |
| types: [labeled, unlabeled] | |
| workflow_dispatch: | |
| inputs: | |
| process_existing: | |
| description: 'Process existing issues with gitvote/passed label' | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| issues: write | |
| contents: read | |
| # Prevent duplicate runs for the same issue to avoid race conditions | |
| # that could create duplicate onboarding issues | |
| concurrency: | |
| group: vote-monitor-${{ github.event.issue.number || 'manual' }} | |
| cancel-in-progress: false | |
| jobs: | |
| create-onboarding-issue: | |
| runs-on: ubuntu-latest | |
| if: | | |
| (github.event_name == 'issues' && (github.event.action == 'labeled' || github.event.action == 'unlabeled')) || | |
| (github.event_name == 'workflow_dispatch' && github.event.inputs.process_existing == 'true') | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Process application vote | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9 | |
| with: | |
| script: | | |
| const { createOnboardingIssue, commentAndClose, ROLE_TYPES } = require('./.github/scripts/create-onboarding-issue.js'); | |
| // Helper function to detect application type from issue title | |
| function detectApplicationType(title) { | |
| const maintainerMatch = title.match(/^New Maintainer - (.+)$/); | |
| if (maintainerMatch) { | |
| return { roleType: ROLE_TYPES.MAINTAINER, applicantName: maintainerMatch[1].trim() }; | |
| } | |
| const contributorMatch = title.match(/^New Contributor - (.+)$/); | |
| if (contributorMatch) { | |
| return { roleType: ROLE_TYPES.CONTRIBUTOR, applicantName: contributorMatch[1].trim() }; | |
| } | |
| return null; | |
| } | |
| if (context.eventName === 'workflow_dispatch') { | |
| // Manual trigger - process existing issues | |
| console.log('🔍 Processing existing issues with gitvote/passed label...'); | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'gitvote/passed' | |
| }); | |
| console.log(`Found ${issues.data.length} issues with gitvote/passed label`); | |
| for (const issue of issues.data) { | |
| const appType = detectApplicationType(issue.title); | |
| if (!appType) { | |
| console.log(`⚠️ Skipping issue #${issue.number} - not a recognized application issue`); | |
| continue; | |
| } | |
| const { roleType, applicantName } = appType; | |
| console.log(`📝 Processing ${roleType} application issue #${issue.number}: "${applicantName}"`); | |
| try { | |
| const result = await createOnboardingIssue(github, context, applicantName, issue.number, roleType); | |
| if (result.alreadyExists) { | |
| console.log(`⏭️ Skipped creating onboarding issue for #${issue.number} - already exists: #${result.issueNumber} (${result.state})`); | |
| continue; | |
| } | |
| await commentAndClose(github, context, issue.number, result.issueNumber, applicantName, roleType); | |
| console.log(`✅ Processed issue #${issue.number} -> created ${roleType} onboarding issue #${result.issueNumber}`); | |
| } catch (error) { | |
| console.error(`❌ Error processing issue #${issue.number}:`, error.message); | |
| } | |
| } | |
| } else { | |
| // Label add/remove trigger - process single issue based on current labels | |
| const issue = context.payload.issue; | |
| // Ensure we only proceed for application issues with a passed vote | |
| const labels = issue.labels || []; | |
| const labelNames = labels.map(l => (typeof l === 'string' ? l : l.name)); | |
| const hasVotePassed = labelNames.includes('gitvote/passed'); | |
| if (!hasVotePassed) { | |
| console.log('ℹ️ Skipping: issue does not have gitvote/passed label.'); | |
| return; | |
| } | |
| // Detect application type from issue title | |
| const appType = detectApplicationType(issue.title); | |
| if (!appType) { | |
| console.log('ℹ️ Skipping: not a recognized application issue (maintainer or contributor).'); | |
| return; | |
| } | |
| const { roleType, applicantName } = appType; | |
| console.log(`🎉 Vote passed for ${roleType} application. Creating onboarding issue...`); | |
| const result = await createOnboardingIssue(github, context, applicantName, issue.number, roleType); | |
| if (result.alreadyExists) { | |
| console.log(`⏭️ Onboarding issue already exists for "${applicantName}": #${result.issueNumber} (${result.state})`); | |
| return; | |
| } | |
| await commentAndClose(github, context, issue.number, result.issueNumber, applicantName, roleType); | |
| console.log(`✅ Successfully created ${roleType} onboarding issue #${result.issueNumber} and closed issue #${issue.number}`); | |
| } | |