Skip to content

Commit 4c66bf7

Browse files
committed
Merge upstream/main into bedrock-integration-backup
2 parents 50d9657 + ab2fb90 commit 4c66bf7

58 files changed

Lines changed: 1275 additions & 26 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/labeler.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Auto-labeling config for actions/labeler
2+
# Maps file path patterns to area/* labels for PRs
3+
# Part of #696
4+
5+
"area/core":
6+
- changed-files:
7+
- any-glob-to-any-file:
8+
- burr/core/**
9+
- burr/lifecycle/**
10+
- burr/system.py
11+
12+
"area/ui":
13+
- changed-files:
14+
- any-glob-to-any-file:
15+
- telemetry/ui/**
16+
17+
"area/storage":
18+
- changed-files:
19+
- any-glob-to-any-file:
20+
- burr/core/persistence.py
21+
- burr/integrations/persisters/**
22+
- burr/tracking/server/s3/**
23+
24+
"area/streaming":
25+
- changed-files:
26+
- any-glob-to-any-file:
27+
- burr/core/parallelism.py
28+
29+
"area/hooks":
30+
- changed-files:
31+
- any-glob-to-any-file:
32+
- burr/lifecycle/**
33+
34+
"area/tracking":
35+
- changed-files:
36+
- any-glob-to-any-file:
37+
- burr/tracking/**
38+
- burr/telemetry.py
39+
- burr/visibility/**
40+
41+
"area/integrations":
42+
- changed-files:
43+
- any-glob-to-any-file:
44+
- burr/integrations/**
45+
46+
"area/visualization":
47+
- changed-files:
48+
- any-glob-to-any-file:
49+
- burr/visibility/**
50+
51+
"area/website":
52+
- changed-files:
53+
- any-glob-to-any-file:
54+
- website/**
55+
- docs/**
56+
57+
"area/ci":
58+
- changed-files:
59+
- any-glob-to-any-file:
60+
- .github/**
61+
- scripts/**
62+
- .pre-commit-config.yaml
63+
- .rat-excludes
64+
- pyproject.toml
65+
- setup.cfg
66+
67+
"area/examples":
68+
- changed-files:
69+
- any-glob-to-any-file:
70+
- examples/**
71+
- burr/examples/**
72+
73+
"area/typing":
74+
- changed-files:
75+
- any-glob-to-any-file:
76+
- burr/core/typing.py
77+
- burr/integrations/pydantic.py

.github/workflows/auto-label.yml

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#<!--
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
#-->
19+
# Auto-labeling for new issues and PRs
20+
# Part of #690 (repo governance) and #696 (auto-labeling)
21+
#
22+
# - New issues get status/needs-triage
23+
# - PRs get area/* labels based on changed files
24+
# - PRs get pr/needs-rebase when they have merge conflicts
25+
#
26+
# Security: this workflow only uses numeric IDs from context (issue_number,
27+
# pull_request.number). No untrusted string input is interpolated.
28+
29+
name: Auto-label
30+
31+
on:
32+
issues:
33+
types: [opened]
34+
pull_request_target:
35+
types: [opened, synchronize]
36+
37+
permissions:
38+
contents: read
39+
issues: write
40+
pull-requests: write
41+
42+
jobs:
43+
triage-issues:
44+
if: github.event_name == 'issues'
45+
runs-on: ubuntu-latest
46+
steps:
47+
- name: Add status/needs-triage to new issues
48+
uses: actions/github-script@v7
49+
with:
50+
github-token: ${{ secrets.GITHUB_TOKEN }}
51+
script: |
52+
await github.rest.issues.addLabels({
53+
owner: context.repo.owner,
54+
repo: context.repo.repo,
55+
issue_number: context.issue.number,
56+
labels: ['status/needs-triage']
57+
});
58+
59+
label-prs:
60+
if: github.event_name == 'pull_request_target'
61+
runs-on: ubuntu-latest
62+
steps:
63+
- name: Apply area/* labels based on changed files
64+
uses: actions/labeler@v5
65+
with:
66+
repo-token: ${{ secrets.GITHUB_TOKEN }}
67+
configuration-path: .github/labeler.yml
68+
sync-labels: false
69+
70+
check-mergeable:
71+
if: github.event_name == 'pull_request_target'
72+
runs-on: ubuntu-latest
73+
steps:
74+
- name: Check for merge conflicts
75+
uses: actions/github-script@v7
76+
with:
77+
github-token: ${{ secrets.GITHUB_TOKEN }}
78+
script: |
79+
const LABEL = 'pr/needs-rebase';
80+
81+
// Wait for GitHub to compute mergeability
82+
await new Promise(r => setTimeout(r, 5000));
83+
84+
const { data: pr } = await github.rest.pulls.get({
85+
owner: context.repo.owner,
86+
repo: context.repo.repo,
87+
pull_number: context.payload.pull_request.number,
88+
});
89+
90+
const labels = pr.labels.map(l => l.name);
91+
const hasLabel = labels.includes(LABEL);
92+
93+
if (pr.mergeable === false && !hasLabel) {
94+
await github.rest.issues.addLabels({
95+
owner: context.repo.owner,
96+
repo: context.repo.repo,
97+
issue_number: pr.number,
98+
labels: [LABEL],
99+
});
100+
console.log(`#${pr.number}: added ${LABEL}`);
101+
} else if (pr.mergeable === true && hasLabel) {
102+
await github.rest.issues.removeLabel({
103+
owner: context.repo.owner,
104+
repo: context.repo.repo,
105+
issue_number: pr.number,
106+
name: LABEL,
107+
});
108+
console.log(`#${pr.number}: removed ${LABEL}`);
109+
} else {
110+
console.log(`#${pr.number}: no change (mergeable=${pr.mergeable}, hasLabel=${hasLabel})`);
111+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#<!--
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
#-->
19+
# Stale issue assignment check
20+
# Part of #690 (repo governance) and #709 (assignment policy)
21+
#
22+
# Policy (documented in CONTRIBUTING.rst):
23+
# - 14 days without activity on an assigned issue -> comment asking for update
24+
# - 21 days total -> unassign + add help wanted
25+
# - Issues with lifecycle/frozen are exempt
26+
#
27+
# Security: only uses numeric IDs and login names from the GitHub API.
28+
# No untrusted string input is interpolated into shell commands.
29+
30+
name: Stale Assignments
31+
32+
on:
33+
schedule:
34+
- cron: "0 9 * * 3" # Every Wednesday at 09:00 UTC
35+
workflow_dispatch:
36+
37+
permissions:
38+
issues: write
39+
40+
jobs:
41+
check-assignments:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- name: Check for stale assignments
45+
uses: actions/github-script@v7
46+
with:
47+
github-token: ${{ secrets.GITHUB_TOKEN }}
48+
script: |
49+
const WARN_DAYS = 14;
50+
const UNASSIGN_DAYS = 21;
51+
const SKIP_LABELS = ['lifecycle/frozen'];
52+
const now = new Date();
53+
54+
let page = 1;
55+
let allIssues = [];
56+
57+
// Paginate through all open issues
58+
while (true) {
59+
const { data: issues } = await github.rest.issues.listForRepo({
60+
owner: context.repo.owner,
61+
repo: context.repo.repo,
62+
state: 'open',
63+
per_page: 100,
64+
page: page,
65+
});
66+
if (issues.length === 0) break;
67+
allIssues = allIssues.concat(issues);
68+
page++;
69+
}
70+
71+
for (const issue of allIssues) {
72+
// Skip PRs (they show up in the issues API)
73+
if (issue.pull_request) continue;
74+
75+
// Skip unassigned
76+
if (!issue.assignees || issue.assignees.length === 0) continue;
77+
78+
const labels = issue.labels.map(l => l.name);
79+
80+
// Skip protected issues
81+
if (SKIP_LABELS.some(skip => labels.includes(skip))) {
82+
console.log(`#${issue.number}: skipped (protected)`);
83+
continue;
84+
}
85+
86+
const updatedAt = new Date(issue.updated_at);
87+
const daysSinceUpdate = Math.floor((now - updatedAt) / (1000 * 60 * 60 * 24));
88+
const assigneeLogins = issue.assignees.map(a => a.login);
89+
90+
// 21+ days -> unassign and add help wanted
91+
if (daysSinceUpdate >= UNASSIGN_DAYS) {
92+
const names = assigneeLogins.map(l => `@${l}`).join(', ');
93+
94+
console.log(`#${issue.number}: unassigning ${names} (${daysSinceUpdate} days)`);
95+
96+
const unassignMsg = [
97+
`Removing assignment from ${names} after ${daysSinceUpdate} days of inactivity`,
98+
'(per the [assignment policy](../blob/main/CONTRIBUTING.rst#issue-assignment-policy)).',
99+
'',
100+
'Feel free to comment if you want to pick this back up or if someone else wants to take it.',
101+
].join('\n');
102+
103+
await github.rest.issues.createComment({
104+
owner: context.repo.owner,
105+
repo: context.repo.repo,
106+
issue_number: issue.number,
107+
body: unassignMsg,
108+
});
109+
110+
await github.rest.issues.removeAssignees({
111+
owner: context.repo.owner,
112+
repo: context.repo.repo,
113+
issue_number: issue.number,
114+
assignees: assigneeLogins,
115+
});
116+
117+
if (!labels.includes('help wanted')) {
118+
await github.rest.issues.addLabels({
119+
owner: context.repo.owner,
120+
repo: context.repo.repo,
121+
issue_number: issue.number,
122+
labels: ['help wanted'],
123+
});
124+
}
125+
126+
continue;
127+
}
128+
129+
// 14+ days -> warn
130+
if (daysSinceUpdate >= WARN_DAYS) {
131+
// Check if we already warned (avoid spamming)
132+
const { data: comments } = await github.rest.issues.listComments({
133+
owner: context.repo.owner,
134+
repo: context.repo.repo,
135+
issue_number: issue.number,
136+
per_page: 5,
137+
});
138+
139+
const recentWarn = comments.some(c =>
140+
c.user.login === 'github-actions[bot]' &&
141+
c.body.includes('assignment policy') &&
142+
(now - new Date(c.created_at)) / (1000 * 60 * 60 * 24) < 10
143+
);
144+
145+
if (recentWarn) {
146+
console.log(`#${issue.number}: skipped (already warned recently)`);
147+
continue;
148+
}
149+
150+
const names = assigneeLogins.map(l => `@${l}`).join(', ');
151+
152+
console.log(`#${issue.number}: warning ${names} (${daysSinceUpdate} days)`);
153+
154+
const warnMsg = [
155+
`${names}, this issue has been inactive for ${daysSinceUpdate} days.`,
156+
'Are you still working on it? Drop a comment to let us know.',
157+
'',
158+
'If there is no update within 7 days, the assignment will be removed',
159+
'so someone else can pick it up',
160+
'(per the [assignment policy](../blob/main/CONTRIBUTING.rst#issue-assignment-policy)).',
161+
].join('\n');
162+
163+
await github.rest.issues.createComment({
164+
owner: context.repo.owner,
165+
repo: context.repo.repo,
166+
issue_number: issue.number,
167+
body: warnMsg,
168+
});
169+
}
170+
}

0 commit comments

Comments
 (0)