Documentation
¶
Overview ¶
Package shellhook runs a user-configured shell script as a pure side effect (§ 结束 Hook): a notification / logging / marking script fired AFTER some unit of work already finished. A hook NEVER produces variables, changes any status, or fails its caller: a non-zero exit, a timeout, or a spawn error is logged at Warn level and otherwise ignored.
It is intentionally simpler than a Shell workflow node (no control file, no output parsing, no helper functions) — it only injects environment and runs the script. Both the Graph engine (Prompt / End node hooks) and the Job service (interactive round end) share this executor so the two entry points keep identical semantics: same timeout, same env rules, same failure handling.
Index ¶
Constants ¶
const OutputMaxRunes = 4000
OutputMaxRunes bounds each captured stream (stdout/stderr) before it is handed to Request.Emit. A hook is meant for short side effects (a notification, a log line); a runaway script must not be able to bloat a persisted event or a detail panel. Truncation happens in Run so what the caller emits is already bounded.
const Timeout = 60 * time.Second
Timeout bounds a single hook so a hung script cannot leak a goroutine for the lifetime of the process. The work has already succeeded by the time its hook runs, so the timeout only affects the side effect, never the caller.
Variables ¶
This section is empty.
Functions ¶
func Run ¶
Run executes a hook script as a pure side effect. It never returns an error: every failure mode (mkdir, tempfile, spawn, non-zero exit, timeout) is logged and swallowed. parent is used only for log correlation — the script runs under its OWN timeout context detached from parent's cancellation, so a caller-level cancel (hard stop, run timeout, worker cancel) cannot kill a side effect that has already been triggered by finished work.
Types ¶
type Outcome ¶
type Outcome struct {
ExitCode int
Stdout string
Stderr string
Message string // non-empty on failure: human-readable cause
Failed bool
}
Outcome is a hook's execution result, handed to Request.Emit so the caller can surface it (as an event, a log line, …). ExitCode is the script's process exit code on a clean run; for a setup failure (mkdir/tempfile/spawn) or a timeout the script never produced a code, so -1 is used.
type Request ¶
type Request struct {
// Script is the resolved script body. Blank (or whitespace-only) is a
// deliberate no-op: nothing runs and nothing is emitted.
Script string
// Workdir is the directory the script RUNS in; "" leaves the child with the
// server process's own working directory. It is deliberately not where the
// script FILE is written — see scriptDir.
Workdir string
// Vars are user-defined variables exported as $name. Keys in the reserved
// QUARTET_ namespace are ignored.
Vars map[string]string
// Disabled names render as the empty string regardless of their stored value
// (and are exported empty even when absent from Vars).
Disabled map[string]struct{}
// Context holds the QUARTET_* runtime context variables, exported LAST so a
// user variable can never shadow them.
Context map[string]string
// LogFields is appended to every log line for correlation, e.g.
// "runId=r-1 nodeId=n-2" or "jobId=j-1 sessionId=s-3".
LogFields string
// Emit, when non-nil, receives the hook's result exactly once on every
// terminal path (success or any failure), so a configured hook always
// produces a signal, never silence. Invoked from the calling goroutine of
// Run — the caller must ensure the closure is safe there.
Emit func(Outcome)
}
Request carries everything a hook script reads. The caller assembles it on its own goroutine (snapshotting any owned state) so Run can execute off-thread without touching shared state.