) => (
- <>
- {row.source?.branchName}
- {row.source?.commit.hash}
- >
+
),
},
{
title: 'Status',
+ field: 'status',
render: (row: Partial) => (
{getStatusComponent(row.status)}
@@ -108,14 +188,32 @@ const generatedColumns: TableColumn[] = [
),
},
+ {
+ title: 'Time',
+ field: 'startTime',
+ render: (row: Partial) => (
+ <>
+
+ run {relativeTimeTo(row?.startTime)}
+
+
+ took {durationHumanized(row?.startTime, row?.stopTime)}
+
+ >
+ ),
+ },
+ {
+ title: 'Workflow',
+ field: 'workflow.name',
+ },
{
title: 'Actions',
+ width: '10%',
render: (row: Partial) => (
),
- width: '10%',
},
];
@@ -130,7 +228,8 @@ type Props = {
pageSize: number;
onChangePageSize: (pageSize: number) => void;
};
-export const CITable: FC = ({
+
+export const CITable = ({
projectName,
loading,
pageSize,
@@ -140,11 +239,16 @@ export const CITable: FC = ({
onChangePage,
onChangePageSize,
total,
-}) => {
+}: Props) => {
return (
{
if (!status) return '';
@@ -41,6 +43,39 @@ const makeReadableStatus = (status: string | undefined) => {
} as Record)[status];
};
+const mapWorkflowDetails = (buildData: BuildSummary) => {
+ // Workflows should be an object: fixed in https://github.com/worldturtlemedia/circleci-api/pull/787
+ const { workflows } = (buildData as any) ?? {};
+
+ return {
+ id: workflows?.workflow_id,
+ url: `${buildData.build_url}/workflows/${workflows?.workflow_id}`,
+ jobName: workflows?.job_name,
+ name: workflows?.workflow_name,
+ };
+};
+
+const mapSourceDetails = (buildData: BuildSummary) => {
+ const commitDetails = getOr({}, 'all_commit_details[0]', buildData);
+
+ return {
+ branchName: String(buildData.branch),
+ commit: {
+ hash: String(buildData.vcs_revision),
+ shortHash: String(buildData.vcs_revision).substr(0, 7),
+ committerName: buildData.committer_name,
+ url: commitDetails.commit_url,
+ },
+ };
+};
+
+const mapUser = (buildData: BuildSummary) => ({
+ isUser: buildData?.user?.is_user || false,
+ login: buildData?.user?.login || 'none',
+ name: (buildData?.user as any)?.name,
+ avatarUrl: (buildData?.user as any)?.avatar_url,
+});
+
export const transform = (
buildsData: BuildSummary[],
restartBuild: { (buildId: number): Promise },
@@ -52,16 +87,14 @@ export const transform = (
? buildData.subject +
(buildData.retry_of ? ` (retry of #${buildData.retry_of})` : '')
: '',
+ startTime: buildData.start_time,
+ stopTime: buildData.stop_time,
onRestartClick: () =>
typeof buildData.build_num !== 'undefined' &&
restartBuild(buildData.build_num),
- source: {
- branchName: String(buildData.branch),
- commit: {
- hash: String(buildData.vcs_revision),
- url: 'todo',
- },
- },
+ source: mapSourceDetails(buildData),
+ workflow: mapWorkflowDetails(buildData),
+ user: mapUser(buildData),
status: makeReadableStatus(buildData.status),
buildUrl: buildData.build_url,
};
@@ -79,6 +112,7 @@ export const useProjectSlugFromEntity = () => {
export function mapVcsType(vcs: string): GitType {
switch (vcs) {
+ case 'gh':
case 'github':
return GitType.GITHUB;
default:
@@ -93,7 +127,7 @@ export function useBuilds() {
const [total, setTotal] = useState(0);
const [page, setPage] = useState(0);
- const [pageSize, setPageSize] = useState(5);
+ const [pageSize, setPageSize] = useState(10);
const getBuilds = useCallback(
async ({ limit, offset }: { limit: number; offset: number }) => {
diff --git a/plugins/circleci/src/util/index.ts b/plugins/circleci/src/util/index.ts
new file mode 100644
index 0000000000..55bb001ae4
--- /dev/null
+++ b/plugins/circleci/src/util/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 './time';
diff --git a/plugins/circleci/src/util/time.test.ts b/plugins/circleci/src/util/time.test.ts
new file mode 100644
index 0000000000..dae029c8d2
--- /dev/null
+++ b/plugins/circleci/src/util/time.test.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { durationHumanized, relativeTimeTo } from './time';
+
+describe('times utils', () => {
+ describe('toRelativeTime', () => {
+ it('should give a relative time of x from today', () => {
+ expect(relativeTimeTo('2020-01-01')).toEqual(expect.any(String));
+ });
+ });
+
+ describe('durationHumanized', () => {
+ it('should give a humanized duration', () => {
+ expect(durationHumanized('2020-11-01', '2020-11-03')).toBe('2 days');
+ });
+ });
+});
diff --git a/plugins/circleci/src/util/time.ts b/plugins/circleci/src/util/time.ts
new file mode 100644
index 0000000000..8756333a87
--- /dev/null
+++ b/plugins/circleci/src/util/time.ts
@@ -0,0 +1,35 @@
+/*
+ * 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 dayjs from 'dayjs';
+import durationPlugin from 'dayjs/plugin/duration';
+import relativeTimePlugin from 'dayjs/plugin/relativeTime';
+
+dayjs.extend(durationPlugin);
+dayjs.extend(relativeTimePlugin);
+
+type DateTimeObject = Date | string | number | undefined;
+
+export function relativeTimeTo(time: DateTimeObject, withoutSuffix = false) {
+ return dayjs().to(dayjs(time), withoutSuffix);
+}
+
+export function durationHumanized(
+ startTime: DateTimeObject,
+ endTime: DateTimeObject,
+) {
+ return dayjs.duration(dayjs(startTime).diff(dayjs(endTime))).humanize();
+}