Skip to content

Commit d474dfc

Browse files
authored
Add more tools and revamp sdk-tools package (#2870)
* Add more tools and revamp sdk-tools package * Add pyopenssl for old tests
1 parent 9c0e7ae commit d474dfc

File tree

8 files changed

+233
-31
lines changed

8 files changed

+233
-31
lines changed

azure-sdk-tools/packaging_requirements.txt

-5
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"""This file is specific to Azure SDK for Python and should be split somewhere else."""
2+
import logging
3+
from pathlib import Path
4+
import subprocess
5+
import tempfile
6+
7+
from github import Github
8+
9+
from azure_devtools.ci_tools.github_tools import (
10+
manage_git_folder,
11+
DashboardCommentableObject
12+
)
13+
14+
_LOGGER = logging.getLogger(__name__)
15+
16+
17+
_STORAGE_ACCOUNT = "http://azuresdkinfrajobstore1.blob.core.windows.net/azure/azure-sdk-for-python/pullrequests/{prnumber}/dist/{file}"
18+
19+
def execute_simple_command(cmd_line, cwd=None, shell=False, env=None):
20+
try:
21+
process = subprocess.Popen(cmd_line,
22+
stderr=subprocess.STDOUT,
23+
stdout=subprocess.PIPE,
24+
universal_newlines=True,
25+
cwd=cwd,
26+
shell=shell,
27+
env=env)
28+
output_buffer = []
29+
for line in process.stdout:
30+
output_buffer.append(line.rstrip())
31+
_LOGGER.info(output_buffer[-1])
32+
process.wait()
33+
output = "\n".join(output_buffer)
34+
if process.returncode:
35+
raise subprocess.CalledProcessError(
36+
process.returncode,
37+
cmd_line,
38+
output
39+
)
40+
return output
41+
except Exception as err:
42+
_LOGGER.error(err)
43+
raise
44+
else:
45+
_LOGGER.info("Return code: %s", process.returncode)
46+
47+
def build_package_from_pr_number(gh_token, sdk_id, pr_number, output_folder, *, with_comment=False):
48+
"""Will clone the given PR branch and vuild the package with the given name."""
49+
50+
con = Github(gh_token)
51+
repo = con.get_repo(sdk_id)
52+
sdk_pr = repo.get_pull(pr_number)
53+
# "get_files" of Github only download the first 300 files. Might not be enough.
54+
package_names = {f.filename.split('/')[0] for f in sdk_pr.get_files() if f.filename.startswith("azure")}
55+
absolute_output_folder = Path(output_folder).resolve()
56+
57+
with tempfile.TemporaryDirectory() as temp_dir, \
58+
manage_git_folder(gh_token, Path(temp_dir) / Path("sdk"), sdk_id, pr_number=pr_number) as sdk_folder:
59+
60+
for package_name in package_names:
61+
_LOGGER.debug("Build {}".format(package_name))
62+
execute_simple_command(
63+
["python", "./build_package.py", "--dest", str(absolute_output_folder), package_name],
64+
cwd=sdk_folder
65+
)
66+
_LOGGER.debug("Build finished: {}".format(package_name))
67+
68+
if with_comment:
69+
files = [f.name for f in absolute_output_folder.iterdir()]
70+
comment_message = None
71+
dashboard = DashboardCommentableObject(sdk_pr, "(message created by the CI based on PR content)")
72+
try:
73+
installation_message = build_installation_message(sdk_pr)
74+
download_message = build_download_message(sdk_pr, files)
75+
comment_message = installation_message + "\n\n" + download_message
76+
dashboard.create_comment(comment_message)
77+
except Exception:
78+
_LOGGER.critical("Unable to do PR comment:\n%s", comment_message)
79+
80+
def build_download_message(sdk_pr, files):
81+
if not files:
82+
return ""
83+
message = "# Direct download\n\nYour files can be directly downloaded here:\n\n"
84+
for filename in files:
85+
message += "- [{}]({})\n".format(
86+
filename,
87+
_STORAGE_ACCOUNT.format(prnumber=sdk_pr.number, file=filename)
88+
)
89+
return message
90+
91+
def build_installation_message(sdk_pr):
92+
# Package starts with "azure" and is at the root of the repo
93+
package_names = {f.filename.split('/')[0] for f in sdk_pr.get_files() if f.filename.startswith("azure")}
94+
95+
result = ["# Installation instruction"]
96+
for package in package_names:
97+
result.append("## Package {}".format(package))
98+
result.append(pr_message_for_package(sdk_pr, package))
99+
return "\n".join(result)
100+
101+
102+
def pr_message_for_package(sdk_pr, package_name):
103+
git_path = '"git+{}@{}#egg={}&subdirectory={}"'.format(
104+
sdk_pr.head.repo.html_url,
105+
sdk_pr.head.ref,
106+
package_name,
107+
package_name
108+
)
109+
110+
pip_install = 'pip install {}'
111+
pip_wheel = 'pip wheel --no-deps {}'
112+
113+
pr_body = "You can install the package `{}` of this PR using the following command:\n\t`{}`".format(
114+
package_name,
115+
pip_install.format(git_path)
116+
)
117+
118+
pr_body += "\n\n"
119+
120+
pr_body += "You can build a wheel to distribute for test using the following command:\n\t`{}`".format(
121+
pip_wheel.format(git_path)
122+
)
123+
124+
pr_body += "\n\n"
125+
pr_body += "If you have a local clone of this repository, you can also do:\n\n"
126+
pr_body += "- `git checkout {}`\n".format(sdk_pr.head.ref)
127+
pr_body += "- `pip install -e ./{}`\n".format(package_name)
128+
pr_body += "\n\n"
129+
pr_body += "Or build a wheel file to distribute for testing:\n\n"
130+
pr_body += "- `git checkout {}`\n".format(sdk_pr.head.ref)
131+
pr_body += "- `pip wheel --no-deps ./{}`\n".format(package_name)
132+
return pr_body
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import argparse
2+
import logging
3+
import os
4+
5+
from packaging_tools.drop_tools import build_package_from_pr_number
6+
7+
_LOGGER = logging.getLogger(__name__)
8+
9+
def generate_main():
10+
"""Main method"""
11+
12+
parser = argparse.ArgumentParser(
13+
description='Build package.',
14+
formatter_class=argparse.RawTextHelpFormatter)
15+
parser.add_argument('--pr-number', '-p',
16+
dest='pr_number', type=int, required=True,
17+
help='PR number')
18+
parser.add_argument('--repo', '-r',
19+
dest='repo_id', default="Azure/azure-sdk-for-python",
20+
help='Repo id. [default: %(default)s]')
21+
parser.add_argument("--with-comment",
22+
dest="with_comment", action="store_true",
23+
help="Do a comment to the original PR with info.")
24+
parser.add_argument("-v", "--verbose",
25+
dest="verbose", action="store_true",
26+
help="Verbosity in INFO mode")
27+
parser.add_argument("--debug",
28+
dest="debug", action="store_true",
29+
help="Verbosity in DEBUG mode")
30+
31+
parser.add_argument('--output-folder', '-o',
32+
dest='output_folder', default='.',
33+
help='Output folder for package. [default: %(default)s]')
34+
35+
args = parser.parse_args()
36+
main_logger = logging.getLogger()
37+
if args.verbose or args.debug:
38+
logging.basicConfig()
39+
main_logger.setLevel(logging.DEBUG if args.debug else logging.INFO)
40+
41+
build_package_from_pr_number(
42+
os.environ.get("GH_TOKEN", None),
43+
args.repo_id,
44+
args.pr_number,
45+
args.output_folder,
46+
with_comment=args.with_comment
47+
)
48+
49+
if __name__ == "__main__":
50+
generate_main()

azure-sdk-tools/setup.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,39 @@
44
# This is a "fake" package, meaning it's not supposed to be released but used
55
# locally with "pip install -e"
66

7+
DEPENDENCIES = [
8+
# Packaging
9+
'packaging',
10+
'wheel',
11+
'Jinja2',
12+
'pytoml',
13+
'json-delta>=2.0',
14+
'azure-devtools[ci_tools]>=1.1.0',
15+
# Tests
16+
'pytest-cov',
17+
'pytest>=3.5.1',
18+
# 'azure-devtools>=0.4.1' override by packaging needs
19+
'readme_renderer',
20+
21+
# Should not be here, but split per package once they have test dependencies
22+
'azure-storage-blob', # azure-servicemanagement-legacy azure-keyvault
23+
'azure-storage-file', # azure-mgmt-batchai
24+
'azure-storage-common', # azure-keyvault
25+
'pyopenssl' # azure-servicemanagement-legacy
26+
]
27+
728
setup(
829
name = "azure-sdk-tools",
930
version = "0.0.0",
1031
author='Microsoft Corporation',
1132
author_email='[email protected]',
1233
url='https://github.com/Azure/azure-sdk-for-python',
1334
packages=find_packages(),
14-
long_description="Specific tools for Azure SDK for Python testing"
35+
long_description="Specific tools for Azure SDK for Python testing",
36+
install_requires=DEPENDENCIES,
37+
entry_points = {
38+
'console_scripts': [
39+
'generate_package=packaging_tools.generate_package:generate_main',
40+
],
41+
}
1542
)

azure-sdk-tools/test-requirements.txt

-12
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from subprocess import CalledProcessError
2+
import tempfile
3+
4+
import pytest
5+
6+
@pytest.mark.skip(reason="test for SDK team that should be manually activated")
7+
def test_build_package_from_pr_number(github_token):
8+
from pathlib import Path
9+
from packaging_tools.drop_tools import build_package_from_pr_number
10+
11+
# Should build package azure-mgmt-advisor 1.0.1
12+
with tempfile.TemporaryDirectory() as temp_dir:
13+
build_package_from_pr_number(github_token, "Azure/azure-sdk-for-python", 2417, temp_dir)
14+
temp_dir_path = Path(temp_dir)
15+
files = set(file.relative_to(temp_dir) for file in temp_dir_path.iterdir())
16+
assert files == {
17+
Path("azure_mgmt_iothubprovisioningservices-0.2.0-py2.py3-none-any.whl"),
18+
Path("azure-mgmt-iothubprovisioningservices-0.2.0.zip")
19+
}
20+
21+
# This PR is broken and can't be built: 2040
22+
with tempfile.TemporaryDirectory() as temp_dir, pytest.raises(CalledProcessError):
23+
build_package_from_pr_number(github_token, "Azure/azure-sdk-for-python", 2040, temp_dir)

requirements.txt

-4
This file was deleted.

scripts/dev_setup.py

-9
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,11 @@ def pip_command(command):
4141
print('Running dev setup...')
4242
print('Root directory \'{}\'\n'.format(root_dir))
4343

44-
# install general requirements
45-
pip_command('install -r requirements.txt')
46-
4744
# install packages
4845
for package_list in [nspkg_packages, content_packages]:
4946
for package_name in package_list:
5047
pip_command('install -e {}'.format(package_name))
5148

52-
# install test requirements
53-
pip_command('install -r azure-sdk-tools/test-requirements.txt')
54-
55-
# install packaging requirements
56-
pip_command('install -r azure-sdk-tools/packaging_requirements.txt')
57-
5849
# Ensure that the site package's azure/__init__.py has the old style namespace
5950
# package declaration by installing the old namespace package
6051
pip_command('install --force-reinstall azure-mgmt-nspkg==1.0.0')

0 commit comments

Comments
 (0)