@sshkeda/task 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +131 -2
- package/package.json +41 -4
- package/src/index.ts +2 -0
- package/src/lib/errors/index.ts +15 -0
- package/src/lib/errors/internal/errors.ts +147 -0
- package/src/lib/kill-leftover-processes/index.ts +17 -0
- package/src/lib/kill-leftover-processes/internal/command-words.ts +16 -0
- package/src/lib/kill-leftover-processes/internal/find-process-ids.ts +36 -0
- package/src/lib/kill-leftover-processes/internal/kill-process-groups.ts +270 -0
- package/src/lib/kill-leftover-processes/internal/list-linux-processes-from-proc.ts +191 -0
- package/src/lib/kill-leftover-processes/internal/list-macos-processes-with-sysctl.ts +335 -0
- package/src/lib/kill-leftover-processes/internal/list-processes.ts +58 -0
- package/src/lib/kill-leftover-processes/internal/mark-run-processes.ts +367 -0
- package/src/lib/kill-leftover-processes/internal/process-cleanup-errors.ts +17 -0
- package/src/lib/kill-leftover-processes/internal/send-signal.ts +32 -0
- package/src/lib/kill-leftover-processes/internal/signal-run-processes.ts +48 -0
- package/src/lib/kill-leftover-processes/internal/started-process-groups.ts +107 -0
- package/src/lib/main-thread/index.ts +3 -0
- package/src/lib/main-thread/internal/listen-for-stop-requests.ts +102 -0
- package/src/lib/main-thread/internal/run-main-thread.ts +81 -0
- package/src/lib/main-thread/internal/run-root-task-fiber.ts +191 -0
- package/src/lib/main-thread/internal/stops-at-first-failure.ts +20 -0
- package/src/lib/main-thread/internal/trace-the-run.ts +107 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/index.ts +7 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/follow-imports-from-entry-files.ts +306 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/load-all-reporting-first-failure.ts +11 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/make-module-graph.ts +48 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/read-configured-loaders.ts +64 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/resolve-module-specifiers.ts +60 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/rewrite-worker-imports.ts +118 -0
- package/src/lib/main-thread/lib/find-code-each-task-depends-on/internal/worker-import-errors.ts +19 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/index.ts +7 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/internal/handle-worker-events.ts +348 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/internal/make-task-runs.ts +207 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/index.ts +1 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/await-within-counted-limit.ts +29 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/execute-task.ts +272 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/make-task-setup-clock.ts +129 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/lib/run-requested-task-once/internal/run-requested-tasks.ts +135 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/define-task-run-state.ts +288 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/make-stopwatch.ts +39 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/share-standard-input-with-root-task.ts +36 -0
- package/src/lib/main-thread/lib/run-tasks-on-workers/shared/stop-task.ts +166 -0
- package/src/lib/main-thread/lib/scan-module-source/index.ts +1 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/find-lexical-bindings.ts +154 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/find-task-declarations.ts +205 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/parse-module-text.ts +34 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/read-literal-string.ts +15 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/read-module-loads.ts +316 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/scan-module-text.ts +70 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/unwrap-type-assertions.ts +10 -0
- package/src/lib/main-thread/lib/scan-module-source/internal/visit-non-reference-identifiers.ts +21 -0
- package/src/lib/main-thread/shared/fail-run.ts +36 -0
- package/src/lib/main-thread/shared/find-failure-reason.ts +43 -0
- package/src/lib/main-thread/shared/find-package-directories.ts +34 -0
- package/src/lib/main-thread/shared/find-tasks-reachable-from-root-task-file.ts +246 -0
- package/src/lib/main-thread/shared/list-directory-paths-up-to-root.ts +13 -0
- package/src/lib/main-thread/shared/main-thread-errors.ts +373 -0
- package/src/lib/main-thread/shared/package-json-errors.ts +93 -0
- package/src/lib/main-thread/shared/provide-discovered-tasks.ts +26 -0
- package/src/lib/main-thread/shared/provide-process-groups.ts +63 -0
- package/src/lib/main-thread/shared/provide-run-root-task-config.ts +66 -0
- package/src/lib/main-thread/shared/provide-stop-state.ts +150 -0
- package/src/lib/main-thread/shared/provide-worker-pool.ts +607 -0
- package/src/lib/main-thread/shared/read-bare-package-name.ts +3 -0
- package/src/lib/main-thread/shared/read-package-json.ts +149 -0
- package/src/lib/main-thread/shared/report-task-failure.ts +31 -0
- package/src/lib/preload-rewritten-imports-into-each-worker/index.ts +5 -0
- package/src/lib/preload-rewritten-imports-into-each-worker/internal/load-rewritten-imports.ts +34 -0
- package/src/lib/supervise-the-run/index.ts +15 -0
- package/src/lib/supervise-the-run/internal/adopt-orphans-on-linux.ts +73 -0
- package/src/lib/supervise-the-run/internal/check-root-task-file.ts +8 -0
- package/src/lib/supervise-the-run/internal/report-ready.ts +12 -0
- package/src/lib/supervise-the-run/internal/share-process-groups.ts +25 -0
- package/src/lib/supervise-the-run/internal/start-watching-the-supervisor.ts +6 -0
- package/src/lib/supervise-the-run/internal/supervise-run.ts +155 -0
- package/src/lib/supervise-the-run/internal/talk-to-the-supervisor.ts +53 -0
- package/src/lib/supervise-the-run/internal/watch-the-supervisor.ts +61 -0
- package/src/lib/task/index.ts +5 -0
- package/src/lib/task/internal/make-tasks.ts +82 -0
- package/src/lib/task/internal/run-command.ts +194 -0
- package/src/lib/task/internal/run-root-task.ts +263 -0
- package/src/lib/task/internal/run-task-lists.ts +51 -0
- package/src/lib/task/internal/trust-output-mode-names.ts +18 -0
- package/src/lib/task/internal/trust-task-results.ts +35 -0
- package/src/lib/task/internal/use-task-services.ts +4 -0
- package/src/lib/worker-thread/index.ts +3 -0
- package/src/lib/worker-thread/internal/answer-main-thread-messages.ts +141 -0
- package/src/lib/worker-thread/internal/describe-task-body-result.ts +91 -0
- package/src/lib/worker-thread/internal/name-config-key-not-set.ts +57 -0
- package/src/lib/worker-thread/internal/run-task-body.ts +117 -0
- package/src/lib/worker-thread/internal/serve-on-the-parent-port.ts +63 -0
- package/src/lib/worker-thread/internal/start-worker-thread.ts +41 -0
- package/src/lib/worker-thread/lib/provide-task-services/index.ts +1 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/make-task-logger.ts +34 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/provide-task-services.ts +86 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/read-standard-input-from-main-thread.ts +58 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/resolve-paths-from-package-directory.ts +61 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/run-commands-in-package-directory.ts +24 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-commands-with-bun.ts +545 -0
- package/src/lib/worker-thread/lib/provide-task-services/internal/spawn-reported-process.ts +151 -0
- package/src/lib/worker-thread/shared/await-main-thread-answers.ts +76 -0
- package/src/lib/worker-thread/shared/decode-task-result-json.ts +114 -0
- package/src/lib/worker-thread/shared/define-task-body-record.ts +11 -0
- package/src/lib/worker-thread/shared/describe-issue-input.ts +52 -0
- package/src/lib/worker-thread/shared/plain-object.ts +8 -0
- package/src/lib/worker-thread/shared/provide-worker-state.ts +160 -0
- package/src/lib/worker-thread/shared/worker-thread-errors.ts +41 -0
- package/src/shared/attribute-reported-failures.ts +25 -0
- package/src/shared/claim-processes-to-report.ts +22 -0
- package/src/shared/command-errors.ts +44 -0
- package/src/shared/complete-discarded-calls-once-sent.ts +12 -0
- package/src/shared/cut-to-graphemes.ts +12 -0
- package/src/shared/environment-config-errors.ts +74 -0
- package/src/shared/error-fields.ts +13 -0
- package/src/shared/find-bun-loader-for-script.ts +32 -0
- package/src/shared/find-node-modules-package-directory.ts +35 -0
- package/src/shared/find-user-locations-in-stack-traces.ts +64 -0
- package/src/shared/format-error-lines.ts +53 -0
- package/src/shared/format-task-lines.ts +21 -0
- package/src/shared/hold-task-logs.ts +24 -0
- package/src/shared/invariant-errors.ts +28 -0
- package/src/shared/mark-task-worker-thread-contexts.ts +29 -0
- package/src/shared/module-graph-errors.ts +49 -0
- package/src/shared/name-config-key.ts +42 -0
- package/src/shared/names-from-package-json.ts +7 -0
- package/src/shared/print-to-standard-error.ts +48 -0
- package/src/shared/process-errors.ts +191 -0
- package/src/shared/read-environment-config.ts +239 -0
- package/src/shared/record-tasks-made.ts +10 -0
- package/src/shared/report-cause.ts +140 -0
- package/src/shared/report-errors.ts +121 -0
- package/src/shared/resolve-task-options.ts +20 -0
- package/src/shared/rpc.ts +161 -0
- package/src/shared/run-in-child-fiber-and-join.ts +16 -0
- package/src/shared/run-marker.ts +7 -0
- package/src/shared/save-standard-stream-flags.ts +57 -0
- package/src/shared/select-cause-reasons.ts +14 -0
- package/src/shared/signal-run-called-twice.ts +11 -0
- package/src/shared/standard-input-streams.ts +41 -0
- package/src/shared/stop-signals.ts +41 -0
- package/src/shared/task-errors.ts +224 -0
- package/src/shared/task-package.ts +5 -0
- package/src/shared/task-results.ts +55 -0
- package/src/shared/task-services.ts +30 -0
- package/src/shared/write-trace-file.ts +112 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Cause, Effect, Layer } from "effect";
|
|
2
|
+
import type { Queue } from "effect";
|
|
3
|
+
import { FetchHttpClient } from "effect/http";
|
|
4
|
+
import { OtlpExporter, OtlpSerialization } from "effect/observability";
|
|
5
|
+
|
|
6
|
+
import { formatCauseLines, formatFailureLines } from "../../../shared/format-error-lines.ts";
|
|
7
|
+
import { printLine } from "../../../shared/print-to-standard-error.ts";
|
|
8
|
+
import { runtimeConfigProviderLayer } from "../../../shared/read-environment-config.ts";
|
|
9
|
+
import type { STOP_SIGNALS } from "../../../shared/stop-signals.ts";
|
|
10
|
+
import type { Task } from "../../task/index.ts";
|
|
11
|
+
import { ModuleGraphLive } from "../lib/find-code-each-task-depends-on/index.ts";
|
|
12
|
+
import { TaskRunsLive } from "../lib/run-tasks-on-workers/index.ts";
|
|
13
|
+
import { DiscoveredTasksLive } from "../shared/provide-discovered-tasks.ts";
|
|
14
|
+
import { ProcessGroupsLive } from "../shared/provide-process-groups.ts";
|
|
15
|
+
import { RunRootTaskConfigLive } from "../shared/provide-run-root-task-config.ts";
|
|
16
|
+
import { StopState } from "../shared/provide-stop-state.ts";
|
|
17
|
+
import { WorkerPoolLive } from "../shared/provide-worker-pool.ts";
|
|
18
|
+
import { runCalledTwiceLayer, stopRequestsLayer } from "./listen-for-stop-requests.ts";
|
|
19
|
+
import type { runRootTaskFiber } from "./run-root-task-fiber.ts";
|
|
20
|
+
import { stopsAtFirstFailure } from "./stops-at-first-failure.ts";
|
|
21
|
+
import { TraceFileLive, RuntimeTracingLive } from "./trace-the-run.ts";
|
|
22
|
+
|
|
23
|
+
export const MainThreadLive = (
|
|
24
|
+
rootTask: Task,
|
|
25
|
+
signals: Queue.Dequeue<(typeof STOP_SIGNALS)[number]>,
|
|
26
|
+
supervisorProcessId: number | null,
|
|
27
|
+
) =>
|
|
28
|
+
stopsAtFirstFailure(TaskRunsLive.pipe(Layer.provideMerge(WorkerPoolLive))).pipe(
|
|
29
|
+
Layer.provideMerge(DiscoveredTasksLive),
|
|
30
|
+
Layer.provideMerge(
|
|
31
|
+
stopsAtFirstFailure(
|
|
32
|
+
ProcessGroupsLive.pipe(
|
|
33
|
+
Layer.provideMerge(RuntimeTracingLive),
|
|
34
|
+
Layer.provideMerge(TraceFileLive),
|
|
35
|
+
Layer.provideMerge(ModuleGraphLive),
|
|
36
|
+
Layer.provideMerge(
|
|
37
|
+
Layer.mergeAll(
|
|
38
|
+
FetchHttpClient.layer,
|
|
39
|
+
runtimeConfigProviderLayer,
|
|
40
|
+
OtlpExporter.layerFlusher,
|
|
41
|
+
OtlpSerialization.layerJson,
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
Layer.provideMerge(runCalledTwiceLayer),
|
|
45
|
+
Layer.provideMerge(RunRootTaskConfigLive(rootTask, supervisorProcessId)),
|
|
46
|
+
),
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
Layer.provideMerge(stopRequestsLayer(signals)),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
export const exitCodeOfTheRun = <Requirements>(
|
|
53
|
+
runningRootTask: Effect.Effect<
|
|
54
|
+
Effect.Success<typeof runRootTaskFiber>,
|
|
55
|
+
Effect.Error<typeof runRootTaskFiber> | Layer.Error<ReturnType<typeof MainThreadLive>>,
|
|
56
|
+
Requirements
|
|
57
|
+
>,
|
|
58
|
+
) =>
|
|
59
|
+
Effect.gen(function* () {
|
|
60
|
+
const shutdown = yield* StopState;
|
|
61
|
+
const { exitCode, runEndingCause } = yield* runningRootTask.pipe(
|
|
62
|
+
Effect.catchTag("StopRequested", (failure) =>
|
|
63
|
+
printLine("Error", formatFailureLines(failure, null)).pipe(
|
|
64
|
+
Effect.as({ exitCode: failure.exitCode, runEndingCause: Cause.fail(failure) }),
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
Effect.matchCauseEffect({
|
|
68
|
+
onFailure: (cause) =>
|
|
69
|
+
Cause.hasInterruptsOnly(cause)
|
|
70
|
+
? Effect.failCause(cause)
|
|
71
|
+
: printLine("Error", formatCauseLines(cause, null)).pipe(
|
|
72
|
+
Effect.as({ exitCode: 1, runEndingCause: cause }),
|
|
73
|
+
),
|
|
74
|
+
onSuccess: (ended) => Effect.succeed(ended),
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
if (runEndingCause !== null) {
|
|
78
|
+
yield* printLine("Info", yield* shutdown.formatRunFailureReasonLine(runEndingCause));
|
|
79
|
+
}
|
|
80
|
+
return exitCode;
|
|
81
|
+
});
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Cause, Array as Arrays, Clock, Deferred, Effect, Schema, Stream, Fiber } from "effect";
|
|
2
|
+
|
|
3
|
+
import { formatFailureLines } from "../../../shared/format-error-lines.ts";
|
|
4
|
+
import { formatSeconds } from "../../../shared/format-task-lines.ts";
|
|
5
|
+
import { dieOnInvariantFailure } from "../../../shared/invariant-errors.ts";
|
|
6
|
+
import { TASK_TRACE_LABEL_PREFIX } from "../../../shared/names-from-package-json.ts";
|
|
7
|
+
import { printLine } from "../../../shared/print-to-standard-error.ts";
|
|
8
|
+
import { availableParallelism } from "../../../shared/read-environment-config.ts";
|
|
9
|
+
import { TaskBodyFailed } from "../../../shared/task-errors.ts";
|
|
10
|
+
import { TASK_PACKAGE } from "../../../shared/task-package.ts";
|
|
11
|
+
import { TaskRuns } from "../lib/run-tasks-on-workers/index.ts";
|
|
12
|
+
import { failRun, failRunOnFailure } from "../shared/fail-run.ts";
|
|
13
|
+
import {
|
|
14
|
+
RootTaskWorkingDirectoryUnavailable,
|
|
15
|
+
StopRequested,
|
|
16
|
+
UnawaitedTasksFailed,
|
|
17
|
+
} from "../shared/main-thread-errors.ts";
|
|
18
|
+
import { DiscoveredTasks } from "../shared/provide-discovered-tasks.ts";
|
|
19
|
+
import { ProcessGroups } from "../shared/provide-process-groups.ts";
|
|
20
|
+
import { RunRootTaskConfig } from "../shared/provide-run-root-task-config.ts";
|
|
21
|
+
import { StopState } from "../shared/provide-stop-state.ts";
|
|
22
|
+
import { WorkerPool } from "../shared/provide-worker-pool.ts";
|
|
23
|
+
|
|
24
|
+
const stopEverythingStarted = Effect.fn("stopEverythingStarted")(function* (
|
|
25
|
+
workerEventHandlingFiber: Fiber.Fiber<void>,
|
|
26
|
+
) {
|
|
27
|
+
const shutdown = yield* StopState;
|
|
28
|
+
const taskRuns = yield* TaskRuns;
|
|
29
|
+
const workerPool = yield* WorkerPool;
|
|
30
|
+
yield* taskRuns.stopAll.pipe(Effect.raceFirst(shutdown.awaitForcedStop));
|
|
31
|
+
const processGroups = yield* ProcessGroups;
|
|
32
|
+
const killAllProcessGroups = shutdown.isForced.pipe(
|
|
33
|
+
Effect.flatMap(processGroups.killAll),
|
|
34
|
+
failRunOnFailure(taskRuns.markFailed),
|
|
35
|
+
);
|
|
36
|
+
const finalizerCommandsWouldEndUnreportedWithTheirWorkers = yield* shutdown.isForced;
|
|
37
|
+
if (finalizerCommandsWouldEndUnreportedWithTheirWorkers) {
|
|
38
|
+
yield* workerPool.terminateAllWorkers;
|
|
39
|
+
yield* killAllProcessGroups;
|
|
40
|
+
}
|
|
41
|
+
yield* workerPool.requestExitOfAllWorkers;
|
|
42
|
+
const workersGone = yield* Deferred.make<false>();
|
|
43
|
+
yield* Effect.all(
|
|
44
|
+
[
|
|
45
|
+
workerPool.awaitWorkerShutdown.pipe(Effect.ensuring(Deferred.succeed(workersGone, false))),
|
|
46
|
+
Effect.raceFirst(
|
|
47
|
+
shutdown.awaitForcedStop.pipe(Effect.as(true)),
|
|
48
|
+
Deferred.await(workersGone),
|
|
49
|
+
).pipe(Effect.flatMap((forced) => (forced ? killAllProcessGroups : Effect.void))),
|
|
50
|
+
],
|
|
51
|
+
{ concurrency: "unbounded", discard: true },
|
|
52
|
+
);
|
|
53
|
+
yield* killAllProcessGroups;
|
|
54
|
+
yield* Fiber.await(workerEventHandlingFiber);
|
|
55
|
+
return yield* shutdown.outcome;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const isStopRequested = Schema.is(StopRequested);
|
|
59
|
+
|
|
60
|
+
const isTaskBodyFailed = Schema.is(TaskBodyFailed);
|
|
61
|
+
|
|
62
|
+
const LARGEST_PROCESS_EXIT_CODE = 255;
|
|
63
|
+
|
|
64
|
+
const MOST_NAMED_FAILED_TASKS = 3;
|
|
65
|
+
|
|
66
|
+
const exitCodeOfRunEndingFailure = (
|
|
67
|
+
runEndingFailure:
|
|
68
|
+
| Effect.Success<ReturnType<typeof stopEverythingStarted>>
|
|
69
|
+
| UnawaitedTasksFailed
|
|
70
|
+
| null,
|
|
71
|
+
rootTaskName: string,
|
|
72
|
+
) => {
|
|
73
|
+
if (runEndingFailure === null) {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
if (isStopRequested(runEndingFailure)) {
|
|
77
|
+
return runEndingFailure.exitCode;
|
|
78
|
+
}
|
|
79
|
+
const isRootTaskBodyFailure =
|
|
80
|
+
isTaskBodyFailed(runEndingFailure) && runEndingFailure.taskName === rootTaskName;
|
|
81
|
+
const failedCommandExitCode = isRootTaskBodyFailure
|
|
82
|
+
? runEndingFailure.failedCommandExitCode
|
|
83
|
+
: undefined;
|
|
84
|
+
const isFailedCommandExitCodeUsable =
|
|
85
|
+
failedCommandExitCode !== undefined &&
|
|
86
|
+
failedCommandExitCode >= 1 &&
|
|
87
|
+
failedCommandExitCode <= LARGEST_PROCESS_EXIT_CODE;
|
|
88
|
+
return isFailedCommandExitCodeUsable ? failedCommandExitCode : 1;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const runRootTaskFiber = Effect.gen(function* () {
|
|
92
|
+
const runRootTaskConfig = yield* RunRootTaskConfig;
|
|
93
|
+
const rootTask = runRootTaskConfig.rootTask;
|
|
94
|
+
const runSpan = yield* Effect.currentSpan.pipe(
|
|
95
|
+
dieOnInvariantFailure("main-thread tracing", "the run has a parent span"),
|
|
96
|
+
);
|
|
97
|
+
const shutdown = yield* StopState;
|
|
98
|
+
const discoveredTasks = yield* DiscoveredTasks;
|
|
99
|
+
const workerPool = yield* WorkerPool;
|
|
100
|
+
const taskRuns = yield* TaskRuns;
|
|
101
|
+
if (discoveredTasks.brokenTasks !== null) {
|
|
102
|
+
yield* printLine("Error", formatFailureLines(discoveredTasks.brokenTasks, null));
|
|
103
|
+
yield* shutdown.recordPrintedFailure(discoveredTasks.brokenTasks);
|
|
104
|
+
yield* failRun(discoveredTasks.brokenTasks, taskRuns.markFailed);
|
|
105
|
+
}
|
|
106
|
+
const workerEventHandlingFiber = yield* workerPool.events.pipe(
|
|
107
|
+
Stream.groupByKey((workerEvent) => workerEvent.workerId),
|
|
108
|
+
Stream.mapEffect(([, events]) => Stream.runForEach(events, taskRuns.handleWorkerEvent), {
|
|
109
|
+
concurrency: "unbounded",
|
|
110
|
+
}),
|
|
111
|
+
Stream.runDrain,
|
|
112
|
+
Effect.withParentSpan(runSpan),
|
|
113
|
+
Effect.forkScoped,
|
|
114
|
+
);
|
|
115
|
+
if (discoveredTasks.tasksByName !== null) {
|
|
116
|
+
yield* taskRuns.registerDiscoveredTasks(discoveredTasks.tasksByName);
|
|
117
|
+
yield* Effect.try({
|
|
118
|
+
try: () => {
|
|
119
|
+
process.chdir(runRootTaskConfig.rootTaskPackageJsonDirectoryPath);
|
|
120
|
+
},
|
|
121
|
+
catch: (cause) =>
|
|
122
|
+
new RootTaskWorkingDirectoryUnavailable({
|
|
123
|
+
packageJsonDirectoryPath: runRootTaskConfig.rootTaskPackageJsonDirectoryPath,
|
|
124
|
+
cause,
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
|
+
yield* Effect.gen(function* () {
|
|
128
|
+
yield* taskRuns.requestRootTaskResult(rootTask.name);
|
|
129
|
+
yield* taskRuns.awaitServingTaskClosure(rootTask.name);
|
|
130
|
+
}).pipe(
|
|
131
|
+
Effect.matchEffect({
|
|
132
|
+
onFailure: (error) => shutdown.recordFirstFailure(error),
|
|
133
|
+
onSuccess: (success) => Effect.succeed(success),
|
|
134
|
+
}),
|
|
135
|
+
Effect.raceFirst(shutdown.awaitFailure),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const failure = yield* stopEverythingStarted(workerEventHandlingFiber);
|
|
140
|
+
const taskRunCounts = yield* taskRuns.counts;
|
|
141
|
+
const elapsedMilliseconds =
|
|
142
|
+
(yield* Clock.currentTimeMillis) - runRootTaskConfig.runStartedAtMilliseconds;
|
|
143
|
+
const unawaitedFailedTaskNames = failure === null ? taskRunCounts.unawaitedFailedTaskNames : [];
|
|
144
|
+
const unawaitedTasksFailed = Arrays.isArrayNonEmpty(unawaitedFailedTaskNames)
|
|
145
|
+
? new UnawaitedTasksFailed({ failedTaskNames: unawaitedFailedTaskNames })
|
|
146
|
+
: null;
|
|
147
|
+
const runEndingFailure = failure ?? unawaitedTasksFailed;
|
|
148
|
+
const runPassed = runEndingFailure === null;
|
|
149
|
+
const failedTaskCount = taskRunCounts.failedTaskNames.length;
|
|
150
|
+
const totalTasks = `${taskRunCounts.total} ${taskRunCounts.total === 1 ? "task" : "tasks"}`;
|
|
151
|
+
const namedFailedTaskNames = taskRunCounts.failedTaskNames.slice(0, MOST_NAMED_FAILED_TASKS);
|
|
152
|
+
const unnamedFailedTaskCount = failedTaskCount - namedFailedTaskNames.length;
|
|
153
|
+
const failedTasks = [
|
|
154
|
+
namedFailedTaskNames.join(", "),
|
|
155
|
+
...(unnamedFailedTaskCount === 0 ? [] : [`and ${unnamedFailedTaskCount} more`]),
|
|
156
|
+
`(${failedTaskCount} of ${totalTasks})`,
|
|
157
|
+
].join(" ");
|
|
158
|
+
const caughtFailures = `${failedTaskCount} caught ${failedTaskCount === 1 ? "failure" : "failures"}`;
|
|
159
|
+
const tasksWithFailures = runPassed ? [totalTasks, caughtFailures] : [failedTasks];
|
|
160
|
+
const tasks = failedTaskCount === 0 ? [totalTasks] : tasksWithFailures;
|
|
161
|
+
const counts = [
|
|
162
|
+
...tasks,
|
|
163
|
+
...(taskRunCounts.stopped === 0 ? [] : [`${taskRunCounts.stopped} stopped`]),
|
|
164
|
+
formatSeconds(elapsedMilliseconds),
|
|
165
|
+
].join(", ");
|
|
166
|
+
const isFailureUnprinted =
|
|
167
|
+
failure !== null && (isStopRequested(failure) || !(yield* shutdown.wasPrinted(failure)));
|
|
168
|
+
const runFailures = [
|
|
169
|
+
...(unawaitedTasksFailed === null ? [] : [unawaitedTasksFailed]),
|
|
170
|
+
...(isFailureUnprinted ? [failure] : []),
|
|
171
|
+
];
|
|
172
|
+
yield* printLine("Info", runPassed ? `passed: ${counts}` : `failed: ${counts}`);
|
|
173
|
+
for (const runFailure of runFailures) {
|
|
174
|
+
yield* printLine("Error", formatFailureLines(runFailure, null));
|
|
175
|
+
}
|
|
176
|
+
const exitCode = exitCodeOfRunEndingFailure(runEndingFailure, rootTask.name);
|
|
177
|
+
yield* Effect.annotateCurrentSpan({
|
|
178
|
+
[`${TASK_TRACE_LABEL_PREFIX}.version`]: TASK_PACKAGE.version,
|
|
179
|
+
[`${TASK_TRACE_LABEL_PREFIX}.entry`]: runRootTaskConfig.rootTaskFilePath,
|
|
180
|
+
[`${TASK_TRACE_LABEL_PREFIX}.root.task`]: rootTask.name,
|
|
181
|
+
[`${TASK_TRACE_LABEL_PREFIX}.exit.code`]: exitCode,
|
|
182
|
+
[`${TASK_TRACE_LABEL_PREFIX}.elapsed.milliseconds`]: elapsedMilliseconds,
|
|
183
|
+
[`${TASK_TRACE_LABEL_PREFIX}.workers.started`]: yield* workerPool.startedWorkerCount,
|
|
184
|
+
[`${TASK_TRACE_LABEL_PREFIX}.workers.limit`]: runRootTaskConfig.workerCount,
|
|
185
|
+
"host.cpus": yield* availableParallelism,
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
exitCode,
|
|
189
|
+
runEndingCause: runEndingFailure === null ? null : Cause.fail(runEndingFailure),
|
|
190
|
+
};
|
|
191
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
|
|
3
|
+
import { StopState } from "../shared/provide-stop-state.ts";
|
|
4
|
+
|
|
5
|
+
export const stopsAtFirstFailure = <Services, Error, Requirements>(
|
|
6
|
+
layer: Layer.Layer<Services, Error, Requirements>,
|
|
7
|
+
) =>
|
|
8
|
+
Layer.effectContext(
|
|
9
|
+
Effect.gen(function* () {
|
|
10
|
+
const shutdown = yield* StopState;
|
|
11
|
+
if (yield* shutdown.hasFailed) {
|
|
12
|
+
const failure = yield* shutdown.awaitFailure;
|
|
13
|
+
return yield* failure;
|
|
14
|
+
}
|
|
15
|
+
return yield* Layer.build(layer).pipe(
|
|
16
|
+
Effect.raceFirst(shutdown.awaitFailure.pipe(Effect.flatMap(Effect.fail))),
|
|
17
|
+
Effect.interruptible,
|
|
18
|
+
);
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Clock,
|
|
3
|
+
Context,
|
|
4
|
+
Duration,
|
|
5
|
+
Effect,
|
|
6
|
+
Fiber,
|
|
7
|
+
FileSystem,
|
|
8
|
+
Layer,
|
|
9
|
+
Logger,
|
|
10
|
+
Option,
|
|
11
|
+
Path,
|
|
12
|
+
Ref,
|
|
13
|
+
Tracer,
|
|
14
|
+
} from "effect";
|
|
15
|
+
|
|
16
|
+
import { formatFailureLines } from "../../../shared/format-error-lines.ts";
|
|
17
|
+
import { formatIsoTimestamp } from "../../../shared/format-task-lines.ts";
|
|
18
|
+
import { printLine } from "../../../shared/print-to-standard-error.ts";
|
|
19
|
+
import { TraceFile, makeTraceFile } from "../../../shared/write-trace-file.ts";
|
|
20
|
+
import { TraceFileIncomplete } from "../shared/main-thread-errors.ts";
|
|
21
|
+
import { RunRootTaskConfig } from "../shared/provide-run-root-task-config.ts";
|
|
22
|
+
|
|
23
|
+
const TRACE_RETENTION = "14 days";
|
|
24
|
+
|
|
25
|
+
const deleteTracesOlderThanRetention = Effect.fn("deleteTracesOlderThanRetention")(function* (
|
|
26
|
+
currentTraceFilePath: string,
|
|
27
|
+
) {
|
|
28
|
+
const fileSystem = yield* FileSystem.FileSystem;
|
|
29
|
+
const path = yield* Path.Path;
|
|
30
|
+
const traceDirectoryPath = path.dirname(currentTraceFilePath);
|
|
31
|
+
const oldestKeptMilliseconds =
|
|
32
|
+
(yield* Clock.currentTimeMillis) - Duration.toMillis(TRACE_RETENTION);
|
|
33
|
+
const fileNames = yield* fileSystem.readDirectory(traceDirectoryPath);
|
|
34
|
+
yield* Effect.forEach(
|
|
35
|
+
fileNames.filter(
|
|
36
|
+
(fileName) => fileName.endsWith(".jsonl") && fileName !== path.basename(currentTraceFilePath),
|
|
37
|
+
),
|
|
38
|
+
(fileName) => {
|
|
39
|
+
const traceFilePath = path.join(traceDirectoryPath, fileName);
|
|
40
|
+
return fileSystem.stat(traceFilePath).pipe(
|
|
41
|
+
Effect.flatMap((information) => {
|
|
42
|
+
const isExpiredTraceFile =
|
|
43
|
+
information.type === "File" &&
|
|
44
|
+
Option.exists(information.mtime, (mtime) => mtime.getTime() < oldestKeptMilliseconds);
|
|
45
|
+
return isExpiredTraceFile ? fileSystem.remove(traceFilePath) : Effect.void;
|
|
46
|
+
}),
|
|
47
|
+
Effect.catchTag("PlatformError", () => Effect.void),
|
|
48
|
+
);
|
|
49
|
+
},
|
|
50
|
+
{ discard: true },
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const TraceFileLive = Layer.effect(
|
|
55
|
+
TraceFile,
|
|
56
|
+
Effect.gen(function* () {
|
|
57
|
+
const runRootTaskConfig = yield* RunRootTaskConfig;
|
|
58
|
+
const timestamp = formatIsoTimestamp(runRootTaskConfig.runStartedAtMilliseconds).replaceAll(
|
|
59
|
+
":",
|
|
60
|
+
"-",
|
|
61
|
+
);
|
|
62
|
+
const traceFilePath = `${runRootTaskConfig.stateDirectoryPath}/traces/${runRootTaskConfig.rootTaskPackageJsonName}/${timestamp}-${runRootTaskConfig.runProcessId}.jsonl`;
|
|
63
|
+
const sweepOfOldTraces = yield* deleteTracesOlderThanRetention(traceFilePath).pipe(
|
|
64
|
+
Effect.catchTag("PlatformError", () => Effect.void),
|
|
65
|
+
Effect.forkScoped,
|
|
66
|
+
);
|
|
67
|
+
const made = yield* Ref.make<Effect.Success<ReturnType<typeof makeTraceFile>> | null>(null);
|
|
68
|
+
yield* Effect.addFinalizer(() =>
|
|
69
|
+
Ref.get(made).pipe(
|
|
70
|
+
Effect.flatMap((madeTraceFile) =>
|
|
71
|
+
madeTraceFile === null
|
|
72
|
+
? Effect.void
|
|
73
|
+
: madeTraceFile.whyIncomplete.pipe(
|
|
74
|
+
Effect.flatMap((whyIncomplete) =>
|
|
75
|
+
whyIncomplete === null
|
|
76
|
+
? printLine("Info", `wrote the trace to ${traceFilePath}`)
|
|
77
|
+
: printLine(
|
|
78
|
+
"Error",
|
|
79
|
+
formatFailureLines(
|
|
80
|
+
new TraceFileIncomplete({ traceFilePath, reason: whyIncomplete }),
|
|
81
|
+
null,
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
),
|
|
87
|
+
Effect.andThen(Fiber.join(sweepOfOldTraces)),
|
|
88
|
+
),
|
|
89
|
+
);
|
|
90
|
+
const traceFile = yield* makeTraceFile({ traceFilePath, thread: "main-thread" });
|
|
91
|
+
yield* Ref.set(made, traceFile);
|
|
92
|
+
return traceFile;
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
export const RuntimeTracingLive = Layer.effectContext(
|
|
97
|
+
Effect.gen(function* () {
|
|
98
|
+
const trace = yield* TraceFile;
|
|
99
|
+
const runSpan = yield* Effect.makeSpanScoped("runRootTaskFiber").pipe(
|
|
100
|
+
Effect.withTracer(trace.tracer),
|
|
101
|
+
);
|
|
102
|
+
return Context.make(Tracer.Tracer, trace.tracer).pipe(
|
|
103
|
+
Context.add(Logger.CurrentLoggers, new Set([trace.logger])),
|
|
104
|
+
Context.add(Tracer.ParentSpan, runSpan),
|
|
105
|
+
);
|
|
106
|
+
}),
|
|
107
|
+
);
|