Skip to content

Commit

Permalink
Bug 1763188 - Add Snap support using TC builds
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Lissy committed Oct 29, 2024
1 parent f558f7d commit 5062911
Show file tree
Hide file tree
Showing 11 changed files with 487 additions and 20 deletions.
63 changes: 63 additions & 0 deletions mozregression/bisector.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,51 @@ def handle_merge(self):
return result


"""
We are just using this to make it clear we have no merge to take care of
We are running an Integration because builds are triggered from cron jobs
on mozilla-central for all Snap package branches
"""


class SnapHandler(IntegrationHandler):
snap_repo = None
_build_infos = {}
snap_rev = {}

def __init__(self, **kwargs):
super(IntegrationHandler, self).__init__(**kwargs)

def record_build_infos(self, build_infos):
self._build_infos["_changeset"] = build_infos._changeset
self._build_infos["_repo_url"] = build_infos._repo_url
self.snap_repo = build_infos._repo_url

def update_build_infos(self, build_infos):
# _build_infos here holds the mozilla-central ones,
# build_infos should be the snap-specific one
self.snap_rev[self._build_infos["_changeset"]] = build_infos.changeset
self.snap_repo = build_infos._repo_url

def get_pushlog_url(self):
# somehow, self.found_repo from this class would not reflect
first_rev, last_rev = self.get_range()
if first_rev == last_rev:
return "%s/pushloghtml?changeset=%s" % (self.snap_repo, first_rev)
return "%s/pushloghtml?fromchange=%s&tochange=%s" % (
self.snap_repo,
first_rev,
last_rev,
)

def revert_build_infos(self, build_infos):
build_infos._changeset = self._build_infos["_changeset"]
build_infos._repo_url = self._build_infos["_repo_url"]

def handle_merge(self):
return None


class IndexPromise(object):
"""
A promise to get a build index.
Expand Down Expand Up @@ -503,11 +548,29 @@ def start_dl(r):
return self.build_range.index(bdata)

def evaluate(self, build_infos):
# we force getting data from app info for snap since we are building everything
# out of mozilla-central
if isinstance(self.handler, SnapHandler):
self.handler.record_build_infos(build_infos)
build_infos._force_update = True
verdict = self.test_runner.evaluate(build_infos, allow_back=bool(self.history))
# old builds do not have metadata about the repo. But once
# the build is installed, we may have it
if self.handler.found_repo is None:
self.handler.found_repo = build_infos.repo_url
if isinstance(self.handler, SnapHandler):
# Some Snap nightly builds are missing SourceRepository/SourceStamp
# So since we dont have a better source of information, let's get back
# what we had
if build_infos.repo_url is None:
LOG.warning(
"Bisection on a Snap package missing SourceRepository/SourceStamp,"
" falling back to mozilla-central revs."
)
build_infos._force_update = False
self.handler.revert_build_infos(build_infos)
else:
self.handler.update_build_infos(build_infos)
return verdict

def ensure_good_and_bad(self):
Expand Down
5 changes: 5 additions & 0 deletions mozregression/branches.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ def create_branches():
):
for alias in aliases:
branches.set_alias(alias, name)

# All of the snap packages builds are done on mozilla-central cron jobs
for name in ("snap-nightly", "snap-beta", "snap-stable", "snap-esr"):
branches.set_branch(name, "mozilla-central")

return branches


Expand Down
5 changes: 3 additions & 2 deletions mozregression/build_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __init__(
task_id=None,
):
self._fetch_config = fetch_config
self._force_update = False # will be set True by SnapHandler
self._build_type = build_type
self._build_url = build_url
self._build_date = build_date
Expand Down Expand Up @@ -136,9 +137,9 @@ def update_from_app_info(self, app_info):
This helps to build the pushlog url for old nightlies.
"""
if self._changeset is None:
if self._changeset is None or self._force_update is True:
self._changeset = app_info.get("application_changeset")
if self._repo_url is None:
if self._repo_url is None or self._force_update is True:
self._repo_url = app_info.get("application_repository")

def persist_filename_for(self, data, regex=True):
Expand Down
47 changes: 47 additions & 0 deletions mozregression/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,21 @@ def create_parser(defaults):
help="Helps to write the configuration file.",
)

parser.add_argument(
"--allow-sudo",
action="store_true",
help=(
"[Snap] Allow the use of sudo for Snap install/remove operations (otherwise,"
" you will be prompted on each)"
),
)

parser.add_argument(
"--disable-snap-connect",
action="store_true",
help="[Snap] Do not automatically perform 'snap connect'",
)

parser.add_argument("--debug", "-d", action="store_true", help="Show the debug output.")

return parser
Expand Down Expand Up @@ -589,6 +604,11 @@ def validate(self):
"x86",
"x86_64",
],
"firefox-snap": [
"aarch64", # will be morphed into arm64
"arm", # will be morphed into armf
"x86_64", # will be morphed into amd64
],
}

user_defined_bits = options.bits is not None
Expand All @@ -607,6 +627,10 @@ def validate(self):
self.logger.warning(
"--arch ignored for Firefox for macOS as it uses unified binary."
)
elif options.app in ("firefox-snap"):
self.logger.warning(
"--arch ignored for Firefox Snap package."
)
options.arch = None
elif options.arch not in arch_options[options.app]:
raise MozRegressionError(
Expand All @@ -618,6 +642,29 @@ def validate(self):
f"`--arch` required for specified app ({options.app}). "
f"Please specify one of {', '.join(arch_options[options.app])}."
)
elif options.app == "firefox-snap" and options.allow_sudo is False:
self.logger.warning(
"Bisection on Snap package without --allow-sudo, you will be prompted for"
" credential on each 'snap' command."
)
elif options.allow_sudo is True and options.app != "firefox-snap":
raise MozRegressionError(
f"--allow-sudo specified for app ({options.app}), but only valid for "
f"firefox-snap. Please verify your config."
)
elif options.disable_snap_connect is True and options.app != "firefox-snap":
raise MozRegressionError(
f"--disable-snap-conncet specified for app ({options.app}), but only valid for "
f"firefox-snap. Please verify your config."
)

if options.app == "firefox-snap" and (
options.repo is None or not options.repo.startswith("snap-")
):
raise MozRegressionError(
f"--repo not specified for app ({options.app}), or not starting with snap-. "
f"Please use correct repo for bisecting Snap package."
)

fetch_config = create_config(
options.app, mozinfo.os, options.bits, mozinfo.processor, options.arch
Expand Down
86 changes: 86 additions & 0 deletions mozregression/fetch_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,3 +812,89 @@ def build_regex(self):
part = "mac"
psuffix = "-asan" if "asan" in self.build_type else ""
return r"jsshell-%s%s\.zip$" % (part, psuffix)


TIMESTAMP_SNAP_UPSTREAM_BUILD = to_utc_timestamp(datetime.datetime(2023, 7, 26, 9, 39, 21))
TIMESTAMP_SNAP_INDEX_RENAME = to_utc_timestamp(datetime.datetime(2023, 11, 17, 21, 46, 39))
# This needs to be updated when we land cross-compilation on treeherder
TIMESTAMP_SNAP_CROSS_COMPILATION = to_utc_timestamp(datetime.datetime(3023, 11, 21, 15, 15, 00))


class FirefoxSnapNightlyConfigMixin(NightlyConfigMixin):
def _get_nightly_repo(self, date):
return "mozilla-central"


class FirefoxSnapIntegrationConfigMixin(IntegrationConfigMixin):
def _idx_key(self, date):
branch_name = self.integration_branch.split("-")[-1]
valid_branches = ("nightly", "beta", "stable", "esr")
if branch_name not in valid_branches:
raise errors.MozRegressionError(
f"No such branch available ({branch_name}), valid are ','.join(valid_branches)"
" (prefix with snap- for --repo)"
)

if date < TIMESTAMP_SNAP_UPSTREAM_BUILD:
raise errors.MozRegressionError("No build before this date")
elif date >= TIMESTAMP_SNAP_UPSTREAM_BUILD and date < TIMESTAMP_SNAP_INDEX_RENAME:
index_base = ""
elif date >= TIMESTAMP_SNAP_INDEX_RENAME:
index_base = "{}-".format(self.arch)

if self.arch != "amd64" and date < TIMESTAMP_SNAP_CROSS_COMPILATION:
raise errors.MozRegressionError(f"No support for build other than amd64 ({self.arch}) provided")

return "{}{}".format(index_base, branch_name)

def tk_routes(self, push):
for build_type in self.build_types:
name = "gecko.v2.mozilla-central.revision.{}.firefox.{}{}".format(
push.changeset,
self._idx_key(push.timestamp),
"-{}".format(build_type)
if build_type != "opt" and build_type != "shippable"
else "",
)
yield name
self._inc_used_build()
return


class SnapCommonConfig(CommonConfig):
def should_use_archive(self):
# We only want to use TaskCluster builds
return False

def build_regex(self):
return r"(firefox_.*)\.snap"


@REGISTRY.register("firefox-snap")
class FirefoxSnapConfig(
SnapCommonConfig, FirefoxSnapIntegrationConfigMixin, FirefoxSnapNightlyConfigMixin
):
BUILD_TYPES = ("shippable", "opt", "debug")
BUILD_TYPE_FALLBACKS = {
"shippable": ("opt",),
"opt": ("shippable",),
}

def __init__(self, os, bits, processor, arch):
super(FirefoxSnapConfig, self).__init__(os, bits, processor, arch)
self.set_build_type("shippable")

def available_archs(self):
return [
"aarch64",
"arm",
"x86_64",
]

def set_arch(self, arch):
mapping = {
"aarch64": "arm64",
"arm": "armhf",
"x86_64": "amd64",
}
self.arch = mapping.get(arch, "amd64")
Loading

0 comments on commit 5062911

Please sign in to comment.