File size: 3,685 Bytes
eb3f11e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import type { LegacyConfigUpdatePlan } from "../../commands/doctor/legacy-config-repair.js";
import { captureTargetDatabaseSchemaContext } from "./schema-preflight.js";
import { UpdatePreMutationError } from "./shared.js";
import { formatUpdateAncestryBlockMessage } from "./update-command-handoff.js";
import { captureOwnedManagedUpdatePreflightContext } from "./update-command-managed-context.js";
import {
  collectServiceInspectionFailureFacts,
  GatewayServiceUpdateOwnershipError,
  type ManagedServiceRootRedirect,
} from "./update-command-service-plan.js";
import {
  maybeStopManagedServiceBeforeMutableUpdate,
  type PreManagedServiceStop,
} from "./update-command-service.js";

export async function inspectUpdateDatabaseContexts(params: {
  roots: readonly string[];
  updateInstallKind: "package" | "git";
  shouldRestart: boolean;
  jsonMode: boolean;
  timeoutMs: number;
  invocationCwd?: string;
  legacyConfigPlan?: LegacyConfigUpdatePlan;
  managedServiceRootRedirect: ManagedServiceRootRedirect | null;
  expectedServices?: ReadonlyMap<string, PreManagedServiceStop>;
}) {
  let service: PreManagedServiceStop | undefined;
  const services = new Map<string, PreManagedServiceStop>();
  for (const root of new Set(params.roots)) {
    const inspected = await maybeStopManagedServiceBeforeMutableUpdate({
      root,
      updateInstallKind: params.updateInstallKind,
      shouldRestart: params.shouldRestart,
      jsonMode: params.jsonMode,
      timeoutMs: params.timeoutMs,
      phase: "inspect",
      expectedService: params.expectedServices?.get(root),
    }).catch((error: unknown) => {
      if (error instanceof GatewayServiceUpdateOwnershipError) {
        throw new UpdatePreMutationError("managed-service-preflight", error.message, {
          failureFacts: error.failureFacts,
        });
      }
      throw error;
    });
    const unavailable =
      inspected.serviceUpdateVerdict?.kind === "unavailable"
        ? inspected.serviceUpdateVerdict
        : undefined;
    if (inspected.blockMessage || unavailable) {
      throw new UpdatePreMutationError(
        "managed-service-preflight",
        formatUpdateAncestryBlockMessage(inspected.blockMessage ?? unavailable!.message),
        { failureFacts: collectServiceInspectionFailureFacts(inspected.serviceUpdateVerdict) },
      );
    }
    if (inspected.serviceUpdateVerdict?.kind === "unresolved") {
      throw new UpdatePreMutationError(
        "managed-service-preflight",
        "Gateway service installation ownership is unresolved. Run `openclaw gateway status --deep` and retry before changing package or Git files.",
      );
    }
    services.set(root, inspected);
    if (inspected.serviceUpdateVerdict?.kind === "owned") {
      service = inspected;
      break;
    }
  }
  const managed = await captureOwnedManagedUpdatePreflightContext({
    stopState: service,
    processEnv: process.env,
    invocationCwd: params.invocationCwd,
    legacyConfigPlan: params.legacyConfigPlan,
  });
  if (params.managedServiceRootRedirect && !managed) {
    throw new UpdatePreMutationError(
      "managed-service-preflight",
      "The managed Gateway service changed before database admission. Retry so its package root and state can be inspected together.",
    );
  }
  // Redirected package replacement does not own the invoking installation's stores.
  const contexts = params.managedServiceRootRedirect
    ? []
    : [
        await captureTargetDatabaseSchemaContext(process.env, {
          legacyConfigPlan: params.legacyConfigPlan,
        }),
      ];
  if (managed) {
    contexts.push(managed);
  }
  return { service, services, contexts, managedEnv: managed?.env };
}