Merge master into codex/unified-creation-flow-phase1
This commit is contained in:
@@ -15,7 +15,11 @@ import {fileURLToPath} from 'node:url';
|
||||
|
||||
import {
|
||||
formatPortDecision,
|
||||
getLinuxDevPortRangeRegistryPaths,
|
||||
getLinuxDevPortRangeUsername,
|
||||
mapDevPortsToPortRange,
|
||||
normalizePort,
|
||||
reserveLinuxDevPortRange,
|
||||
resolveDevStackPorts,
|
||||
} from './dev-stack-port-utils.mjs';
|
||||
import {
|
||||
@@ -60,6 +64,7 @@ function usage() {
|
||||
--spacetime-port <port> SpacetimeDB 端口
|
||||
--spacetime-data-dir <path> SpacetimeDB 本地数据目录
|
||||
--database <name> SpacetimeDB 数据库名
|
||||
--port-range <start-end> Linux 用户端口段,手动指定示例 10000-10099;默认自动从 10000-10099 起分配
|
||||
--watch 文件改动后刷新/重启对应模块
|
||||
--no-interactive 关闭交互式手动命令
|
||||
|
||||
@@ -110,6 +115,7 @@ function parseArgs(argv, baseEnv) {
|
||||
spacetimePort: normalizePort(env.SPACETIME_PORT, 3101),
|
||||
spacetimeDataDir: resolve(serverRsDir, '.spacetimedb/local/data'),
|
||||
spacetimeServerUrl: String(env.GENARRATIVE_SPACETIME_SERVER_URL ?? '').trim(),
|
||||
portRangeSpec: String(env.GENARRATIVE_DEV_PORT_RANGE ?? '').trim(),
|
||||
database:
|
||||
readLocalSpacetimeDatabase() ||
|
||||
String(env.GENARRATIVE_SPACETIME_DATABASE ?? '').trim() ||
|
||||
@@ -185,6 +191,10 @@ function parseArgs(argv, baseEnv) {
|
||||
options.database = readValue();
|
||||
explicitOptions.add('database');
|
||||
break;
|
||||
case '--port-range':
|
||||
options.portRangeSpec = readValue();
|
||||
explicitOptions.add('portRangeSpec');
|
||||
break;
|
||||
case '--log':
|
||||
options.apiLog = readValue();
|
||||
break;
|
||||
@@ -728,6 +738,8 @@ class DevRunner {
|
||||
adminWebTargetHost: resolveClientHost(options.adminWebHost),
|
||||
spacetimeServer: initialSpacetimeServer,
|
||||
apiTarget: `http://${resolveClientHost(options.apiHost)}:${options.apiPort}`,
|
||||
portRange: null,
|
||||
portRangeReservation: null,
|
||||
};
|
||||
this.services = new Map();
|
||||
this.watchers = [];
|
||||
@@ -751,6 +763,7 @@ class DevRunner {
|
||||
ensureSpacetimeToolVersionMatchesWorkspace();
|
||||
}
|
||||
|
||||
await this.prepareLinuxPortRange(command);
|
||||
await this.tryReuseExistingSpacetime(command);
|
||||
await this.resolvePorts(command);
|
||||
this.registerServices();
|
||||
@@ -758,6 +771,50 @@ class DevRunner {
|
||||
this.writeDevStackState();
|
||||
}
|
||||
|
||||
async prepareLinuxPortRange(command) {
|
||||
if (process.platform !== 'linux') {
|
||||
return;
|
||||
}
|
||||
|
||||
const requestedRange = String(this.options.portRangeSpec ?? '').trim();
|
||||
const allocation = await reserveLinuxDevPortRange({
|
||||
env: {
|
||||
...this.baseEnv,
|
||||
GENARRATIVE_DEV_PORT_RANGE: requestedRange,
|
||||
},
|
||||
username: getLinuxDevPortRangeUsername(this.baseEnv),
|
||||
});
|
||||
|
||||
if (!allocation) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.portRangeReservation = allocation;
|
||||
this.state.portRange = allocation.range;
|
||||
this.baseEnv.GENARRATIVE_DEV_PORT_RANGE = allocation.range.label;
|
||||
|
||||
const mappedPorts = mapDevPortsToPortRange(allocation.range);
|
||||
if (!mappedPorts) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.explicitOptions.has('webPort')) {
|
||||
this.options.webPort = mappedPorts.webPort;
|
||||
}
|
||||
if (!this.explicitOptions.has('apiPort')) {
|
||||
this.options.apiPort = mappedPorts.apiPort;
|
||||
}
|
||||
if (!this.explicitOptions.has('spacetimePort')) {
|
||||
this.options.spacetimePort = mappedPorts.spacetimePort;
|
||||
}
|
||||
if (!this.explicitOptions.has('adminWebPort')) {
|
||||
this.options.adminWebPort = mappedPorts.adminWebPort;
|
||||
}
|
||||
|
||||
this.state.spacetimeServer = `http://${this.options.spacetimeHost}:${this.options.spacetimePort}`;
|
||||
this.state.apiTarget = `http://${this.state.apiTargetHost}:${this.options.apiPort}`;
|
||||
}
|
||||
|
||||
shouldValidateSpacetimeToolVersion(command) {
|
||||
if (command === 'spacetime') {
|
||||
return true;
|
||||
@@ -806,6 +863,10 @@ class DevRunner {
|
||||
]);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (!this.isCandidateSpacetimeWithinAssignedRange(candidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pingUrl = buildUrl(candidate, '/v1/ping');
|
||||
if (!pingUrl || !(await isHttpReady(pingUrl))) {
|
||||
continue;
|
||||
@@ -836,6 +897,21 @@ class DevRunner {
|
||||
);
|
||||
}
|
||||
|
||||
isCandidateSpacetimeWithinAssignedRange(candidateUrl) {
|
||||
const range = this.state.portRange;
|
||||
if (!range) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const url = new URL(candidateUrl);
|
||||
const port = Number(url.port);
|
||||
return Number.isInteger(port) && port >= range.start && port <= range.end;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async resolvePorts(command) {
|
||||
const {options} = this;
|
||||
const portConfig = {};
|
||||
@@ -845,6 +921,7 @@ class DevRunner {
|
||||
portConfig.spacetime = {
|
||||
host: options.spacetimeHost,
|
||||
preferredPort: options.spacetimePort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -853,6 +930,7 @@ class DevRunner {
|
||||
portConfig.api = {
|
||||
host: options.apiHost,
|
||||
preferredPort: options.apiPort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -860,6 +938,7 @@ class DevRunner {
|
||||
portConfig.web = {
|
||||
host: options.webHost,
|
||||
preferredPort: options.webPort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -867,6 +946,7 @@ class DevRunner {
|
||||
portConfig.adminWeb = {
|
||||
host: options.adminWebHost,
|
||||
preferredPort: options.adminWebPort,
|
||||
portRange: this.state.portRange,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -959,6 +1039,15 @@ class DevRunner {
|
||||
console.log(`[dev] repo: ${repoRoot}`);
|
||||
console.log(`[dev] command: ${command}`);
|
||||
console.log(`[dev] watch: ${options.watch ? 'on' : 'off'}`);
|
||||
if (state.portRange) {
|
||||
const owner =
|
||||
state.portRangeReservation?.username ||
|
||||
getLinuxDevPortRangeUsername(this.baseEnv);
|
||||
console.log(`[dev] port-range: ${state.portRange.label} (${owner})`);
|
||||
console.log(
|
||||
`[dev] port-range-registry: ${getLinuxDevPortRangeRegistryPaths(this.baseEnv).registryPath}`,
|
||||
);
|
||||
}
|
||||
console.log(`[dev] web: http://127.0.0.1:${options.webPort}`);
|
||||
console.log(`[dev] admin web: http://${state.adminWebTargetHost}:${options.adminWebPort}/admin/`);
|
||||
console.log(`[dev] api-server: ${state.apiTarget}`);
|
||||
|
||||
Reference in New Issue
Block a user