Example #1
0
    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
Example #2
0
    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"]
Example #3
0
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",
    }
Example #4
0
    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
Example #5
0
    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>"
        )
Example #7
0
 def version_info(self):
     try:
         return parse_release(self.version)
     except RelayError:
         # This can happen on invalid legacy releases
         return None
Example #8
0
 def __init__(self, activity: Activity) -> None:
     super().__init__(activity)
     self.version = self.activity.data.get("version", "")
     self.version_parsed = parse_release(self.version)["description"]
Example #9
0
def test_parse_release_error():
    with pytest.raises(sentry_relay.InvalidReleaseErrorBadCharacters):
        sentry_relay.parse_release("/var/foo/foo")
Example #10
0
    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)