diff --git a/Scripts/bump_build_tag.py b/Scripts/bump_build_tag.py
index dc7d3385a0..48874ea30c 100755
--- a/Scripts/bump_build_tag.py
+++ b/Scripts/bump_build_tag.py
@@ -1,435 +1,80 @@
#!/usr/bin/env python3
-import sys
-import os
-import re
-import subprocess
+
import argparse
-import inspect
-import feature_flags_common
-from datetime import date
+import plistlib
+import subprocess
+
+INFO_PLIST_PATHS = [
+ "Signal/Signal-Info.plist",
+ "SignalShareExtension/Info.plist",
+ "SignalNSE/Info.plist",
+]
-def fail(message):
- file_name = __file__
- current_line_no = inspect.stack()[1][2]
- current_function_name = inspect.stack()[1][3]
- print("Failure in:", file_name, current_line_no, current_function_name)
- print(message)
- sys.exit(1)
+def run(args):
+ subprocess.run(args, check=True)
-def execute_command(command):
- try:
- print(" ".join(command))
- output = subprocess.check_output(command, text=True)
- if output:
- print(output)
- except subprocess.CalledProcessError as e:
- print(e.output)
- sys.exit(1)
+def capture(args):
+ return subprocess.run(args, check=True, capture_output=True, encoding="utf8").stdout
-def find_project_root():
- path = os.path.abspath(os.curdir)
-
- while True:
- # print 'path', path
- if not os.path.exists(path):
- break
- git_path = os.path.join(path, ".git")
- if os.path.exists(git_path):
- return path
- new_path = os.path.abspath(os.path.dirname(path))
- if not new_path or new_path == path:
- break
- path = new_path
-
- fail("Could not find project root path")
-
-
-def is_valid_version_1(value):
- regex = re.compile(r"^(\d+)$")
- match = regex.search(value)
- return match is not None
-
-
-def is_valid_version_3(value):
- regex = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
- match = regex.search(value)
- return match is not None
-
-
-def is_valid_version_4(value):
- regex = re.compile(r"^(\d+)\.(\d+)\.(\d+).(\d+)$")
- match = regex.search(value)
- return match is not None
-
-
-def set_versions(plist_file_path, release_version, build_version_1, build_version_4):
- if not is_valid_version_3(release_version):
- fail("Invalid release version: %s" % release_version)
- if not is_valid_version_1(build_version_1):
- fail("Invalid build version 1: %s" % build_version_1)
- if not is_valid_version_4(build_version_4):
- fail("Invalid build version 4: %s" % build_version_4)
-
- with open(plist_file_path, "rt") as f:
- text = f.read()
- # print 'text', text
-
- # CFBundleShortVersionString is the release version.
- #
- # CFBundleShortVersionString
- # 2.20.0
- file_regex = re.compile(
- r"CFBundleShortVersionString\s*([\d\.]+)",
- re.MULTILINE,
- )
- file_match = file_regex.search(text)
- # print 'match', match
- if not file_match:
- fail("Could not parse .plist")
- text = text[: file_match.start(1)] + release_version + text[file_match.end(1) :]
-
- # CFBundleVersion is the build version 1.
- #
- # CFBundleVersion
- # 3
- file_regex = re.compile(
- r"CFBundleVersion\s*([\d\.]+)", re.MULTILINE
- )
- file_match = file_regex.search(text)
- # print 'match', match
- if not file_match:
- fail("Could not parse .plist")
- text = text[: file_match.start(1)] + build_version_1 + text[file_match.end(1) :]
-
- with open(plist_file_path, "wt") as f:
- f.write(text)
-
-
-# Represents a version string with 1 values, e.g. 1.
-class Version1:
- def __init__(self, build):
- self.build = build
-
- def formatted(self):
- return str(self.build)
-
-
-# Represents a version string with 3 dotted values, e.g. 1.2.3.
-class Version3:
+class Version:
def __init__(self, major, minor, patch):
self.major = major
self.minor = minor
self.patch = patch
- def formatted(self):
- return str(self.major) + "." + str(self.minor) + "." + str(self.patch)
+ def pretty(self):
+ return f"{self.major}.{self.minor}.{self.patch}"
+
+ def pretty2(self):
+ assert self.patch == 0
+ return f"{self.major}.{self.minor}"
-# Represents a version string with 4 dotted values, e.g. 1.2.3.4.
-class Version4:
- def __init__(self, major, minor, patch, build):
- self.major = major
- self.minor = minor
- self.patch = patch
- self.build = build
-
- def formatted(self):
- return (
- str(self.major)
- + "."
- + str(self.minor)
- + "."
- + str(self.patch)
- + "."
- + str(self.build)
- )
-
- def asVersion3(self):
- return Version3(self.major, self.minor, self.patch)
+def parse_version(value):
+ components = list(map(int, value.split(".")))
+ assert len(components) in (2, 3)
+ while len(components) < 3:
+ components.append(0)
+ return Version(components[0], components[1], components[2])
-def parse_version_4(text):
- # print 'text', text
- regex = re.compile(r"^(\d+)\.(\d+)\.(\d+)\.?(\d+)?$")
- match = regex.search(text)
- # print 'match', match
- if not match:
- fail("Could not parse .plist")
- if len(match.groups()) < 3 or len(match.groups()) > 4:
- fail("Could not parse .plist")
- major = int(match.group(1))
- minor = int(match.group(2))
- patch = int(match.group(3))
- if match.group(4) != None:
- build = int(match.group(4))
- else:
- build = 0
-
- version = Version4(major, minor, patch, build)
- # Verify that roundtripping yields the same value (or a version3 equivalent)
- if version.formatted() != text and version.asVersion3().formatted() != text:
- fail("Could not parse .plist")
-
- return version
-
-
-def parse_version_1(text):
- build = int(text)
-
- version = Version1(build)
-
- # Verify that roundtripping yields the same value.
- if version.formatted() != text:
- fail("Could not parse .plist")
-
- return version
-
-
-def get_versions(plist_file_path):
- with open(plist_file_path, "rt") as f:
- text = f.read()
- # print 'text', text
-
- # CFBundleShortVersionString identifies the release track.
- # CFBundleVersion uniqely identifies the build within the release track.
- #
- # Previously, we used version strings like this:
- #
- # CFBundleShortVersionString
- # 2.13.0
- # CFBundleVersion
- # 2.13.0.13
- #
- # We now use version strings like this:
- #
- # CFBundleShortVersionString
- # 2.13.0
- # CFBundleVersion
- # 13
- #
- # See:
- #
- # * https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleshortversionstring
- # * https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleversion
- # * https://developer.apple.com/library/archive/technotes/tn2420/_index.html
- release_version_regex = re.compile(
- r"CFBundleShortVersionString\s*(\d+\.\d+\.\d+)",
- re.MULTILINE,
- )
- release_version_match = release_version_regex.search(text)
- # print 'match', match
- if not release_version_match:
- fail("Could not parse .plist")
-
- build_version_1_regex = re.compile(
- r"CFBundleVersion\s*(\d+)", re.MULTILINE
- )
- build_version_1_match = build_version_1_regex.search(text)
- # print 'match', match
- if not build_version_1_match:
- fail("Could not parse .plist")
-
- release_version_str = release_version_match.group(1)
- print("CFBundleShortVersionString:", release_version_str)
- release_version = parse_version_4(release_version_str).asVersion3()
- print("old_release_version:", release_version.formatted())
-
- build_version_1_str = build_version_1_match.group(1)
- print("CFBundleVersion:", build_version_1_str)
- build_version_1 = parse_version_1(build_version_1_str)
- print("old_build_version_1:", build_version_1.formatted())
-
- return release_version, build_version_1
-
-
-def get_tag_variant(args):
- is_internal = args.internal
- is_nightly = args.nightly
- is_beta = args.beta
-
- argument_tag = ""
- if is_internal:
- argument_tag = "internal"
- elif is_nightly:
- argument_tag = "nightly"
- elif is_beta:
- argument_tag = "beta"
-
- current_flag = feature_flags_common.get_feature_flag()
-
- # Some of these flags are legacy.
- if current_flag in ["internal"]:
- feature_flag_tag = "internal"
- elif current_flag in ["beta"]:
- feature_flag_tag = "beta"
- elif current_flag in ["production"]:
- feature_flag_tag = ""
- else:
- print("Unrecognized feature flag: " + current_flag)
- feature_flag_tag = None
-
- if is_nightly or feature_flag_tag == None:
- # Just trust the tag variant specified via argument if:
- # - It's a nightly build. Those are automated and we shouldn't bug a script with interactive input requests
- # - We don't recognize the build variant.
- return argument_tag
- elif argument_tag == feature_flag_tag:
- return argument_tag
- else:
- # A mismatch! Let's check with the user to see if they really wanted
- # a tag variant that matched the current feature flag.
- argument_tag_string = argument_tag if len(argument_tag) > 0 else "production"
- feature_flag_tag_string = (
- feature_flag_tag if len(feature_flag_tag) > 0 else "production"
- )
-
- print(
- "Feature flag mismatch! Arguments specify a "
- + argument_tag_string
- + " tag but the current feature flag indicates a "
- + feature_flag_tag_string
- + " tag may be more appropriate."
- )
- prefer_feature_flag = input(
- "Proceed with a " + feature_flag_tag_string + " instead? (Y/n) "
- )
-
- if len(prefer_feature_flag) == 0 or prefer_feature_flag[0] in "Yy":
- return feature_flag_tag
- else:
- return argument_tag
+def set_version(path, version):
+ with open(path, "rb") as file:
+ contents = plistlib.load(file)
+ contents["CFBundleShortVersionString"] = version.pretty()
+ with open(path, "wb") as file:
+ plistlib.dump(contents, file)
if __name__ == "__main__":
- parser = argparse.ArgumentParser(description="Precommit cleanup script.")
- parser.add_argument("--version", help="used for starting a new version.")
-
- type_group = parser.add_mutually_exclusive_group()
- type_group.add_argument(
- "--internal", action="store_true", help="used to indicate throwaway builds."
+ parser = argparse.ArgumentParser(description="bumps the marketing version")
+ parser.add_argument(
+ "--version",
+ metavar="x.y.z",
+ required=True,
+ help="specify the new marketing version number",
)
- type_group.add_argument(
- "--nightly", action="store_true", help="used to indicate nightly builds."
- )
- type_group.add_argument(
- "--beta", action="store_true", help="used to indicate beta builds."
+ parser.add_argument(
+ "--nightly", action="store_true", help="specify that this is a nightly build"
)
- args = parser.parse_args()
- tag_variant = get_tag_variant(args)
+ ns = parser.parse_args()
- project_root_path = find_project_root()
- # print 'project_root_path', project_root_path
- # plist_path
- main_plist_path = os.path.join(project_root_path, "Signal", "Signal-Info.plist")
- if not os.path.exists(main_plist_path):
- fail("Could not find main app info .plist")
-
- sae_plist_path = os.path.join(
- project_root_path, "SignalShareExtension", "Info.plist"
- )
- if not os.path.exists(sae_plist_path):
- fail("Could not find share extension info .plist")
-
- nse_plist_path = os.path.join(project_root_path, "SignalNSE", "Info.plist")
- if not os.path.exists(nse_plist_path):
- fail("Could not find NSE info .plist")
-
- output = subprocess.check_output(["git", "status", "--porcelain"], text=True)
- if len(output.strip()) > 0:
+ output = capture(["git", "status", "--porcelain"]).rstrip()
+ if len(output) > 0:
print(output)
- fail("Git repository has untracked files.")
- output = subprocess.check_output(["git", "diff", "--shortstat"], text=True)
- if len(output.strip()) > 0:
- print(output)
- fail("Git repository has untracked files.")
+ print("Repository has uncommitted changes.")
+ exit(1)
- # Ensure .plist is in xml format, not binary.
- plist_paths = [
- main_plist_path,
- sae_plist_path,
- nse_plist_path,
- ]
- for plist_path in plist_paths:
- print("plist_path:", plist_path)
+ version = parse_version(ns.version)
- output = subprocess.check_output(
- ["plutil", "-convert", "xml1", plist_path], text=True
- )
- # print 'output', output
+ for path in INFO_PLIST_PATHS:
+ set_version(path, version)
- # ---------------
- # Main App
- # ---------------
-
- old_release_version, old_build_version_1 = get_versions(main_plist_path)
-
- if args.version:
- # Update version to the provided argument
- # e.g. --version 1.2.3 -> "1.2.3", "0"
- # e.g. --version 1.2.3.4 -> "1.2.3" "4"
- new_build_version_4 = parse_version_4(args.version.strip())
- new_build_version_1 = Version1(new_build_version_4.build)
- new_release_version_3 = new_build_version_4.asVersion3()
- # print 'new_release_version_3:', new_release_version_3.formatted()
-
- else:
- # Bump patch.
- new_release_version_3 = old_release_version
- new_build_version_1 = Version1(old_build_version_1.build + 1)
- new_build_version_4 = Version4(
- new_release_version_3.major,
- new_release_version_3.minor,
- new_release_version_3.patch,
- old_build_version_1.build + 1,
- )
-
- new_release_version_3 = new_release_version_3.formatted()
- new_build_version_1 = new_build_version_1.formatted()
- new_build_version_4 = new_build_version_4.formatted()
-
- # For example:
- #
- # old_release_version: 5.19.0
- # old_build_version_1: 42
- # new_release_version_3: 5.19.0
- # new_build_version_1: 43
- # new_build_version_4: 5.19.0.43
- print("new_release_version_3:", new_release_version_3)
- print("new_build_version_1:", new_build_version_1)
- print("new_build_version_4:", new_build_version_4)
-
- for plist_path in plist_paths:
- set_versions(
- plist_path, new_release_version_3, new_build_version_1, new_build_version_4
- )
-
- # ---------------
- # Git
- # ---------------
- command = ["git", "add", "."]
- execute_command(command)
-
- if tag_variant == "internal":
- commit_message = '"Bump build to %s." (Internal)' % new_build_version_4
- elif tag_variant == "beta":
- commit_message = '"Bump build to %s." (Beta)' % new_build_version_4
- elif tag_variant == "nightly":
- commit_message = '"Bump build to %s." (nightly-%s)' % (
- new_build_version_4,
- date.today().strftime("%m-%d-%Y"),
- )
- else:
- commit_message = '"Bump build to %s."' % new_build_version_4
- command = ["git", "commit", "-m", commit_message]
- execute_command(command)
-
- tag_name = new_build_version_4
- if len(tag_variant) > 0:
- tag_name += "-" + tag_variant
-
- command = ["git", "tag", tag_name]
- execute_command(command)
+ run(["git", "add", *INFO_PLIST_PATHS])
+ run(["git", "commit", "-m", f"Bump version to {version.pretty()}"])
+ if version.patch == 0:
+ run(["git", "tag", f"version-{version.pretty2()}"])
diff --git a/Scripts/feature_flags_common.py b/Scripts/feature_flags_common.py
index e395ba15d0..d6568447bd 100755
--- a/Scripts/feature_flags_common.py
+++ b/Scripts/feature_flags_common.py
@@ -36,15 +36,6 @@ extension FeatureBuild {
)
-def extract(value):
- return value.split("\n")[11][40:]
-
-
-def get_feature_flag():
- with open(FILE_PATH, "r") as file:
- return extract(file.read())
-
-
def set_feature_flags(new_flags_level):
output = capture(["git", "status", "--porcelain"]).rstrip()
if len(output) > 0:
@@ -53,7 +44,6 @@ def set_feature_flags(new_flags_level):
exit(1)
new_value = generate(new_flags_level)
- assert extract(new_value) == str(new_flags_level)
with open(FILE_PATH, "r") as file:
old_value = file.read()
diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist
index c5ec3ec9b9..25dc1c9ba8 100644
--- a/Signal/Signal-Info.plist
+++ b/Signal/Signal-Info.plist
@@ -44,7 +44,7 @@
CFBundleVersion
- 4
+ 0
ITSAppUsesNonExemptEncryption
LOGS_EMAIL
diff --git a/SignalNSE/Info.plist b/SignalNSE/Info.plist
index 95739d4fc9..695b457e3e 100644
--- a/SignalNSE/Info.plist
+++ b/SignalNSE/Info.plist
@@ -19,7 +19,7 @@
CFBundleShortVersionString
7.8.0
CFBundleVersion
- 4
+ 0
NSAppTransportSecurity
NSExceptionDomains
diff --git a/SignalShareExtension/Info.plist b/SignalShareExtension/Info.plist
index 7d3696e96a..110f35fd22 100644
--- a/SignalShareExtension/Info.plist
+++ b/SignalShareExtension/Info.plist
@@ -19,7 +19,7 @@
CFBundleShortVersionString
7.8.0
CFBundleVersion
- 4
+ 0
ITSAppUsesNonExemptEncryption
NSAppTransportSecurity