Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 199 additions & 0 deletions .github/workflows/issue-triage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
---
name: Issue Triage

'on':
issues:
types: [opened]
workflow_dispatch:
inputs:
process_unlabeled:
description: 'Process all open issues without labels'
required: false
default: 'true'
type: boolean

permissions:
issues: write
contents: read

jobs:
triage-new-issue:
name: Triage New Issue
if: github.event_name == 'issues'
runs-on: ubuntu-latest
steps:
- name: Get available labels
id: get-labels
uses: actions/github-script@v7
with:
script: |
const labels = await github.rest.issues.listLabelsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
});
const labelNames = labels.data.map(label => label.name);
return labelNames.join(', ');

- name: Set environment variables
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
LABELS_RESULT: ${{ steps.get-labels.outputs.result }}
run: |
{
echo "AVAILABLE_LABELS=${LABELS_RESULT}"
echo "ISSUE_TITLE=${ISSUE_TITLE}"
echo "ISSUE_BODY<<EOF"
echo "${ISSUE_BODY}"
echo "EOF"
} >> "$GITHUB_ENV"

- name: Analyze issue with AI
id: ai-triage
uses: actions/ai-inference@v1
with:
prompt: |
## Role

You are an issue triage assistant. Analyze the current GitHub
issue and identify the most appropriate existing labels. Use the
available tools to gather information; do not ask for information
to be provided.

## Guidelines

- Only use labels that are from the list of available labels.
- You can choose multiple labels to apply.
- When generating shell commands, you **MUST NOT** use command
substitution with `$(...)`, `<(...)`, or `>(...)`. This is a
security measure to prevent unintended command execution.

## Input Data

**Available Labels** (comma-separated):
```
${{ env.AVAILABLE_LABELS }}
```

**Issue Title**:
```
${{ env.ISSUE_TITLE }}
```

**Issue Body**:
```
${{ env.ISSUE_BODY }}
```

**Output File Path**:
```
${{ env.GITHUB_ENV }}
```

## Steps

1. Review the issue title, issue body, and available labels
provided above.

2. Based on the issue title and issue body, classify the issue
and choose all appropriate labels from the list of available
labels.

3. Output the selected labels as a comma-separated list to the
file at the path specified in "Output File Path" using the
following format:
```
SELECTED_LABELS=label1,label2,label3
```

- name: Apply labels
uses: actions/github-script@v7
with:
script: |
const selectedLabels = process.env.SELECTED_LABELS;
if (!selectedLabels || selectedLabels.trim() === '') {
console.log('No labels selected by AI');
return;
}

const labels = selectedLabels.split(',')
.map(l => l.trim())
.filter(l => l.length > 0);

if (labels.length > 0) {
console.log(`Applying labels: ${labels.join(', ')}`);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: labels
});
} else {
console.log('No valid labels to apply');
}

triage-unlabeled-issues:
name: Triage Unlabeled Issues
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Get available labels
id: get-labels
uses: actions/github-script@v7
with:
script: |
const labels = await github.rest.issues.listLabelsForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 100
});
const labelNames = labels.data.map(label => label.name);
return labelNames.join(', ');

- name: Find and triage unlabeled issues
uses: actions/github-script@v7
with:
script: |
const availableLabels = ${{ steps.get-labels.outputs.result }};

// Get all open issues
const issues = await github.paginate(
github.rest.issues.listForRepo,
{
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100
}
);

console.log(`Found ${issues.length} open issues`);

// Filter issues without labels
const unlabeledIssues = issues.filter(issue =>
!issue.pull_request && issue.labels.length === 0
);

console.log(
`Found ${unlabeledIssues.length} unlabeled issues`
);

if (unlabeledIssues.length === 0) {
console.log('No unlabeled issues to process');
return;
}

// For manual dispatch, we'll log that AI triage is not yet
// implemented for batch processing
console.log(
'Note: Batch AI triage for unlabeled issues requires ' +
'manual triggering of individual issue workflows or ' +
'implementing batch AI processing in the future.'
);

for (const issue of unlabeledIssues) {
console.log(
`Issue #${issue.number}: "${issue.title}" needs triage`
);
}
1 change: 1 addition & 0 deletions .github/workflows/sync-workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
^.editorconfig
^.github/workflows/code-quality.yml
^.github/workflows/regenerate-readme.yml
^.github/workflows/issue-triage.yml
TARGET_REPOS: |
wp-cli/admin-command
wp-cli/cache-command
Expand Down