import glob
import os
import re
import sys
from lxml import etree
from functions import replace_files
class VersionBumper:
module_list = [
"AWSAPIGateway",
"AWSAutoScaling",
"AWSChimeSDKIdentity",
"AWSChimeSDKMessaging",
"AWSCloudWatch",
"AWSCognitoAuth",
"AWSCognitoIdentityProvider",
"AWSCognitoIdentityProviderASF",
"AWSComprehend",
"AWSConnect",
"AWSConnectParticipant",
"AWSCore",
"AWSDynamoDB",
"AWSEC2",
"AWSElasticLoadBalancing",
"AWSIoT",
"AWSKMS",
"AWSKinesis",
"AWSKinesisVideo",
"AWSKinesisVideoArchivedMedia",
"AWSKinesisVideoSignaling",
"AWSKinesisVideoWebRTCStorage",
"AWSLambda",
"AWSLex",
"AWSLocation",
"AWSLocationXCF",
"AWSLogs",
"AWSMachineLearning",
"AWSPinpoint",
"AWSPolly",
"AWSRekognition",
"AWSS3",
"AWSSES",
"AWSSNS",
"AWSSQS",
"AWSSageMakerRuntime",
"AWSSimpleDB",
"AWSTextract",
"AWSTranscribe",
"AWSTranscribeStreaming",
"AWSTranslate",
"AWSAuthSDK/Sources/AWSAuthCore",
"AWSAuthSDK/Sources/AWSAuthUI",
"AWSAuthSDK/Sources/AWSAppleSignIn",
"AWSAuthSDK/Sources/AWSFacebookSignIn",
"AWSAuthSDK/Sources/AWSGoogleSignIn",
"AWSAuthSDK/Sources/AWSMobileClient",
"AWSAuthSDK/Sources/AWSMobileClientXCF",
"AWSAuthSDK/Sources/AWSUserPoolsSignIn",
]
def __init__(self, root, new_sdk_version):
self._root = root
self._new_sdk_version = new_sdk_version
def bump_sdk_version(self):
self.bump_plists()
self.bump_services()
self.bump_podspecs()
self.bump_changelog()
self.bump_generate_docs()
def bump_plists(self):
for module in VersionBumper.module_list:
filename = os.path.join(root, module, "Info.plist")
self.bump_plist(filename)
def bump_plist(self, filename):
tree = etree.parse(filename)
root_node = tree.getroot()
namespaces = root_node.nsmap
dict_node = root_node.find("./dict", namespaces)
set_version = False
for child in dict_node:
if child.tag == "key" and child.text == "CFBundleShortVersionString":
set_version = True
else:
if set_version:
child.text = self._new_sdk_version
break
plist_string = VersionBumper.format_plist(tree)
plist = open(filename, "w")
plist.write(plist_string)
plist.close()
@staticmethod
def format_plist(tree):
"""
Adjusts lxml's pretty-printed XML format to match Xcode's default and avoid
semantically uninteresting diffs
:param tree: the lxml tree to format
:return: a pretty-printed string matching Xcode's plist format
"""
plist_string = VersionBumper.tree_to_string(tree)
flags = re.MULTILINE | re.IGNORECASE
# Prepend XML prolog. This could be partially done with `lxml.tostring`'s
# xml_declaration option, but that returns single-quoted attributes
formatted_plist = '\n' + plist_string
# Replace self-closing '' tags with explicitly closed tags
formatted_plist = re.sub(r"", "", formatted_plist, flags=flags)
# Adjust lxml's default space-based indentation to Xcode's tab-based. Use the
# multiline flag and match beginning of each line.
formatted_plist = re.sub(r"^\s+<", "\t<", formatted_plist, flags=flags)
return formatted_plist
@staticmethod
def tree_to_string(tree):
plist_bytes = etree.tostring(
tree, pretty_print=True, xml_declaration=False, encoding="UTF-8"
)
plist_string = plist_bytes.decode("utf-8")
return plist_string
def bump_services(self):
service_pattern = {
"match": r'(NSString[[:space:]]+\*const[[:space:]]+AWS.+SDKVersion[[:space:]]*=[[:space:]]+@")[0-9]+\.[0-9]+\.[0-9]+"', # noqa: E501
"replace": r'\1{version}"'.format(version=self._new_sdk_version),
"files": [],
}
# Add files for each module
for module in VersionBumper.module_list:
path = "{0}/{0}Service.m".format(module)
if os.path.isfile(os.path.join(root, path)):
service_pattern["files"].append(path)
# Add files for special modules
service_pattern["files"].extend(
[
"AWSAPIGateway/AWSAPIGatewayClient.m",
"AWSCognitoAuth/AWSCognitoAuth.m",
"AWSCore/Service/AWSService.m",
"AWSIoT/AWSIoTDataService.m",
"AWSKinesis/AWSFirehoseService.m",
"AWSLex/AWSLexInteractionKit.m",
"AWSPinpoint/AWSPinpointTargeting/AWSPinpointTargetingService.m",
"AWSPolly/AWSPollySynthesizeSpeechURLBuilder.m",
"AWSS3/AWSS3PreSignedURL.m",
]
)
replace_files(self._root, service_pattern)
def bump_podspecs(self):
podspec_pattern1 = {
"match": r"(dependency[[:space:]]+'AWS.+'[[:space:]]*,[[:space:]]*')[0-9]+\.[0-9]+\.[0-9]+(')", # noqa: E501
"replace": r"\1{version}\2".format(version=self._new_sdk_version),
"files": [],
}
podspec_pattern2 = {
"match": r"(s\.version[[:space:]]+=[[:space:]]*')[0-9]+\.[0-9]+\.[0-9]+(')",
"replace": r"\1{version}\2".format(version=self._new_sdk_version),
"files": [],
}
for file in glob.glob(os.path.join(root, "*.podspec")):
podspec_pattern1["files"].append(file)
podspec_pattern2["files"].append(file)
replace_files(self._root, podspec_pattern1)
replace_files(self._root, podspec_pattern2)
def bump_changelog(self):
changelog_pattern = {
"match": r"## Unreleased",
"replace": "## Unreleased\\\n\\\n-Features for next release\\\n\\\n## {version}".format(
version=self._new_sdk_version
),
"files": ["CHANGELOG.md"],
}
replace_files(self._root, changelog_pattern)
def bump_generate_docs(self):
generate_documentation_pattern = {
"match": r'VERSION="[0-9]+\.[0-9]+\.[0-9]+"',
"replace": r'VERSION="{version}"'.format(version=self._new_sdk_version),
"files": ["CircleciScripts/generate_documentation.sh"],
}
replace_files(self._root, generate_documentation_pattern)
if __name__ == "__main__":
root = sys.argv[1]
new_sdk_version = sys.argv[2]
bumper = VersionBumper(root, new_sdk_version)
bumper.bump_sdk_version()