Skip to content

Commit fd8edf7

Browse files
authored
Merge branch 'dev' into fix/diagnostics-success-output
2 parents 52b8d35 + 7c80ac0 commit fd8edf7

383 files changed

Lines changed: 24440 additions & 3101 deletions

File tree

Some content is hidden

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

0 commit comments

Comments
 (0)