diff --git a/.github/workflows/update-health-dashboard.yml b/.github/workflows/update-health-dashboard.yml new file mode 100644 index 000000000000..b627dd77a82c --- /dev/null +++ b/.github/workflows/update-health-dashboard.yml @@ -0,0 +1,138 @@ +name: Update GitHub Health Dashboard + +# Trigger configuration +on: + # Run daily at midnight UTC + schedule: + - cron: '0 0 * * *' + + # Allow manual triggering from GitHub UI + workflow_dispatch: + +# Define permissions for the GITHUB_TOKEN +permissions: + # Required to read repository contents + contents: read + # Required to read issues and PRs + issues: read + pull-requests: read + # Required to update discussions + discussions: write + +jobs: + update-dashboard: + name: 'Update Health Dashboard' + runs-on: ubuntu-latest + + steps: + - name: 'Update Dashboard' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const discussionNumber = 5285; + + console.log(`Fetching health metrics for ${owner}/${repo}`); + + // Fetch open issues + const { data: openIssues } = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + per_page: 100 + }); + + // Filter out PRs (issues API includes PRs) + const issues = openIssues.filter(issue => !issue.pull_request); + const totalOpenIssues = issues.length; + + // Count issues without labels (recent ones) + const issuesWithoutLabels = issues.filter(issue => issue.labels.length === 0); + + // Count issues by specific labels + const labelCounts = {}; + const targetLabels = ['P0', 'P1', 'Compaction', 'Windows', 'Provider', 'MCP/Extensions', 'Linux']; + + targetLabels.forEach(label => { + labelCounts[label] = issues.filter(issue => + issue.labels.some(l => l.name === label) + ).length; + }); + + // Fetch open PRs + const { data: openPRs } = await github.rest.pulls.list({ + owner, + repo, + state: 'open', + per_page: 100 + }); + + const totalOpenPRs = openPRs.length; + + // Format the dashboard markdown + const timestamp = new Date().toUTCString(); + const formattedDate = new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + + const dashboardBody = '# GitHub Health\n\n' + + '**Updated:** ' + formattedDate + '\n\n' + + '## Issues\n\n' + + '**Open:** ' + totalOpenIssues + '\n\n' + + '**No labels:** ' + issuesWithoutLabels.length + ' (these are recent)\n\n' + + '### Label Breakdown\n\n' + + '| Label | Count |\n' + + '|-------|-------|\n' + + '| [P0](https://github.com/' + owner + '/' + repo + '/labels/P0) | ' + (labelCounts['P0'] || 0) + ' |\n' + + '| [P1](https://github.com/' + owner + '/' + repo + '/labels/P1) | ' + (labelCounts['P1'] || 0) + ' |\n' + + '| [Compaction](https://github.com/' + owner + '/' + repo + '/labels/Compaction) | ' + (labelCounts['Compaction'] || 0) + ' |\n' + + '| [Windows](https://github.com/' + owner + '/' + repo + '/labels/Windows) | ' + (labelCounts['Windows'] || 0) + ' |\n' + + '| [Provider](https://github.com/' + owner + '/' + repo + '/labels/Provider) | ' + (labelCounts['Provider'] || 0) + ' |\n' + + '| [MCP/Extensions](https://github.com/' + owner + '/' + repo + '/labels/MCP%2FExtensions) | ' + (labelCounts['MCP/Extensions'] || 0) + ' |\n' + + '| [Linux](https://github.com/' + owner + '/' + repo + '/labels/Linux) | ' + (labelCounts['Linux'] || 0) + ' |\n\n' + + '## Open PRs\n\n' + + '**Total:** ' + totalOpenPRs + '\n\n' + + '---\n\n' + + '*Last updated: ' + timestamp + '*\n' + + '*This dashboard is automatically updated daily by [GitHub Actions](https://github.com/' + owner + '/' + repo + '/actions/workflows/update-health-dashboard.yml)*'; + + console.log('Dashboard generated, updating discussion...'); + + // Get the discussion node ID using GraphQL + const discussionQuery = ` + query GetDiscussion($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + discussion(number: $number) { + id + } + } + } + `; + + const discussionResult = await github.graphql(discussionQuery, { + owner, + repo, + number: discussionNumber + }); + + const discussionId = discussionResult.repository.discussion.id; + console.log(`Found discussion ID: ${discussionId}`); + + // Update the discussion using GraphQL mutation + const updateMutation = ` + mutation UpdateDiscussion($discussionId: ID!, $body: String!) { + updateDiscussion(input: {discussionId: $discussionId, body: $body}) { + discussion { + id + number + } + } + } + `; + + await github.graphql(updateMutation, { + discussionId, + body: dashboardBody + }); + + console.log(`Successfully updated discussion #${discussionNumber}`);