chore: collect customer issues pending triage and send them to slack #2
Workflow file for this run
This file contains 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: Collect customer issues | |
on: | |
pull_request: | |
schedule: | |
- cron: "0 15 * * *" | |
workflow_dispatch: | |
jobs: | |
collect-issues-pending-triage: | |
name: Pending Triage | |
runs-on: ubuntu-latest | |
env: | |
DAYS: 30 | |
steps: | |
- name: Retrieve and format issues | |
id: retrieve-issues | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
// List of labels to filter by | |
const requiredLabels = [ | |
"triage", | |
]; | |
// List of core developers to exclude | |
const coreDevelopers = [ | |
"mikeldking", | |
"axiomofjoy", | |
"anticorrelator", | |
"cephalization", | |
"Parker-Stafford", | |
"Jgilhuly", | |
"RogerHYang", | |
]; | |
// Access the DAYS environment variable | |
const days = parseInt(process.env.DAYS || '7', 10); | |
// Calculate the cutoff date | |
const cutoffDate = new Date(); | |
cutoffDate.setDate(cutoffDate.getDate() - days); | |
// Fetch issues created since DAYS ago | |
const issues = await github.paginate( | |
github.rest.issues.listForRepo, | |
{ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
state: "open", | |
since: cutoffDate.toISOString(), | |
per_page: 100, | |
} | |
); | |
// Check if issue has any of the required labels | |
function hasRequiredLabel(issueLabels, requiredLabels) { | |
return issueLabels.some(label => requiredLabels.includes(label.name)); | |
} | |
// Filter issues | |
const filteredIssues = issues.filter(issue => | |
!coreDevelopers.includes(issue.user.login) && | |
hasRequiredLabel(issue.labels, requiredLabels) && | |
new Date(issue.created_at) > cutoffDate && | |
!issue.pull_request | |
).sort((a, b) => b.number - a.number); | |
// Function to calculate "X days ago" from created_at date | |
function timeAgo(createdAt) { | |
const createdDate = new Date(createdAt); | |
const now = new Date(); | |
const diffInMs = now - createdDate; | |
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24)); | |
if (diffInDays === 0) { | |
return "Today"; | |
} else if (diffInDays === 1) { | |
return "Yesterday"; | |
} else { | |
return `${diffInDays} days ago`; | |
} | |
} | |
// Format the issues as a Markdown message for Slack | |
if (filteredIssues.length === 0) { | |
core.setOutput("has_issues", "false"); | |
} else { | |
core.setOutput("has_issues", "true"); | |
let message = `*🛠️ Phoenix Customer Issues Opened in the Last ${days} Day(s) Pending Triage*\n\n`; | |
// Separate issues into two lists: those with the "bug" label and those without | |
const bugIssues = filteredIssues.filter(issue => | |
issue.labels.some(label => label.name === 'bug') | |
); | |
const enhancementIssues = filteredIssues.filter(issue => | |
!issue.labels.some(label => label.name === 'bug') | |
); | |
const issueGroups = [ | |
[bugIssues, "*🐛 Bugs*\n"], | |
[enhancementIssues, "*💡 Enhancements*\n"] | |
]; | |
issueGroups.forEach(([issues, header]) => { | |
if (issues.length > 0) { | |
message += `${header}`; | |
issues.forEach((issue, i) => { | |
bugIssues.forEach((issue, i) => { | |
message += `${i + 1}. *<${issue.html_url}|#${issue.number}>:* ${issue.title}` | |
message += ` (by ${issue.user.login}, ${timeAgo(issue.created_at)}, with ${issue.comments} comments` | |
if (issue.assignees.length > 0) { | |
message += `, assigned to ${issue.assignees.map(assignee => assignee.login).sort().join(' ')}`; | |
} | |
message += `)\n`; | |
}); | |
} | |
}); | |
core.setOutput("slack_message", message); | |
} | |
- name: Send message to Slack | |
uses: slackapi/slack-github-action@v1 | |
if: steps.retrieve-issues.outputs.has_issues == 'true' | |
with: | |
payload: | | |
{ | |
"type": "mrkdwn", | |
"text": ${{ toJSON(steps.retrieve-issues.outputs.slack_message) }} | |
} | |
env: | |
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |