fix
This commit is contained in:
@@ -7,9 +7,10 @@ const { spawn } = require("node:child_process");
|
||||
|
||||
function startRun(steps) {
|
||||
const emitter = new EventEmitter();
|
||||
const state = { cancelled: false, child: null };
|
||||
const state = { cancelled: false, child: null, pendingDecision: null };
|
||||
|
||||
setImmediate(async () => {
|
||||
let hadFailure = false;
|
||||
for (let index = 0; index < steps.length; index += 1) {
|
||||
if (state.cancelled) {
|
||||
emitter.emit("event", { type: "done", success: false, cancelled: true });
|
||||
@@ -35,12 +36,27 @@ function startRun(steps) {
|
||||
status: "failed",
|
||||
error: error.message || String(error),
|
||||
});
|
||||
emitter.emit("event", { type: "done", success: false });
|
||||
return;
|
||||
|
||||
if (index === steps.length - 1) {
|
||||
emitter.emit("event", { type: "done", success: false, hadFailure: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const decision = await new Promise((resolve) => {
|
||||
state.pendingDecision = resolve;
|
||||
emitter.emit("event", { type: "awaiting-decision", index });
|
||||
});
|
||||
state.pendingDecision = null;
|
||||
|
||||
if (decision !== "skip") {
|
||||
emitter.emit("event", { type: "done", success: false, hadFailure: true });
|
||||
return;
|
||||
}
|
||||
hadFailure = true;
|
||||
}
|
||||
}
|
||||
|
||||
emitter.emit("event", { type: "done", success: true });
|
||||
emitter.emit("event", { type: "done", success: !hadFailure, hadFailure });
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -48,8 +64,16 @@ function startRun(steps) {
|
||||
emitter.on(event, handler);
|
||||
return this;
|
||||
},
|
||||
decide(value) {
|
||||
if (state.pendingDecision) {
|
||||
state.pendingDecision(value);
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
state.cancelled = true;
|
||||
if (state.pendingDecision) {
|
||||
state.pendingDecision("abort");
|
||||
}
|
||||
if (state.child) {
|
||||
try {
|
||||
state.child.kill("SIGTERM");
|
||||
|
||||
@@ -21,7 +21,7 @@ const {
|
||||
} = require("./frp-config");
|
||||
|
||||
const NVM_INSTALL_VERSION = "v0.40.4";
|
||||
const NVM_INSTALL_COMMAND = `set -euo pipefail
|
||||
const NVM_INSTALL_COMMAND = `set -eo pipefail
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
if [ ! -s "$NVM_DIR/nvm.sh" ]; then
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useReducer } from "react";
|
||||
import React, { useEffect, useReducer, useRef } from "react";
|
||||
import { Box, Text } from "ink";
|
||||
import Spinner from "ink-spinner";
|
||||
import SelectInput from "ink-select-input";
|
||||
@@ -31,8 +31,18 @@ function reducer(state, action) {
|
||||
}
|
||||
return { ...state, log: next };
|
||||
}
|
||||
case "awaiting-decision":
|
||||
return { ...state, awaitingDecision: action.index };
|
||||
case "decision-made":
|
||||
return { ...state, awaitingDecision: null };
|
||||
case "done":
|
||||
return { ...state, finished: true, success: action.success, cancelled: !!action.cancelled };
|
||||
return {
|
||||
...state,
|
||||
finished: true,
|
||||
success: action.success,
|
||||
cancelled: !!action.cancelled,
|
||||
awaitingDecision: null,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -47,16 +57,26 @@ export default function RunLog({ nav, plan, origin }) {
|
||||
finished: false,
|
||||
success: false,
|
||||
cancelled: false,
|
||||
awaitingDecision: null,
|
||||
});
|
||||
const runRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const run = startRun(plan.steps);
|
||||
runRef.current = run;
|
||||
run.on("event", (event) => {
|
||||
dispatch(event);
|
||||
});
|
||||
return () => run.cancel();
|
||||
}, [plan]);
|
||||
|
||||
const handleDecision = (item) => {
|
||||
if (runRef.current) {
|
||||
runRef.current.decide(item.value);
|
||||
}
|
||||
dispatch({ type: "decision-made" });
|
||||
};
|
||||
|
||||
const recentLog = state.log.slice(-MAX_LOG_LINES);
|
||||
|
||||
return (
|
||||
@@ -82,7 +102,20 @@ export default function RunLog({ nav, plan, origin }) {
|
||||
))}
|
||||
</Box>
|
||||
) : null}
|
||||
{state.finished ? <FinishedFooter state={state} nav={nav} origin={origin} /> : (
|
||||
{state.finished ? (
|
||||
<FinishedFooter state={state} nav={nav} origin={origin} />
|
||||
) : state.awaitingDecision != null ? (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Text color="yellow">Step {state.awaitingDecision + 1} failed. Continue?</Text>
|
||||
<SelectInput
|
||||
items={[
|
||||
{ label: "Skip and continue", value: "skip" },
|
||||
{ label: "Abort run", value: "abort" },
|
||||
]}
|
||||
onSelect={handleDecision}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box marginTop={1}>
|
||||
<Text color="cyan">
|
||||
<Spinner type="dots" />
|
||||
@@ -168,6 +201,8 @@ function FinishedFooter({ state, nav, origin }) {
|
||||
<Text color="yellow">Cancelled.</Text>
|
||||
) : state.success ? (
|
||||
<Text color="green">All steps completed successfully.</Text>
|
||||
) : Object.values(state.statuses).includes("failed") && !state.cancelled ? (
|
||||
<Text color="yellow">Run finished with skipped failures.</Text>
|
||||
) : (
|
||||
<Text color="red">Run failed.</Text>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user