From aad8f15a3c1414f8a5258ef47322d127c68b98ae Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Fri, 2 Oct 2026 11:03:11 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20dev:all=20=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E6=AE=B5=E4=B8=8E=E5=89=8D=E7=AB=AF=E5=B0=B1=E7=BB=AA?= =?UTF-8?q?=E5=88=A4=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 CLI port-range 同步到 AGC 端口解析环境。 将主站和后台 HTTP readiness 限制为 2xx。 补充真实 HTTP 状态码与端口段传递测试。 --- scripts/dev-all.mjs | 23 ++++++++++++++++++--- scripts/dev-all.test.ts | 46 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/scripts/dev-all.mjs b/scripts/dev-all.mjs index 34918d6d6..448b33cc9 100644 --- a/scripts/dev-all.mjs +++ b/scripts/dev-all.mjs @@ -91,6 +91,20 @@ function buildRootDevArgs(argv = []) { ]; } +function buildDevAllBaseEnvironment(argv = [], baseEnv = process.env) { + let portRange; + for (let index = 0; index < argv.length; index += 1) { + if (argv[index] === '--port-range') { + portRange = argv[index + 1]; + index += 1; + } + } + + return portRange + ? { ...baseEnv, GENARRATIVE_DEV_PORT_RANGE: portRange } + : { ...baseEnv }; +} + function buildDevAllEnvironment(baseEnv = process.env, agcVitePort) { if (!Number.isInteger(agcVitePort) || agcVitePort < 1024) { throw new Error(`AGC Vite 端口无效: ${agcVitePort}`); @@ -120,7 +134,7 @@ async function isHttpReady(url, timeoutMs = 1_000) { const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { signal: controller.signal }); - return response.status >= 200 && response.status < 500; + return response.status >= 200 && response.status < 300; } catch { return false; } finally { @@ -511,11 +525,12 @@ async function runDevAll(argv = process.argv.slice(2), dependencies = {}) { } try { + const baseEnv = buildDevAllBaseEnvironment(argv); const endpoint = await resolveEndpoint({ - env: process.env, + env: baseEnv, strictConfigured: true, }); - const env = buildDevAllEnvironment(process.env, endpoint.port); + const env = buildDevAllEnvironment(baseEnv, endpoint.port); const rootArgs = buildRootDevArgs(argv); console.log( @@ -589,10 +604,12 @@ async function runDevAll(argv = process.argv.slice(2), dependencies = {}) { export { agcDatabase, agcDataDir, + buildDevAllBaseEnvironment, buildDevAllEnvironment, buildManagedSpawnOptions, buildRootDevArgs, buildStartupSummary, + isHttpReady, isSharedRootStackReady, readDevStackState, renderStartupTable, diff --git a/scripts/dev-all.test.ts b/scripts/dev-all.test.ts index 9a3352c03..be5f2c37e 100644 --- a/scripts/dev-all.test.ts +++ b/scripts/dev-all.test.ts @@ -1,4 +1,6 @@ import { EventEmitter } from 'node:events'; +import { createServer } from 'node:http'; +import type { AddressInfo } from 'node:net'; import { resolve } from 'node:path'; import { describe, expect, test } from 'vitest'; @@ -6,10 +8,12 @@ import { describe, expect, test } from 'vitest'; import { agcDatabase, agcDataDir, + buildDevAllBaseEnvironment, buildDevAllEnvironment, buildManagedSpawnOptions, buildRootDevArgs, buildStartupSummary, + isHttpReady, isSharedRootStackReady, renderStartupTable, runDevAll, @@ -86,6 +90,48 @@ describe('dev:all 参数和共享开发栈合同', () => { expect(baseEnv.AGC_DEV_ADMIN_WEB).toBe('1'); }); + test('CLI port-range 覆盖旧环境并同步给 AGC', () => { + const env = buildDevAllBaseEnvironment(['--port-range', '10100-10199'], { + GENARRATIVE_DEV_PORT_RANGE: '10000-10099', + }); + + expect(env.GENARRATIVE_DEV_PORT_RANGE).toBe('10100-10199'); + }); + + test.each([ + [200, true], + [204, true], + [299, true], + [400, false], + [401, false], + [403, false], + [404, false], + [500, false], + [503, false], + ])('HTTP %i 的前端就绪结果为 %s', async (status, expected) => { + const server = createServer((_request, response) => { + response.writeHead(status).end(); + }); + await new Promise((resolveListen) => { + server.listen(0, '127.0.0.1', resolveListen); + }); + + try { + const { port } = server.address() as AddressInfo; + await expect(isHttpReady(`http://127.0.0.1:${port}`)).resolves.toBe( + expected, + ); + } finally { + server.closeAllConnections(); + await new Promise((resolveClose, rejectClose) => { + server.close((error) => { + if (error) rejectClose(error); + else resolveClose(); + }); + }); + } + }); + test('Windows 下通过 shell 启动 npm.cmd', () => { const options = buildManagedSpawnOptions(process.env);