def massage_semver_cols_into_release_object_data(kwargs): """ Helper function that takes kwargs as an argument and massages into it the release semver columns (if possible) Inputs: * kwargs: data of the release that is about to be created """ if "version" in kwargs: try: version_info = parse_release(kwargs["version"]) package = version_info.get("package") version_parsed = version_info.get("version_parsed") if version_parsed is not None and all( validate_bigint(version_parsed[field]) for field in ("major", "minor", "patch", "revision")): build_code = version_parsed.get("build_code") build_number = ReleaseQuerySet._convert_build_code_to_build_number( build_code) kwargs.update({ "major": version_parsed.get("major"), "minor": version_parsed.get("minor"), "patch": version_parsed.get("patch"), "revision": version_parsed.get("revision"), "prerelease": version_parsed.get("pre") or "", "build_code": build_code, "build_number": build_number, "package": package, }) except RelayError: # This can happen on invalid legacy releases pass
def __init__(self, activity: Activity) -> None: super().__init__(activity) self.group = None self.organization = self.project.organization self.user_id_team_lookup: Optional[Mapping[int, List[int]]] = None self.email_list: Set[str] = set() self.user_ids: Set[int] = set() self.deploy = get_deploy(activity) self.release = get_release(activity, self.organization) if not self.release: self.repos: Iterable[Mapping[str, Any]] = set() self.projects: Set[Project] = set() self.version = "unknown" self.version_parsed = self.version return self.projects = set(self.release.projects.all()) self.commit_list = get_commits_for_release(self.release) self.email_list = { c.author.email for c in self.commit_list if c.author } users = get_users_by_emails(self.email_list, self.organization) self.user_ids = {u.id for u in users.values()} self.repos = get_repos(self.commit_list, users, self.organization) self.environment = get_environment_for_deploy(self.deploy) self.group_counts_by_project = get_group_counts_by_project( self.release, self.projects) self.version = self.release.version self.version_parsed = parse_release(self.version)["description"]
def test_parse_release(): parsed = sentry_relay.parse_release("[email protected]+20200101100") assert parsed == { "build_hash": None, "description": "1.0-rc1 (20200101100)", "package": "org.example.FooApp", "version_parsed": { "build_code": "20200101100", "components": 2, "major": 1, "minor": 0, "patch": 0, "pre": "rc1", }, "version_raw": "1.0rc1+20200101100", }
def is_semver_version(version): """ Method that checks if a version follows semantic versioning """ # If version is not a valid release version, or it has no package then we return False if not Release.is_valid_version(version) or "@" not in version: return False try: version_info = parse_release(version) version_parsed = version_info.get("version_parsed") return version_parsed is not None and all( validate_bigint(version_parsed[field]) for field in ("major", "minor", "patch", "revision")) except RelayError: # This can happen on invalid legacy releases return False
def is_semver_version(version): """ Method that checks if a version follows semantic versioning """ if not Release.is_valid_version(version): return False # Release name has to contain package_name to be parsed correctly by parse_release version = version if "@" in version else f"{SEMVER_FAKE_PACKAGE}@{version}" try: version_info = parse_release(version) version_parsed = version_info.get("version_parsed") return version_parsed is not None and all( validate_bigint(version_parsed[field]) for field in ("major", "minor", "patch", "revision")) except RelayError: # This can happen on invalid legacy releases return False
def test_sends_deployment_notification(self): """ Test that an email AND Slack notification are sent with the expected values when a release is deployed. """ release = self.create_release() version_parsed = self.version_parsed = parse_release( release.version)["description"] url = f"/api/0/organizations/{self.organization.slug}/releases/{release.version}/deploys/" with self.tasks(): response = self.client.post( url, format="json", data={"environment": self.environment.name}) assert response.status_code == 201, response.content msg = mail.outbox[0] # check the txt version assert f"Version {version_parsed} was deployed to {self.environment.name} on" in msg.body # check the html version assert ( f"Version {version_parsed} was deployed to {self.environment.name}\n </h2>\n" in msg.alternatives[0][0]) attachment, text = get_attachment() assert ( text == f"Release {version_parsed} was deployed to {self.environment.name} for this project" ) assert ( attachment["actions"][0]["url"] == f"http://testserver/organizations/{self.organization.slug}/releases/{release.version}/?project={self.project.id}&unselectedSeries=Healthy/" ) assert ( attachment["footer"] == f"{self.project.slug} | <http://testserver/settings/account/notifications/deploy/?referrer=release-activity-slack-user|Notification Settings>" )
def version_info(self): try: return parse_release(self.version) except RelayError: # This can happen on invalid legacy releases return None
def __init__(self, activity: Activity) -> None: super().__init__(activity) self.version = self.activity.data.get("version", "") self.version_parsed = parse_release(self.version)["description"]
def test_parse_release_error(): with pytest.raises(sentry_relay.InvalidReleaseErrorBadCharacters): sentry_relay.parse_release("/var/foo/foo")
def has_resolution(cls, group, release): """ Determine if a resolution exists for the given group and release. This is used to suggest if a regression has occurred. """ def compare_release_dates_for_in_next_release(res_release, res_release_datetime, release): """ Helper function that compares release versions based on date for `GroupResolution.Type.in_next_release` """ return res_release == release.id or res_release_datetime > release.date_added try: res_type, res_release, res_release_datetime, current_release_version = ( cls.objects.filter( group=group).select_related("release").values_list( "type", "release__id", "release__date_added", "current_release_version")[0]) except IndexError: return False # if no release is present, we assume we've gone from "no release" to "some release" # in application configuration, and thus this must be older if not release: return True # if current_release_version was set, then it means that initially Group was resolved in # next release, which means a release will have a resolution if it is the same as # `current_release_version` or was released before it according to either its semver version # or its date. We make that decision based on whether the project follows semantic # versioning or not if current_release_version: follows_semver = follows_semver_versioning_scheme( project_id=group.project.id, org_id=group.organization.id, release_version=release.version, ) if follows_semver: try: # If current_release_version == release.version => 0 # If current_release_version < release.version => -1 # If current_release_version > release.version => 1 current_release_raw = parse_release( current_release_version).get("version_raw") release_raw = parse_release( release.version).get("version_raw") return compare_version_relay(current_release_raw, release_raw) >= 0 except RelayError: ... else: try: current_release_obj = Release.objects.get( organization_id=group.organization.id, version=current_release_version) return compare_release_dates_for_in_next_release( res_release=current_release_obj.id, res_release_datetime=current_release_obj.date_added, release=release, ) except Release.DoesNotExist: ... # We still fallback to the older model if either current_release_version was not set ( # i.e. In all resolved cases except for Resolved in Next Release) or if for whatever # reason the semver/date checks fail (which should not happen!) if res_type in (None, cls.Type.in_next_release): # Add metric here to ensure that this code branch ever runs given that # clear_expired_resolutions changes the type to `in_release` once a Release instance # is created metrics.incr("groupresolution.has_resolution.in_next_release", sample_rate=1.0) return compare_release_dates_for_in_next_release( res_release=res_release, res_release_datetime=res_release_datetime, release=release) elif res_type == cls.Type.in_release: if res_release == release.id: return False if res_release_datetime < release.date_added: return False return True else: raise NotImplementedError
def get(self, request: Request) -> Response: org = Organization(id=1, slug="organization", name="My Company") projects = [ Project(id=1, organization=org, slug="project", name="My Project"), Project(id=2, organization=org, slug="another-project", name="Another Project"), Project(id=3, organization=org, slug="yet-another-project", name="Yet Another Project"), ] version = "6c998f755f304593a4713abd123eaf8833a2de5e" version_parsed = parse_release(version)["description"] release = Release( organization_id=org.id, version=version, date_added=datetime.datetime(2016, 10, 12, 15, 39, tzinfo=pytz.utc), ) deploy = Deploy( release=release, organization_id=org.id, environment_id=1, date_finished=datetime.datetime(2016, 10, 12, 15, 39, tzinfo=pytz.utc), ) release_links = [ absolute_uri(f"/organizations/{org.slug}/releases/{release.version}/?project={p.id}") for p in projects ] repos = [ { "name": "getsentry/getsentry", "commits": [ ( Commit( key="48b86fcd677da3dba5679d7a738240ce6fb74b20", date_added=datetime.datetime(2016, 10, 11, 15, 39, tzinfo=pytz.utc), ), None, ), ( Commit( key="a53a2756bb8d111b43196210b34df90b87ed336b", message="Fix billing", author=CommitAuthor(name="David Cramer", email="*****@*****.**"), date_added=datetime.datetime(2016, 10, 11, 16, 45, tzinfo=pytz.utc), ), User(email="*****@*****.**", name="David Cramer"), ), ], }, { "name": "getsentry/sentry", "commits": [ ( Commit( key="3c8eb3b4af6ee2a29c68daa188fc730c8e4b39fd", date_added=datetime.datetime(2016, 10, 10, 15, 39, tzinfo=pytz.utc), ), None, ), ( Commit( key="373562702009df1692da6eb80a933139f29e094b", message="Fix padding", author=CommitAuthor(name="Chris Jennings", email="*****@*****.**"), date_added=datetime.datetime(2016, 10, 10, 16, 39, tzinfo=pytz.utc), ), None, ), ( Commit( key="631cd9096bd9811a046a472bb0aa8b573e86e1f1", message="Update README.rst", author=CommitAuthor(name="David Cramer", email="*****@*****.**"), date_added=datetime.datetime(2016, 10, 11, 10, 39, tzinfo=pytz.utc), ), User(email="*****@*****.**", name="David Cramer"), ), ], }, ] return MailPreview( html_template="sentry/emails/activity/release.html", text_template="sentry/emails/activity/release.txt", context={ "author_count": 1, "commit_count": 4, "deploy": deploy, "environment": "production", "file_count": 5, "project_count": len(projects), "projects": zip(projects, release_links, [6, 1, 0]), "reason": GroupSubscriptionReason.descriptions[GroupSubscriptionReason.committed], "release": release, "repos": repos, "setup_repo_link": absolute_uri(f"/organizations/{org.slug}/repos/"), "version_parsed": version_parsed, }, ).render(request)