1
+ # --------------------------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for license information.
4
+ # --------------------------------------------------------------------------------------------
5
+
6
+ import os
7
+ import argparse
8
+ import requests
9
+ import json
10
+ import logging
11
+ from github import Github , Auth
12
+ from ci_tools .functions import discover_targeted_packages
13
+
14
+ logging .getLogger ().setLevel (logging .INFO )
15
+ root_dir = os .path .abspath (os .path .join (os .path .abspath (__file__ ), ".." , ".." , ".." , ".." ))
16
+
17
+ def get_build_info (service_directory : str , package_name : str ) -> str :
18
+ """Get the build info from the build link."""
19
+ build_id = os .getenv ("BUILD_BUILDID" )
20
+ timeline_link = f"https://dev.azure.com/azure-sdk/internal/_apis/build/builds/{ build_id } /timeline?api-version=6.0"
21
+
22
+ token = os .environ ["SYSTEM_ACCESSTOKEN" ]
23
+ AUTH_HEADERS = {"Authorization" : f"Bearer { token } " }
24
+
25
+ try :
26
+ # Make the API request
27
+ response = requests .get (timeline_link , headers = AUTH_HEADERS )
28
+ response_json = json .loads (response .text )
29
+
30
+ for task in response_json ["records" ]:
31
+ if "Run Pylint Next" in task ["name" ]:
32
+ log_link = task ['log' ]['url' ] + "?api-version=6.0"
33
+ # Get the log file from the build link
34
+ log_output = requests .get (log_link , headers = AUTH_HEADERS )
35
+ build_output = log_output .content .decode ("utf-8" )
36
+ new_output = (build_output .split (f"next-pylint: commands[3]> python /mnt/vss/_work/1/s/eng/tox/run_pylint.py -t { service_directory } --next=True" )[1 ]).split (f"ERROR:root:{ package_name } exited with linting error" )[0 ]
37
+ return new_output
38
+ except Exception as e :
39
+ logging .error (f"Exception occurred while getting build info: { e } " )
40
+ return "Error getting build info"
41
+
42
+
43
+
44
+ def main (targeted_packages ):
45
+ for package in targeted_packages :
46
+ # iterate through the packages and find if there is an issue associated with the package
47
+ # there may be multiple packages in the targeted_packages list
48
+ logging .info (f"Processing package: { package } " )
49
+
50
+ # update open GitHub issues matching the package pattern
51
+ auth = Auth .Token (os .environ ["GH_TOKEN" ])
52
+ g = Github (auth = auth )
53
+ repo = g .get_repo ("Azure/azure-sdk-for-python" )
54
+
55
+ package_name = package .split ("/" )[- 1 ]
56
+ logging .info (f"Searching for issues in repo { repo .name } for package { package_name } " )
57
+
58
+ for issue in repo .get_issues (state = "open" , labels = ["pylint" ], creator = "azure-sdk" ):
59
+ if f"{ package_name } needs linting updates for pylint version" in issue .title :
60
+ logging .info (f"Found issue for package { package_name } : { issue .title } " )
61
+ build_info = get_build_info (package , package_name )
62
+
63
+ template = (
64
+ f"\n \n **Pylint Errors:**"
65
+ f"\n \n { build_info } "
66
+ f"\n "
67
+ f"\n \n **How to fix:**"
68
+ f"\n **Note:** If you are a bot. Fix the issue by fixing the above pylint errors."
69
+ )
70
+ if issue .body :
71
+ first_section = issue .body .split ("**How to fix:**" )[0 ]
72
+ new_body = first_section + template + "\n " + issue .body .split ("**How to fix:**" )[1 ]
73
+ issue .edit (body = new_body )
74
+ logging .info (f"Updated issue #{ issue .number } for package { package_name } " )
75
+
76
+ if __name__ == "__main__" :
77
+ parser = argparse .ArgumentParser (
78
+ description = """
79
+ This script is the single point for all checks invoked by CI within this repo. It works in two phases.
80
+ 1. Identify which packages in the repo are in scope for this script invocation, based on a glob string and a service directory.
81
+ 2. Invoke one or multiple `tox` environments for each package identified as in scope.
82
+
83
+ In the case of an environment invoking `pytest`, results can be collected in a junit xml file, and test markers can be selected via --mark_arg.
84
+ """
85
+ )
86
+
87
+ parser .add_argument (
88
+ "glob_string" ,
89
+ nargs = "?" ,
90
+ help = (
91
+ "A comma separated list of glob strings that will target the top level directories that contain packages."
92
+ 'Examples: All = "azure-*", Single = "azure-keyvault", Targeted Multiple = "azure-keyvault,azure-mgmt-resource"'
93
+ ),
94
+ )
95
+
96
+ parser .add_argument ("--disablecov" , help = ("Flag. Disables code coverage." ), action = "store_true" )
97
+
98
+ parser .add_argument (
99
+ "--service" ,
100
+ help = ("Name of service directory (under sdk/) to test. Example: --service applicationinsights" ),
101
+ )
102
+
103
+ parser .add_argument (
104
+ "-w" ,
105
+ "--wheel_dir" ,
106
+ dest = "wheel_dir" ,
107
+ help = "Location for prebuilt artifacts (if any)" ,
108
+ )
109
+
110
+ parser .add_argument (
111
+ "-i" ,
112
+ "--injected-packages" ,
113
+ dest = "injected_packages" ,
114
+ default = "" ,
115
+ help = "Comma or space-separated list of packages that should be installed prior to dev_requirements. If local path, should be absolute." ,
116
+ )
117
+
118
+ parser .add_argument (
119
+ "--filter-type" ,
120
+ dest = "filter_type" ,
121
+ default = "Build" ,
122
+ help = "Filter type to identify eligible packages. for e.g. packages filtered in Build can pass filter type as Build," ,
123
+ choices = ["Build" , "Docs" , "Regression" , "Omit_management" , "None" ],
124
+ )
125
+
126
+
127
+ args = parser .parse_args ()
128
+
129
+ # We need to support both CI builds of everything and individual service
130
+ # folders. This logic allows us to do both.
131
+ if args .service and args .service != "auto" :
132
+ service_dir = os .path .join ("sdk" , args .service )
133
+ target_dir = os .path .join (root_dir , service_dir )
134
+ else :
135
+ target_dir = root_dir
136
+
137
+ logging .info (f"Beginning discovery for { args .service } and root dir { root_dir } . Resolving to { target_dir } ." )
138
+
139
+ if args .filter_type == "None" :
140
+ args .filter_type = "Build"
141
+ compatibility_filter = False
142
+ else :
143
+ compatibility_filter = True
144
+
145
+ targeted_packages = discover_targeted_packages (
146
+ args .glob_string , target_dir , "" , args .filter_type , compatibility_filter
147
+ )
148
+
149
+ if len (targeted_packages ) == 0 :
150
+ logging .info (f"No packages collected for targeting string { args .glob_string } and root dir { root_dir } . Exit 0." )
151
+ exit (0 )
152
+
153
+ main (targeted_packages )
0 commit comments