Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions scripts/ci/find_extension_upgraded.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
import os
import sys
import json
from subprocess import check_output
from pkg_resources import parse_version

def separator_line():
print('-' * 100)

class AzdevExtensionHelper:
def __init__(self, extension_name):
self.name = extension_name

def _get_metadata_flags(self, setup_py_dir):
for root, _, files in os.walk(setup_py_dir):
if 'azext_metadata.json' in files:
metadata_path = os.path.join(root, 'azext_metadata.json')
with open(metadata_path, 'r') as f:
metadata = json.load(f)
is_experimental = metadata.get('azext.isExperimental', False)
is_preview = metadata.get('azext.isPreview', False)
return is_experimental, is_preview
return False, False

def _adjust_version_for_preview(self, version, setup_py_dir):
is_experimental, is_preview = self._get_metadata_flags(setup_py_dir)
if is_experimental or is_preview:
version_parts = version.split('.')
if all(part.isdigit() for part in version_parts):
version = f"{version}b1"
return version

def is_version_upgrade(self):
with open('src/index.json') as fd:
current_extensions = json.loads(fd.read()).get("extensions")

setup_py = f'src/{self.name}/setup.py'
if not os.path.isfile(setup_py):
print('no setup.py')
return False

setup_py_dir = os.path.dirname(setup_py)
cmd = f'{sys.executable} setup.py --name'
self.name = check_output(cmd, shell=True, cwd=setup_py_dir).decode('utf-8').strip()
self.name = self.name.replace('_', '-')

metadata = current_extensions.get(self.name, None)
if metadata is None: # for new added extension
return True

current_max_entry = max(metadata, key=lambda e: parse_version(e['metadata']['version']))
current_max_version = current_max_entry['metadata']['version']
current_max_version = self._adjust_version_for_preview(current_max_version, setup_py_dir)
print(f'current max version is {current_max_version}')

cmd = f'{sys.executable} setup.py --version'
modified_version = check_output(cmd, shell=True, cwd=setup_py_dir).decode('utf-8').strip()
print(f'modified version is {modified_version}')

if parse_version(current_max_version) > parse_version(modified_version):
err = f'version downgrade is not allowed in extension {setup_py}. [{current_max_version} -> {modified_version}]'
raise Exception(err)

if parse_version(current_max_version) == parse_version(modified_version):
return False

return True

def find_modified_files_against_master_branch():
"""
Deleted files don't count in diff
"""
cmd = 'git --no-pager diff --diff-filter=ACMRT --name-only HEAD~1 -- src/'
files = check_output(cmd.split()).decode('utf-8').split('\n')
separator_line()
print('modified files:')
for f in files:
print(f)
separator_line()
return [f for f in files if len(f) > 0]

def contain_extension_code(files):
with open('src/index.json', 'r') as fd:
current_extensions = json.loads(fd.read()).get("extensions")
current_extension_homes = set(f'src/{name}' for name in current_extensions)

for file in files:
if any([file.startswith(prefix) for prefix in current_extension_homes]):
return True

# for new added extensions or modules that src folder name does not match its wheel package name
for file in files:
if 'src/' in file and os.path.isfile(file) and os.path.isdir(os.path.dirname(file)):
new_extension_home = os.path.dirname(file)
new_extension_home = os.path.join(*new_extension_home.split('/')[:2])
if os.path.isfile(os.path.join(new_extension_home, 'setup.py')):
return True
return False

def main():
modified_files = find_modified_files_against_master_branch()
if 'src/index.json' in modified_files:
modified_files.remove('src/index.json')

# check setup.py
for f in modified_files:
if f.endswith('setup.py'):
break
else:
separator_line()
print('no setup.py is modified, no need to publish')
separator_line()
return

# check source code
if not contain_extension_code(modified_files):
separator_line()
print('no extension source code is modified, no need to publish')
separator_line()
return

extension_names = set()
for f in modified_files:
src, name, *_ = f.split('/')
if os.path.isdir(os.path.join(src, name)):
extension_names.add(name)

for name in extension_names:
azdev_extension = AzdevExtensionHelper(name)
if azdev_extension.is_version_upgrade() is False:
print(f'extension [{name}] is not upgrade, no need to help publish')
continue
with open('./upgrade_extensions.txt', 'a') as fd:
fd.write(name + '\n')

if __name__ == '__main__':
main()
Loading