diff --git a/backend/status/helper.ts b/backend/status/helper.ts
index 172a06be..fdada369 100644
--- a/backend/status/helper.ts
+++ b/backend/status/helper.ts
@@ -1,4 +1,4 @@
-import Status, { Process, Stage, State } from 'types/status';
+import Status, { Duration, Process, Stage, State, StepState } from 'types/status';
const statusesExpire = 60 * 60 * 24 * 7; // 7 days
const statusesTimeout = 60 * 60 * 2; // 2 hours
@@ -40,7 +40,7 @@ export const getStuckStatuses = (statuses: Status[]): Status[] =>
return false;
});
-export const fixStatusStates = (status: Status): Status => {
+export const processStatusChanges = (status: Status): Status => {
const processes = status.processes
// Sort processes by creation time
.sort(
@@ -53,10 +53,41 @@ export const fixStatusStates = (status: Status): Status => {
return {
...status,
state: determineStatusState(processes),
- processes,
+ processes: patchProcessDurations(processes),
};
};
+const setDuration = (state: StepState | Process['state'], currentDuration?: Duration): Duration => {
+ if (state === 'running' || state === 'warning') {
+ return {
+ ran: currentDuration?.ran || 0,
+ start: currentDuration?.start ? currentDuration.start : new Date().toUTCString(),
+ };
+ }
+
+ let duration = currentDuration?.ran || 0;
+ if (currentDuration?.start) {
+ duration += Math.abs(new Date(currentDuration.start).getTime() - new Date().getTime());
+ }
+ return {
+ ran: duration,
+ };
+};
+
+export const patchProcessDurations = (processes: Process[]): Process[] =>
+ processes.map((process) => ({
+ ...process,
+ stages: process.stages.map((stage) => ({
+ ...stage,
+ steps: stage.steps.map((step) => ({
+ ...step,
+ duration: setDuration(step.state, step.duration),
+ })),
+ duration: setDuration(stage.state, stage.duration),
+ })),
+ duration: setDuration(process.state, process.duration),
+ }));
+
export const fixStuckStatus = (status: Status): Status => ({
...status,
processes: status.processes.map((process) => {
diff --git a/backend/status/manager.ts b/backend/status/manager.ts
index 037e6c4f..8a422ac3 100644
--- a/backend/status/manager.ts
+++ b/backend/status/manager.ts
@@ -2,7 +2,7 @@ import StorageManager from 'backend/storage/manager';
import Status from 'types/status';
import StatusEvents from './events';
-import { fixStatusStates, fixStuckStatus, getExpiredStatuses, getStuckStatuses } from './helper';
+import { fixStuckStatus, getExpiredStatuses, getStuckStatuses, processStatusChanges } from './helper';
class StatusManager {
statuses: Status[] = [];
@@ -46,7 +46,7 @@ class StatusManager {
}
setStatus(status: Status): void {
- status = fixStatusStates(status);
+ status = processStatusChanges(status);
let replacedStatus = false;
const statuses = [
diff --git a/cypress/integration/github/push.spec.js b/cypress/integration/github/push.spec.js
index ca221433..15415bfa 100644
--- a/cypress/integration/github/push.spec.js
+++ b/cypress/integration/github/push.spec.js
@@ -1,26 +1,27 @@
///
context('A running GitHub push', () => {
- it('opens the CIMonitor dashboard', () => {
- cy.visit('/');
- });
+ it('opens the CIMonitor dashboard', () => {
+ cy.visit('/');
+ });
- it('pushes a GitHub action flow', () => {
- for (let count = 1; count <= 8; count++) {
- if (count > 1) {
- cy.wait(1000);
- }
- cy.github(`push-failed/${count}`);
- }
+ it('pushes a GitHub failed action flow', () => {
+ for (let count = 1; count <= 8; count++) {
+ if (count > 1) {
+ cy.wait(1000);
+ }
+ cy.github(`push-failed/${count}`);
+ }
- cy.wait(2000);
+ cy.wait(2000);
+ });
- // Push all created events
- for (let count = 1; count <= 14; count++) {
- if (count > 1) {
- cy.wait(1000);
- }
- cy.github(`push/${count}`);
- }
- });
+ it('pushes a GitHub successful action flow', () => {
+ for (let count = 1; count <= 14; count++) {
+ if (count > 1) {
+ cy.wait(1000);
+ }
+ cy.github(`push/${count}`);
+ }
+ });
});
diff --git a/frontend/App/Statuses/Status/RunTime.tsx b/frontend/App/Statuses/Status/RunTime.tsx
new file mode 100644
index 00000000..9db1a03c
--- /dev/null
+++ b/frontend/App/Statuses/Status/RunTime.tsx
@@ -0,0 +1,48 @@
+import { ReactElement, useEffect, useState } from 'react';
+
+import { Duration } from '/types/status';
+
+const getTimeRan = (milliseconds: number = 0) => {
+ if (milliseconds === 0) {
+ return '';
+ }
+
+ let seconds = Math.round(milliseconds / 1000);
+ const minutes = Math.floor(seconds / 60);
+ seconds = seconds - minutes * 60;
+
+ return `${minutes}:${seconds < 10 ? `0${seconds}` : seconds}`;
+};
+
+type Props = {
+ duration?: Duration;
+};
+
+const RunTime = ({ duration }: Props): ReactElement | null => {
+ const [runDuration, setRunDuration] = useState(duration?.ran || 0);
+
+ useEffect(() => {
+ let intervalId = undefined;
+
+ const updateTime = (duration: Duration) => {
+ const ran = duration?.ran || 0;
+
+ if (!duration?.start) {
+ return ran;
+ }
+
+ setRunDuration(Math.abs(new Date(duration.start).getTime() - new Date().getTime()) + ran);
+ };
+
+ if (duration.start) {
+ intervalId = setInterval(() => updateTime(duration), 1000);
+ }
+
+ updateTime(duration);
+
+ return () => clearInterval(intervalId);
+ }, [duration, setRunDuration]);
+ return {getTimeRan(runDuration)};
+};
+
+export default RunTime;
diff --git a/frontend/App/Statuses/Status/Status.tsx b/frontend/App/Statuses/Status/Status.tsx
index b051c4ee..46802bd6 100644
--- a/frontend/App/Statuses/Status/Status.tsx
+++ b/frontend/App/Statuses/Status/Status.tsx
@@ -2,6 +2,7 @@ import { ReactElement } from 'react';
import { Body, Box, Boxes, Container, Details, LinkBox, Project, UserImage } from './Status.style';
+import RunTime from '/frontend/App/Statuses/Status/RunTime';
import Icon from '/frontend/components/Icon';
import useSetting from '/frontend/hooks/useSetting';
@@ -28,6 +29,8 @@ const pettyUrl = (url: string) =>
const Statuses = ({ status }: Props): ReactElement => {
const showAvatars = useSetting('showAvatars');
+ const activeProcess = status.processes[0] || null;
+
return (
@@ -56,6 +59,11 @@ const Statuses = ({ status }: Props): ReactElement => {
+ {!!activeProcess && (
+
+
+
+ )}
{!!status.userImage && showAvatars && (
diff --git a/types/status.ts b/types/status.ts
index 230a1793..3b7d45bc 100644
--- a/types/status.ts
+++ b/types/status.ts
@@ -11,12 +11,17 @@ export type StepState =
| 'timeout'
| 'stopped';
+export type Duration = {
+ start?: string;
+ ran: number;
+};
+
export type Step = {
id: string;
title: string;
state: StepState;
time: string;
- duration?: number;
+ duration?: Duration;
};
export type Stage = {
@@ -25,6 +30,7 @@ export type Stage = {
state: StepState;
steps: Step[];
time: string;
+ duration?: Duration;
};
export type Process = {
@@ -33,7 +39,7 @@ export type Process = {
state: State;
stages: Stage[];
time: string;
- duration?: number;
+ duration?: Duration;
};
export type Status = {