Skip to content

Commit 52093b7

Browse files
authored
Mgmt: Changelog tool (Azure#18612)
* add change log tools * add change log in generation * install requests * rename all get method * format pom * simplify check return type * add comments for calc definition stages for methods * use tuple for return type * split generate and compile, for changelog execute * update changelog file path * fix OLD_JAR and NEW_JAR * fix generation * refactor name for calc stage
1 parent 3dc38dd commit 52093b7

File tree

13 files changed

+753
-29
lines changed

13 files changed

+753
-29
lines changed

eng/mgmt/automation/changelog.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import sys
4+
import logging
5+
import argparse
6+
7+
pwd = os.getcwd()
8+
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
9+
from parameters import *
10+
import generate
11+
os.chdir(pwd)
12+
13+
14+
def parse_args() -> argparse.Namespace:
15+
parser = argparse.ArgumentParser()
16+
parser.add_argument('-s', '--service', required = True)
17+
parser.add_argument('--suffix')
18+
parser.add_argument('-c', '--compile', action = 'store_true')
19+
return parser.parse_args()
20+
21+
22+
def main():
23+
args = vars(parse_args())
24+
sdk_root = os.path.abspath(
25+
os.path.join(os.path.dirname(sys.argv[0]), SDK_ROOT))
26+
service = args['service']
27+
generate.update_parameters(args.get('suffix'))
28+
29+
if args.get('compile'):
30+
generate.compile_package(sdk_root, service)
31+
32+
versions = generate.get_version(sdk_root, service).split(';')
33+
stable_version = versions[1]
34+
current_version = versions[2]
35+
generate.compare_with_maven_package(sdk_root, service, stable_version,
36+
current_version)
37+
38+
39+
if __name__ == "__main__":
40+
logging.basicConfig(
41+
level = logging.INFO,
42+
format = '%(asctime)s %(levelname)s %(message)s',
43+
datefmt = '%Y-%m-%d %X',
44+
)
45+
main()

eng/mgmt/automation/generate.py

Lines changed: 150 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
import shutil
99
import logging
1010
import argparse
11+
import requests
12+
import tempfile
1113
import subprocess
1214
import collections
1315
import urllib.parse
16+
from typing import Tuple
1417

1518
pwd = os.getcwd()
1619
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
@@ -34,7 +37,6 @@ def generate(
3437
use: str,
3538
tag: str = None,
3639
version: str = None,
37-
compile: bool = True,
3840
**kwargs,
3941
):
4042
module = ARTIFACT_FORMAT.format(service)
@@ -71,15 +73,108 @@ def generate(
7173
update_root_pom(sdk_root, service)
7274
update_version(sdk_root, service)
7375

74-
if compile:
75-
if os.system('mvn clean verify package -f {0}/pom.xml -pl {1}:{2} -am'.
76-
format(sdk_root, GROUP_ID, module)) != 0:
77-
logging.error('[GENERATE] Maven build fail')
78-
return False
76+
return True
77+
7978

79+
def compile_package(sdk_root, service):
80+
module = ARTIFACT_FORMAT.format(service)
81+
if os.system(
82+
'mvn clean verify package -f {0}/pom.xml -pl {1}:{2} -am'.format(
83+
sdk_root, GROUP_ID, module)) != 0:
84+
logging.error('[COMPILE] Maven build fail')
85+
return False
8086
return True
8187

8288

89+
def generate_changelog_and_breaking_change(
90+
sdk_root,
91+
old_jar,
92+
new_jar,
93+
**kwargs,
94+
) -> Tuple[bool, str]:
95+
logging.info('[CHANGELOG] changelog jar: {0} -> {1}'.format(
96+
old_jar, new_jar))
97+
stdout = subprocess.run(
98+
'mvn clean compile exec:java -q -f {0}/eng/mgmt/changelog/pom.xml -DOLD_JAR="{1}" -DNEW_JAR="{2}"'
99+
.format(sdk_root, old_jar, new_jar),
100+
stdout = subprocess.PIPE,
101+
shell = True,
102+
).stdout
103+
logging.info('[CHANGELOG] changelog output: {0}'.format(stdout))
104+
105+
config = json.loads(stdout)
106+
return (config.get('breaking', False), config.get('changelog', ''))
107+
108+
109+
def update_changelog(changelog_file, changelog):
110+
version_pattern = '^## (\d+\.\d+\.\d+(?:-[\w\d\.]+)?) \((.*?)\)'
111+
with open(changelog_file, 'r') as fin:
112+
old_changelog = fin.read()
113+
114+
first_version = re.search(version_pattern, old_changelog, re.M)
115+
if not first_version:
116+
logging.error(
117+
'[Changelog][Skip] Cannot read first version from {}'.format(
118+
changelog_file))
119+
return
120+
121+
left = old_changelog[first_version.end():]
122+
second_version = re.search(version_pattern, left, re.M)
123+
if not second_version:
124+
logging.error(
125+
'[Changelog][Skip] Cannot read second version from {}'.format(
126+
changelog_file))
127+
return
128+
129+
first_version_part = old_changelog[:first_version.end() +
130+
second_version.start()]
131+
first_version_part = re.sub('\s+$', '', first_version_part)
132+
first_version_part += '\n\n' + changelog.strip() + '\n\n'
133+
134+
with open(changelog_file, 'w') as fout:
135+
fout.write(first_version_part +
136+
old_changelog[first_version.end() + second_version.start():])
137+
138+
logging.info('[Changelog][Success] Write to changelog')
139+
140+
141+
def compare_with_maven_package(sdk_root, service, stable_version,
142+
current_version):
143+
if stable_version == current_version:
144+
logging.info('[Changelog][Skip] no previous version')
145+
return
146+
147+
module = ARTIFACT_FORMAT.format(service)
148+
r = requests.get(
149+
MAVEN_URL.format(group_id = GROUP_ID.replace('.', '/'),
150+
artifact_id = module,
151+
version = stable_version))
152+
r.raise_for_status()
153+
old_jar_fd, old_jar = tempfile.mkstemp('.jar')
154+
try:
155+
with os.fdopen(old_jar_fd, 'wb') as tmp:
156+
tmp.write(r.content)
157+
new_jar = os.path.join(
158+
sdk_root,
159+
JAR_FORMAT.format(service = service,
160+
artifact_id = module,
161+
version = current_version))
162+
if not os.path.exists(new_jar):
163+
raise Exception('Cannot found built jar in {0}'.format(new_jar))
164+
breaking, changelog = generate_changelog_and_breaking_change(
165+
sdk_root, old_jar, new_jar)
166+
if changelog and changelog.strip() != '':
167+
changelog_file = os.path.join(
168+
sdk_root,
169+
CHANGELOG_FORMAT.format(service = service,
170+
artifact_id = module))
171+
update_changelog(changelog_file, changelog)
172+
else:
173+
logging.error('[Changelog][Skip] Cannot get changelog')
174+
finally:
175+
os.remove(old_jar)
176+
177+
83178
def add_module_to_modules(modules: str, module: str) -> str:
84179
post_module = re.search(r'([^\S\n\r]*)</modules>', modules)
85180
indent = post_module.group(1)
@@ -95,7 +190,7 @@ def add_module_to_modules(modules: str, module: str) -> str:
95190
return '<modules>\n' + ''.join(all_module) + post_module.group()
96191

97192

98-
def add_module_to_default_profile(pom: str, module: str) -> (bool, str):
193+
def add_module_to_default_profile(pom: str, module: str) -> Tuple[bool, str]:
99194
for profile in re.finditer(r'<profile>[\s\S]*?</profile>', pom):
100195
profile_value = profile.group()
101196
if re.search(r'<id>default</id>', profile_value):
@@ -119,7 +214,7 @@ def add_module_to_default_profile(pom: str, module: str) -> (bool, str):
119214
return (False, '')
120215

121216

122-
def add_module_to_pom(pom: str, module: str) -> (bool, str):
217+
def add_module_to_pom(pom: str, module: str) -> Tuple[bool, str]:
123218
if pom.find('<module>{0}</module>'.format(module)) >= 0:
124219
logging.info('[POM][Skip] pom already has module {0}'.format(module))
125220
return (True, pom)
@@ -207,7 +302,9 @@ def update_service_ci_and_pom(sdk_root: str, service: str):
207302
with open(pom_xml_file, 'r') as fin:
208303
pom_xml = fin.read()
209304
else:
210-
pom_xml = POM_FORMAT.format(service = service, group_id = GROUP_ID, artifact_id = module)
305+
pom_xml = POM_FORMAT.format(service = service,
306+
group_id = GROUP_ID,
307+
artifact_id = module)
211308

212309
logging.info('[POM][Process] dealing with pom.xml')
213310
success, pom_xml = add_module_to_pom(pom_xml, module)
@@ -217,6 +314,26 @@ def update_service_ci_and_pom(sdk_root: str, service: str):
217314
logging.info('[POM][Success] Write to pom.xml')
218315

219316

317+
def get_version(
318+
sdk_root: str,
319+
service: str,
320+
) -> str:
321+
version_file = os.path.join(sdk_root, 'eng/versioning/version_client.txt')
322+
module = ARTIFACT_FORMAT.format(service)
323+
project = '{0}:{1}'.format(GROUP_ID, module)
324+
325+
with open(version_file, 'r') as fin:
326+
for line in fin.readlines():
327+
version_line = line.strip()
328+
if version_line.startswith('#'):
329+
continue
330+
versions = version_line.split(';')
331+
if versions[0] == project:
332+
return version_line
333+
logging.error('Cannot get version of {0}'.format(project))
334+
return None
335+
336+
220337
def update_version(sdk_root: str, service: str):
221338
pwd = os.getcwd()
222339
try:
@@ -254,13 +371,13 @@ def write_version(
254371
fout.write('\n')
255372

256373

257-
def set_or_increase_version_and_generate(
374+
def set_or_increase_version(
258375
sdk_root: str,
259376
service: str,
260377
preview = True,
261378
version = None,
262379
**kwargs,
263-
):
380+
) -> Tuple[str, str]:
264381
version_file = os.path.join(sdk_root, 'eng/versioning/version_client.txt')
265382
module = ARTIFACT_FORMAT.format(service)
266383
project = '{0}:{1}'.format(GROUP_ID, module)
@@ -305,13 +422,12 @@ def set_or_increase_version_and_generate(
305422
# version is given, set and return
306423
if version:
307424
if not stable_version:
308-
stable_version = current_version
425+
stable_version = version
309426
logging.info(
310427
'[VERSION][Set] set to given version "{0}"'.format(version))
311428
write_version(version_file, lines, version_index, project,
312-
stable_version, current_version)
313-
generate(sdk_root, service, version = version, **kwargs)
314-
return
429+
stable_version, version)
430+
return stable_version, version
315431

316432
current_versions = list(re.findall(version_pattern, current_version)[0])
317433
stable_versions = re.findall(version_pattern, stable_version)
@@ -328,7 +444,6 @@ def set_or_increase_version_and_generate(
328444

329445
write_version(version_file, lines, version_index, project,
330446
stable_version, current_version)
331-
generate(sdk_root, service, version = current_version, **kwargs)
332447
else:
333448
# TODO: auto-increase for stable version and beta version if possible
334449
current_version = version_format.format(*current_versions)
@@ -340,7 +455,8 @@ def set_or_increase_version_and_generate(
340455

341456
write_version(version_file, lines, version_index, project,
342457
stable_version, current_version)
343-
generate(sdk_root, service, version = current_version, **kwargs)
458+
459+
return stable_version, current_version
344460

345461

346462
def parse_args() -> argparse.Namespace:
@@ -375,11 +491,6 @@ def parse_args() -> argparse.Namespace:
375491
default = AUTOREST_CORE_VERSION,
376492
help = 'Autorest version',
377493
)
378-
parser.add_argument(
379-
'--compile',
380-
action = 'store_true',
381-
help = 'Do compile after generation or not',
382-
)
383494
parser.add_argument('--suffix', help = 'Suffix for namespace and artifact')
384495
parser.add_argument(
385496
'--auto-commit-external-change',
@@ -414,7 +525,7 @@ def valid_service(service: str):
414525
return re.sub('[^a-z0-9_]', '', service.lower())
415526

416527

417-
def read_api_specs(api_specs_file: str) -> (str, dict):
528+
def read_api_specs(api_specs_file: str) -> Tuple[str, dict]:
418529
# return comment and api_specs
419530

420531
with open(api_specs_file) as fin:
@@ -517,14 +628,20 @@ def sdk_automation(input_file: str, output_file: str):
517628
else:
518629
tag = 'package-resources-2020-10'
519630

520-
set_or_increase_version_and_generate(
631+
stable_version, current_version = set_or_increase_version(
632+
sdk_root,
633+
service,
634+
)
635+
generate(
521636
sdk_root,
522637
service,
523638
spec_root = config['specFolder'],
524639
readme = readme,
525640
autorest = AUTOREST_CORE_VERSION,
526641
use = AUTOREST_JAVA,
527-
tag = tag)
642+
tag = tag,
643+
)
644+
compile_package(sdk_root, service)
528645

529646
generated_folder = OUTPUT_FOLDER_FORMAT.format(service)
530647
packages.append({
@@ -587,7 +704,13 @@ def main():
587704
service = get_and_update_service_from_api_specs(api_specs_file, spec,
588705
args['service'])
589706
args['service'] = service
590-
set_or_increase_version_and_generate(sdk_root, **args)
707+
stable_version, current_version = set_or_increase_version(sdk_root, **args)
708+
args['version'] = current_version
709+
generate(sdk_root, **args)
710+
711+
compile_package(sdk_root, service)
712+
compare_with_maven_package(sdk_root, service, stable_version,
713+
current_version)
591714

592715
if args.get('auto_commit_external_change') and args.get(
593716
'user_name') and args.get('user_email'):
@@ -606,7 +729,7 @@ def main():
606729

607730
if __name__ == '__main__':
608731
logging.basicConfig(
609-
level = logging.DEBUG,
732+
level = logging.INFO,
610733
format = '%(asctime)s %(levelname)s %(message)s',
611734
datefmt = '%Y-%m-%d %X',
612735
)

eng/mgmt/automation/generation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ steps:
1818
- bash: |
1919
sudo apt-get install -y --upgrade python3-pip python3-setuptools
2020
pip3 install --upgrade wheel
21-
pip3 install --upgrade PyYAML
21+
pip3 install --upgrade PyYAML requests
2222
displayName: Update python
2323

2424
- task: NodeTool@0

eng/mgmt/automation/init.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
sudo apt-get install -y --upgrade python3-pip python3-setuptools
44
pip3 install --upgrade wheel
5-
pip3 install --upgrade PyYAML
5+
pip3 install --upgrade PyYAML requests
66

77
cat << EOF > $2
88
{"envs": {"PATH": "$JAVA_HOME_11_X64/bin:$PATH", "JAVA_HOME": "$JAVA_HOME_11_X64"}}

eng/mgmt/automation/parameters.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
OUTPUT_FOLDER_FORMAT = None
1313

1414
# Constant parameters
15+
MAVEN_URL = 'https://repo1.maven.org/maven2/{group_id}/{artifact_id}/{version}/{artifact_id}-{version}.jar'
16+
1517
SDK_ROOT = '../../../' # related to file dir
1618
AUTOREST_CORE_VERSION = '3.0.6350'
1719
AUTOREST_JAVA = '@autorest/java@4.0.9'
@@ -22,6 +24,8 @@
2224
CI_FILE_FORMAT = 'sdk/{0}/ci.yml'
2325
POM_FILE_FORMAT = 'sdk/{0}/pom.xml'
2426
README_FORMAT = 'specification/{0}/resource-manager/readme.md'
27+
JAR_FORMAT = 'sdk/{service}/{artifact_id}/target/{artifact_id}-{version}.jar'
28+
CHANGELOG_FORMAT = 'sdk/{service}/{artifact_id}/CHANGELOG.md'
2529

2630
MODELERFOUR_ARGUMENTS = '--pipeline.modelerfour.additional-checks=false --pipeline.modelerfour.lenient-model-deduplication=true'
2731
FLUENTLITE_ARGUMENTS = '--java {0} --azure-arm --verbose --sdk-integration --fluent=lite --java.fluent=lite --java.license-header=MICROSOFT_MIT_SMALL'.format(

0 commit comments

Comments
 (0)