统一 AGC 开发端口分配
Project CI / Repository checks (push) Failing after 1m2s
Project CI / Frontend tests (push) Successful in 3m19s
Project CI / Backend tests (push) Successful in 4m18s
Project CI / Native shell tests (push) Failing after 11m47s

将 AGC Vite 纳入 Linux 用户端口段第六槽位
同步 Tauri devUrl、Vite 监听和配套后端端口预留
兼容迁移旧五端口注册记录并阻止重复分配
补齐动态配置顺序、跨平台和进程生命周期回归测试
更新开发运维文档、端口 skill 与项目共享记忆
This commit is contained in:
2026-08-08 16:18:45 +08:00
parent 6b1dfc77d9
commit 83f07fc58d
19 changed files with 710 additions and 96 deletions
+109 -4
View File
@@ -1,4 +1,4 @@
import {mkdtempSync, readFileSync, rmSync} from 'node:fs';
import {mkdtempSync, readFileSync, rmSync, writeFileSync} from 'node:fs';
import {createServer} from 'node:net';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
@@ -47,7 +47,7 @@ async function reserveConsecutivePorts() {
}
describe('dev stack port utils', () => {
it('解析端口段并映射到五个 dev 端口', () => {
it('解析端口段并映射到五个 dev 服务和 AGC 端口', () => {
expect(parsePortRangeSpec('10000-10099')).toEqual({
start: 10000,
end: 10099,
@@ -59,9 +59,10 @@ describe('dev stack port utils', () => {
spacetimePort: 10002,
adminWebPort: 10003,
bgfilterWorkerPort: 10004,
agcVitePort: 10005,
});
expect(() => parsePortRangeSpec('10000-10003')).toThrow(
'端口段至少需要 5 个端口',
expect(() => parsePortRangeSpec('10000-10004')).toThrow(
'端口段至少需要 6 个端口',
);
});
@@ -122,6 +123,21 @@ describe('dev stack port utils', () => {
expect(new Set(Object.values(resolvedPorts)).size).toBe(5);
});
it('解析 dev 服务时跳过父启动器预留的 AGC Vite 端口', async () => {
const server = await reservePort(0);
const preferredPort = server.address().port;
await new Promise((resolve) => server.close(resolve));
const resolvedPorts = await resolveDevStackPorts(
{
api: {host: '127.0.0.1', preferredPort},
},
{reservedPorts: new Set([preferredPort])},
);
expect(resolvedPorts.api).toBeGreaterThan(preferredPort);
});
it('端口段内会一直漂移到段尾,不会被默认 200 次尝试截断', async () => {
const rangeStart = 10000;
const rangeEnd = 10300;
@@ -142,6 +158,95 @@ describe('dev stack port utils', () => {
const linuxIt = process.platform === 'linux' ? it : it.skip;
linuxIt('Linux 升级时保留旧 v4 五端口记录并阻止重复分配', async () => {
const tempRoot = mkdtempSync(join(tmpdir(), 'genarrative-port-range-'));
const registryPath = join(tempRoot, 'registry.json');
const lockPath = join(tempRoot, 'registry.lock');
try {
writeFileSync(
registryPath,
JSON.stringify({
version: 4,
updatedAt: '2026-08-01T00:00:00.000Z',
allocations: {
alice: {
username: 'alice',
range: {start: 10000, end: 10004, label: '10000-10004'},
claimedAt: '2026-08-01T00:00:00.000Z',
updatedAt: '2026-08-01T00:00:00.000Z',
source: 'manual',
},
},
}),
);
const bobAllocation = await reserveLinuxDevPortRange({
env: {
USER: 'bob',
LOGNAME: 'bob',
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempRoot,
},
username: 'bob',
requestedRange: null,
registryPath,
lockPath,
});
const registry = JSON.parse(readFileSync(registryPath, 'utf8'));
expect(bobAllocation.range.label).toBe('10100-10199');
expect(registry.allocations.alice.range.label).toBe('10000-10004');
expect(registry.allocations.bob.range.label).toBe('10100-10199');
} finally {
rmSync(tempRoot, {recursive: true, force: true});
}
});
linuxIt('Linux 当前用户的旧五端口记录会在无冲突时扩出 AGC 槽位', async () => {
const tempRoot = mkdtempSync(join(tmpdir(), 'genarrative-port-range-'));
const registryPath = join(tempRoot, 'registry.json');
const lockPath = join(tempRoot, 'registry.lock');
try {
writeFileSync(
registryPath,
JSON.stringify({
version: 4,
updatedAt: '2026-08-01T00:00:00.000Z',
allocations: {
alice: {
username: 'alice',
range: {start: 10000, end: 10004, label: '10000-10004'},
claimedAt: '2026-08-01T00:00:00.000Z',
updatedAt: '2026-08-01T00:00:00.000Z',
source: 'manual',
},
},
}),
);
const allocation = await reserveLinuxDevPortRange({
env: {
USER: 'alice',
LOGNAME: 'alice',
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempRoot,
},
username: 'alice',
requestedRange: null,
registryPath,
lockPath,
});
expect(allocation.range.label).toBe('10000-10005');
expect(
JSON.parse(readFileSync(registryPath, 'utf8')).allocations.alice.range
.label,
).toBe('10000-10005');
} finally {
rmSync(tempRoot, {recursive: true, force: true});
}
});
linuxIt('Linux 未手动指定端口段时从 10000 开始按 100 端口块自动分配', async () => {
const tempRoot = mkdtempSync(join(tmpdir(), 'genarrative-port-range-'));
const registryPath = join(tempRoot, 'registry.json');