feat(runtime): start and ship the local agent
This commit is contained in:
parent
70c7de1efc
commit
78758a6c2c
6 changed files with 81 additions and 2 deletions
|
|
@ -11,6 +11,7 @@
|
||||||
!**/components.json
|
!**/components.json
|
||||||
|
|
||||||
!app/**
|
!app/**
|
||||||
|
!apps/agent/**
|
||||||
!packages/**
|
!packages/**
|
||||||
!public/**
|
!public/**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,8 @@ COPY --from=deps /deps/shoutrrr /usr/local/bin/shoutrrr
|
||||||
|
|
||||||
COPY ./package.json ./bun.lock ./
|
COPY ./package.json ./bun.lock ./
|
||||||
COPY ./packages/core/package.json ./packages/core/package.json
|
COPY ./packages/core/package.json ./packages/core/package.json
|
||||||
|
COPY ./packages/contracts/package.json ./packages/contracts/package.json
|
||||||
|
COPY ./apps/agent/package.json ./apps/agent/package.json
|
||||||
|
|
||||||
RUN bun install --frozen-lockfile --ignore-scripts
|
RUN bun install --frozen-lockfile --ignore-scripts
|
||||||
|
|
||||||
|
|
@ -86,11 +88,14 @@ WORKDIR /app
|
||||||
|
|
||||||
COPY ./package.json ./bun.lock ./
|
COPY ./package.json ./bun.lock ./
|
||||||
COPY ./packages/core/package.json ./packages/core/package.json
|
COPY ./packages/core/package.json ./packages/core/package.json
|
||||||
|
COPY ./packages/contracts/package.json ./packages/contracts/package.json
|
||||||
|
COPY ./apps/agent/package.json ./apps/agent/package.json
|
||||||
RUN bun install --frozen-lockfile
|
RUN bun install --frozen-lockfile
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
RUN bun build apps/agent/src/index.ts --outfile .output/agent/index.mjs --target bun
|
||||||
|
|
||||||
FROM base AS production
|
FROM base AS production
|
||||||
|
|
||||||
|
|
|
||||||
58
app/server/modules/lifecycle/__tests__/shutdown.test.ts
Normal file
58
app/server/modules/lifecycle/__tests__/shutdown.test.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { afterEach, describe, expect, mock, spyOn, test } from "bun:test";
|
||||||
|
import { Scheduler } from "../../../core/scheduler";
|
||||||
|
import * as backendModule from "../../backends/backend";
|
||||||
|
import type { VolumeBackend } from "../../backends/backend";
|
||||||
|
import * as bootstrapModule from "../bootstrap";
|
||||||
|
import { createTestVolume } from "~/test/helpers/volume";
|
||||||
|
|
||||||
|
const loadShutdownModule = async () => {
|
||||||
|
const moduleUrl = new URL("../shutdown.ts", import.meta.url);
|
||||||
|
moduleUrl.searchParams.set("test", crypto.randomUUID());
|
||||||
|
return import(moduleUrl.href);
|
||||||
|
};
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shutdown", () => {
|
||||||
|
test("stops the agent runtime before unmounting mounted volumes", async () => {
|
||||||
|
const events: string[] = [];
|
||||||
|
const stopScheduler = mock(async () => {
|
||||||
|
events.push("scheduler.stop");
|
||||||
|
});
|
||||||
|
const stopApplicationRuntime = mock(async () => {
|
||||||
|
events.push("agents.stop");
|
||||||
|
});
|
||||||
|
const unmountVolume = mock(async () => {
|
||||||
|
events.push("backend.unmount");
|
||||||
|
return { status: "unmounted" as const };
|
||||||
|
});
|
||||||
|
|
||||||
|
await createTestVolume({
|
||||||
|
name: "Shutdown test volume",
|
||||||
|
config: {
|
||||||
|
backend: "directory",
|
||||||
|
path: "/Applications",
|
||||||
|
},
|
||||||
|
status: "mounted",
|
||||||
|
});
|
||||||
|
|
||||||
|
spyOn(Scheduler, "stop").mockImplementation(stopScheduler);
|
||||||
|
spyOn(bootstrapModule, "stopApplicationRuntime").mockImplementation(stopApplicationRuntime);
|
||||||
|
spyOn(backendModule, "createVolumeBackend").mockImplementation(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
mount: async () => ({ status: "mounted" as const }),
|
||||||
|
unmount: unmountVolume,
|
||||||
|
checkHealth: async () => ({ status: "mounted" as const }),
|
||||||
|
}) satisfies VolumeBackend,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { shutdown } = await loadShutdownModule();
|
||||||
|
|
||||||
|
await shutdown();
|
||||||
|
|
||||||
|
expect(events).toEqual(["scheduler.stop", "agents.stop", "backend.unmount"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { runDbMigrations } from "../../db/db";
|
import { runDbMigrations } from "../../db/db";
|
||||||
|
import { agentManager, spawnLocalAgent, stopAgentRuntime } from "../agents/agents-manager";
|
||||||
import { runMigrations } from "./migrations";
|
import { runMigrations } from "./migrations";
|
||||||
import { startup } from "./startup";
|
import { startup } from "./startup";
|
||||||
|
|
||||||
|
|
@ -7,6 +8,8 @@ let bootstrapPromise: Promise<void> | undefined;
|
||||||
const runBootstrap = async () => {
|
const runBootstrap = async () => {
|
||||||
await runDbMigrations();
|
await runDbMigrations();
|
||||||
await runMigrations();
|
await runMigrations();
|
||||||
|
agentManager.start();
|
||||||
|
await spawnLocalAgent();
|
||||||
await startup();
|
await startup();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -22,3 +25,11 @@ export const bootstrapApplication = async () => {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const stopApplicationRuntime = async () => {
|
||||||
|
try {
|
||||||
|
await stopAgentRuntime();
|
||||||
|
} finally {
|
||||||
|
bootstrapPromise = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ import { Scheduler } from "../../core/scheduler";
|
||||||
import { db } from "../../db/db";
|
import { db } from "../../db/db";
|
||||||
import { logger } from "@zerobyte/core/node";
|
import { logger } from "@zerobyte/core/node";
|
||||||
import { createVolumeBackend } from "../backends/backend";
|
import { createVolumeBackend } from "../backends/backend";
|
||||||
|
import { stopApplicationRuntime } from "./bootstrap";
|
||||||
|
|
||||||
export const shutdown = async () => {
|
export const shutdown = async () => {
|
||||||
await Scheduler.stop();
|
await Scheduler.stop();
|
||||||
|
await stopApplicationRuntime();
|
||||||
|
|
||||||
const volumes = await db.query.volumesTable.findMany({
|
const volumes = await db.query.volumesTable.findMany({
|
||||||
where: { status: "mounted" },
|
where: { status: "mounted" },
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { definePlugin } from "nitro";
|
import { definePlugin } from "nitro";
|
||||||
import { bootstrapApplication } from "../modules/lifecycle/bootstrap";
|
import { bootstrapApplication, stopApplicationRuntime } from "../modules/lifecycle/bootstrap";
|
||||||
import { logger } from "@zerobyte/core/node";
|
import { logger } from "@zerobyte/core/node";
|
||||||
import { toMessage } from "../utils/errors";
|
import { toMessage } from "../utils/errors";
|
||||||
|
|
||||||
export default definePlugin(async () => {
|
export default definePlugin(async (nitroApp) => {
|
||||||
|
nitroApp.hooks.hook("close", stopApplicationRuntime);
|
||||||
|
|
||||||
await bootstrapApplication().catch((err) => {
|
await bootstrapApplication().catch((err) => {
|
||||||
logger.error(`Bootstrap failed: ${toMessage(err)}`);
|
logger.error(`Bootstrap failed: ${toMessage(err)}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue