Skip to content

Bug 1763188 - Add Snap support using TC builds #1450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading