Skip to content

Collect customer issues #9

Collect customer issues

Collect customer issues #9

name: Collect customer issues
on:
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`;
}
}
// Function to get unique participants from comments on an issue, excluding the author
async function getParticipants(issueNumber, author) {
try {
const comments = await github.paginate(github.rest.issues.listComments, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
per_page: 100,
});
// Extract unique usernames of commenters, excluding the author
const uniqueParticipants = [
...new Set(comments.map(comment => comment.user.login).filter(username => username !== author))
].sort();
return uniqueParticipants;
} catch (error) {
console.error(`Error fetching comments for issue #${issueNumber}: ${error}`);
return [];
}
}
// 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)`;
message += ` Pending <https://github.com/Arize-ai/phoenix/issues?q=is%3Aissue+is%3Aopen+label%3Atriage|Triage>`;
message += `*\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*"],
[enhancementIssues, "*💡 Enhancements or Inquiries*"]
];
// Use `for...of` loop to allow async/await inside the loop
for (const [issues, header] of issueGroups) {
if (issues.length > 0) {
message += `${header}\n`;
for (const [i, issue] of issues.entries()) {
message += `${i + 1}. *<${issue.html_url}|#${issue.number}>:* ${issue.title}`;
message += ` (by <https://github.com/${issue.user.login}|${issue.user.login}>`;
message += `; ${timeAgo(issue.created_at)}`;
if (issue.comments > 0) {
message += `; ${issue.comments} comments`;
const participants = await getParticipants(issue.number, issue.user.login);
if (participants.length > 0) {
message += `; participants: `;
message += participants.map(participant => `<https://github.com/${participant}|${participant}>`).join(", ");
}
}
if (issue.assignees.length > 0) {
message += `; assigned to: `
message += issue.assignees.map(assignee => `<https://github.com/${assignee.login}|${assignee.login}>`).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 }}