Add api for configuring tools in the explore plugin
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint')],
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
# explore-react
|
||||
|
||||
This package provides helpers to the `explore` plugin that can be imported by
|
||||
any other plugin or app.
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "@backstage/plugin-explore-react",
|
||||
"version": "0.0.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "plugins/explore-react"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "backstage-cli plugin:build",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"clean": "backstage-cli clean",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"start": "backstage-cli plugin:serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.5.0",
|
||||
"@backstage/dev-utils": "^0.1.8",
|
||||
"@backstage/test-utils": "^0.1.6",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"cross-fetch": "^3.0.6",
|
||||
"msw": "^0.21.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
+1
-9
@@ -14,12 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type CardData = {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
image: string;
|
||||
tags?: string[];
|
||||
lifecycle?: string;
|
||||
newsTag?: string;
|
||||
};
|
||||
export * from './tools';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core';
|
||||
|
||||
export const exploreToolsConfigRef = createApiRef<ExploreToolsConfig>({
|
||||
id: 'plugin.explore.toolsconfig',
|
||||
description: 'Used to configure tools displayed in the explore plugin',
|
||||
});
|
||||
|
||||
export type ExploreTool = {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
image: string;
|
||||
tags?: string[];
|
||||
lifecycle?: string;
|
||||
newsTag?: string;
|
||||
};
|
||||
|
||||
export interface ExploreToolsConfig {
|
||||
getTools: () => Promise<ExploreTool[]>;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { exploreToolsConfigRef } from './api';
|
||||
export type { ExploreTool, ExploreToolsConfig } from './api';
|
||||
@@ -33,6 +33,7 @@
|
||||
"@backstage/catalog-model": "^0.7.0",
|
||||
"@backstage/core": "^0.5.0",
|
||||
"@backstage/plugin-catalog": "^0.2.12",
|
||||
"@backstage/plugin-explore-react": "^0.0.1",
|
||||
"@backstage/theme": "^0.2.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -43,6 +43,7 @@ export const DomainExplorerContent = () => {
|
||||
<ContentHeader title="Domains">
|
||||
<SupportButton>Discover the domains in your ecosystem.</SupportButton>
|
||||
</ContentHeader>
|
||||
|
||||
{loading && <Progress />}
|
||||
{!loading && (!entities || entities.length === 0) && (
|
||||
<EmptyState
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ExploreTool } from '@backstage/plugin-explore-react';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import {
|
||||
Button,
|
||||
@@ -27,7 +28,6 @@ import {
|
||||
} from '@material-ui/core';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import { CardData } from './types';
|
||||
|
||||
// TODO: Align styling between Domain and ToolCard
|
||||
|
||||
@@ -69,7 +69,7 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
}));
|
||||
|
||||
type Props = {
|
||||
card: CardData;
|
||||
card: ExploreTool;
|
||||
objectFit?: 'cover' | 'contain';
|
||||
};
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ExploreTool } from '@backstage/plugin-explore-react';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { ToolCard } from './ToolCard';
|
||||
import { CardData } from './types';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
container: {
|
||||
@@ -30,7 +30,7 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
}));
|
||||
|
||||
type ToolCardGridProps = {
|
||||
tools: CardData[];
|
||||
tools: ExploreTool[];
|
||||
};
|
||||
|
||||
export const ToolCardGrid = ({ tools }: ToolCardGridProps) => {
|
||||
@@ -38,7 +38,7 @@ export const ToolCardGrid = ({ tools }: ToolCardGridProps) => {
|
||||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
{tools.map((card: CardData, ix: any) => (
|
||||
{tools.map((card: ExploreTool, ix: any) => (
|
||||
<ToolCard card={card} key={ix} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -15,4 +15,3 @@
|
||||
*/
|
||||
export { ToolCard } from './ToolCard';
|
||||
export { ToolCardGrid } from './ToolCardGrid';
|
||||
export type { CardData } from './types';
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core';
|
||||
import {
|
||||
ExploreTool,
|
||||
exploreToolsConfigRef,
|
||||
} from '@backstage/plugin-explore-react';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { ToolExplorerContent } from './ToolExplorerContent';
|
||||
|
||||
describe('<ToolExplorerContent />', () => {
|
||||
const exploreToolsConfigApi: jest.Mocked<typeof exploreToolsConfigRef.T> = {
|
||||
getTools: jest.fn(),
|
||||
};
|
||||
|
||||
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<MemoryRouter>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.with(exploreToolsConfigRef, exploreToolsConfigApi)}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>
|
||||
</MemoryRouter>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('renders a grid of tools', async () => {
|
||||
const tools: ExploreTool[] = [
|
||||
{
|
||||
title: 'Lighthouse',
|
||||
description:
|
||||
"Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.",
|
||||
url: '/lighthouse',
|
||||
image:
|
||||
'https://raw.githubusercontent.com/GoogleChrome/lighthouse/8b3d7f052b2e64dd857e741d7395647f487697e7/assets/lighthouse-logo.png',
|
||||
tags: ['web', 'seo', 'accessibility', 'performance'],
|
||||
},
|
||||
{
|
||||
title: 'Tech Radar',
|
||||
description:
|
||||
'Tech Radar is a list of technologies, complemented by an assessment result, called ring assignment.',
|
||||
url: '/tech-radar',
|
||||
image:
|
||||
'https://storage.googleapis.com/wf-blogs-engineering-media/2018/09/fe13bb32-wf-tech-radar-hero-1024x597.png',
|
||||
tags: ['standards', 'landscape'],
|
||||
},
|
||||
];
|
||||
exploreToolsConfigApi.getTools.mockResolvedValue(tools);
|
||||
|
||||
const { getByText } = render(<ToolExplorerContent />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('Lighthouse')).toBeInTheDocument();
|
||||
expect(getByText('Tech Radar')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders empty state', async () => {
|
||||
exploreToolsConfigApi.getTools.mockResolvedValue([]);
|
||||
|
||||
const { getByText } = render(<ToolExplorerContent />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(getByText('No tools to display')).toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -13,97 +13,40 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Content, ContentHeader, SupportButton } from '@backstage/core';
|
||||
import {
|
||||
Content,
|
||||
ContentHeader,
|
||||
EmptyState,
|
||||
Progress,
|
||||
SupportButton,
|
||||
useApi,
|
||||
} from '@backstage/core';
|
||||
import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { exploreToolsConfigRef } from '../../../../explore-react/src';
|
||||
import { ToolCardGrid } from '../ToolCard';
|
||||
|
||||
// TODO: Provide API to configure the toolsCards
|
||||
|
||||
const toolsCards = [
|
||||
{
|
||||
title: 'New Relic',
|
||||
description:
|
||||
'Observability platform built to help engineers create and monitor their software',
|
||||
url: '/newrelic',
|
||||
image: 'https://i.imgur.com/L37ikrX.jpg',
|
||||
tags: ['newrelic', 'performance', 'monitoring', 'errors', 'alerting'],
|
||||
},
|
||||
{
|
||||
title: 'CircleCI',
|
||||
description:
|
||||
'Provides builds overview, detailed build info and retriggering functionality for CircleCI.',
|
||||
url: '/circleci',
|
||||
image: 'https://miro.medium.com/max/1200/1*hkTBp22vLAqlIHkrkZHPnw.png',
|
||||
tags: ['circleci', 'ci', 'dev'],
|
||||
},
|
||||
{
|
||||
title: 'Sentry',
|
||||
description:
|
||||
'Self-hosted and cloud-based error monitoring that helps software teams discover, triage, and prioritize errors in real-time.',
|
||||
url: '/sentry',
|
||||
image: 'https://sentry-brand.storage.googleapis.com/sentry-logo-black.png',
|
||||
tags: ['sentry', 'monitoring', 'errors'],
|
||||
},
|
||||
{
|
||||
title: 'Lighthouse',
|
||||
description:
|
||||
"Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.",
|
||||
url: '/lighthouse',
|
||||
image:
|
||||
'https://raw.githubusercontent.com/GoogleChrome/lighthouse/8b3d7f052b2e64dd857e741d7395647f487697e7/assets/lighthouse-logo.png',
|
||||
tags: ['web', 'seo', 'accessibility', 'performance'],
|
||||
},
|
||||
{
|
||||
title: 'Tech Radar',
|
||||
description:
|
||||
'Tech Radar is a list of technologies, complemented by an assessment result, called ring assignment.',
|
||||
url: '/tech-radar',
|
||||
image:
|
||||
'https://storage.googleapis.com/wf-blogs-engineering-media/2018/09/fe13bb32-wf-tech-radar-hero-1024x597.png',
|
||||
tags: ['standards', 'landscape'],
|
||||
},
|
||||
{
|
||||
title: 'Cost Insights',
|
||||
description: 'Insights into cloud costs for your organization.',
|
||||
url: '/cost-insights',
|
||||
image: 'https://cloud.google.com/images/press/logo-cloud.png',
|
||||
tags: ['cloud', 'finops'],
|
||||
},
|
||||
{
|
||||
title: 'GraphiQL',
|
||||
description:
|
||||
'Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.',
|
||||
url: '/graphiql',
|
||||
image:
|
||||
'https://camo.githubusercontent.com/517398c3fbe0687d3d4dcbe05da82970b882e75a/68747470733a2f2f64337676366c703535716a6171632e636c6f756466726f6e742e6e65742f6974656d732f33413061324e314c3346324f304c3377326e316a2f477261706869514c382e706e673f582d436c6f75644170702d56697369746f722d49643d3433363432',
|
||||
tags: ['graphql', 'dev'],
|
||||
},
|
||||
{
|
||||
title: 'GitOps Clusters',
|
||||
description:
|
||||
'Create GitOps-managed clusters with Backstage. Currently supports EKS flavors and profiles like Machine Learning Ops (MLOps)',
|
||||
url: '/gitops-clusters',
|
||||
image: 'https://miro.medium.com/max/801/1*R28u8gj-hVdDFISoYqPhrQ.png',
|
||||
tags: ['gitops', 'dev'],
|
||||
},
|
||||
{
|
||||
title: 'Rollbar',
|
||||
description:
|
||||
'Error monitoring and crash reporting for agile development and continuous delivery',
|
||||
url: '/rollbar',
|
||||
image:
|
||||
'https://images.ctfassets.net/cj4mgtttlyx7/4DfiWj9CbuHBi10uWK7JHn/5e94a6c5dbd5d50bdcd8d9e78f88689b/rollbar-seo.png',
|
||||
tags: ['rollbar', 'monitoring', 'errors'],
|
||||
},
|
||||
];
|
||||
|
||||
export const ToolExplorerContent = () => {
|
||||
const exploreToolsConfigApi = useApi(exploreToolsConfigRef);
|
||||
const { value: tools, loading } = useAsync(async () => {
|
||||
return await exploreToolsConfigApi.getTools();
|
||||
}, [exploreToolsConfigApi]);
|
||||
|
||||
return (
|
||||
<Content noPadding>
|
||||
<ContentHeader title="Tools">
|
||||
<SupportButton>Discover the tools in your ecosystem.</SupportButton>
|
||||
</ContentHeader>
|
||||
<ToolCardGrid tools={toolsCards} />
|
||||
|
||||
{loading && <Progress />}
|
||||
{!loading && (!tools || tools.length === 0) && (
|
||||
<EmptyState
|
||||
missing="info"
|
||||
title="No tools to display"
|
||||
description={`You haven't added any tools yet.`}
|
||||
/>
|
||||
)}
|
||||
{!loading && tools && <ToolCardGrid tools={tools} />}
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,11 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import { createApiFactory, createPlugin } from '@backstage/core';
|
||||
import { exploreToolsConfigRef } from '@backstage/plugin-explore-react';
|
||||
import { exploreRouteRef } from './routes';
|
||||
import { exampleTools } from './util/examples';
|
||||
|
||||
export const explorePlugin = createPlugin({
|
||||
id: 'explore',
|
||||
apis: [
|
||||
// Register a default for exploreToolsConfigRef, you may want to override
|
||||
// the API locally in your app.
|
||||
createApiFactory({
|
||||
api: exploreToolsConfigRef,
|
||||
deps: {},
|
||||
factory: () => ({
|
||||
async getTools() {
|
||||
return exampleTools;
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
explore: exploreRouteRef,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const exampleTools = [
|
||||
{
|
||||
title: 'New Relic',
|
||||
description:
|
||||
'Observability platform built to help engineers create and monitor their software',
|
||||
url: '/newrelic',
|
||||
image: 'https://i.imgur.com/L37ikrX.jpg',
|
||||
tags: ['newrelic', 'performance', 'monitoring', 'errors', 'alerting'],
|
||||
},
|
||||
{
|
||||
title: 'CircleCI',
|
||||
description:
|
||||
'Provides builds overview, detailed build info and retriggering functionality for CircleCI.',
|
||||
url: '/circleci',
|
||||
image: 'https://miro.medium.com/max/1200/1*hkTBp22vLAqlIHkrkZHPnw.png',
|
||||
tags: ['circleci', 'ci', 'dev'],
|
||||
},
|
||||
{
|
||||
title: 'Sentry',
|
||||
description:
|
||||
'Self-hosted and cloud-based error monitoring that helps software teams discover, triage, and prioritize errors in real-time.',
|
||||
url: '/sentry',
|
||||
image: 'https://sentry-brand.storage.googleapis.com/sentry-logo-black.png',
|
||||
tags: ['sentry', 'monitoring', 'errors'],
|
||||
},
|
||||
{
|
||||
title: 'Lighthouse',
|
||||
description:
|
||||
"Google's Lighthouse tool is a great resource for benchmarking and improving the accessibility, performance, SEO, and best practices of your website.",
|
||||
url: '/lighthouse',
|
||||
image:
|
||||
'https://raw.githubusercontent.com/GoogleChrome/lighthouse/8b3d7f052b2e64dd857e741d7395647f487697e7/assets/lighthouse-logo.png',
|
||||
tags: ['web', 'seo', 'accessibility', 'performance'],
|
||||
},
|
||||
{
|
||||
title: 'Tech Radar',
|
||||
description:
|
||||
'Tech Radar is a list of technologies, complemented by an assessment result, called ring assignment.',
|
||||
url: '/tech-radar',
|
||||
image:
|
||||
'https://storage.googleapis.com/wf-blogs-engineering-media/2018/09/fe13bb32-wf-tech-radar-hero-1024x597.png',
|
||||
tags: ['standards', 'landscape'],
|
||||
},
|
||||
{
|
||||
title: 'Cost Insights',
|
||||
description: 'Insights into cloud costs for your organization.',
|
||||
url: '/cost-insights',
|
||||
image: 'https://cloud.google.com/images/press/logo-cloud.png',
|
||||
tags: ['cloud', 'finops'],
|
||||
},
|
||||
{
|
||||
title: 'GraphiQL',
|
||||
description:
|
||||
'Integrates GraphiQL as a tool to browse GraphiQL endpoints inside Backstage.',
|
||||
url: '/graphiql',
|
||||
image:
|
||||
'https://camo.githubusercontent.com/517398c3fbe0687d3d4dcbe05da82970b882e75a/68747470733a2f2f64337676366c703535716a6171632e636c6f756466726f6e742e6e65742f6974656d732f33413061324e314c3346324f304c3377326e316a2f477261706869514c382e706e673f582d436c6f75644170702d56697369746f722d49643d3433363432',
|
||||
tags: ['graphql', 'dev'],
|
||||
},
|
||||
{
|
||||
title: 'GitOps Clusters',
|
||||
description:
|
||||
'Create GitOps-managed clusters with Backstage. Currently supports EKS flavors and profiles like Machine Learning Ops (MLOps)',
|
||||
url: '/gitops-clusters',
|
||||
image: 'https://miro.medium.com/max/801/1*R28u8gj-hVdDFISoYqPhrQ.png',
|
||||
tags: ['gitops', 'dev'],
|
||||
},
|
||||
{
|
||||
title: 'Rollbar',
|
||||
description:
|
||||
'Error monitoring and crash reporting for agile development and continuous delivery',
|
||||
url: '/rollbar',
|
||||
image:
|
||||
'https://images.ctfassets.net/cj4mgtttlyx7/4DfiWj9CbuHBi10uWK7JHn/5e94a6c5dbd5d50bdcd8d9e78f88689b/rollbar-seo.png',
|
||||
tags: ['rollbar', 'monitoring', 'errors'],
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user