278 lines
7 KiB
JavaScript
278 lines
7 KiB
JavaScript
import React, { useReducer, createContext, useMemo } from "react";
|
|
import Initiator from "@qltr/initiator";
|
|
const JobContext = createContext();
|
|
|
|
export const jobStatus = {
|
|
OK: "o",
|
|
QUEUED: "q",
|
|
PENDING: "p",
|
|
CANCELED: "c",
|
|
ACTIVE: "a",
|
|
ERROR: "e",
|
|
};
|
|
|
|
const ACTIONS = {
|
|
CREATE: "c",
|
|
UPDATE: "u",
|
|
DELETE: "d",
|
|
PIPELINE: "p",
|
|
};
|
|
|
|
const url = "https://qualiteer.elijahparker3.repl.co/";
|
|
|
|
const initialState = {
|
|
jobs: [],
|
|
pipelines: [],
|
|
};
|
|
|
|
const reducer = (state, action) => {
|
|
// Current Jobs
|
|
const { jobs, pipelines } = state;
|
|
var jobIndex;
|
|
// Actions
|
|
switch (action.type) {
|
|
case ACTIONS.CREATE:
|
|
jobs.push(action.job);
|
|
return { ...state, jobs };
|
|
|
|
case ACTIONS.UPDATE:
|
|
jobIndex = jobs.findIndex(
|
|
(j) => j.jobId === (action.job.jobId ?? action.jobId)
|
|
);
|
|
jobs[jobIndex] = { ...jobs[jobIndex], ...action.job };
|
|
return { ...state, jobs };
|
|
|
|
case ACTIONS.DELETE:
|
|
jobIndex = jobs.findIndex((j) => j.jobId === action.jobId);
|
|
jobs.splice(jobIndex, 1);
|
|
return { ...state, jobs };
|
|
|
|
case ACTIONS.PIPELINE:
|
|
return { ...state, pipelines };
|
|
default:
|
|
return state;
|
|
}
|
|
};
|
|
|
|
export const JobProvider = ({ children }) => {
|
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
|
|
const jobUpdate = (job, jobId) =>
|
|
dispatch({ type: ACTIONS.UPDATE, jobId, job });
|
|
const jobCreate = (job) =>
|
|
dispatch({ type: ACTIONS.CREATE, job: { ...job, log: [] } });
|
|
const jobDelete = (jobId) => dispatch({ type: ACTIONS.DELETE, jobId });
|
|
|
|
const updatePipelines = (pipelines) =>
|
|
dispatch({ type: ACTIONS.pipeline, pipelines });
|
|
|
|
function retryAll(failing) {
|
|
// Query Full Locator
|
|
console.log("Would retry all failing tests!");
|
|
return jobFactory({ testNames: ["single"] });
|
|
}
|
|
|
|
function pipelineComponentJob(jobPipeline, pipelineReq) {
|
|
const i = new Initiator(url);
|
|
const jobId = `j${Date.now()}`;
|
|
const job = {
|
|
name: jobId,
|
|
status: jobStatus.PENDING,
|
|
jobId,
|
|
isPipeline: true,
|
|
initiator: i,
|
|
pipelineId: jobPipeline.id,
|
|
branchId: pipelineReq.pipeline.__test,
|
|
};
|
|
const request = {
|
|
image: "node",
|
|
name: jobId,
|
|
...pipelineReq,
|
|
};
|
|
|
|
jobCreate(job);
|
|
const onLog = (d) => {
|
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
|
|
|
job.log.push(d);
|
|
job.status = jobStatus.ACTIVE;
|
|
jobUpdate({ ...job }, jobId);
|
|
};
|
|
|
|
const onClose = (c) => {
|
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
|
job.exitcode = c;
|
|
job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR;
|
|
jobUpdate({ ...job }, jobId);
|
|
};
|
|
|
|
const onPipelineTrigger = (p) => {
|
|
const { triggers } = p;
|
|
for (var t in triggers) {
|
|
const delay = triggers[t].__testDelay ?? 0;
|
|
delete triggers[t].__testDelay;
|
|
const jobReq = {
|
|
...request,
|
|
pipeline: {
|
|
...p,
|
|
triggers: triggers[t],
|
|
__test: t,
|
|
},
|
|
};
|
|
jobPipeline.pendingTriggers.push({
|
|
testName: t,
|
|
timer: setTimeout(
|
|
() => pipelineComponentJob(jobPipeline, jobReq),
|
|
delay
|
|
),
|
|
triggerAt: Date.now() + delay,
|
|
});
|
|
}
|
|
};
|
|
const started = i.newPipelineJob(
|
|
request,
|
|
onLog,
|
|
onClose,
|
|
null,
|
|
onPipelineTrigger
|
|
);
|
|
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
|
}
|
|
|
|
function pipelineFactory(builderCache) {
|
|
const { tree, branches, selectedBranches } = builderCache;
|
|
const __test = Object.keys(tree)[0];
|
|
const pipelineReq = {
|
|
image: "node",
|
|
pipeline: { __test, triggers: { ...tree[__test] } },
|
|
};
|
|
const id = `pij${Date.now()}`;
|
|
const pipeline = { id, branches, pendingTriggers: [], selectedBranches };
|
|
const { pipelines } = state;
|
|
pipelines.push(pipeline);
|
|
updatePipelines([...pipelines]);
|
|
pipelineComponentJob(pipeline, pipelineReq);
|
|
return pipeline;
|
|
}
|
|
|
|
function pipelineCancel(pipelineId) {
|
|
const pipeline = state.pipelines.find((p) => p.id === pipelineId);
|
|
pipeline.isCanceled = true;
|
|
pipeline.pendingTriggers.forEach((t) => clearTimeout(t));
|
|
const jobs = state.jobs.filter(
|
|
(j) => j.isPipeline && j.pipelineId === pipelineId
|
|
);
|
|
for (var j of jobs) {
|
|
if (j.initiator.sk) j.initiator.sk.close();
|
|
j.status = jobStatus.CANCELED;
|
|
jobUpdate({ ...j }, j.jobId);
|
|
}
|
|
}
|
|
|
|
function pipelineDestroy(pipelineId) {
|
|
const pipelineIndex = state.pipelines.findIndex((p) => p.id === pipelineId);
|
|
const pipeline = state.pipelines[pipelineIndex];
|
|
pipeline.pendingTriggers.forEach((t) => clearTimeout(t));
|
|
const jobs = state.jobs.filter(
|
|
(j) => j.isPipeline && j.pipelineId === pipelineId
|
|
);
|
|
for (var j of jobs) {
|
|
if (
|
|
j.initiator.sk &&
|
|
j.status !== jobStatus.OK &&
|
|
j.status !== jobStatus.ERROR &&
|
|
j.status !== jobStatus.CANCELED
|
|
) {
|
|
j.initiator.sk.close();
|
|
}
|
|
jobDelete(j.jobId);
|
|
}
|
|
state.pipelines.splice(pipelineIndex, 1);
|
|
}
|
|
|
|
function jobCancel(jobId) {
|
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
|
|
|
if (job.initiator.sk) job.initiator.sk.close();
|
|
job.status = jobStatus.CANCELED;
|
|
jobUpdate({ ...job }, jobId);
|
|
}
|
|
|
|
function jobDestroy(jobId) {
|
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
|
|
|
if (
|
|
job.initiator.sk &&
|
|
job.status !== jobStatus.OK &&
|
|
job.status !== jobStatus.ERROR &&
|
|
job.status !== jobStatus.CANCELED
|
|
) {
|
|
job.initiator.sk.close();
|
|
}
|
|
jobDelete(jobId);
|
|
}
|
|
|
|
function jobFactory(builderCache) {
|
|
if (builderCache.tree) return pipelineFactory(builderCache);
|
|
// Find test
|
|
const i = new Initiator(url);
|
|
const jobId = `j${Date.now()}`;
|
|
const job = {
|
|
name: jobId,
|
|
status: jobStatus.PENDING,
|
|
jobId,
|
|
isPipeline: false,
|
|
builderCache,
|
|
initiator: i,
|
|
};
|
|
|
|
const request = {
|
|
testNames: builderCache.testNames,
|
|
image: "node",
|
|
type: "single",
|
|
name: jobId,
|
|
};
|
|
|
|
jobCreate(job);
|
|
|
|
const onLog = (d) => {
|
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
|
job.log.push(d);
|
|
job.status = jobStatus.ACTIVE;
|
|
jobUpdate({ ...job }, jobId);
|
|
};
|
|
|
|
const onClose = (c) => {
|
|
const job = state.jobs.find((j) => j.jobId === jobId);
|
|
job.exitcode = c;
|
|
job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR;
|
|
jobUpdate({ ...job }, jobId);
|
|
};
|
|
|
|
const started = i.newJob(request, onLog, onClose);
|
|
started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId));
|
|
|
|
return jobId;
|
|
}
|
|
|
|
const context = {
|
|
state,
|
|
dispatch,
|
|
jobUpdate,
|
|
jobCreate,
|
|
jobDelete,
|
|
retryAll,
|
|
jobFactory,
|
|
jobCancel,
|
|
jobDestroy,
|
|
pipelineCancel,
|
|
pipelineDestroy,
|
|
};
|
|
const contextValue = useMemo(() => context, [state, dispatch]);
|
|
|
|
return (
|
|
<JobContext.Provider value={contextValue}>{children}</JobContext.Provider>
|
|
);
|
|
};
|
|
|
|
export default JobContext;
|