fix(runtime): preserve merge resolutions after rebase

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-16 19:35:18 +00:00
1 parent 26bc68becd
commit 6d6c4ddf15
5 files changed
+67 -31

No files matched your search

@@ -1,3 +1,3 @@
ALTER TABLE "workspace_runtime_services" ADD COLUMN "exposure" jsonb;
ALTER TABLE "workspace_runtime_services" ADD COLUMN "exposure_handle" text;
ALTER TABLE "workspace_runtime_services" ADD COLUMN "backend_url" text;
ALTER TABLE "workspace_runtime_services" ADD COLUMN IF NOT EXISTS "exposure" jsonb;
ALTER TABLE "workspace_runtime_services" ADD COLUMN IF NOT EXISTS "exposure_handle" text;
ALTER TABLE "workspace_runtime_services" ADD COLUMN IF NOT EXISTS "backend_url" text;
-12
View File
@@ -714,18 +714,6 @@ importers:
specifier: ^22.20.1
version: 22.20.1
packages/tailscale-https-broker:
devDependencies:
'@types/node':
specifier: ^22.20.1
version: 22.20.1
typescript:
specifier: ^5.7.3
version: 5.9.3
vitest:
specifier: ^4.1.10
version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(jsdom@28.1.0(@noble/hashes@2.2.0))(vite@7.3.1(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.23.1))
packages/teams-catalog:
devDependencies:
'@types/node':
@@ -96,13 +96,15 @@ if (optedIn && !live) {
// An ephemeral legacy port rather than the real template's 45439, so this
// never contends with the live workspace runtime on the same host.
const reservePort = async () => {
const probe = net.createServer();
await new Promise<void>((resolve) => probe.listen(0, "127.0.0.1", resolve));
const address = probe.address();
const port = typeof address === "object" && address ? address.port : null;
await new Promise<void>((resolve, reject) => probe.close((e) => (e ? reject(e) : resolve())));
if (!port) throw new Error("failed to reserve a legacy port");
return port;
for (let attempt = 0; attempt < 100; attempt += 1) {
const probe = net.createServer();
await new Promise<void>((resolve) => probe.listen(0, "127.0.0.1", resolve));
const address = probe.address();
const port = typeof address === "object" && address ? address.port : null;
await new Promise<void>((resolve, reject) => probe.close((e) => (e ? reject(e) : resolve())));
if (port && port <= 55_535 && (port < 42_000 || port > 42_999)) return port;
}
throw new Error("failed to reserve a legacy port outside the broker range");
};
const companyId = randomUUID();
+11 -9
View File
@@ -6580,15 +6580,17 @@ describeEmbeddedPostgres("workspace runtime startup reconciliation", () => {
process.env.PAPERCLIP_INSTANCE_ID = `runtime-https-backfill-${randomUUID()}`;
const reservePort = async () => {
const probe = net.createServer();
await new Promise<void>((resolve) => probe.listen(0, "127.0.0.1", resolve));
const address = probe.address();
const port = typeof address === "object" && address ? address.port : null;
await new Promise<void>((resolve, reject) => {
probe.close((error) => error ? reject(error) : resolve());
});
if (!port) throw new Error("Failed to reserve HTTPS backfill test port");
return port;
for (let attempt = 0; attempt < 100; attempt += 1) {
const probe = net.createServer();
await new Promise<void>((resolve) => probe.listen(0, "127.0.0.1", resolve));
const address = probe.address();
const port = typeof address === "object" && address ? address.port : null;
await new Promise<void>((resolve, reject) => {
probe.close((error) => error ? reject(error) : resolve());
});
if (port && port <= 55_535 && (port < 42_000 || port > 42_999)) return port;
}
throw new Error("Failed to reserve an HTTPS backfill test port outside the broker range");
};
const isLoopbackPortFree = async (port: number) => {
const probe = net.createServer();
+44
View File
@@ -4801,6 +4801,48 @@ async function spawnLocalRuntimeService(input: StartLocalRuntimeServiceInput): P
scopeType: input.scopeType,
scopeId: input.scopeId,
});
let fixedPortRegistryMatch = false;
if (!exposureConfig && canAllocateFixedPort && identityPort) {
const identityTemplateData = buildTemplateData({
workspace: input.workspace,
agent: input.agent,
issue: input.issue,
adapterEnv: input.adapterEnv,
port: identityPort,
});
const identityExpose = parseObject(input.service.expose);
const identityReadiness = parseObject(input.service.readiness);
const identityUrlTemplate =
asString(identityExpose.urlTemplate, "")
|| asString(identityReadiness.urlTemplate, "");
const identityBackendUrl = identityUrlTemplate
? renderTemplate(identityUrlTemplate, identityTemplateData)
: null;
const identityServiceKey = createLocalServiceKey({
profileKind: "workspace-runtime",
serviceName,
cwd: identity.serviceCwd,
command,
envFingerprint: serviceIdentityFingerprint,
port: identityPort,
scope: {
scopeType: input.scopeType,
scopeId: input.scopeId,
executionWorkspaceId: input.executionWorkspaceId ?? null,
reuseKey: input.reuseKey,
},
});
fixedPortRegistryMatch = Boolean(await findAdoptableLocalService({
serviceKey: identityServiceKey,
profileKind: "workspace-runtime",
serviceName,
command,
cwd: identity.serviceCwd,
envFingerprint: serviceIdentityFingerprint,
port: identityPort,
url: identityBackendUrl,
}));
}
const runtimeId = input.runtimeServiceId ?? stoppedReuseCandidate?.id ?? randomUUID();
// An exposed runtime always takes its port from the dedicated broker range, so
// a configured or previously used port is a *preference*, not a constraint. It
@@ -4838,6 +4880,8 @@ async function spawnLocalRuntimeService(input: StartLocalRuntimeServiceInput): P
remediation: "Retry the start or configure a different runtime service port.",
});
}
} else if (!reservedExposure && canAllocateFixedPort && fixedPortRegistryMatch) {
port = explicitPort;
} else if (!reservedExposure && canAllocateFixedPort) {
port = await allocateIsolatedWorkspacePort({
db: input.db,