Skip to content

Commit fbe774b

Browse files
Merge branch 'main' into leighleighleigh/ulp-timer
2 parents 7c50046 + 94c00fe commit fbe774b

34 files changed

+1649
-919
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
const INACTIVITY_DAYS = 45;
2+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
3+
const REMINDER_MARKER = "<!-- assignee-reminder -->";
4+
const COMMENT_TEMPLATE = `${REMINDER_MARKER}
5+
Dear {mentions},
6+
7+
This is a gentle reminder that it has been {days} days since the last update on this issue.
8+
Please review the issue and provide any updates or progress in the comments.
9+
10+
Thank you.`;
11+
12+
async function run({ github, context, core, dryRun }) {
13+
const owner = context.repo.owner;
14+
const repo = context.repo.repo;
15+
const now = Date.now();
16+
const inactivityMs = INACTIVITY_DAYS * MS_PER_DAY;
17+
18+
let totalChecked = 0;
19+
let totalReminders = 0;
20+
21+
core.info(`Starting assignee-reminder (dryRun=${dryRun})`);
22+
core.info(`Inactivity threshold: ${INACTIVITY_DAYS} days`);
23+
24+
for await (const { data: issues } of github.paginate.iterator(
25+
github.rest.issues.listForRepo,
26+
{
27+
owner,
28+
repo,
29+
state: "open",
30+
assignee: "*",
31+
per_page: 100,
32+
},
33+
)) {
34+
for (const issue of issues) {
35+
if (issue.pull_request) continue;
36+
37+
totalChecked++;
38+
39+
const reminded = await processIssue({
40+
github,
41+
owner,
42+
repo,
43+
issue,
44+
now,
45+
inactivityMs,
46+
dryRun,
47+
core,
48+
});
49+
50+
if (reminded) totalReminders++;
51+
}
52+
}
53+
54+
core.info("--- Summary ---");
55+
core.info(`Total issues checked: ${totalChecked}`);
56+
core.info(`Total reminders sent: ${totalReminders}`);
57+
}
58+
59+
async function processIssue({
60+
github,
61+
owner,
62+
repo,
63+
issue,
64+
now,
65+
inactivityMs,
66+
dryRun,
67+
core,
68+
}) {
69+
const assignees = issue.assignees.map((a) => a.login);
70+
const assigneeSet = new Set(assignees.map((a) => a.toLowerCase()));
71+
72+
const activityByAssignee = {};
73+
let lastReminderAt = 0;
74+
75+
for await (const { data: comments } of github.paginate.iterator(
76+
github.rest.issues.listComments,
77+
{ owner, repo, issue_number: issue.number, per_page: 100 },
78+
)) {
79+
for (const comment of comments) {
80+
if (comment.body?.includes(REMINDER_MARKER)) {
81+
const t = new Date(comment.created_at).getTime();
82+
lastReminderAt = Math.max(lastReminderAt, t);
83+
continue;
84+
}
85+
86+
const login = comment.user?.login?.toLowerCase();
87+
if (login && assigneeSet.has(login)) {
88+
const t = new Date(comment.created_at).getTime();
89+
activityByAssignee[login] = Math.max(activityByAssignee[login] || 0, t);
90+
}
91+
}
92+
}
93+
94+
for await (const { data: events } of github.paginate.iterator(
95+
github.rest.issues.listEvents,
96+
{ owner, repo, issue_number: issue.number, per_page: 100 },
97+
)) {
98+
for (const event of events) {
99+
if (event.event === "assigned" && event.assignee) {
100+
const login = event.assignee.login.toLowerCase();
101+
if (assigneeSet.has(login)) {
102+
const t = new Date(event.created_at).getTime();
103+
activityByAssignee[login] = Math.max(activityByAssignee[login] || 0, t);
104+
}
105+
}
106+
}
107+
}
108+
109+
const issueCreatedAt = new Date(issue.created_at).getTime();
110+
const lastAssigneeActivity = Math.max(
111+
...Object.values(activityByAssignee),
112+
issueCreatedAt,
113+
);
114+
115+
if (now - lastAssigneeActivity < inactivityMs) return false;
116+
117+
if (lastReminderAt > lastAssigneeActivity && now - lastReminderAt < inactivityMs) {
118+
return false;
119+
}
120+
121+
const mentions = assignees.map((a) => `@${a}`).join(", ");
122+
const days = Math.floor((now - lastAssigneeActivity) / MS_PER_DAY);
123+
124+
if (dryRun) {
125+
core.info(
126+
`[DRY RUN] #${issue.number}: Would remind ${mentions} (inactive for ${days} days)`,
127+
);
128+
return true;
129+
}
130+
131+
const body = COMMENT_TEMPLATE
132+
.replace("{mentions}", mentions)
133+
.replace("{days}", days);
134+
135+
await github.rest.issues.createComment({
136+
owner,
137+
repo,
138+
issue_number: issue.number,
139+
body,
140+
});
141+
142+
core.info(`#${issue.number}: Reminder sent to ${mentions} (inactive for ${days} days)`);
143+
return true;
144+
}
145+
146+
module.exports = { run };
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
const BUG_JURY_LABEL = "Bug Jury";
2+
const TRACKER_LABEL = "backlog-bot-do-not-remove";
3+
4+
async function run({ github, context, core, batchSize, dryRun }) {
5+
const owner = context.repo.owner;
6+
const repo = context.repo.repo;
7+
8+
core.info(`Starting bug-jury-labeler (dryRun=${dryRun}, batchSize=${batchSize})`);
9+
10+
const allIssues = [];
11+
for await (const { data: issues } of github.paginate.iterator(
12+
github.rest.issues.listForRepo,
13+
{
14+
owner,
15+
repo,
16+
state: "open",
17+
assignee: "none",
18+
sort: "created",
19+
direction: "asc",
20+
per_page: 100,
21+
},
22+
)) {
23+
for (const issue of issues) {
24+
if (issue.pull_request) continue;
25+
allIssues.push(issue);
26+
}
27+
}
28+
29+
core.info(`Total open unassigned issues: ${allIssues.length}`);
30+
31+
if (allIssues.length === 0) {
32+
core.info("No unassigned issues found. Nothing to do.");
33+
return;
34+
}
35+
36+
const alreadyProcessed = [];
37+
let candidates = [];
38+
39+
for (const issue of allIssues) {
40+
if (issue.labels.some((l) => l.name === TRACKER_LABEL)) {
41+
alreadyProcessed.push(issue);
42+
} else {
43+
candidates.push(issue);
44+
}
45+
}
46+
47+
core.info(`Already processed (have "${TRACKER_LABEL}"): ${alreadyProcessed.length}`);
48+
core.info(`Candidates (no tracker label): ${candidates.length}`);
49+
50+
let cycleRestarted = false;
51+
52+
if (candidates.length === 0 && alreadyProcessed.length > 0) {
53+
core.info("All unassigned issues have been processed. Restarting cycle.");
54+
cycleRestarted = true;
55+
56+
for (const issue of alreadyProcessed) {
57+
if (dryRun) {
58+
core.info(
59+
`[DRY RUN] #${issue.number}: Would remove "${TRACKER_LABEL}" label (cycle restart)`,
60+
);
61+
} else {
62+
await github.rest.issues.removeLabel({
63+
owner,
64+
repo,
65+
issue_number: issue.number,
66+
name: TRACKER_LABEL,
67+
});
68+
core.info(`#${issue.number}: Removed "${TRACKER_LABEL}" label (cycle restart)`);
69+
}
70+
}
71+
72+
candidates = alreadyProcessed;
73+
}
74+
75+
const batch = candidates.slice(0, batchSize);
76+
77+
if (batch.length === 0) {
78+
core.info("No issues to label.");
79+
return;
80+
}
81+
82+
let labeled = 0;
83+
for (const issue of batch) {
84+
if (dryRun) {
85+
core.info(
86+
`[DRY RUN] #${issue.number}: Would add "${BUG_JURY_LABEL}" + "${TRACKER_LABEL}" labels`,
87+
);
88+
} else {
89+
await github.rest.issues.addLabels({
90+
owner,
91+
repo,
92+
issue_number: issue.number,
93+
labels: [BUG_JURY_LABEL, TRACKER_LABEL],
94+
});
95+
core.info(`#${issue.number}: Added "${BUG_JURY_LABEL}" + "${TRACKER_LABEL}" labels`);
96+
}
97+
labeled++;
98+
}
99+
100+
core.info("--- Summary ---");
101+
core.info(`Total unassigned issues: ${allIssues.length}`);
102+
core.info(`Already processed: ${alreadyProcessed.length}`);
103+
core.info(`Newly labeled: ${labeled}`);
104+
core.info(`Cycle restarted: ${cycleRestarted}`);
105+
core.info(`Remaining unlabeled after this run: ${candidates.length - labeled}`);
106+
}
107+
108+
module.exports = { run };
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Assignee inactivity reminder
2+
3+
on:
4+
schedule:
5+
- cron: "0 2 * * *" # every day at 02:00 UTC
6+
workflow_dispatch:
7+
inputs:
8+
dry_run:
9+
description: "Dry run (log only, do not post comments)"
10+
required: false
11+
type: boolean
12+
default: true
13+
14+
permissions:
15+
issues: write
16+
contents: read
17+
18+
jobs:
19+
reminder:
20+
name: Check assigned issues
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v6
24+
25+
- uses: actions/github-script@v8
26+
env:
27+
DRY_RUN: ${{ github.event_name == 'schedule' && 'false' || inputs.dry_run }}
28+
with:
29+
script: |
30+
const { run } = require('./.github/scripts/assignee-reminder.js');
31+
const dryRun = process.env.DRY_RUN !== 'false';
32+
await run({ github, context, core, dryRun });
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Bug Jury labeler
2+
3+
on:
4+
schedule:
5+
- cron: "0 2 * * 1" # Every Monday at 02:00 UTC
6+
workflow_dispatch:
7+
inputs:
8+
dry_run:
9+
description: "Dry run (log only, do not label)"
10+
required: false
11+
type: boolean
12+
default: true
13+
batch_size:
14+
description: "Number of issues to label"
15+
required: false
16+
type: number
17+
default: 10
18+
19+
permissions:
20+
issues: write
21+
contents: read
22+
23+
jobs:
24+
label:
25+
name: Label unassigned issues for Bug Jury
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v6
29+
30+
- uses: actions/github-script@v8
31+
env:
32+
DRY_RUN: ${{ github.event_name == 'schedule' && 'false' || inputs.dry_run }}
33+
BATCH_SIZE: ${{ inputs.batch_size || '10' }}
34+
with:
35+
script: |
36+
const { run } = require('./.github/scripts/bug-jury-labeler.js');
37+
const dryRun = process.env.DRY_RUN !== 'false';
38+
const batchSize = parseInt(process.env.BATCH_SIZE, 10) || 10;
39+
await run({ github, context, core, batchSize, dryRun });

esp-hal-procmacros/src/doc_replace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl Parse for Replacements {
181181
let branches = conditions
182182
.into_iter()
183183
.map(Some)
184-
.zip(lit_strs.into_iter())
184+
.zip(lit_strs)
185185
.collect::<Vec<_>>();
186186
add_inline_replacement(&arg.placeholder, branches);
187187
}

esp-hal/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737
- C5: Add AES support (#4983)
3838
- C5: Add USB Serial/JTAG support (#5008)
3939
- C5: Add PARL_IO support (#5042)
40-
- `esp_hal::interrupt::RunLevel` (#4996, #5108)
40+
- `esp_hal::interrupt::{RunLevel, ElevatedRunLevel}` (#4996, #5108, #5146)
4141
- MAC addresses for radio interfaces getter: `esp_hal::efuse::interface_mac_address(InterfaceMacAddress::)`. (#5002)
4242
- `ShaXContext` objects now implement `digest::core_api::BlockSizeUser` (and thus they can be used with the `hmac` crate) (#5050)
4343
- C5: Add ASSIST_DEBUG support (#5058)

esp-hal/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a
8585
| PARL_IO | | | | ⚒️ | ⚒️ | ⚒️ | | |
8686
| PCNT | ⚒️ | | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ |
8787
| PHY | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ |
88-
| PSRAM | ⚒️ | | | | | | ⚒️ | ⚒️ |
88+
| PSRAM | ⚒️ | | | [❌][5141] [^1] | | | ⚒️ | ⚒️ |
8989
| RGB display | ⚒️ | | | | | | ❌ | ⚒️ |
9090
| RMT | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ |
9191
| RNG | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ |
@@ -114,6 +114,10 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a
114114
* ❌: Not supported
115115
* ⚒️: Partial support
116116
* ✔️: Supported
117+
118+
[^1]: This cell is clickable and will open the peripheral's issue on GitHub
119+
120+
[5141]: https://github.com/esp-rs/esp-hal/issues/5141
117121
<!-- end chip support table -->
118122

119123
## `unstable` feature

0 commit comments

Comments
 (0)