daily-issues-recap.yml 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. name: Daily Issues Recap
  2. on:
  3. schedule:
  4. # Run at 6 PM EST (23:00 UTC, or 22:00 UTC during daylight saving)
  5. - cron: "0 23 * * *"
  6. workflow_dispatch: # Allow manual trigger for testing
  7. jobs:
  8. daily-recap:
  9. runs-on: blacksmith-4vcpu-ubuntu-2404
  10. permissions:
  11. contents: read
  12. issues: read
  13. steps:
  14. - name: Checkout repository
  15. uses: actions/checkout@v4
  16. with:
  17. fetch-depth: 1
  18. - uses: ./.github/actions/setup-bun
  19. - name: Install opencode
  20. run: curl -fsSL https://opencode.ai/install | bash
  21. - name: Generate daily issues recap
  22. id: recap
  23. env:
  24. OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
  25. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  26. OPENCODE_PERMISSION: |
  27. {
  28. "bash": {
  29. "*": "deny",
  30. "gh issue*": "allow",
  31. "gh search*": "allow"
  32. },
  33. "webfetch": "deny",
  34. "edit": "deny",
  35. "write": "deny"
  36. }
  37. run: |
  38. # Get today's date range
  39. TODAY=$(date -u +%Y-%m-%d)
  40. opencode run -m opencode/claude-sonnet-4-5 "Generate a daily issues recap for the OpenCode repository.
  41. TODAY'S DATE: ${TODAY}
  42. STEP 1: Gather today's issues
  43. Search for all issues created today (${TODAY}) using:
  44. gh issue list --repo ${{ github.repository }} --state all --search \"created:${TODAY}\" --json number,title,body,labels,state,comments,createdAt,author --limit 500
  45. STEP 2: Analyze and categorize
  46. For each issue created today, categorize it:
  47. **Severity Assessment:**
  48. - CRITICAL: Crashes, data loss, security issues, blocks major functionality
  49. - HIGH: Significant bugs affecting many users, important features broken
  50. - MEDIUM: Bugs with workarounds, minor features broken
  51. - LOW: Minor issues, cosmetic, nice-to-haves
  52. **Activity Assessment:**
  53. - Note issues with high comment counts or engagement
  54. - Note issues from repeat reporters (check if author has filed before)
  55. STEP 3: Cross-reference with existing issues
  56. For issues that seem like feature requests or recurring bugs:
  57. - Search for similar older issues to identify patterns
  58. - Note if this is a frequently requested feature
  59. - Identify any issues that are duplicates of long-standing requests
  60. STEP 4: Generate the recap
  61. Create a structured recap with these sections:
  62. ===DISCORD_START===
  63. **Daily Issues Recap - ${TODAY}**
  64. **Summary Stats**
  65. - Total issues opened today: [count]
  66. - By category: [bugs/features/questions]
  67. **Critical/High Priority Issues**
  68. [List any CRITICAL or HIGH severity issues with brief descriptions and issue numbers]
  69. **Most Active/Discussed**
  70. [Issues with significant engagement or from active community members]
  71. **Trending Topics**
  72. [Patterns noticed - e.g., 'Multiple reports about X', 'Continued interest in Y feature']
  73. **Duplicates & Related**
  74. [Issues that relate to existing open issues]
  75. ===DISCORD_END===
  76. STEP 5: Format for Discord
  77. Format the recap as a Discord-compatible message:
  78. - Use Discord markdown (**, __, etc.)
  79. - BE EXTREMELY CONCISE - this is an EOD summary, not a detailed report
  80. - Use hyperlinked issue numbers with suppressed embeds: [#1234](<https://github.com/${{ github.repository }}/issues/1234>)
  81. - Group related issues on single lines where possible
  82. - Add emoji sparingly for critical items only
  83. - HARD LIMIT: Keep under 1800 characters total
  84. - Skip sections that have nothing notable (e.g., if no critical issues, omit that section)
  85. - Prioritize signal over completeness - only surface what matters
  86. OUTPUT: Output ONLY the content between ===DISCORD_START=== and ===DISCORD_END=== markers. Include the markers so I can extract it." > /tmp/recap_raw.txt
  87. # Extract only the Discord message between markers
  88. sed -n '/===DISCORD_START===/,/===DISCORD_END===/p' /tmp/recap_raw.txt | grep -v '===DISCORD' > /tmp/recap.txt
  89. echo "recap_file=/tmp/recap.txt" >> $GITHUB_OUTPUT
  90. - name: Post to Discord
  91. env:
  92. DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_ISSUES_WEBHOOK_URL }}
  93. run: |
  94. if [ -z "$DISCORD_WEBHOOK_URL" ]; then
  95. echo "Warning: DISCORD_ISSUES_WEBHOOK_URL secret not set, skipping Discord post"
  96. cat /tmp/recap.txt
  97. exit 0
  98. fi
  99. # Read the recap
  100. RECAP_RAW=$(cat /tmp/recap.txt)
  101. RECAP_LENGTH=${#RECAP_RAW}
  102. echo "Recap length: ${RECAP_LENGTH} chars"
  103. # Function to post a message to Discord
  104. post_to_discord() {
  105. local msg="$1"
  106. local content=$(echo "$msg" | jq -Rs '.')
  107. curl -s -H "Content-Type: application/json" \
  108. -X POST \
  109. -d "{\"content\": ${content}}" \
  110. "$DISCORD_WEBHOOK_URL"
  111. sleep 1
  112. }
  113. # If under limit, send as single message
  114. if [ "$RECAP_LENGTH" -le 1950 ]; then
  115. post_to_discord "$RECAP_RAW"
  116. else
  117. echo "Splitting into multiple messages..."
  118. remaining="$RECAP_RAW"
  119. while [ ${#remaining} -gt 0 ]; do
  120. if [ ${#remaining} -le 1950 ]; then
  121. post_to_discord "$remaining"
  122. break
  123. else
  124. chunk="${remaining:0:1900}"
  125. last_newline=$(echo "$chunk" | grep -bo $'\n' | tail -1 | cut -d: -f1)
  126. if [ -n "$last_newline" ] && [ "$last_newline" -gt 500 ]; then
  127. chunk="${remaining:0:$last_newline}"
  128. remaining="${remaining:$((last_newline+1))}"
  129. else
  130. chunk="${remaining:0:1900}"
  131. remaining="${remaining:1900}"
  132. fi
  133. post_to_discord "$chunk"
  134. fi
  135. done
  136. fi
  137. echo "Posted daily recap to Discord"