mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-16 09:33:24 +00:00
refactor(flags): route control-plane workspaces through runtime flags (#27337)
This commit is contained in:
@@ -10,8 +10,8 @@ import { GlobalBus } from "@/bus/global"
|
||||
import { Auth } from "@/auth"
|
||||
import { SyncEvent } from "@/sync"
|
||||
import { EventSequenceTable, EventTable } from "@/sync/event.sql"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { Filesystem } from "@/util/filesystem"
|
||||
import { ProjectID } from "@/project/schema"
|
||||
import { Slug } from "@opencode-ai/core/util/slug"
|
||||
@@ -175,6 +175,7 @@ export const layer = Layer.effect(
|
||||
const http = yield* HttpClient.HttpClient
|
||||
const sync = yield* SyncEvent.Service
|
||||
const vcs = yield* Vcs.Service
|
||||
const flags = yield* RuntimeFlags.Service
|
||||
const connections = new Map<WorkspaceID, ConnectionStatus>()
|
||||
const syncFibers = yield* FiberMap.make<WorkspaceID, void, SyncLoopError>()
|
||||
|
||||
@@ -482,7 +483,7 @@ export const layer = Layer.effect(
|
||||
})
|
||||
|
||||
const startSync = Effect.fn("Workspace.startSync")(function* (space: Info) {
|
||||
if (!Flag.OPENCODE_EXPERIMENTAL_WORKSPACES) return
|
||||
if (!flags.experimentalWorkspaces) return
|
||||
|
||||
const adapter = getAdapter(space.projectID, space.type)
|
||||
const target = yield* EffectBridge.fromPromise(() => adapter.target(space)).pipe(
|
||||
@@ -1040,6 +1041,7 @@ export const defaultLayer = layer.pipe(
|
||||
Layer.provide(Project.defaultLayer),
|
||||
Layer.provide(Vcs.defaultLayer),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.provide(RuntimeFlags.defaultLayer),
|
||||
)
|
||||
|
||||
const TIMEOUT = 5000
|
||||
|
||||
@@ -6,7 +6,7 @@ import path from "node:path"
|
||||
import { setTimeout as delay } from "node:timers/promises"
|
||||
import { NodeHttpServer } from "@effect/platform-node"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { FetchHttpClient, HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
|
||||
import { eq } from "drizzle-orm"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
@@ -33,24 +33,43 @@ import * as Workspace from "../../src/control-plane/workspace"
|
||||
import { AppRuntime } from "@/effect/app-runtime"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { InstanceBootstrap } from "@/project/bootstrap"
|
||||
import { Auth } from "@/auth"
|
||||
import { SessionPrompt } from "@/session/prompt"
|
||||
import { Project } from "@/project/project"
|
||||
import { Vcs } from "@/project/vcs"
|
||||
import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
|
||||
void Log.init({ print: false })
|
||||
|
||||
const testServerLayer = Layer.mergeAll(
|
||||
NodeHttpServer.layer(Http.createServer, { host: "127.0.0.1", port: 0 }),
|
||||
Workspace.defaultLayer.pipe(Layer.provide(InstanceStore.defaultLayer), Layer.provide(InstanceBootstrap.defaultLayer)),
|
||||
SessionNs.defaultLayer,
|
||||
)
|
||||
const it = testEffect(testServerLayer)
|
||||
|
||||
const originalWorkspacesFlag = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
|
||||
const originalEnv = {
|
||||
OPENCODE_AUTH_CONTENT: process.env.OPENCODE_AUTH_CONTENT,
|
||||
OPENCODE_EXPERIMENTAL_WORKSPACES: process.env.OPENCODE_EXPERIMENTAL_WORKSPACES,
|
||||
OTEL_EXPORTER_OTLP_HEADERS: process.env.OTEL_EXPORTER_OTLP_HEADERS,
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
||||
OTEL_RESOURCE_ATTRIBUTES: process.env.OTEL_RESOURCE_ATTRIBUTES,
|
||||
}
|
||||
|
||||
const workspaceLayer = (experimentalWorkspaces: boolean) =>
|
||||
Workspace.layer.pipe(
|
||||
Layer.provide(Auth.defaultLayer),
|
||||
Layer.provide(SessionNs.defaultLayer),
|
||||
Layer.provide(SyncEvent.defaultLayer),
|
||||
Layer.provide(SessionPrompt.defaultLayer),
|
||||
Layer.provide(Project.defaultLayer),
|
||||
Layer.provide(Vcs.defaultLayer),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces })),
|
||||
Layer.provide(InstanceStore.defaultLayer.pipe(Layer.provide(InstanceBootstrap.defaultLayer))),
|
||||
)
|
||||
|
||||
const testServerLayer = Layer.mergeAll(
|
||||
NodeHttpServer.layer(Http.createServer, { host: "127.0.0.1", port: 0 }),
|
||||
workspaceLayer(true),
|
||||
SessionNs.defaultLayer,
|
||||
)
|
||||
const it = testEffect(testServerLayer)
|
||||
|
||||
type RecordedCreate = {
|
||||
info: WorkspaceInfo
|
||||
env: Record<string, string | undefined>
|
||||
@@ -94,6 +113,7 @@ beforeEach(() => {
|
||||
Database.close()
|
||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true
|
||||
restoreEnv()
|
||||
process.env.OPENCODE_EXPERIMENTAL_WORKSPACES = "true"
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -141,6 +161,12 @@ const isWorkspaceSyncing = (id: WorkspaceID) =>
|
||||
const startWorkspaceSyncing = (projectID: ProjectID) => {
|
||||
void runWorkspace(Workspace.Service.use((workspace) => workspace.startWorkspaceSyncing(projectID)))
|
||||
}
|
||||
const startWorkspaceSyncingWithFlag = (projectID: ProjectID, experimentalWorkspaces: boolean) =>
|
||||
Effect.runPromise(
|
||||
Workspace.Service.use((workspace) => workspace.startWorkspaceSyncing(projectID)).pipe(
|
||||
Effect.provide(workspaceLayer(experimentalWorkspaces)),
|
||||
),
|
||||
)
|
||||
const waitForWorkspaceSync = (workspaceID: WorkspaceID, state: Record<string, number>, signal?: AbortSignal) =>
|
||||
runWorkspace(Workspace.Service.use((workspace) => workspace.waitForSync(workspaceID, state, signal)))
|
||||
|
||||
@@ -980,7 +1006,6 @@ describe("workspace CRUD", () => {
|
||||
describe("workspace sync state", () => {
|
||||
test("startWorkspaceSyncing is disabled by the experimental workspace flag", async () => {
|
||||
await withInstance(async (dir) => {
|
||||
Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = false
|
||||
const type = unique("flag-disabled")
|
||||
const info = workspaceInfo(Instance.project.id, type)
|
||||
const session = await AppRuntime.runPromise(SessionNs.Service.use((svc) => svc.create({})))
|
||||
@@ -988,7 +1013,7 @@ describe("workspace sync state", () => {
|
||||
insertWorkspace(info)
|
||||
registerAdapter(Instance.project.id, type, localAdapter(path.join(dir, "flag-disabled")).adapter)
|
||||
|
||||
startWorkspaceSyncing(Instance.project.id)
|
||||
await startWorkspaceSyncingWithFlag(Instance.project.id, false)
|
||||
await delay(25)
|
||||
|
||||
expect((await workspaceStatus()).find((item) => item.workspaceID === info.id)?.status).toBeUndefined()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { afterAll, afterEach, describe, expect } from "bun:test"
|
||||
import { Effect, Layer, Option } from "effect"
|
||||
import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||
import { EffectFlock } from "@opencode-ai/core/util/effect-flock"
|
||||
@@ -17,6 +18,11 @@ import { Plugin } from "../../src/plugin/index"
|
||||
import { InstanceBootstrap } from "../../src/project/bootstrap-service"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Project } from "../../src/project/project"
|
||||
import { Vcs } from "../../src/project/vcs"
|
||||
import { Session } from "../../src/session/session"
|
||||
import { SessionPrompt } from "../../src/session/prompt"
|
||||
import { SyncEvent } from "../../src/sync"
|
||||
import { disposeAllInstances, provideTmpdirInstance } from "../fixture/fixture"
|
||||
import { testEffect } from "../lib/effect"
|
||||
import { NpmTest } from "../fake/npm"
|
||||
@@ -42,8 +48,16 @@ const pluginLayer = Plugin.layer.pipe(
|
||||
Layer.provide(RuntimeFlags.layer({ disableDefaultPlugins: true })),
|
||||
)
|
||||
const noopBootstrapLayer = Layer.succeed(InstanceBootstrap.Service, InstanceBootstrap.Service.of({ run: Effect.void }))
|
||||
const workspaceLayer = Workspace.defaultLayer.pipe(
|
||||
const workspaceLayer = Workspace.layer.pipe(
|
||||
Layer.provide(Auth.defaultLayer),
|
||||
Layer.provide(Session.defaultLayer),
|
||||
Layer.provide(SyncEvent.defaultLayer),
|
||||
Layer.provide(SessionPrompt.defaultLayer),
|
||||
Layer.provide(Project.defaultLayer),
|
||||
Layer.provide(Vcs.defaultLayer),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.provide(InstanceStore.defaultLayer.pipe(Layer.provide(noopBootstrapLayer))),
|
||||
Layer.provide(RuntimeFlags.layer({ experimentalWorkspaces: true })),
|
||||
)
|
||||
const it = testEffect(Layer.mergeAll(pluginLayer, workspaceLayer, CrossSpawnSpawner.defaultLayer))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user