Skip to content

Commit 47660f3

Browse files
committed
Merge branch 'main' of https://github.com/Azure/azure-sdk-for-python into switch_to_protocol
* 'main' of https://github.com/Azure/azure-sdk-for-python: release_iseus_status_auto_reply (Azure#20441) Clarify LogsQueryClient query parameter description (Azure#20467) Sync eng/common directory with azure-sdk-tools for PR 1953 (Azure#20466) Skip eng common workflow enforcer for private repos (Azure#20462) remove iter_text and iter_lines (Azure#20460)
2 parents 0304a0c + 22d59ed commit 47660f3

File tree

12 files changed

+357
-70
lines changed

12 files changed

+357
-70
lines changed

eng/common/TestResources/New-TestResources.ps1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ try {
289289
$AzureTestPrincipal
290290
} else {
291291
Log "TestApplicationId was not specified; creating a new service principal in subscription '$SubscriptionId'"
292-
$global:AzureTestPrincipal = New-AzADServicePrincipal -Role Owner -Scope "/subscriptions/$SubscriptionId" -DisplayName "test-resources-$($baseName).microsoft.com"
292+
$suffix = (New-Guid).ToString('n').Substring(0, 4)
293+
$global:AzureTestPrincipal = New-AzADServicePrincipal -Role Owner -Scope "/subscriptions/$SubscriptionId" -DisplayName "test-resources-$($baseName)$suffix.microsoft.com"
293294
$global:AzureTestSubscription = $SubscriptionId
294295

295296
Log "Created service principal '$($AzureTestPrincipal.ApplicationId)'"

eng/common/pipelines/templates/steps/eng-common-workflow-enforcer.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,4 @@ steps:
2121
}
2222
}
2323
displayName: Prevent changes to eng/common outside of azure-sdk-tools repo
24-
condition: and(succeeded(), ne(variables['Skip.EngCommonWorkflowEnforcer'], 'true'))
24+
condition: and(succeeded(), ne(variables['Skip.EngCommonWorkflowEnforcer'], 'true'), not(endsWith(variables['Build.Repository.Name'], '-pr')))

scripts/release_issue_status/main.py

Lines changed: 88 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
from datetime import date, datetime
66
import subprocess as sp
77
from azure.storage.blob import BlobClient
8+
import reply_generator as rg
9+
from update_issue_body import update_issue_body, find_readme_link
10+
import traceback
811

912
_NULL = ' '
1013
_FILE_OUT = 'release_issue_status.csv'
14+
_FILE_OUT_PYTHON = 'release_python_status.md'
1115
_PYTHON_SDK_ADMINISTRATORS = {'msyyc', 'RAY-316', 'BigCat20196'}
1216

13-
1417
def my_print(cmd):
1518
print('==' + cmd + ' ==\n')
1619

@@ -20,6 +23,20 @@ def print_check(cmd):
2023
sp.check_call(cmd, shell=True)
2124

2225

26+
def output_python_md(issue_status_python):
27+
with open(_FILE_OUT_PYTHON, 'w') as file_out:
28+
file_out.write('| issue | author | package | assignee | bot advice | created date of issue | delay from created date |\n')
29+
file_out.write('| ------ | ------ | ------ | ------ | ------ | ------ | :-----: |\n')
30+
file_out.writelines([item.output_python() for item in sorted(issue_status_python, key=_key_select)])
31+
32+
33+
def output_csv(issue_status):
34+
with open(_FILE_OUT, 'w') as file_out:
35+
file_out.write('language,issue,author,package,created date,delay from created date,latest update time,'
36+
'delay from latest update,status,bot advice\n')
37+
file_out.writelines([item.output() for item in sorted(issue_status, key=_key_select)])
38+
39+
2340
class IssueStatus:
2441
link = _NULL
2542
author = _NULL
@@ -36,6 +53,7 @@ class IssueStatus:
3653
whether_author_comment = True
3754
issue_object = _NULL
3855
labels = _NULL
56+
assignee = _NULL
3957

4058
def output(self):
4159
return '{},{},{},{},{},{},{},{},{},{}\n'.format(self.language, self.link, self.author,
@@ -46,6 +64,13 @@ def output(self):
4664
self.delay_from_latest_update,
4765
self.status, self.bot_advice)
4866

67+
68+
def output_python(self):
69+
return '| [#{}]({}) | {} | {} | {} | {} | {} | {} |\n'.format(self.link.split('/')[-1], self.link, self.author,
70+
self.package, self.assignee, self.bot_advice,
71+
str(date.fromtimestamp(self.create_date)),
72+
self.delay_from_create_date)
73+
4974

5075
def _extract(str_list, key_word):
5176
for item in str_list:
@@ -86,8 +111,7 @@ def _extract_author_latest_comment(comments):
86111
def _whether_author_comment(comments):
87112
q = set(comment.user.login for comment in comments)
88113
diff = q.difference(_PYTHON_SDK_ADMINISTRATORS)
89-
90-
return len(diff) > 0
114+
return len(diff) > 0
91115

92116
def _latest_comment_time(comments, delay_from_create_date):
93117
q = [(comment.updated_at.timestamp(), comment.user.login)
@@ -96,13 +120,50 @@ def _latest_comment_time(comments, delay_from_create_date):
96120

97121
return delay_from_create_date if not q else int((time.time() - q[-1][0]) / 3600 / 24)
98122

123+
124+
def auto_reply(item, sdk_repo, rest_repo, duplicated_issue):
125+
print("==========new issue number: {}".format(item.issue_object.number))
126+
if 'auto-link' not in item.labels:
127+
try:
128+
package_name, readme_link = update_issue_body(sdk_repo, rest_repo, item.issue_object.number)
129+
print("pkname, readme", package_name, readme_link)
130+
item.package = package_name
131+
key = ('Python', item.package)
132+
duplicated_issue[key] = duplicated_issue.get(key, 0) + 1
133+
except Exception as e:
134+
item.bot_advice = 'failed to modify the body of the new issue. Please modify manually'
135+
item.labels.append('attention')
136+
print(e)
137+
raise
138+
item.labels.append('auto-link')
139+
item.issue_object.set_labels(*item.labels)
140+
else:
141+
try:
142+
readme_link = find_readme_link(sdk_repo, item.issue_object.number)
143+
except Exception as e:
144+
print('Issue: {} updates body failed'.format(item.issue_object.number))
145+
item.bot_advice = 'failed to find Readme link, Please check !!'
146+
item.labels.append('attention')
147+
raise
148+
try:
149+
reply = rg.begin_reply_generate(item=item, rest_repo=rest_repo, readme_link=readme_link)
150+
except Exception as e:
151+
item.bot_advice = 'auto reply failed, Please intervene manually !!'
152+
print('Error from auto reply ========================')
153+
print('Issue:{}'.format(item.issue_object.number))
154+
print(traceback.format_exc())
155+
print('==============================================')
156+
157+
99158
def main():
100159
# get latest issue status
101160
g = Github(os.getenv('TOKEN')) # please fill user_token
102-
repo = g.get_repo('Azure/sdk-release-request')
103-
label1 = repo.get_label('ManagementPlane')
104-
open_issues = repo.get_issues(state='open', labels=[label1])
161+
sdk_repo = g.get_repo('Azure/sdk-release-request')
162+
rest_repo = g.get_repo('Azure/azure-rest-api-specs')
163+
label1 = sdk_repo.get_label('ManagementPlane')
164+
open_issues = sdk_repo.get_issues(state='open', labels=[label1])
105165
issue_status = []
166+
issue_status_python = []
106167
duplicated_issue = dict()
107168
start_time = time.time()
108169
for item in open_issues:
@@ -124,7 +185,9 @@ def main():
124185
issue.issue_object = item
125186
issue.labels = [label.name for label in item.labels]
126187
issue.days_from_latest_commit = _latest_comment_time(item.get_comments(), issue.delay_from_create_date)
127-
188+
if item.assignee:
189+
issue.assignee = item.assignee.login
190+
128191
issue_status.append(issue)
129192
key = (issue.language, issue.package)
130193
duplicated_issue[key] = duplicated_issue.get(key, 0) + 1
@@ -135,18 +198,23 @@ def main():
135198
# rule2: if latest comment is from author, need response asap
136199
# rule3: if comment num is 0, it is new issue, better to deal with it asap
137200
# rule4: if delay from latest update is over 7 days, better to deal with it soon.
138-
# rule5: if delay from created date is over 30 days and owner never reply, close it.
139-
# rule6: if delay from created date is over 15 days and owner never reply, remind owner to handle it.
201+
# rule5: if delay from created date is over 30 days, better to close.
202+
# rule6: if delay from created date is over 30 days and owner never reply, close it.
203+
# rule7: if delay from created date is over 15 days and owner never reply, remind owner to handle it.
140204
for item in issue_status:
141205
if item.status == 'release':
142206
item.bot_advice = 'better to release asap.'
143-
elif item.author == item.author_latest_comment:
144-
item.bot_advice = 'new comment for author.'
145-
elif item.comment_num == 0:
207+
elif item.comment_num == 0 and 'Python' in item.labels:
146208
item.bot_advice = 'new issue and better to confirm quickly.'
209+
try:
210+
auto_reply(item, sdk_repo, rest_repo, duplicated_issue)
211+
except Exception as e:
212+
continue
213+
elif not item.author_latest_comment in _PYTHON_SDK_ADMINISTRATORS:
214+
item.bot_advice = 'new comment for author.'
147215
elif item.delay_from_latest_update >= 7:
148216
item.bot_advice = 'delay for a long time and better to handle now.'
149-
217+
150218
if item.days_from_latest_commit >= 30 and item.language == 'Python' and '30days attention' not in item.labels:
151219
item.labels.append('30days attention')
152220
item.issue_object.set_labels(*item.labels)
@@ -157,17 +225,17 @@ def main():
157225
' please deal with it ASAP. We will close the issue if there is still no response after 15 days!')
158226
item.labels.append('15days attention')
159227
item.issue_object.set_labels(*item.labels)
160-
161-
228+
162229
# judge whether there is duplicated issue for same package
163230
if item.package != _NULL and duplicated_issue.get((item.language, item.package)) > 1:
164231
item.bot_advice = f'Warning:There is duplicated issue for {item.package}. ' + item.bot_advice
232+
233+
if item.language == 'Python':
234+
issue_status_python.append(item)
165235

166236
# output result
167-
with open(_FILE_OUT, 'w') as file_out:
168-
file_out.write('language,issue,author,package,created date,delay from created date,latest update time,'
169-
'delay from latest update,status,bot advice\n')
170-
file_out.writelines([item.output() for item in sorted(issue_status, key=_key_select)])
237+
output_python_md(issue_status_python)
238+
output_csv(issue_status)
171239

172240
# commit to github
173241
print_check('git add .')
@@ -179,7 +247,7 @@ def main():
179247
blob_name=_FILE_OUT)
180248
with open(_FILE_OUT, 'rb') as data:
181249
blob.upload_blob(data, overwrite=True)
182-
250+
183251

184252
if __name__ == '__main__':
185253
main()
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import re
2+
3+
issue_object_rg = None
4+
5+
6+
def weather_change_readme(rest_repo, link_dict, labels):
7+
# to see whether need change readme
8+
contents = str(rest_repo.get_contents(link_dict['readme_path']).decoded_content)
9+
pattern_tag = re.compile(r'tag: package-[\w+-.]+')
10+
package_tag = pattern_tag.search(contents).group()
11+
package_tag = package_tag.split(':')[1].strip()
12+
readme_python_contents = str(rest_repo.get_contents(link_dict['readme_python_path']).decoded_content)
13+
whether_multi_api = 'multi-api' in readme_python_contents
14+
whether_same_tag = package_tag == link_dict['readme_tag']
15+
whether_change_readme = not whether_same_tag or whether_multi_api and not 'MultiAPI' in labels
16+
return whether_change_readme
17+
18+
19+
# parse owner's comment and get links
20+
def get_links(readme_link):
21+
link_dict = {}
22+
comment_body = issue_object_rg.body
23+
pattern_readme = re.compile(r'/specification/([\w-]+/)+readme.md')
24+
pattern_resource_manager = re.compile(r'/specification/([\w-]+/)+resource-manager')
25+
pattern_tag = re.compile(r'package-[\w+-.]+')
26+
readme_path = pattern_readme.search(readme_link).group()
27+
readme_tag = pattern_tag.search(comment_body).group()
28+
resource_manager = pattern_resource_manager.search(readme_link).group()
29+
link_dict['readme_path'] = readme_path
30+
link_dict['readme_python_path'] = readme_path[:readme_path.rfind('/')] + '/readme.python.md'
31+
link_dict['readme_tag'] = readme_tag
32+
link_dict['resource_manager'] = resource_manager
33+
return link_dict
34+
35+
36+
def get_latest_pr_from_readme(rest_repo, link_dict):
37+
commits = rest_repo.get_commits(path=link_dict['resource_manager'])
38+
latest_commit = [commit for commit in commits][0]
39+
latest_pr_brief = latest_commit.commit.message
40+
latest_pr_number = re.findall('\(\#[0-9]+\)', latest_pr_brief)
41+
latest_pr_number_int = []
42+
for number in latest_pr_number:
43+
number = int(re.search('\d+', number).group())
44+
latest_pr_number_int.append(number)
45+
latest_pr_number_int.sort()
46+
47+
return latest_pr_number_int[-1]
48+
49+
50+
def latest_pr_parse(rest_repo, latest_pr_number):
51+
latest_pr = rest_repo.get_issue(latest_pr_number)
52+
latest_pr_comments = latest_pr.get_comments()
53+
b = [i for i in latest_pr_comments]
54+
for comment in latest_pr_comments:
55+
if '<h3>Swagger Generation Artifacts</h3>' in comment.body:
56+
return swagger_generator_parse(comment.body, latest_pr_number)
57+
58+
59+
def swagger_generator_parse(context, latest_pr_number):
60+
track1_info_model = ''
61+
try:
62+
if '<b> azure-sdk-for-python</b>' in context:
63+
pattern_python_t1 = re.compile('<b> azure-sdk-for-python</b>.+?</details>', re.DOTALL)
64+
python_t1 = re.search(pattern_python_t1, context).group()
65+
prttern_python_track1 = re.compile('<ul>\s+?<li>\s+?<a.+</ul>', re.DOTALL)
66+
python_track1_info = re.search(prttern_python_track1, python_t1).group()
67+
track1_info_model = '<details open><summary><b> python-track1</b></summary>{} </details>'.format(
68+
python_track1_info)
69+
except Exception as e:
70+
print('track1 generate error')
71+
pattern_python = re.compile('<b> azure-sdk-for-python-track2</b>.+?</details>', re.DOTALL)
72+
python = re.search(pattern_python, context).group()
73+
# the way that reply not contains [Release SDK Changes]
74+
# pattern_python_track2 = re.compile('<ul>\s*?<li>\s*?<a.*</ul>', re.DOTALL)
75+
pattern_python_track2 = re.compile('<b>track2_.*</ul>', re.DOTALL)
76+
python_track2_info = re.search(pattern_python_track2, python).group()
77+
track2_info_model = '<details open><summary><b> python-track2</b></summary>{} </details>'.format(
78+
python_track2_info)
79+
info_model = 'hi @{} Please check the package whether works well and the changelog info is as below:\n' \
80+
'{}\n{}\n' \
81+
'\n* (The version of the package is only a temporary version for testing)\n' \
82+
'\nhttps://github.com/Azure/azure-rest-api-specs/pull/{}\n' \
83+
.format(issue_object_rg.user.login, track1_info_model, track2_info_model, str(latest_pr_number))
84+
85+
return info_model
86+
87+
88+
def reply_owner(reply_content):
89+
issue_object_rg.create_comment(reply_content)
90+
91+
92+
def add_label(label_name, labels):
93+
if label_name not in labels:
94+
labels.append(label_name)
95+
issue_object_rg.set_labels(*labels)
96+
97+
98+
def begin_reply_generate(item, rest_repo, readme_link):
99+
global issue_object_rg
100+
issue_object_rg = item.issue_object
101+
link_dict = get_links(readme_link)
102+
labels = item.labels
103+
whether_change_readme = weather_change_readme(rest_repo, link_dict, labels)
104+
105+
if not whether_change_readme:
106+
latest_pr_number = get_latest_pr_from_readme(rest_repo,link_dict)
107+
reply_content = latest_pr_parse(rest_repo, latest_pr_number)
108+
reply_owner(reply_content)
109+
add_label('auto-ask-check', labels)
110+
else:
111+
print('issue {} need config readme***********'.format(issue_object_rg.number))

0 commit comments

Comments
 (0)