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 9d8d3c8
Show file tree
Hide file tree
Showing 10 changed files with 504 additions and 19 deletions.
85 changes: 85 additions & 0 deletions mozregression/bisector.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,75 @@ def handle_merge(self):
return result


class SnapHandler(IntegrationHandler):
"""
Snap packages for all snap branches are built from cron jobs on
mozilla-central, so it maps into the IntegrationHandler as per the
definition of mozregression.
All branches of the snap package have their own pinning of the source
repository / source code information, so the changeset on mozilla-central
and other informations are not relevant and it is required to extract it
from somewhere else (e.g., application.ini).
There are also no merge to account for so handle_merge() is a no-op.
"""

snap_repo = None
_build_infos = {}
snap_rev = {}

def record_build_infos(self, build_infos):
"""
The way the build infos is produced on mozilla-central does not match
requirements of snap package: it is required that the information is
extracted from a different source since all branches of the snap
package are built from mozilla-central. Without this, then the buildid,
changeset and repository informations are matching mozilla-central when
the build was triggered and not what the snap package was built from.
"""
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):
"""
Keep track of the Snap-specific build infos that have been collected
and given in parameter in build_infos, and keep a copy of the
mozilla-central ones within _build_infos if it is required to revert
later.
"""
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):
"""
Some old Snap nightly builds are missing SourceRepository/SourceStamp
Since there is not a better source of information, revert to the
informations provided by mozilla-central.
"""
build_infos._changeset = self._build_infos["_changeset"]
build_infos._repo_url = self._build_infos["_repo_url"]

def handle_merge(self):
"""
No-op by definition of how the snap packages are built on
mozilla-central cron jobs.
"""
return None


class IndexPromise(object):
"""
A promise to get a build index.
Expand Down Expand Up @@ -503,11 +572,27 @@ 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)
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."
)
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
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 9d8d3c8

Please sign in to comment.