diff --git a/config-ui/src/plugins/components/connection-list/index.tsx b/config-ui/src/plugins/components/connection-list/index.tsx index e0985b1d14d..7544e856753 100644 --- a/config-ui/src/plugins/components/connection-list/index.tsx +++ b/config-ui/src/plugins/components/connection-list/index.tsx @@ -27,6 +27,7 @@ import { PATHS } from '@/config'; import { useAppSelector } from '@/hooks'; import { getPluginConfig, ConnectionStatus, ConnectionForm } from '@/plugins'; import { WebHookConnection } from '@/plugins/register/webhook'; +import { DeveloperTelemetryConnection } from '@/plugins/register/developer-telemetry'; const ModalTitle = styled.div` display: flex; @@ -67,6 +68,10 @@ export const ConnectionList = ({ plugin, onCreate }: Props) => { return ; } + if (plugin === 'developer_telemetry') { + return ; + } + const handleShowForm = (id: ID) => { setOpen(true); setConnectionId(id); diff --git a/config-ui/src/plugins/register/developer-telemetry/assets/icon.svg b/config-ui/src/plugins/register/developer-telemetry/assets/icon.svg new file mode 100644 index 00000000000..0f071ae74c9 --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/assets/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/config-ui/src/plugins/register/developer-telemetry/components/create-dialog.tsx b/config-ui/src/plugins/register/developer-telemetry/components/create-dialog.tsx new file mode 100644 index 00000000000..db8624feead --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/create-dialog.tsx @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useState } from 'react'; +import { CheckCircleOutlined } from '@ant-design/icons'; +import { Modal, Input, InputNumber } from 'antd'; + +import { Block, CopyText, ExternalLink } from '@/components'; +import { request, operator } from '@/utils'; +import { formatReportEndpoint } from './utils'; + +import * as S from '../styled'; + +interface Props { + open: boolean; + onCancel: () => void; + onSubmitAfter?: (id: ID) => void; +} + +export const CreateDialog = ({ open, onCancel, onSubmitAfter }: Props) => { + const [operating, setOperating] = useState(false); + const [step, setStep] = useState(1); + const [name, setName] = useState(''); + const [record, setRecord] = useState({ + id: 0, + reportEndpoint: '', + apiKey: '', + endpoint: '', + }); + + const handleSubmit = async () => { + if (step === 1) { + const [success, res] = await operator( + async () => { + const response = await request('/plugins/developer_telemetry/connections', { + method: 'POST', + data: { + name, + endpoint: window.location.origin, + proxy: '', + rateLimitPerHour: 10000, + }, + }); + return response; + }, + { + setOperating, + hideToast: true, + }, + ); + + if (success && res) { + setStep(2); + setRecord({ + id: res.id, + apiKey: res.apiKey?.apiKey || '', + endpoint: res.endpoint || window.location.origin, + reportEndpoint: formatReportEndpoint( + res.endpoint || window.location.origin, + res.id, + res.apiKey?.apiKey || '', + ), + }); + onSubmitAfter?.(res.id); + } + } else { + onCancel(); + } + }; + + return ( + + {step === 1 && ( + + + setName(e.target.value)} /> + + + )} + {step === 2 && ( + +

+ + CURL commands generated. Please copy them now. +

+

+ A non-expired API key is automatically generated for the authentication of the developer telemetry. This key + will only show now. You can revoke it in the developer telemetry page at any time. +

+ +
Post to send developer metrics
+ +
+
+ )} +
+ ); +}; + +export default CreateDialog; diff --git a/config-ui/src/plugins/register/developer-telemetry/components/delete-dialog.tsx b/config-ui/src/plugins/register/developer-telemetry/components/delete-dialog.tsx new file mode 100644 index 00000000000..48557a3a6c1 --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/delete-dialog.tsx @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useState, useMemo } from 'react'; +import { Modal } from 'antd'; + +import { request, operator } from '@/utils'; +import { TelemetryConnectionType } from '../types'; + +interface Props { + initialId: ID; + connections: TelemetryConnectionType[]; + onCancel: () => void; + onSubmitAfter?: (id: ID) => void; +} + +export const DeleteDialog = ({ initialId, connections, onCancel, onSubmitAfter }: Props) => { + const connection = useMemo(() => connections.find((c) => c.id === initialId), [initialId, connections]); + const [operating, setOperating] = useState(false); + + const handleSubmit = async () => { + const [success] = await operator( + async () => { + await request(`/plugins/developer_telemetry/connections/${initialId}`, { + method: 'DELETE', + }); + }, + { + setOperating, + formatMessage: () => 'Connection deleted successfully', + }, + ); + + if (success) { + onSubmitAfter?.(initialId); + onCancel(); + } + }; + + if (!connection) { + return null; + } + + return ( + +

+ Are you sure you want to delete the connection {connection.name}? +

+

This action cannot be undone. All associated API keys will be revoked.

+
+ ); +}; + +export default DeleteDialog; diff --git a/config-ui/src/plugins/register/developer-telemetry/components/edit-dialog.tsx b/config-ui/src/plugins/register/developer-telemetry/components/edit-dialog.tsx new file mode 100644 index 00000000000..b4423c2035c --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/edit-dialog.tsx @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useState, useMemo, useEffect } from 'react'; +import { Modal, Input } from 'antd'; + +import { Block } from '@/components'; +import { request, operator } from '@/utils'; +import { TelemetryConnectionType } from '../types'; + +interface Props { + initialId: ID; + connections: TelemetryConnectionType[]; + onCancel: () => void; +} + +export const EditDialog = ({ initialId, connections, onCancel }: Props) => { + const connection = useMemo(() => connections.find((c) => c.id === initialId), [initialId, connections]); + const [operating, setOperating] = useState(false); + const [name, setName] = useState(''); + + useEffect(() => { + if (connection) { + setName(connection.name); + } + }, [connection]); + + const handleSubmit = async () => { + const [success] = await operator( + async () => { + await request(`/plugins/developer_telemetry/connections/${initialId}`, { + method: 'PATCH', + data: { + name, + }, + }); + }, + { + setOperating, + }, + ); + + if (success) { + onCancel(); + } + }; + + if (!connection) { + return null; + } + + return ( + + + setName(e.target.value)} /> + + + ); +}; + +export default EditDialog; diff --git a/config-ui/src/plugins/register/developer-telemetry/components/index.ts b/config-ui/src/plugins/register/developer-telemetry/components/index.ts new file mode 100644 index 00000000000..6ca31852190 --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/index.ts @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { default as CreateDialog } from './create-dialog'; +export { default as ViewDialog } from './view-dialog'; +export { default as EditDialog } from './edit-dialog'; +export { default as DeleteDialog } from './delete-dialog'; +export { default as SelectorDialog } from './selector-dialog'; diff --git a/config-ui/src/plugins/register/developer-telemetry/components/selector-dialog.tsx b/config-ui/src/plugins/register/developer-telemetry/components/selector-dialog.tsx new file mode 100644 index 00000000000..92736d091fd --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/selector-dialog.tsx @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useState, useEffect } from 'react'; +import { Modal } from 'antd'; +import MillerColumnsSelect from 'miller-columns-select'; + +import { Block, Loading } from '@/components'; +import { request } from '@/utils'; +import { TelemetryConnectionType } from '../types'; + +import * as S from '../styled'; + +interface Props { + open: boolean; + saving: boolean; + onCancel: () => void; + onSubmit: (items: TelemetryConnectionType[]) => void; +} + +export const SelectorDialog = ({ open, saving, onCancel, onSubmit }: Props) => { + const [selectedIds, setSelectedIds] = useState([]); + const [connections, setConnections] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (open) { + const loadConnections = async () => { + setLoading(true); + try { + const res = await request('/plugins/developer_telemetry/connections'); + setConnections(res); + } catch (error) { + console.error('Failed to load connections:', error); + } finally { + setLoading(false); + } + }; + loadConnections(); + } + }, [open]); + + const handleSubmit = () => onSubmit(connections.filter((it) => selectedIds.includes(it.id))); + + return ( + + + + false} + renderLoading={() => } + items={connections.map((it) => ({ + parentId: null, + id: it.id, + title: it.name, + name: it.name, + }))} + selectedIds={selectedIds} + onSelectItemIds={setSelectedIds} + /> + + + + ); +}; + +export default SelectorDialog; diff --git a/config-ui/src/plugins/register/developer-telemetry/components/utils.ts b/config-ui/src/plugins/register/developer-telemetry/components/utils.ts new file mode 100644 index 00000000000..7ff173a032e --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/utils.ts @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 formatReportEndpoint = (baseUrl: string, connectionId: ID, apiKey: string) => { + return `curl -X POST ${baseUrl}/api/rest/plugins/developer_telemetry/connections/${connectionId}/report \\ + -H "Content-Type: application/json" \\ + -H "Authorization: Bearer ${apiKey}" \\ + -d '{ + "date": "2026-02-18", + "developer": "user@hostname", + "email": "user@example.com", + "name": "Developer Name", + "hostname": "dev-machine", + "metrics": { + "active_hours": 8, + "tools_used": ["vscode", "git", "docker"], + "commands": {"git": 50, "npm": 20}, + "projects": ["/path/to/project"] + } + }'`; +}; diff --git a/config-ui/src/plugins/register/developer-telemetry/components/view-dialog.tsx b/config-ui/src/plugins/register/developer-telemetry/components/view-dialog.tsx new file mode 100644 index 00000000000..12f3909fddd --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/components/view-dialog.tsx @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useMemo } from 'react'; +import { Modal } from 'antd'; + +import { Block, CopyText, ExternalLink } from '@/components'; +import { TelemetryConnectionType } from '../types'; +import { formatReportEndpoint } from './utils'; + +import * as S from '../styled'; + +interface Props { + initialId: ID; + connections: TelemetryConnectionType[]; + onCancel: () => void; +} + +export const ViewDialog = ({ initialId, connections, onCancel }: Props) => { + const connection = useMemo(() => connections.find((c) => c.id === initialId), [initialId, connections]); + const prefix = useMemo(() => `${window.location.origin}`, []); + + if (!connection) { + return null; + } + + const reportEndpoint = formatReportEndpoint(prefix, connection.id, '{API_KEY}'); + + return ( + + +

+ Copy the following CURL command to your telemetry collector to push developer metrics by making a POST to + DevLake. Please replace the {'{'}API_KEY{'}'} with your actual API key. +

+ +
Post to send telemetry data
+ +

+ See the documentation for + the full payload schema. +

+
+

+ Note: For security reasons, the API key is only shown once during connection creation and + cannot be retrieved later. If you've lost your API key, please delete and recreate this connection. +

+
+
+ ); +}; + +export default ViewDialog; diff --git a/config-ui/src/plugins/register/developer-telemetry/config.tsx b/config-ui/src/plugins/register/developer-telemetry/config.tsx new file mode 100644 index 00000000000..56cd82443c3 --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/config.tsx @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { IPluginConfig } from '@/types'; + +import Icon from './assets/icon.svg?react'; + +export const DeveloperTelemetryConfig: IPluginConfig = { + plugin: 'developer_telemetry', + name: 'Developer Telemetry', + icon: ({ color }) => , + sort: 14, + isBeta: true, + connection: { + docLink: '', + fields: [], + initialValues: {}, + }, + dataScope: {}, +}; diff --git a/config-ui/src/plugins/register/developer-telemetry/connection.tsx b/config-ui/src/plugins/register/developer-telemetry/connection.tsx new file mode 100644 index 00000000000..2e1d5dfba6c --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/connection.tsx @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 { useState, useEffect } from 'react'; +import { EyeOutlined, FormOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons'; +import { Flex, Table, Space, Button } from 'antd'; + +import { request } from '@/utils'; +import { TelemetryConnectionType } from './types'; +import { CreateDialog, ViewDialog, EditDialog, DeleteDialog } from './components'; + +type Type = 'add' | 'edit' | 'show' | 'delete'; + +interface Props { + filterIds?: ID[]; + onCreateAfter?: (id: ID) => void; + onDeleteAfter?: (id: ID) => void; +} + +export const DeveloperTelemetryConnection = ({ filterIds, onCreateAfter, onDeleteAfter }: Props) => { + const [type, setType] = useState(); + const [currentID, setCurrentID] = useState(); + const [connections, setConnections] = useState([]); + const [loading, setLoading] = useState(false); + + const loadConnections = async () => { + setLoading(true); + try { + const res = await request('/plugins/developer_telemetry/connections'); + setConnections(res); + } catch (error) { + console.error('Failed to load connections:', error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadConnections(); + }, []); + + const handleHideDialog = () => { + setType(undefined); + setCurrentID(undefined); + loadConnections(); + }; + + const handleShowDialog = (t: Type, r?: TelemetryConnectionType) => { + setType(t); + setCurrentID(r?.id); + }; + + return ( + + ( + handleShowDialog('show', row)} style={{ cursor: 'pointer' }}> + {name} + + ), + }, + { + title: 'Endpoint', + dataIndex: 'endpoint', + key: 'endpoint', + }, + { + title: '', + dataIndex: '', + key: 'action', + align: 'center', + width: 200, + render: (_, row) => ( + + + + {type === 'add' && onCreateAfter?.(id)} />} + {type === 'show' && currentID && ( + + )} + {type === 'edit' && currentID && ( + + )} + {type === 'delete' && currentID && ( + onDeleteAfter?.(id)} + /> + )} + + ); +}; diff --git a/config-ui/src/plugins/register/developer-telemetry/index.ts b/config-ui/src/plugins/register/developer-telemetry/index.ts new file mode 100644 index 00000000000..e02bbea1481 --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 * from './types'; +export * from './config'; +export * from './connection'; +export { default as DeveloperTelemetrySelectorDialog } from './components/selector-dialog'; diff --git a/config-ui/src/plugins/register/developer-telemetry/styled.ts b/config-ui/src/plugins/register/developer-telemetry/styled.ts new file mode 100644 index 00000000000..4d701da2581 --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/styled.ts @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 styled from 'styled-components'; + +export const Wrapper = styled.div` + h2 { + margin: 0 0 24px; + padding: 0; + + svg { + margin-right: 8px; + color: #4db764; + } + } + + h5 { + margin: 10px 0; + font-size: 14px; + font-weight: 600; + } + + p { + margin: 8px 0; + } +`; diff --git a/config-ui/src/plugins/register/developer-telemetry/types.ts b/config-ui/src/plugins/register/developer-telemetry/types.ts new file mode 100644 index 00000000000..96c998e234b --- /dev/null +++ b/config-ui/src/plugins/register/developer-telemetry/types.ts @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 type TelemetryConnectionType = { + id: ID; + name: string; + endpoint: string; + proxy?: string; + rateLimitPerHour: number; + apiKey?: { + apiKey: string; + allowedPath: string; + }; +}; diff --git a/config-ui/src/plugins/register/index.ts b/config-ui/src/plugins/register/index.ts index 5a20f339fc3..9f60901bf56 100644 --- a/config-ui/src/plugins/register/index.ts +++ b/config-ui/src/plugins/register/index.ts @@ -25,6 +25,7 @@ import { BambooConfig } from './bamboo'; import { BitbucketConfig } from './bitbucket'; import { BitbucketServerConfig } from './bitbucket-server'; import { CircleCIConfig } from './circleci'; +import { DeveloperTelemetryConfig } from './developer-telemetry'; import { GitHubConfig } from './github'; import { GitLabConfig } from './gitlab'; import { JenkinsConfig } from './jenkins'; @@ -49,6 +50,7 @@ export const pluginConfigs: IPluginConfig[] = [ BitbucketConfig, BitbucketServerConfig, CircleCIConfig, + DeveloperTelemetryConfig, GitHubConfig, GitLabConfig, JenkinsConfig,