summaryrefslogtreecommitdiffstats
path: root/python/mozrelease/mozrelease/update_verify.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/mozrelease/mozrelease/update_verify.py')
-rw-r--r--python/mozrelease/mozrelease/update_verify.py275
1 files changed, 275 insertions, 0 deletions
diff --git a/python/mozrelease/mozrelease/update_verify.py b/python/mozrelease/mozrelease/update_verify.py
new file mode 100644
index 0000000000..49fe21db15
--- /dev/null
+++ b/python/mozrelease/mozrelease/update_verify.py
@@ -0,0 +1,275 @@
+# -*- coding: utf-8 -*-
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import re
+
+from six import string_types
+
+from .chunking import getChunk
+
+
+class UpdateVerifyError(Exception):
+ pass
+
+
+class UpdateVerifyConfig(object):
+ comment_regex = re.compile("^#")
+ key_write_order = (
+ "release",
+ "product",
+ "platform",
+ "build_id",
+ "locales",
+ "channel",
+ "patch_types",
+ "from",
+ "aus_server",
+ "ftp_server_from",
+ "ftp_server_to",
+ "to",
+ "mar_channel_IDs",
+ "override_certs",
+ "to_build_id",
+ "to_display_version",
+ "to_app_version",
+ "updater_package",
+ )
+ global_keys = (
+ "product",
+ "channel",
+ "aus_server",
+ "to",
+ "to_build_id",
+ "to_display_version",
+ "to_app_version",
+ "override_certs",
+ )
+ release_keys = (
+ "release",
+ "build_id",
+ "locales",
+ "patch_types",
+ "from",
+ "ftp_server_from",
+ "ftp_server_to",
+ "mar_channel_IDs",
+ "platform",
+ "updater_package",
+ )
+ first_only_keys = (
+ "from",
+ "aus_server",
+ "to",
+ "to_build_id",
+ "to_display_version",
+ "to_app_version",
+ "override_certs",
+ )
+ compare_attrs = global_keys + ("releases",)
+
+ def __init__(
+ self,
+ product=None,
+ channel=None,
+ aus_server=None,
+ to=None,
+ to_build_id=None,
+ to_display_version=None,
+ to_app_version=None,
+ override_certs=None,
+ ):
+ self.product = product
+ self.channel = channel
+ self.aus_server = aus_server
+ self.to = to
+ self.to_build_id = to_build_id
+ self.to_display_version = to_display_version
+ self.to_app_version = to_app_version
+ self.override_certs = override_certs
+ self.releases = []
+
+ def __eq__(self, other):
+ self_list = [getattr(self, attr) for attr in self.compare_attrs]
+ other_list = [getattr(other, attr) for attr in self.compare_attrs]
+ return self_list == other_list
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def _parseLine(self, line):
+ entry = {}
+ items = re.findall(r"\w+=[\"'][^\"']*[\"']", line)
+ for i in items:
+ m = re.search(r"(?P<key>\w+)=[\"'](?P<value>.+)[\"']", i).groupdict()
+ if m["key"] not in self.global_keys and m["key"] not in self.release_keys:
+ raise UpdateVerifyError(
+ "Unknown key '%s' found on line:\n%s" % (m["key"], line)
+ )
+ if m["key"] in entry:
+ raise UpdateVerifyError(
+ "Multiple values found for key '%s' on line:\n%s" % (m["key"], line)
+ )
+ entry[m["key"]] = m["value"]
+ if not entry:
+ raise UpdateVerifyError("No parseable data in line '%s'" % line)
+ return entry
+
+ def _addEntry(self, entry, first):
+ releaseKeys = {}
+ for k, v in entry.items():
+ if k in self.global_keys:
+ setattr(self, k, entry[k])
+ elif k in self.release_keys:
+ # "from" is reserved in Python
+ if k == "from":
+ releaseKeys["from_path"] = v
+ else:
+ releaseKeys[k] = v
+ self.addRelease(**releaseKeys)
+
+ def read(self, config):
+ f = open(config)
+ # Only the first non-comment line of an update verify config should
+ # have a "from" and"ausServer". Ignore any subsequent lines with them.
+ first = True
+ for line in f.readlines():
+ # Skip comment lines
+ if self.comment_regex.search(line):
+ continue
+ self._addEntry(self._parseLine(line), first)
+ first = False
+
+ def write(self, fh):
+ first = True
+ for releaseInfo in self.releases:
+ for key in self.key_write_order:
+ if key in self.global_keys and (
+ first or key not in self.first_only_keys
+ ):
+ value = getattr(self, key)
+ elif key in self.release_keys:
+ value = releaseInfo[key]
+ else:
+ value = None
+ if value is not None:
+ fh.write(key.encode("utf-8"))
+ fh.write(b"=")
+ if isinstance(value, (list, tuple)):
+ fh.write(('"%s" ' % " ".join(value)).encode("utf-8"))
+ else:
+ fh.write(('"%s" ' % value).encode("utf-8"))
+ # Rewind one character to avoid having a trailing space
+ fh.seek(-1, os.SEEK_CUR)
+ fh.write(b"\n")
+ first = False
+
+ def addRelease(
+ self,
+ release=None,
+ build_id=None,
+ locales=[],
+ patch_types=["complete"],
+ from_path=None,
+ ftp_server_from=None,
+ ftp_server_to=None,
+ mar_channel_IDs=None,
+ platform=None,
+ updater_package=None,
+ ):
+ """Locales and patch_types can be passed as either a string or a list.
+ If a string is passed, they will be converted to a list for internal
+ storage"""
+ if self.getRelease(build_id, from_path):
+ raise UpdateVerifyError(
+ "Couldn't add release identified by build_id '%s' and from_path '%s': "
+ "already exists in config" % (build_id, from_path)
+ )
+ if isinstance(locales, string_types):
+ locales = sorted(list(locales.split()))
+ if isinstance(patch_types, string_types):
+ patch_types = list(patch_types.split())
+ self.releases.append(
+ {
+ "release": release,
+ "build_id": build_id,
+ "locales": locales,
+ "patch_types": patch_types,
+ "from": from_path,
+ "ftp_server_from": ftp_server_from,
+ "ftp_server_to": ftp_server_to,
+ "mar_channel_IDs": mar_channel_IDs,
+ "platform": platform,
+ "updater_package": updater_package,
+ }
+ )
+
+ def addLocaleToRelease(self, build_id, locale, from_path=None):
+ r = self.getRelease(build_id, from_path)
+ if not r:
+ raise UpdateVerifyError(
+ "Couldn't add '%s' to release identified by build_id '%s' and from_path '%s': "
+ "'%s' doesn't exist in this config."
+ % (locale, build_id, from_path, build_id)
+ )
+ r["locales"].append(locale)
+ r["locales"] = sorted(r["locales"])
+
+ def getRelease(self, build_id, from_path):
+ for r in self.releases:
+ if r["build_id"] == build_id and r["from"] == from_path:
+ return r
+ return {}
+
+ def getFullReleaseTests(self):
+ return [r for r in self.releases if r["from"] is not None]
+
+ def getQuickReleaseTests(self):
+ return [r for r in self.releases if r["from"] is None]
+
+ def getChunk(self, chunks, thisChunk):
+ fullTests = []
+ quickTests = []
+ for test in self.getFullReleaseTests():
+ for locale in test["locales"]:
+ fullTests.append([test["build_id"], locale, test["from"]])
+ for test in self.getQuickReleaseTests():
+ for locale in test["locales"]:
+ quickTests.append([test["build_id"], locale, test["from"]])
+ allTests = getChunk(fullTests, chunks, thisChunk)
+ allTests.extend(getChunk(quickTests, chunks, thisChunk))
+
+ newConfig = UpdateVerifyConfig(
+ self.product,
+ self.channel,
+ self.aus_server,
+ self.to,
+ self.to_build_id,
+ self.to_display_version,
+ self.to_app_version,
+ self.override_certs,
+ )
+ for t in allTests:
+ build_id, locale, from_path = t
+ if from_path == "None":
+ from_path = None
+ r = self.getRelease(build_id, from_path)
+ try:
+ newConfig.addRelease(
+ r["release"],
+ build_id,
+ locales=[],
+ ftp_server_from=r["ftp_server_from"],
+ ftp_server_to=r["ftp_server_to"],
+ patch_types=r["patch_types"],
+ from_path=from_path,
+ mar_channel_IDs=r["mar_channel_IDs"],
+ platform=r["platform"],
+ updater_package=r["updater_package"],
+ )
+ except UpdateVerifyError:
+ pass
+ newConfig.addLocaleToRelease(build_id, locale, from_path)
+ return newConfig