name: External Plugin Rerun Intake Commands on: issue_comment: types: [created] concurrency: group: external-plugin-intake-${{ github.event.issue.number }} cancel-in-progress: false permissions: contents: read issues: write jobs: parse-command: runs-on: ubuntu-latest if: >- !github.event.issue.pull_request && startsWith(github.event.comment.body, '/rerun-intake') outputs: should-run: ${{ steps.evaluate.outputs.should-run }} base-result: ${{ steps.evaluate.outputs.base-result }} valid: ${{ steps.evaluate.outputs.valid }} plugin-json: ${{ steps.evaluate.outputs.plugin-json }} issue-state: ${{ steps.evaluate.outputs.issue-state }} issue-labels: ${{ steps.evaluate.outputs.issue-labels }} steps: - name: Checkout staged branch uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: ref: staged - name: Validate command and evaluate intake id: evaluate uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: script: | const path = require('path'); const { pathToFileURL } = require('url'); const intake = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake.mjs')).href); core.setOutput('should-run', 'false'); const commentAuthor = context.payload.comment.user?.login; if (!commentAuthor || context.payload.comment.user?.type === 'Bot' || commentAuthor === 'github-actions[bot]') { core.info('Ignoring /rerun-intake from a bot or unknown actor.'); return; } if (!intake.parseRerunIntakeCommand(context.payload.comment.body)) { core.info('No supported /rerun-intake command was found.'); return; } const { data: currentIssue } = await github.rest.issues.get({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); const labelNames = new Set((currentIssue.labels || []).map((label) => label.name)); const isExternalPluginIssue = labelNames.has('external-plugin') || String(currentIssue.body || '').includes(intake.ISSUE_FORM_MARKER); if (!isExternalPluginIssue) { core.info('Ignoring /rerun-intake because the issue is not an external plugin submission.'); return; } if (labelNames.has('approved') || labelNames.has('re-review-due') || labelNames.has('re-review-follow-up')) { core.info('Ignoring /rerun-intake because the issue is already approved or in the six-month re-review flow.'); return; } const issueAuthor = currentIssue.user?.login; const isIssueAuthor = Boolean(issueAuthor && commentAuthor === issueAuthor); let hasWriteAccess = false; if (!isIssueAuthor) { const permission = await github.rest.repos.getCollaboratorPermissionLevel({ owner: context.repo.owner, repo: context.repo.repo, username: commentAuthor }); hasWriteAccess = ['admin', 'write', 'maintain'].includes(permission.data.permission); } if (!isIssueAuthor && !hasWriteAccess) { core.info(`Ignoring /rerun-intake because ${commentAuthor} is neither the issue author nor a maintainer.`); return; } const canRerunFromCurrentState = currentIssue.state === 'open' || labelNames.has('rejected'); if (!canRerunFromCurrentState) { core.info('Ignoring /rerun-intake because the issue is closed outside the intake/rejection flow.'); return; } const baseResult = await intake.evaluateExternalPluginIssue({ issue: currentIssue, token: process.env.GITHUB_TOKEN }); core.setOutput('should-run', 'true'); core.setOutput('base-result', JSON.stringify(baseResult)); core.setOutput('valid', baseResult.valid ? 'true' : 'false'); core.setOutput('plugin-json', JSON.stringify(baseResult.plugin || {})); core.setOutput('issue-state', currentIssue.state); core.setOutput('issue-labels', JSON.stringify([...labelNames])); quality-gates: needs: parse-command if: >- needs.parse-command.outputs.should-run == 'true' && needs.parse-command.outputs.valid == 'true' uses: ./.github/workflows/external-plugin-quality-gates.yml with: plugin-json: ${{ needs.parse-command.outputs.plugin-json }} apply-state: runs-on: ubuntu-latest needs: [parse-command, quality-gates] if: always() && needs.parse-command.outputs.should-run == 'true' steps: - name: Checkout staged branch uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: ref: staged - name: Apply merged intake evaluation uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 env: BASE_RESULT_JSON: ${{ needs.parse-command.outputs.base-result }} BASE_VALID: ${{ needs.parse-command.outputs.valid }} QUALITY_RESULT_JSON: ${{ needs.quality-gates.outputs.quality-result }} QUALITY_JOB_RESULT: ${{ needs.quality-gates.result }} ISSUE_STATE: ${{ needs.parse-command.outputs.issue-state }} ISSUE_LABELS: ${{ needs.parse-command.outputs.issue-labels }} with: script: | const path = require('path'); const { pathToFileURL } = require('url'); const intake = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake.mjs')).href); const intakeState = await import(pathToFileURL(path.join(process.env.GITHUB_WORKSPACE, 'eng', 'external-plugin-intake-state.mjs')).href); const baseResult = JSON.parse(process.env.BASE_RESULT_JSON); let finalResult = baseResult; if (process.env.BASE_VALID === 'true') { let qualityResult; if (process.env.QUALITY_JOB_RESULT === 'failure' || process.env.QUALITY_JOB_RESULT === 'cancelled') { qualityResult = { overall_status: 'infra_error', skill_validator_status: 'infra_error', smoke_status: 'infra_error', failure_class: 'infra', summary: 'Quality-gate workflow failed unexpectedly. Re-run intake to retry.', }; } else if (process.env.QUALITY_RESULT_JSON) { qualityResult = JSON.parse(process.env.QUALITY_RESULT_JSON); } else { qualityResult = { overall_status: 'infra_error', skill_validator_status: 'infra_error', smoke_status: 'infra_error', failure_class: 'infra', summary: 'Quality-gate workflow did not return results. Re-run intake to retry.', }; } finalResult = intake.applyQualityGateResult(baseResult, qualityResult); } await intakeState.applyExternalPluginIntakeEvaluation({ github, owner: context.repo.owner, repo: context.repo.repo, issueNumber: context.issue.number, evaluation: finalResult }); const issueState = process.env.ISSUE_STATE; const labels = new Set(JSON.parse(process.env.ISSUE_LABELS || '[]')); if (finalResult.intakeState === 'rejected' && issueState === 'open') { await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, state: 'closed' }); return; } if (finalResult.intakeState !== 'rejected' && issueState === 'closed' && labels.has('rejected')) { await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, state: 'open' }); }