2021-05-12 21:18:17 +08:00
|
|
|
import { useMutation, useQuery } from "@apollo/client";
|
|
|
|
import { Link, useResponse, useRouter } from '@curi/react-dom';
|
|
|
|
import { faPlayCircle, faVial } from "@fortawesome/free-solid-svg-icons";
|
|
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
|
|
import {
|
|
|
|
CircularProgress,
|
|
|
|
Collapse,
|
|
|
|
IconButton,
|
|
|
|
LinearProgress,
|
|
|
|
List,
|
|
|
|
ListItem,
|
|
|
|
ListItemIcon,
|
|
|
|
ListItemSecondaryAction,
|
|
|
|
ListItemText,
|
|
|
|
makeStyles,
|
|
|
|
useTheme,
|
|
|
|
} from "@material-ui/core";
|
|
|
|
import {
|
|
|
|
Cancel,
|
|
|
|
CheckCircle,
|
|
|
|
CloudDownload,
|
|
|
|
ShoppingCart,
|
|
|
|
Timer,
|
|
|
|
} from "@material-ui/icons";
|
|
|
|
import { format } from "date-fns";
|
|
|
|
import { equals, find, propEq, takeWhile } from "ramda";
|
|
|
|
import React, { FC, Fragment, ReactNode, useMemo, useState } from "react";
|
|
|
|
import {
|
|
|
|
Commit,
|
|
|
|
CreatePipelineTaskInput,
|
|
|
|
Pipeline,
|
|
|
|
PipelineTask,
|
|
|
|
TaskStatuses,
|
|
|
|
WorkUnit,
|
|
|
|
PipelineUnits,
|
|
|
|
} from "../generated/graphql";
|
|
|
|
import { CREATE_PIPELINE_TASK } from "./muations";
|
|
|
|
import { COMMITS } from "./queries";
|
2021-05-09 15:29:11 +08:00
|
|
|
|
|
|
|
interface Props {
|
|
|
|
pipeline: Pipeline;
|
|
|
|
}
|
|
|
|
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
|
|
root: {
|
|
|
|
flex: "1 1 100%",
|
|
|
|
},
|
|
|
|
nested: {
|
|
|
|
paddingLeft: theme.spacing(4),
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
|
2021-05-12 21:18:17 +08:00
|
|
|
export const CommitList: FC<Props> = ({ pipeline }) => {
|
2021-05-09 15:29:11 +08:00
|
|
|
const { data, loading } = useQuery<{ commits: Commit[] }>(COMMITS, {
|
|
|
|
variables: {
|
|
|
|
pipelineId: pipeline.id,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
const classes = useStyles();
|
|
|
|
|
|
|
|
return (
|
|
|
|
<section className={classes.root}>
|
|
|
|
{(() => {
|
|
|
|
if (loading) {
|
|
|
|
return <LinearProgress color="secondary" />;
|
|
|
|
}
|
|
|
|
|
2021-05-09 16:42:19 +08:00
|
|
|
return (
|
|
|
|
<List>
|
|
|
|
{data?.commits.map((commit) => (
|
2021-05-12 21:18:17 +08:00
|
|
|
<Item key={commit.hash} commit={commit} pipeline={pipeline} />
|
2021-05-09 16:42:19 +08:00
|
|
|
))}
|
|
|
|
</List>
|
|
|
|
);
|
2021-05-09 15:29:11 +08:00
|
|
|
})()}
|
|
|
|
</section>
|
2021-05-12 21:18:17 +08:00
|
|
|
);
|
|
|
|
};
|
2021-05-09 15:29:11 +08:00
|
|
|
|
2021-05-12 21:18:17 +08:00
|
|
|
const unitActionPairs: Array<[PipelineUnits, ReactNode, string]> = [
|
|
|
|
[PipelineUnits.Checkout, <ShoppingCart />, "checkout"],
|
|
|
|
[
|
|
|
|
PipelineUnits.InstallDependencies,
|
|
|
|
<CloudDownload />,
|
|
|
|
"install dependencies",
|
|
|
|
],
|
|
|
|
[PipelineUnits.Test, <FontAwesomeIcon icon={faVial} />, "test"],
|
|
|
|
[PipelineUnits.Deploy, <FontAwesomeIcon icon={faPlayCircle} />, "deploy"],
|
|
|
|
];
|
|
|
|
|
|
|
|
const Item: FC<{ commit: Commit; pipeline: Pipeline }> = ({
|
|
|
|
commit,
|
|
|
|
pipeline,
|
|
|
|
}) => {
|
2021-05-09 15:29:11 +08:00
|
|
|
const [isOpen, setOpen] = useState(() => false);
|
|
|
|
const classes = useStyles();
|
2021-05-09 16:42:19 +08:00
|
|
|
|
2021-05-12 21:18:17 +08:00
|
|
|
const [createTask, { loading }] = useMutation<
|
|
|
|
{ createPipelineTask: PipelineTask },
|
|
|
|
{ task: CreatePipelineTaskInput }
|
|
|
|
>(CREATE_PIPELINE_TASK);
|
|
|
|
|
|
|
|
const units = useMemo(
|
|
|
|
() => pipeline.workUnitMetadata.units.map((unit) => unit.type),
|
|
|
|
[pipeline]
|
|
|
|
);
|
|
|
|
|
|
|
|
const {navigate, url} = useRouter();
|
|
|
|
const { response } = useResponse();
|
|
|
|
|
|
|
|
const handleCreateTask = (unit: PipelineUnits) => {
|
|
|
|
const _units = [...takeWhile(equals(unit), units), unit];
|
|
|
|
createTask({
|
|
|
|
variables: {
|
|
|
|
task: {
|
|
|
|
units: _units,
|
|
|
|
pipelineId: pipeline.id,
|
|
|
|
commit: commit.hash,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}).then(({data}) => {
|
|
|
|
navigate({
|
|
|
|
url: url({
|
|
|
|
name: "pipeline-task-detail",
|
|
|
|
params: { ...response.params, taskId: data?.createPipelineTask.id},
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
const actions = useMemo(
|
|
|
|
() =>
|
|
|
|
units.map((unit) => {
|
|
|
|
const pair = find(propEq(0, unit), unitActionPairs);
|
|
|
|
return (
|
|
|
|
pair && (
|
|
|
|
<IconButton
|
|
|
|
aria-label={pair[2]}
|
|
|
|
disabled={loading}
|
|
|
|
onClick={() => handleCreateTask(unit)}
|
|
|
|
>
|
|
|
|
{pair[1]}
|
|
|
|
</IconButton>
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
[units]
|
|
|
|
);
|
2021-05-09 15:29:11 +08:00
|
|
|
return (
|
|
|
|
<Fragment>
|
|
|
|
<ListItem button onClick={() => setOpen(!isOpen)}>
|
|
|
|
<ListItemText
|
|
|
|
primary={commit.message}
|
|
|
|
secondary={format(commit.date, "yyyy-MM-dd HH:mm:ss")}
|
|
|
|
/>
|
2021-05-12 21:18:17 +08:00
|
|
|
<ListItemSecondaryAction>{actions}</ListItemSecondaryAction>
|
2021-05-09 15:29:11 +08:00
|
|
|
</ListItem>
|
2021-05-12 21:18:17 +08:00
|
|
|
{loading && <LinearProgress color="secondary" />}
|
2021-05-09 15:29:11 +08:00
|
|
|
<Collapse in={isOpen} timeout="auto" unmountOnExit>
|
|
|
|
<List component="div" disablePadding>
|
2021-05-09 16:42:19 +08:00
|
|
|
{commit.tasks.map((task) => (
|
|
|
|
<Link
|
|
|
|
key={task.id}
|
|
|
|
name="pipeline-task-detail"
|
|
|
|
params={{ ...response.params, taskId: task.id }}
|
|
|
|
>
|
|
|
|
<TaskItem task={task} />
|
|
|
|
</Link>
|
|
|
|
))}
|
2021-05-09 15:29:11 +08:00
|
|
|
</List>
|
|
|
|
</Collapse>
|
|
|
|
</Fragment>
|
|
|
|
);
|
2021-05-12 21:18:17 +08:00
|
|
|
};
|
2021-05-09 15:29:11 +08:00
|
|
|
|
2021-05-12 21:18:17 +08:00
|
|
|
const TaskItem: FC<{ task: PipelineTask }> = ({ task }) => {
|
2021-05-09 15:29:11 +08:00
|
|
|
const classes = useStyles();
|
|
|
|
const theme = useTheme();
|
|
|
|
const statusIcon: ReactNode = (() => {
|
|
|
|
switch (task.status) {
|
|
|
|
case TaskStatuses.Pending:
|
|
|
|
return <Timer style={{ color: theme.palette.info.main }} />;
|
|
|
|
case TaskStatuses.Success:
|
|
|
|
return <CheckCircle style={{ color: theme.palette.success.main }} />;
|
|
|
|
case TaskStatuses.Failed:
|
|
|
|
return <Cancel style={{ color: theme.palette.error.main }} />;
|
|
|
|
case TaskStatuses.Working:
|
2021-05-12 21:18:17 +08:00
|
|
|
return (
|
|
|
|
<CircularProgress style={{ color: theme.palette.secondary.main }} />
|
|
|
|
);
|
2021-05-09 15:29:11 +08:00
|
|
|
}
|
2021-05-12 21:18:17 +08:00
|
|
|
})();
|
2021-05-09 15:29:11 +08:00
|
|
|
return (
|
|
|
|
<ListItem button className={classes.nested}>
|
|
|
|
<ListItemIcon>{statusIcon}</ListItemIcon>
|
|
|
|
<ListItemText primary={format(task.startedAt, "yyyy-MM-dd HH:mm:ss")} />
|
|
|
|
</ListItem>
|
|
|
|
);
|
2021-05-12 21:18:17 +08:00
|
|
|
};
|