Extract pretest install logic to Node.js script #18
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: Issue Assessment | ||
| on: | ||
| issues: | ||
| types: [labeled] | ||
| jobs: | ||
| # Comment when need-more-info label is manually added | ||
| need-more-info: | ||
| if: github.event.label.name == 'need-more-info' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| issues: write | ||
| steps: | ||
| - name: Add comment | ||
| 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: `## Additional Information Needed | ||
| This issue is missing information we need to investigate the problem. | ||
| ### Please provide: | ||
| - [ ] **Reproduction repository** — A public GitHub repo that demonstrates the issue | ||
| - [ ] **Prettier extension logs** — Click "Prettier" in the status bar and copy the output | ||
| - [ ] **Steps to reproduce** — Clear steps we can follow to see the issue | ||
| ### Why this matters | ||
| Most issues are specific to your project's configuration. Without a reproduction, we usually cannot diagnose the problem. | ||
| ### Resources | ||
| - [Creating a minimal reproduction](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md) | ||
| - [Writing a good issue](https://github.com/prettier/prettier-vscode/blob/main/docs/writing-an-issue.md) | ||
| > **Note:** Issues without the requested information may be closed after 7 days.` | ||
| }); | ||
| triage: | ||
| if: github.event.label.name == 'triage-pending' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| issues: write | ||
| models: read | ||
| contents: read | ||
| outputs: | ||
| triage_labels: ${{ steps.triage.outputs.ai_assessments }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| - name: Run Issue Triage | ||
| id: triage | ||
| uses: github/ai-assessment-comment-labeler@main | ||
| with: | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| issue_number: ${{ github.event.issue.number }} | ||
| issue_body: ${{ github.event.issue.body }} | ||
| ai_review_label: "triage-pending" | ||
| prompts_directory: "./.github/prompts" | ||
| labels_to_prompts_mapping: "triage-pending,issue-triage.prompt.yml" | ||
| - name: Remove pending label | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| await github.rest.issues.removeLabel({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| name: 'triage-pending' | ||
| }).catch(() => {}); | ||
| post-triage: | ||
| runs-on: ubuntu-latest | ||
| needs: triage | ||
| if: always() && needs.triage.result == 'success' | ||
| permissions: | ||
| issues: write | ||
| steps: | ||
| - name: Process triage results and post comments | ||
| uses: actions/github-script@v7 | ||
| env: | ||
| TRIAGE_OUTPUT: ${{ needs.triage.outputs.triage_labels }} | ||
| with: | ||
| script: | | ||
| const issueNumber = context.issue.number; | ||
| const triageOutput = process.env.TRIAGE_OUTPUT; | ||
| let triageAssessments = []; | ||
| try { | ||
| triageAssessments = JSON.parse(triageOutput || '[]'); | ||
| } catch (e) { | ||
| console.log('Could not parse triage output:', e); | ||
| } | ||
| const triageLabel = triageAssessments[0]?.assessmentLabel || ''; | ||
| console.log('Triage assessment label:', triageLabel); | ||
| const responses = { | ||
| 'ai:issue-triage:needs-reproduction': { | ||
| addLabel: 'need-more-info', | ||
| comment: `## Reproduction Repository Needed | ||
| Thanks for the report! To investigate this issue, we need more information. | ||
| ### Please provide: | ||
| 1. **A GitHub repository** that reproduces the issue | ||
| 2. **Step-by-step instructions** to reproduce the problem | ||
| 3. **Prettier extension logs** — Click "Prettier" in the status bar and copy the output | ||
| ### Why this matters | ||
| Most issues depend on specific project configurations. A reproduction repository lets us see exactly what you're experiencing. | ||
| ### Resources | ||
| - [Creating a minimal reproduction](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md) | ||
| - [Writing a good issue](https://github.com/prettier/prettier-vscode/blob/main/docs/writing-an-issue.md) | ||
| > **Note:** Issues without reproduction info may be closed after 7 days.` | ||
| }, | ||
| 'ai:issue-triage:prettier-core': { | ||
| addLabel: 'prettier-core', | ||
| comment: `## This Looks Like a Prettier Core Issue | ||
| Thanks for the report! This issue appears to be about **how Prettier formats code**, not about the VS Code extension. | ||
| ### Understanding the difference | ||
| | Prettier Core | VS Code Extension | | ||
| |--------------|-------------------| | ||
| | Decides *how* code is formatted | Integrates Prettier into VS Code | | ||
| | Formatting rules and output | Editor integration, settings UI | | ||
| | [prettier/prettier](https://github.com/prettier/prettier) | [prettier/prettier-vscode](https://github.com/prettier/prettier-vscode) | | ||
| ### To confirm this is a Prettier Core issue | ||
| Run in your terminal: | ||
| \`\`\`bash | ||
| npx prettier --write yourfile.js | ||
| \`\`\` | ||
| If you see the same formatting behavior, it's a Prettier Core issue. | ||
| ### Next steps | ||
| 1. Check the [Prettier Playground](https://prettier.io/playground/) to test formatting | ||
| 2. Search [existing Prettier issues](https://github.com/prettier/prettier/issues) | ||
| 3. Open an issue at **[prettier/prettier](https://github.com/prettier/prettier/issues/new)** | ||
| ### If this IS an extension issue | ||
| If the CLI formats differently than the extension, please reply with: | ||
| - Output from \`npx prettier --version\` | ||
| - Prettier extension logs | ||
| - Example showing the difference` | ||
| }, | ||
| 'ai:issue-triage:performance-info-needed': { | ||
| addLabel: 'need-more-info', | ||
| comment: `## Performance Information Needed | ||
| Thanks for reporting this performance issue! To investigate, we need additional details. | ||
| ### Please provide: | ||
| | Information | How to get it | | ||
| |------------|---------------| | ||
| | Prettier version | Check the Prettier output panel | | ||
| | File size | Size of slow-to-format files | | ||
| | File types affected | Which languages/extensions | | ||
| | Prettier plugins | List any plugins you're using | | ||
| | Debug logs | Enable with \`"prettier.enableDebugLogs": true\` | | ||
| ### Quick test | ||
| Compare with the CLI: | ||
| \`\`\`bash | ||
| time npx prettier --write yourfile.js | ||
| \`\`\` | ||
| If the CLI is also slow, the issue is with Prettier Core, not this extension. | ||
| ### Optional: VS Code performance profile | ||
| 1. Open Command Palette → **Developer: Start Performance Profile** | ||
| 2. Format a document | ||
| 3. Stop the profile and attach it here | ||
| > **Note:** Issues without the requested information may be closed after 7 days.` | ||
| }, | ||
| 'ai:issue-triage:incomplete-template': { | ||
| addLabel: 'need-more-info', | ||
| comment: `## Issue Template Incomplete | ||
| Thanks for opening this issue! Some required information is missing. | ||
| ### Please edit your issue to include: | ||
| - [ ] **Summary** — Brief description of the problem | ||
| - [ ] **Reproduction repository** — Public GitHub repo demonstrating the issue | ||
| - [ ] **Steps to reproduce** — Clear steps to see the problem | ||
| - [ ] **Expected vs actual result** — What should happen vs what happens | ||
| - [ ] **Environment info:** | ||
| - VS Code version (\`Help → About → Copy\`) | ||
| - Prettier extension version | ||
| - Operating system | ||
| - [ ] **Prettier logs** — Click "Prettier" in status bar, copy output | ||
| ### Resources | ||
| - [Writing a good issue](https://github.com/prettier/prettier-vscode/blob/main/docs/writing-an-issue.md) | ||
| - [Troubleshooting guide](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md) | ||
| > **Note:** Issues without the requested information may be closed after 7 days.` | ||
| }, | ||
| 'ai:issue-triage:support-question': { | ||
| comment: `## This Looks Like a Support Question | ||
| Thanks for reaching out! This appears to be a question about configuration or usage rather than a bug report. | ||
| ### Get help here: | ||
| | Resource | Best for | | ||
| |----------|----------| | ||
| | [Stack Overflow](https://stackoverflow.com/questions/tagged/prettier-vscode) | Configuration questions | | ||
| | [Prettier Docs](https://prettier.io/docs/en/index.html) | Formatting options | | ||
| | [Extension Settings](https://github.com/prettier/prettier-vscode#settings) | VS Code settings | | ||
| | [Troubleshooting Guide](https://github.com/prettier/prettier-vscode/blob/main/docs/troubleshooting.md) | Common issues | | ||
| ### If this is actually a bug | ||
| Please reopen with: | ||
| 1. What you've already tried | ||
| 2. A minimal reproduction repository | ||
| 3. Prettier extension logs | ||
| We're happy to help with confirmed bugs!` | ||
| } | ||
| }; | ||
| if (triageLabel && responses[triageLabel]) { | ||
| const response = responses[triageLabel]; | ||
| if (response.comment) { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| body: response.comment | ||
| }); | ||
| } | ||
| if (response.addLabel) { | ||
| await github.rest.issues.addLabels({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| labels: [response.addLabel] | ||
| }); | ||
| } | ||
| } | ||
| # Duplicate detection | ||
| duplicate-search: | ||
| runs-on: ubuntu-latest | ||
| needs: triage | ||
| if: always() && needs.triage.result == 'success' | ||
| permissions: | ||
| issues: write | ||
| models: read | ||
| contents: read | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| - name: Search for candidate issues | ||
| id: search | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const issueTitle = context.payload.issue.title; | ||
| const issueBody = context.payload.issue.body || ''; | ||
| const issueNumber = context.issue.number; | ||
| // Common words to filter out | ||
| const stopWords = new Set([ | ||
| 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being', | ||
| 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', | ||
| 'should', 'may', 'might', 'must', 'can', 'need', 'to', 'of', 'in', | ||
| 'for', 'on', 'with', 'at', 'by', 'from', 'as', 'into', 'through', | ||
| 'during', 'before', 'after', 'then', 'once', 'here', 'there', 'when', | ||
| 'where', 'why', 'how', 'all', 'each', 'few', 'more', 'most', 'other', | ||
| 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', | ||
| 'than', 'too', 'very', 'just', 'and', 'but', 'if', 'or', 'because', | ||
| 'until', 'while', 'this', 'that', 'these', 'those', 'i', 'me', 'my', | ||
| 'we', 'our', 'you', 'your', 'he', 'him', 'she', 'her', 'it', 'its', | ||
| 'they', 'them', 'their', 'what', 'which', 'who', 'whom', | ||
| 'prettier', 'vscode', 'extension', 'code', 'file', 'files', 'issue', | ||
| 'bug', 'error', 'problem', 'help', 'please', 'work', 'working', | ||
| 'works', 'doesnt', "doesn't", 'dont', "don't", 'cant', "can't", | ||
| 'wont', "won't", 'using', 'use', 'get', 'getting', 'not', 'format', | ||
| 'formatting', 'save', 'saving' | ||
| ]); | ||
| const extractKeywords = (text) => { | ||
| return text | ||
| .toLowerCase() | ||
| .replace(/[^\w\s-]/g, ' ') | ||
| .split(/\s+/) | ||
| .filter(word => word.length > 2 && !stopWords.has(word)); | ||
| }; | ||
| const titleKeywords = extractKeywords(issueTitle); | ||
| console.log('Keywords:', titleKeywords); | ||
| if (titleKeywords.length === 0) { | ||
| console.log('No keywords, skipping'); | ||
| core.setOutput('candidates', '[]'); | ||
| core.setOutput('has_candidates', 'false'); | ||
| return; | ||
| } | ||
| const searchKeywords = titleKeywords.slice(0, 4).join(' '); | ||
| const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue ${searchKeywords}`; | ||
| let candidates = []; | ||
| try { | ||
| const result = await github.rest.search.issuesAndPullRequests({ | ||
| q: query, | ||
| sort: 'updated', | ||
| order: 'desc', | ||
| per_page: 10 | ||
| }); | ||
| for (const issue of result.data.items) { | ||
| if (issue.number === issueNumber) continue; | ||
| candidates.push({ | ||
| number: issue.number, | ||
| title: issue.title, | ||
| body: (issue.body || '').substring(0, 500), | ||
| state: issue.state, | ||
| url: issue.html_url | ||
| }); | ||
| } | ||
| } catch (e) { | ||
| console.log('Search error:', e.message); | ||
| } | ||
| candidates = candidates.slice(0, 8); | ||
| console.log(`Found ${candidates.length} candidates`); | ||
| // Build comparison text for AI | ||
| const newIssue = `## NEW ISSUE #${issueNumber} | ||
| Title: ${issueTitle} | ||
| Body: ${issueBody.substring(0, 1000)}`; | ||
| let candidateText = ''; | ||
| for (const c of candidates) { | ||
| candidateText += `\n\n## CANDIDATE ISSUE #${c.number} (${c.state}) | ||
| Title: ${c.title} | ||
| Body: ${c.body}`; | ||
| } | ||
| const comparisonInput = newIssue + '\n\n---\n\nCANDIDATE ISSUES TO COMPARE:' + candidateText; | ||
| core.setOutput('candidates', JSON.stringify(candidates)); | ||
| core.setOutput('has_candidates', candidates.length > 0 ? 'true' : 'false'); | ||
| core.setOutput('comparison_input', comparisonInput); | ||
| - name: Duplicate Analysis | ||
| id: analysis | ||
| if: steps.search.outputs.has_candidates == 'true' | ||
| uses: github/ai-assessment-comment-labeler@main | ||
| with: | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| issue_number: ${{ github.event.issue.number }} | ||
| issue_body: ${{ steps.search.outputs.comparison_input }} | ||
| ai_review_label: "triage-pending" | ||
| prompts_directory: "./.github/prompts" | ||
| labels_to_prompts_mapping: "triage-pending,duplicate-comparison.prompt.yml" | ||
| suppress_comments: true | ||
| suppress_labels: true | ||
| - name: Post duplicate analysis results | ||
| if: steps.search.outputs.has_candidates == 'true' | ||
| uses: actions/github-script@v7 | ||
| env: | ||
| AI_OUTPUT: ${{ steps.analysis.outputs.ai_assessments }} | ||
| CANDIDATES: ${{ steps.search.outputs.candidates }} | ||
| with: | ||
| script: | | ||
| const issueNumber = context.issue.number; | ||
| const aiOutput = process.env.AI_OUTPUT; | ||
| const candidatesJson = process.env.CANDIDATES; | ||
| let candidates = []; | ||
| try { | ||
| candidates = JSON.parse(candidatesJson || '[]'); | ||
| } catch (e) { | ||
| console.log('Could not parse candidates'); | ||
| return; | ||
| } | ||
| if (candidates.length === 0) { | ||
| console.log('No candidates to analyze'); | ||
| return; | ||
| } | ||
| // Try to parse AI response | ||
| let aiAssessments = []; | ||
| let analysis = null; | ||
| try { | ||
| aiAssessments = JSON.parse(aiOutput || '[]'); | ||
| const response = aiAssessments[0]?.response || ''; | ||
| console.log('AI Response:', response); | ||
| // Try to extract JSON from the response | ||
| const jsonMatch = response.match(/\{[\s\S]*\}/); | ||
| if (jsonMatch) { | ||
| analysis = JSON.parse(jsonMatch[0]); | ||
| } | ||
| } catch (e) { | ||
| console.log('Could not parse AI analysis:', e.message); | ||
| } | ||
| // Build comment based on AI analysis | ||
| let comment = ''; | ||
| let hasDuplicates = false; | ||
| let hasRelated = false; | ||
| if (analysis && (analysis.duplicates?.length > 0 || analysis.related?.length > 0)) { | ||
| // AI found duplicates or related issues | ||
| if (analysis.duplicates?.length > 0) { | ||
| hasDuplicates = true; | ||
| comment += `### Likely Duplicate Issues Found\n\n`; | ||
| comment += `This issue appears to be a duplicate of:\n\n`; | ||
| comment += `| Issue | Title | Confidence | Reason |\n`; | ||
| comment += `|-------|-------|------------|--------|\n`; | ||
| for (const dup of analysis.duplicates) { | ||
| const candidate = candidates.find(c => c.number === dup.number); | ||
| if (candidate) { | ||
| const title = candidate.title.replace(/\|/g, '\\|').substring(0, 40) + (candidate.title.length > 40 ? '...' : ''); | ||
| const status = candidate.state === 'open' ? '🟢' : '🔴'; | ||
| comment += `| ${status} #${dup.number} | ${title} | ${dup.confidence} | ${dup.reason} |\n`; | ||
| } | ||
| } | ||
| comment += `\n**Please check these issues.** If your issue is indeed a duplicate, close this issue and add your information to the existing one instead.\n\n`; | ||
| } | ||
| if (analysis.related?.length > 0) { | ||
| hasRelated = true; | ||
| comment += `### Possibly Related Issues\n\n`; | ||
| comment += `| Issue | Title | Reason |\n`; | ||
| comment += `|-------|-------|--------|\n`; | ||
| for (const rel of analysis.related) { | ||
| const candidate = candidates.find(c => c.number === rel.number); | ||
| if (candidate) { | ||
| const title = candidate.title.replace(/\|/g, '\\|').substring(0, 50) + (candidate.title.length > 50 ? '...' : ''); | ||
| const status = candidate.state === 'open' ? '🟢' : '🔴'; | ||
| comment += `| ${status} #${rel.number} | ${title} | ${rel.reason} |\n`; | ||
| } | ||
| } | ||
| comment += `\nThese issues might provide useful context or workarounds.\n\n`; | ||
| } | ||
| } else { | ||
| // Fallback: just show candidate issues if AI analysis failed | ||
| console.log('AI analysis unavailable, showing keyword matches'); | ||
| comment = `### Potentially Related Issues\n\n`; | ||
| comment += `I found some existing issues that might be related based on keywords:\n\n`; | ||
| comment += `| Issue | Title | Status |\n`; | ||
| comment += `|-------|-------|--------|\n`; | ||
| for (const c of candidates.slice(0, 5)) { | ||
| const title = c.title.replace(/\|/g, '\\|').substring(0, 50) + (c.title.length > 50 ? '...' : ''); | ||
| const status = c.state === 'open' ? '🟢 Open' : '🔴 Closed'; | ||
| comment += `| #${c.number} | ${title} | ${status} |\n`; | ||
| } | ||
| comment += `\nPlease check if any of these address your issue.\n\n`; | ||
| } | ||
| comment += `---\n<sub>This analysis was generated automatically to help identify duplicate issues.</sub>`; | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| body: comment | ||
| }); | ||