) => (
+ <>
+ {row.source?.branchName}
+ {row.source?.commit.hash}
+ >
+ ),
+ },
+ {
+ title: 'Status',
+ render: (row: Partial) => {
+ return (
+
+
+
+ )},
+ },
+ {
+ title: 'Actions',
+ render: (row: Partial) => (
+
+
+
+ ),
+ width: '10%',
+ },
+];
+
+type Props = {
+ loading: boolean;
+ retry: () => void;
+ builds: CITableBuildInfo[];
+ projectName: string;
+ page: number;
+ onChangePage: (page: number) => void;
+ total: number;
+ pageSize: number;
+ onChangePageSize: (pageSize: number) => void;
+};
+export const CITable: FC = ({
+ projectName,
+ loading,
+ pageSize,
+ page,
+ retry,
+ builds,
+ onChangePage,
+ onChangePageSize,
+ total,
+}) => {
+ return (
+ ,
+ tooltip: 'Refresh Data',
+ isFreeAction: true,
+ onClick: () => retry(),
+ },
+ ]}
+ data={builds}
+ onChangePage={onChangePage}
+ onChangeRowsPerPage={onChangePageSize}
+ title={
+
+
+
+ {projectName}
+
+ }
+ columns={generatedColumns}
+ />
+ );
+};
diff --git a/plugins/jenkins/src/pages/BuildsPage/lib/CITable/index.ts b/plugins/jenkins/src/pages/BuildsPage/lib/CITable/index.ts
new file mode 100644
index 0000000000..358939e69f
--- /dev/null
+++ b/plugins/jenkins/src/pages/BuildsPage/lib/CITable/index.ts
@@ -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.
+ */
+export { CITable } from './CITable';
+export type { CITableBuildInfo } from './CITable';
diff --git a/plugins/jenkins/src/pages/BuildsPage/lib/Status/JenkinsRunStatus.tsx b/plugins/jenkins/src/pages/BuildsPage/lib/Status/JenkinsRunStatus.tsx
new file mode 100644
index 0000000000..8ac4880e38
--- /dev/null
+++ b/plugins/jenkins/src/pages/BuildsPage/lib/Status/JenkinsRunStatus.tsx
@@ -0,0 +1,71 @@
+/*
+ * 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 {StatusPending, StatusRunning, StatusOK, StatusWarning, StatusAborted, StatusError} from '@backstage/core';
+import React from 'react';
+
+export const JenkinsRunStatus = ({
+ status,
+ }: {
+ status: string | undefined;
+}) => {
+ if (status === undefined) return null;
+ switch (status.toLowerCase()) {
+ case 'queued':
+ case 'scheduled':
+ return (
+ <>
+ Queued
+ >
+ );
+ case 'running':
+ return (
+ <>
+ In progress
+ >
+ );
+ case 'unstable':
+ return (
+ <>
+ Unstable
+ >
+ );
+ case 'failure':
+ return (
+ <>
+ Failed
+ >
+ );
+ case 'success':
+ return (
+ <>
+ Completed
+ >
+ );
+ case 'aborted':
+ return (
+ <>
+ Aborted
+ >
+ );
+ default:
+ return (
+ <>
+ {status}
+ >
+ );
+ }
+};
diff --git a/plugins/jenkins/src/pages/BuildsPage/lib/Status/index.ts b/plugins/jenkins/src/pages/BuildsPage/lib/Status/index.ts
new file mode 100644
index 0000000000..928d91c61d
--- /dev/null
+++ b/plugins/jenkins/src/pages/BuildsPage/lib/Status/index.ts
@@ -0,0 +1,16 @@
+/*
+ * 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 { JenkinsRunStatus } from './JenkinsRunStatus'
diff --git a/plugins/jenkins/src/plugin.ts b/plugins/jenkins/src/plugin.ts
index f5b2210257..62fd3a108e 100644
--- a/plugins/jenkins/src/plugin.ts
+++ b/plugins/jenkins/src/plugin.ts
@@ -15,16 +15,20 @@
*/
import { createPlugin, createRouteRef } from '@backstage/core';
-import { App } from './components/App';
+import {DetailedViewPage} from "./pages/BuildWithStepsPage";
-export const rootRouteRef = createRouteRef({
- path: '/jenkins',
- title: 'jenkins',
+export const buildRouteRef = createRouteRef({
+ path: '/jenkins/job',
+ title: 'Jenkins run',
});
+
export const plugin = createPlugin({
id: 'jenkins',
register({ router }) {
- router.addRoute(rootRouteRef, App, { exact: false });
+ router.addRoute(buildRouteRef, DetailedViewPage);
},
});
+
+export { JenkinsBuildsWidget } from './components/JenkinsPluginWidget/JenkinsBuildsWidget';
+export { JenkinsLastBuildWidget } from './components/JenkinsPluginWidget/JenkinsLastBuildWidget';
diff --git a/plugins/jenkins/src/setupTests.ts b/plugins/jenkins/src/setupTests.ts
index e34bc46f4b..8553642152 100644
--- a/plugins/jenkins/src/setupTests.ts
+++ b/plugins/jenkins/src/setupTests.ts
@@ -15,4 +15,5 @@
*/
import '@testing-library/jest-dom';
+
require('jest-fetch-mock').enableMocks();
diff --git a/plugins/jenkins/src/state/index.ts b/plugins/jenkins/src/state/index.ts
new file mode 100644
index 0000000000..d21a380c2a
--- /dev/null
+++ b/plugins/jenkins/src/state/index.ts
@@ -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.
+ */
+export * from './useBuilds';
+export * from './useBuildWithSteps';
diff --git a/plugins/jenkins/src/state/useAsyncPolling.ts b/plugins/jenkins/src/state/useAsyncPolling.ts
new file mode 100644
index 0000000000..7ea0755368
--- /dev/null
+++ b/plugins/jenkins/src/state/useAsyncPolling.ts
@@ -0,0 +1,37 @@
+/*
+ * 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 { useRef } from 'react';
+
+export const useAsyncPolling = (
+ pollingFn: () => Promise,
+ interval: number,
+) => {
+ const isPolling = useRef(false);
+ const startPolling = async () => {
+ if (isPolling.current === true) return;
+ isPolling.current = true;
+
+ while (isPolling.current === true) {
+ await pollingFn();
+ await new Promise(resolve => setTimeout(resolve, interval));
+ }
+ };
+
+ const stopPolling = () => {
+ isPolling.current = false;
+ };
+ return { startPolling, stopPolling };
+};
diff --git a/plugins/jenkins/src/state/useBuildWithSteps.ts b/plugins/jenkins/src/state/useBuildWithSteps.ts
new file mode 100644
index 0000000000..a681438685
--- /dev/null
+++ b/plugins/jenkins/src/state/useBuildWithSteps.ts
@@ -0,0 +1,63 @@
+/*
+ * 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 { errorApiRef, useApi } from '@backstage/core';
+import { useCallback } from 'react';
+import { useAsyncRetry } from 'react-use';
+import { jenkinsApiRef } from '../api/index';
+import { useAsyncPolling } from './useAsyncPolling';
+
+const INTERVAL_AMOUNT = 1500;
+export function useBuildWithSteps(buildName: string) {
+ const api = useApi(jenkinsApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const getBuildWithSteps = useCallback(async () => {
+ try {
+ const build = await api.getBuild(buildName);
+ return Promise.resolve(build);
+ } catch (e) {
+ errorApi.post(e);
+ return Promise.reject(e);
+ }
+ }, [buildName, api, errorApi]);
+
+ const restartBuild = async () => {
+ try {
+ await api.retry(buildName);
+ } catch (e) {
+ errorApi.post(e);
+ }
+ };
+
+ const { loading, value, retry } = useAsyncRetry(() => getBuildWithSteps(), [
+ getBuildWithSteps,
+ ]);
+
+ const { startPolling, stopPolling } = useAsyncPolling(
+ getBuildWithSteps,
+ INTERVAL_AMOUNT,
+ );
+
+ return [
+ { loading, value, retry },
+ {
+ restartBuild,
+ getBuildWithSteps,
+ startPolling,
+ stopPolling,
+ },
+ ] as const;
+}
diff --git a/plugins/jenkins/src/state/useBuilds.ts b/plugins/jenkins/src/state/useBuilds.ts
new file mode 100644
index 0000000000..deb3125637
--- /dev/null
+++ b/plugins/jenkins/src/state/useBuilds.ts
@@ -0,0 +1,82 @@
+/*
+ * 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 { errorApiRef, useApi } from '@backstage/core';
+import { useCallback, useEffect, useState } from 'react';
+import { useAsyncRetry } from 'react-use';
+import { jenkinsApiRef } from '../api';
+
+export function useBuilds(owner: string, repo: string, branch?: string) {
+ const api = useApi(jenkinsApiRef);
+ const errorApi = useApi(errorApiRef);
+
+ const [total, setTotal] = useState(0);
+ const [page, setPage] = useState(0);
+ const [pageSize, setPageSize] = useState(5);
+
+ const getBuilds = useCallback(async () => {
+ try {
+ let build;
+ if (branch) {
+ build = await api.getLastBuild(`${owner}/${repo}/${branch}`);
+ } else {
+ build = await api.getFolder(`${owner}/${repo}`);
+ }
+ return build;
+ } catch (e) {
+ errorApi.post(e);
+ return Promise.reject(e);
+ }
+ }, [api, branch, errorApi, owner, repo]);
+
+ const restartBuild = async (buildName: string) => {
+ try {
+ await api.retry(buildName);
+ } catch (e) {
+ errorApi.post(e);
+ }
+ };
+
+ useEffect(() => {
+ getBuilds().then(b => {
+ const size = Array.isArray(b) ? b?.[0].build_num! : 1;
+ setTotal(size);
+ });
+ }, [repo, getBuilds]);
+
+ const { loading, value, retry } = useAsyncRetry(
+ () => getBuilds().then(builds => builds ?? [], restartBuild),
+ [page, pageSize, getBuilds],
+ );
+
+ const projectName = `${owner}/${repo}`;
+ return [
+ {
+ page,
+ pageSize,
+ loading,
+ value,
+ projectName,
+ total,
+ },
+ {
+ getBuilds,
+ setPage,
+ setPageSize,
+ restartBuild,
+ retry,
+ },
+ ] as const;
+}