|
@@ -14,10 +14,17 @@ import ExternalLink from 'sentry/components/links/externalLink';
|
|
|
import Link from 'sentry/components/links/link';
|
|
|
import Placeholder from 'sentry/components/placeholder';
|
|
|
import type {PlatformKey} from 'sentry/data/platformCategories';
|
|
|
-import {IconClose} from 'sentry/icons/iconClose';
|
|
|
+import {IconClose, IconWarning} from 'sentry/icons';
|
|
|
import {t} from 'sentry/locale';
|
|
|
import space from 'sentry/styles/space';
|
|
|
-import type {Event, Frame, Organization, Project} from 'sentry/types';
|
|
|
+import {
|
|
|
+ CodecovStatusCode,
|
|
|
+ Event,
|
|
|
+ Frame,
|
|
|
+ Organization,
|
|
|
+ Project,
|
|
|
+ StacktraceLinkResult,
|
|
|
+} from 'sentry/types';
|
|
|
import {StacktraceLinkEvents} from 'sentry/utils/analytics/integrations/stacktraceLinkAnalyticsEvents';
|
|
|
import {getAnalyicsDataForEvent} from 'sentry/utils/events';
|
|
|
import {
|
|
@@ -100,6 +107,56 @@ function StacktraceLinkSetup({organization, project, event}: StacktraceLinkSetup
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+function shouldshowCodecovFeatures(
|
|
|
+ organization: Organization,
|
|
|
+ match: StacktraceLinkResult
|
|
|
+) {
|
|
|
+ const enabled =
|
|
|
+ organization.features.includes('codecov-stacktrace-integration') &&
|
|
|
+ organization.codecovAccess;
|
|
|
+
|
|
|
+ const validStatus = [
|
|
|
+ CodecovStatusCode.COVERAGE_EXISTS,
|
|
|
+ CodecovStatusCode.NO_COVERAGE_DATA,
|
|
|
+ ].includes(match.codecovStatusCode!);
|
|
|
+
|
|
|
+ return enabled && validStatus && match.config?.provider.key === 'github';
|
|
|
+}
|
|
|
+
|
|
|
+interface CodecovLinkProps {
|
|
|
+ sourceUrl: string | undefined;
|
|
|
+ codecovStatusCode?: CodecovStatusCode;
|
|
|
+}
|
|
|
+
|
|
|
+function CodecovLink({sourceUrl, codecovStatusCode}: CodecovLinkProps) {
|
|
|
+ if (codecovStatusCode === CodecovStatusCode.NO_COVERAGE_DATA) {
|
|
|
+ return (
|
|
|
+ <CodecovWarning>
|
|
|
+ {t('Code Coverage not found')}
|
|
|
+ <IconWarning size="xs" color="errorText" />
|
|
|
+ </CodecovWarning>
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (codecovStatusCode === CodecovStatusCode.COVERAGE_EXISTS) {
|
|
|
+ if (!sourceUrl) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ const codecovUrl = sourceUrl.replaceAll(
|
|
|
+ new RegExp('github.com/.[^/]*', 'g'),
|
|
|
+ 'app.codecov.io/gh'
|
|
|
+ );
|
|
|
+
|
|
|
+ return (
|
|
|
+ <OpenInLink href={codecovUrl} openInNewTab>
|
|
|
+ {t('View Coverage Tests on Codecov')}
|
|
|
+ <StyledIconWrapper>{getIntegrationIcon('codecov', 'sm')}</StyledIconWrapper>
|
|
|
+ </OpenInLink>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+}
|
|
|
+
|
|
|
interface StacktraceLinkProps {
|
|
|
event: Event;
|
|
|
frame: Frame;
|
|
@@ -223,6 +280,12 @@ export function StacktraceLink({frame, event, line}: StacktraceLinkProps) {
|
|
|
</StyledIconWrapper>
|
|
|
{t('Open this line in %s', match.config.provider.name)}
|
|
|
</OpenInLink>
|
|
|
+ {shouldshowCodecovFeatures(organization, match) && (
|
|
|
+ <CodecovLink
|
|
|
+ sourceUrl={match.sourceUrl}
|
|
|
+ codecovStatusCode={match.codecovStatusCode}
|
|
|
+ />
|
|
|
+ )}
|
|
|
</CodeMappingButtonContainer>
|
|
|
);
|
|
|
}
|
|
@@ -327,3 +390,10 @@ const StyledLink = styled(Link)`
|
|
|
${LinkStyles}
|
|
|
color: ${p => p.theme.gray300};
|
|
|
`;
|
|
|
+
|
|
|
+const CodecovWarning = styled('div')`
|
|
|
+ display: flex;
|
|
|
+ color: ${p => p.theme.errorText};
|
|
|
+ gap: ${space(0.75)};
|
|
|
+ align-items: center;
|
|
|
+`;
|