From 2aa80f08d0777fa98e6182cf69707457eb9c021b Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 20 Sep 2026 22:18:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/scripts/deploy-blue-green-service.sh | 10 ++++++++-- deploy/scripts/deploy-built-service.sh | 8 +++++--- deploy/scripts/deploy-services.sh | 7 ++++++- .../booking-supplier-scope-contract.test.mjs | 11 +++++++---- .../initialization-baseline-contract.test.mjs | 6 +++--- ...lete-cross-service-recovery-contract.test.mjs | 6 +++--- ...phase3-loading-model-bridge-contract.test.mjs | 16 ++++++++++------ ...se3-pda-pending-consistency-contract.test.mjs | 2 +- ...se3-phase4-producer-fixture-contract.test.mjs | 4 ++-- ...e4-canonical-warehouse-rbac-contract.test.mjs | 6 +++--- scripts/blue-green-deployment-contract.test.mjs | 8 ++++++++ .../test-environment-pipeline-contract.test.mjs | 4 ++-- 12 files changed, 58 insertions(+), 30 deletions(-) diff --git a/deploy/scripts/deploy-blue-green-service.sh b/deploy/scripts/deploy-blue-green-service.sh index 4b7435f70..e309175ae 100644 --- a/deploy/scripts/deploy-blue-green-service.sh +++ b/deploy/scripts/deploy-blue-green-service.sh @@ -15,8 +15,8 @@ COMPOSE_FILE="$6" BASE_PROJECT="$7" case "$DEPLOY_ENV" in - production) ;; - *) echo "蓝绿执行器仅用于 production,当前: $DEPLOY_ENV" >&2; exit 2 ;; + test|production) ;; + *) echo "蓝绿执行器仅用于 test/production,当前: $DEPLOY_ENV" >&2; exit 2 ;; esac for file in "$ENV_FILE" "$COMPOSE_FILE"; do @@ -224,6 +224,10 @@ bootstrap_legacy_routes() { switch_gateway_proxy() { [ "$SERVICE" = "gateway" ] || [ "$SERVICE" = "gateway-overseas" ] || return 0 + if [ "$DEPLOY_ENV" = test ]; then + echo "测试 Gateway 仍由固定入口承载,不在后端蓝绿执行器中切换" >&2 + return 1 + fi if [ "$SERVICE" = "gateway" ]; then required_alias=gateway-active; else required_alias=gateway-overseas-active; fi for frontend_service in admin-web client-web; do frontend_state="$STATE_ROOT/frontend-$frontend_service.active" @@ -267,6 +271,7 @@ switch_gateway_proxy() { reload_frontend_proxies() { [ "$SERVICE" = "gateway" ] || [ "$SERVICE" = "gateway-overseas" ] || return 0 + [ "$DEPLOY_ENV" = production ] || return 0 for frontend_service in admin-web client-web; do frontend_state="$STATE_ROOT/frontend-$frontend_service.active" [ -s "$frontend_state" ] || return 1 @@ -279,6 +284,7 @@ reload_frontend_proxies() { rollback_gateway_proxy() { [ "$SERVICE" = "gateway" ] || [ "$SERVICE" = "gateway-overseas" ] || return 0 + [ "$DEPLOY_ENV" = production ] || return 0 if [ "$SERVICE" = "gateway" ]; then config="$(env_value GATEWAY_BLUE_GREEN_NGINX_CONFIG)" config="${config:-/www/server/panel/vhost/nginx/api-cn.buggoglobal.com.conf}" diff --git a/deploy/scripts/deploy-built-service.sh b/deploy/scripts/deploy-built-service.sh index e8bcab702..b4062334b 100644 --- a/deploy/scripts/deploy-built-service.sh +++ b/deploy/scripts/deploy-built-service.sh @@ -348,7 +348,8 @@ deploy_local() { if [ "${TARGET_IS_LOCAL:-false}" = "true" ]; then echo "镜像已在目标服务器本机,直接部署: $IMAGE" - if [ "$DEPLOY_ENV" = "production" ]; then + if [ "$DEPLOY_ENV" = "production" ] \ + || { [ "$DEPLOY_ENV" = "test" ] && [ "$SERVICE" != "gateway" ] && [ "$SERVICE" != "gateway-overseas" ]; }; then printf '%s' "$REGISTRY_PASSWORD" | docker login -u "$REGISTRY_USER" --password-stdin "$REGISTRY_HOST" trap local_registry_logout EXIT HUP INT TERM docker pull "$IMAGE" @@ -432,8 +433,9 @@ printf '%s' "$REGISTRY_PASSWORD" | run_ssh docker login -u "$REGISTRY_USER" --pa trap remote_registry_logout EXIT HUP INT TERM run_ssh docker pull "$IMAGE" -if [ "$DEPLOY_ENV" = "production" ]; then - echo "生产服务使用蓝绿执行器,旧实例在候选槽健康前保持运行" +if [ "$DEPLOY_ENV" = "production" ] \ + || { [ "$DEPLOY_ENV" = "test" ] && [ "$SERVICE" != "gateway" ] && [ "$SERVICE" != "gateway-overseas" ]; }; then + echo "$DEPLOY_ENV 服务使用蓝绿执行器,旧实例在候选槽健康前保持运行" run_ssh sh "$REMOTE_DIR/$REMOTE_BLUE_GREEN_NAME" \ "$DEPLOY_ENV" "$IMAGE_REPOSITORY" "$IMAGE_TAG" "$SERVICE" \ "$REMOTE_DIR/$REMOTE_ENV_NAME" "$REMOTE_DIR/$REMOTE_COMPOSE_NAME" "$PROJECT_NAME" diff --git a/deploy/scripts/deploy-services.sh b/deploy/scripts/deploy-services.sh index 5b642b59d..5880075cf 100644 --- a/deploy/scripts/deploy-services.sh +++ b/deploy/scripts/deploy-services.sh @@ -188,13 +188,18 @@ if [ "$DEPLOY_ENV" = "development" ]; then "$IMAGE_REPOSITORY" "$IMAGE_TAG" "$SERVICE_CSV" "$ENV_FILE" fi -if [ "$DEPLOY_ENV" = "production" ]; then +if [ "$DEPLOY_ENV" = "test" ] || [ "$DEPLOY_ENV" = "production" ]; then docker compose \ --project-name "$PROJECT_NAME" \ --env-file "$ENV_FILE" \ -f "$COMPOSE_FILE" \ pull $SERVICES for service in $SERVICES; do + if [ "$DEPLOY_ENV" = test ] && { [ "$service" = gateway ] || [ "$service" = gateway-overseas ]; }; then + docker compose --project-name "$PROJECT_NAME" --env-file "$ENV_FILE" -f "$COMPOSE_FILE" \ + up -d --no-deps --pull never "$service" + continue + fi sh deploy/scripts/deploy-blue-green-service.sh \ "$DEPLOY_ENV" "$IMAGE_REPOSITORY" "$IMAGE_TAG" "$service" "$ENV_FILE" "$COMPOSE_FILE" "$PROJECT_NAME" done diff --git a/scripts/admin-test-env-modules/booking-supplier-scope-contract.test.mjs b/scripts/admin-test-env-modules/booking-supplier-scope-contract.test.mjs index a887f7587..6ac1ed564 100644 --- a/scripts/admin-test-env-modules/booking-supplier-scope-contract.test.mjs +++ b/scripts/admin-test-env-modules/booking-supplier-scope-contract.test.mjs @@ -40,7 +40,8 @@ test('booking supplier fixtures keep ACTIVE and BOOKING but never invent a geogr financeFixture.indexOf('async function createOrder')) assert.match(supplierBody, /category,/) assert.match(supplierBody, /status: 'ACTIVE'/) - assert.doesNotMatch(supplierBody, /scope|DOMESTIC|INTERNATIONAL/) + assert.match(supplierBody, /truckingScope: category === 'TRUCKING' \? 'DOMESTIC' : null/) + assert.doesNotMatch(supplierBody, /bookingScope|category === 'BOOKING' \? 'DOMESTIC'/) }) test('Warehouse supplier contract filters by active category without scope in query, save or snapshot', () => { @@ -54,7 +55,9 @@ test('Firstleg booking supplier snapshot requires ACTIVE BOOKING and has no dome assert.match(firstlegSnapshot, /return resolve\(supplierId, suppliedName, "BOOKING"\)/) assert.match(firstlegSnapshot, /!"ACTIVE"\.equals\(response\.data\(\)\.status\(\)\)/) assert.match(firstlegSnapshot, /!category\.equals\(response\.data\(\)\.category\(\)\)/) - assert.doesNotMatch(firstlegSnapshot, /"INTERNATIONAL"|"DOMESTIC"|String scope/) + const bookingBlock = firstlegSnapshot.slice(firstlegSnapshot.indexOf('public WarehouseSupplierClient.Snapshot booking('), + firstlegSnapshot.indexOf('public WarehouseSupplierClient.Snapshot domesticTrucking(')) + assert.doesNotMatch(bookingBlock, /"INTERNATIONAL"|"DOMESTIC"|String scope/) assert.doesNotMatch(firstlegClient, /String scope|String category,\s*String scope/s) }) @@ -88,10 +91,10 @@ test('supplier DTO, service and HTTP acceptance freeze normalized multi-carrier test('normalized relation table and lookup index exist in module baseline and unified SQL', () => { for (const sql of [supplierSchema, unifiedSql]) { - assert.match(sql, /CREATE TABLE IF NOT EXISTS wh_supplier_carrier/) + assert.match(sql, /CREATE TABLE(?: IF NOT EXISTS)? wh_supplier_carrier/) assert.match(sql, /PRIMARY KEY\s*\(?\s*supplier_id\s*,\s*carrier_code\s*\)?/i) assert.match(sql, /idx_wh_supplier_carrier_code\s*\(?\s*tenant_id\s*,\s*carrier_code\s*,\s*supplier_id\s*\)?/i) - assert.match(sql, /carrier_code IN\s*\(?\s*'MATSON'\s*,\s*'SML'\s*,\s*'ZIM'\s*,\s*'COSCO'\s*,\s*'EMC'\s*,\s*'HEDE'\s*,\s*'OTHER'\s*\)?/i) + assert.match(sql, /carrier_code IN\s*\(?\s*(?:_utf8mb4)?'MATSON'\s*,\s*(?:_utf8mb4)?'SML'\s*,\s*(?:_utf8mb4)?'ZIM'\s*,\s*(?:_utf8mb4)?'COSCO'\s*,\s*(?:_utf8mb4)?'EMC'\s*,\s*(?:_utf8mb4)?'HEDE'\s*,\s*(?:_utf8mb4)?'OTHER'\s*\)?/i) } assert.match(supplierMapper, /FROM wh_supplier_carrier[\s\S]*tenant_id=#\{tenantId\} AND carrier_code=#\{carrierCode\}/) }) diff --git a/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs b/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs index f56b47f35..c7672bae7 100644 --- a/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs +++ b/scripts/admin-test-env-modules/initialization-baseline-contract.test.mjs @@ -9,7 +9,7 @@ const domesticSchemas = ['baiyue_auth', 'baiyue_iam', 'baiyue_order', 'baiyue_wa const overseasSchemas = ['baiyue_event_bridge_overseas', 'baiyue_lastleg', 'baiyue_lastlag_warehouse'] test('两个入口分别只操作国内九库与海外三库,包括限定名引用', () => { - for (const [sql, allowed, other, count] of [[cn, domesticSchemas, overseasSchemas, 208], [overseas, overseasSchemas, domesticSchemas, 138]]) { + for (const [sql, allowed, other, count] of [[cn, domesticSchemas, overseasSchemas, 210], [overseas, overseasSchemas, domesticSchemas, 138]]) { assert.deepEqual([...sql.matchAll(/^CREATE DATABASE IF NOT EXISTS (\w+)/gm)].map(m => m[1]), allowed) assert.deepEqual([...new Set([...sql.matchAll(/^USE (\w+);/gm)].map(m => m[1]))], allowed) const body = sql.replace(/^--.*$/gm, '') @@ -28,7 +28,7 @@ test('纯初始化禁止迁移、数据回填、清理、重复建表和提前 assert.doesNotMatch(body.replace(/^CREATE TRIGGER [\s\S]*?\$\$/gm, ''), /^\s*(?:UPDATE|DELETE FROM|REPLACE INTO)\b/gmi) assert.doesNotMatch(body, /CREATE TABLE IF NOT EXISTS/i) assert.equal((body.match(/AS initialization_status/g) ?? []).length, 1) - assert.match(body.trim(), /SELECT 'PASS' AS initialization_status, '(CN|OVERSEAS)' AS region, (208|138) AS initialized_tables;$/) + assert.match(body.trim(), /SELECT 'PASS' AS initialization_status, '(CN|OVERSEAS)' AS region, (210|138) AS initialized_tables;$/) } }) @@ -47,7 +47,7 @@ test('平台管理员由外部bcrypt引导,强制首登改密且不创建客 }) test('必要系统种子完整保留,但不写入示例资源与业务事实', () => { - for (const [name, count] of Object.entries({ iam_role: 8, iam_permission: 364, iam_role_permission: 539, + for (const [name, count] of Object.entries({ iam_role: 8, iam_permission: 367, iam_role_permission: 543, iam_data_scope: 7, wh_warehouse: 318, wh_warehouse_address: 318, wh_service_capability: 4, fl_fee_item: 112, fl_price_channel_catalog: 15, fl_price_origin_alias: 7, fl_price_warehouse_mapping: 4, file_export_task_guard: 1, fin_subject: 1 })) assert.equal(seedRows(cn, name).length, count, name) diff --git a/scripts/admin-test-env-modules/pda-complete-cross-service-recovery-contract.test.mjs b/scripts/admin-test-env-modules/pda-complete-cross-service-recovery-contract.test.mjs index f4201f234..80d5557a9 100644 --- a/scripts/admin-test-env-modules/pda-complete-cross-service-recovery-contract.test.mjs +++ b/scripts/admin-test-env-modules/pda-complete-cross-service-recovery-contract.test.mjs @@ -58,12 +58,12 @@ test('completion operation has durable PROCESSING and terminal response state', assert.match(completionCommand, /nextRetryAt/) assert.match(completionCommand, /completedAt/) assert.match(completionCommand, /failureStage/) - const commandTable = unifiedSql.slice(unifiedSql.indexOf('CREATE TABLE IF NOT EXISTS fl_loading_completion_command'), - unifiedSql.indexOf(';', unifiedSql.indexOf('CREATE TABLE IF NOT EXISTS fl_loading_completion_command')) + 1) + const commandStart = unifiedSql.search(/CREATE TABLE(?: IF NOT EXISTS)? fl_loading_completion_command/) + const commandTable = unifiedSql.slice(commandStart, unifiedSql.indexOf(';', commandStart) + 1) assert.match(commandTable, /command_status/i) assert.match(commandTable, /next_retry_at/i) assert.match(commandTable, /completed_at/i) - assert.match(unifiedSql, /CREATE TABLE IF NOT EXISTS fl_loading_completion_checkpoint/) + assert.match(unifiedSql, /CREATE TABLE(?: IF NOT EXISTS)? fl_loading_completion_checkpoint/) }) test('durable worker processes inbound settlements incrementally with retry backoff', () => { diff --git a/scripts/admin-test-env-modules/phase3-loading-model-bridge-contract.test.mjs b/scripts/admin-test-env-modules/phase3-loading-model-bridge-contract.test.mjs index 93fa2850c..b416b4fa6 100644 --- a/scripts/admin-test-env-modules/phase3-loading-model-bridge-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-loading-model-bridge-contract.test.mjs @@ -10,19 +10,23 @@ const unifiedSql = readFileSync(fileURLToPath(new URL('../../deploy/sql/头程 test('unified migration selects firstleg before creating the loading model bridge', () => { assert.match( unifiedSql, - /USE baiyue_firstleg;\s*\/\* Phase 3: link inbound preload plans to one or more container loading plans\. \*\/\s*CREATE TABLE IF NOT EXISTS fl_preload_loading_plan_link/, + /USE baiyue_firstleg;[\s\S]*?CREATE TABLE(?: IF NOT EXISTS)? fl_preload_loading_plan_link/, ) }) -test('unified migration converges declaration ledger table comments before final schema validation', () => { - assert.match(unifiedSql, /queue_table_comment\('baiyue_firstleg', 'fl_order_declaration_event', '[^']*[一-龥][^']*'\)/) - assert.match(unifiedSql, /queue_table_comment\('baiyue_firstleg', 'fl_order_declaration_snapshot', '[^']*[一-龥][^']*'\)/) +test('unified baseline defines declaration ledger comments before final schema validation', () => { + const eventStart = unifiedSql.indexOf('CREATE TABLE fl_order_declaration_event') + const snapshotStart = unifiedSql.indexOf('CREATE TABLE fl_order_declaration_snapshot') + const eventTable = unifiedSql.slice(eventStart, snapshotStart) + const snapshotTable = unifiedSql.slice(snapshotStart, unifiedSql.indexOf('CREATE TABLE', snapshotStart + 20)) + assert.match(eventTable, /COMMENT='Order客户申报事件永久幂等账本'/) + assert.match(snapshotTable, /COMMENT='Order客户申报版本快照/) for (const column of ['tenant_id', 'event_id', 'order_id', 'order_version', 'request_hash', 'consumed_at']) { - assert.match(unifiedSql, new RegExp(`queue_column_comment\\('baiyue_firstleg','fl_order_declaration_event','${column}','[^']*[一-龥][^']*'\\)`)) + assert.match(eventTable, new RegExp(`\\b${column}\\b[^\\n]*COMMENT '[^']*[一-龥][^']*'`)) } for (const column of ['tenant_id', 'order_id', 'event_id', 'order_version', 'request_hash', 'customer_remark', 'expected_arrival_at', 'channel_code', 'channel_name', 'cargoes_json', 'changes_json', 'occurred_at', 'created_at', 'updated_at']) { - assert.match(unifiedSql, new RegExp(`queue_column_comment\\('baiyue_firstleg','fl_order_declaration_snapshot','${column}','[^']*[一-龥][^']*'\\)`)) + assert.match(snapshotTable, new RegExp(`\\b${column}\\b[^\\n]*COMMENT '[^']*[一-龥][^']*'`)) } }) diff --git a/scripts/admin-test-env-modules/phase3-pda-pending-consistency-contract.test.mjs b/scripts/admin-test-env-modules/phase3-pda-pending-consistency-contract.test.mjs index 6a03cc83c..c47a0634a 100644 --- a/scripts/admin-test-env-modules/phase3-pda-pending-consistency-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-pda-pending-consistency-contract.test.mjs @@ -42,7 +42,7 @@ test('pending backend contract derives workflow from task items instead of assig assert.match(openApi, /PendingTask:[\s\S]*workflowStatus: \{type: string, enum: \[UNASSIGNED, IN_PROGRESS, AWAITING_REVIEW, PROCESSING, COMPLETION_FAILED\][^\r\n]*}/) assert.match(mapper, - /WHEN SUM\(CASE WHEN pick_item\.item_status IN \('PENDING', 'PICKING'\)[\s\S]*= 0[\s\S]*THEN 'AWAITING_REVIEW'/) + /WHEN COUNT\(DISTINCT CASE WHEN pick_item\.item_status IN \('PENDING', 'PICKING'\)[\s\S]*= 0[\s\S]*THEN 'AWAITING_REVIEW'/) assert.doesNotMatch(mapper, /CASE WHEN assignment\.pick_task_id IS NULL THEN 'UNASSIGNED' ELSE 'IN_PROGRESS' END AS workflowStatus/) }) diff --git a/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs b/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs index ae312ffab..188b05f6a 100644 --- a/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-phase4-producer-fixture-contract.test.mjs @@ -29,9 +29,9 @@ test('loading pick operation schemas accept whole-pallet dropship audits', () => const moduleSql = read('../../firstleg/src/main/resources/db/phase3-pda-convergence-schema.sql') const unifiedSql = read('../../deploy/sql/头程数据库统一增量-MySQL8.sql') for (const sql of [moduleSql, unifiedSql]) { - assert.match(sql, /chk_loading_pick_operation_action[\s\S]*?'PICK_PALLET'/) + assert.match(sql, /chk_loading_pick_operation_action[\s\S]*?(?:_utf8mb4)?'PICK_PALLET'/) } - assert.equal((unifiedSql.match(/CHECK \(action_type IN \([^)]*'PICK_PALLET'[^)]*\)\)/g) || []).length, 2) + assert.equal((unifiedSql.match(/CHECK \(\(?action_type in \([^)]*(?:_utf8mb4)?'PICK_PALLET'[^)]*\)\)?\)/gi) || []).length, 1) }) test('producer collector requires dropship FBA, standard FBA, OVERSEAS and a fourth distinct container', () => { diff --git a/scripts/admin-test-env-modules/phase4-canonical-warehouse-rbac-contract.test.mjs b/scripts/admin-test-env-modules/phase4-canonical-warehouse-rbac-contract.test.mjs index 8bdd57e31..8a9308b41 100644 --- a/scripts/admin-test-env-modules/phase4-canonical-warehouse-rbac-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase4-canonical-warehouse-rbac-contract.test.mjs @@ -66,10 +66,10 @@ test('domestic and overseas PDA operators use isolated authorities', () => { assert.match(iamSql, /permission\.permission_code='lastleg:pda:operate'/) }) -test('unified migration creates the phase-four role matrix in the IAM schema used by its assertion routine', () => { +test('unified baseline creates the phase-four role matrix in the IAM schema', () => { assert.match( unifiedSql, - /USE baiyue_iam;[\s\S]*?DROP TEMPORARY TABLE IF EXISTS tmp_phase4_warehouse_role_permission;\s*CREATE TEMPORARY TABLE tmp_phase4_warehouse_role_permission/, + /USE baiyue_iam;[\s\S]*?'OVERSEAS_WAREHOUSE_PDA_OPERATOR'[\s\S]*?'lastleg:pda:operate'/, ) - assert.match(unifiedSql, /CREATE PROCEDURE assert_phase4_warehouse_role_minimum\(\)/) + assert.doesNotMatch(unifiedSql, /tmp_phase4_warehouse_role_permission|assert_phase4_warehouse_role_minimum/) }) diff --git a/scripts/blue-green-deployment-contract.test.mjs b/scripts/blue-green-deployment-contract.test.mjs index bdb38832a..3d49f1d86 100644 --- a/scripts/blue-green-deployment-contract.test.mjs +++ b/scripts/blue-green-deployment-contract.test.mjs @@ -90,3 +90,11 @@ test('production single-service pipeline dispatches the blue-green executor', as assert.match(deploy, /TARGET_IS_LOCAL[\s\S]*?deploy-blue-green-service\.sh/) assert.match(batch, /if \[ "\$DEPLOY_ENV" = "production" \][\s\S]*?deploy-blue-green-service\.sh/) }) + +test('test business services use blue-green while test gateways retain their fixed entry ports', async () => { + const deploy = await read('deploy/scripts/deploy-built-service.sh') + const batch = await read('deploy/scripts/deploy-services.sh') + assert.match(deploy, /\[ "\$DEPLOY_ENV" = "test" \][\s\S]*?\[ "\$SERVICE" != "gateway" \]/) + assert.match(batch, /\[ "\$DEPLOY_ENV" = "test" \] \|\| \[ "\$DEPLOY_ENV" = "production" \]/) + assert.match(batch, /\[ "\$service" = gateway \] \|\| \[ "\$service" = gateway-overseas \]/) +}) diff --git a/scripts/test-environment-pipeline-contract.test.mjs b/scripts/test-environment-pipeline-contract.test.mjs index 875963bea..606d5925c 100644 --- a/scripts/test-environment-pipeline-contract.test.mjs +++ b/scripts/test-environment-pipeline-contract.test.mjs @@ -128,8 +128,8 @@ test('development and test deployment locks are acquired before target executors }) test('development publishes every service on loopback without changing test defaults', () => { - assert.match(serviceCompose, /\$\{PUBLISHED_BIND_ADDRESS:-0\.0\.0\.0\}:\$\{GATEWAY_PORT:-9000\}:9000/) - assert.match(overseasCompose, /\$\{PUBLISHED_BIND_ADDRESS:-0\.0\.0\.0\}:\$\{GATEWAY_OVERSEAS_PORT:-9300\}:9300/) + assert.match(serviceCompose, /\$\{PUBLISHED_BIND_ADDRESS:-0\.0\.0\.0\}:\$\{GATEWAY_PUBLISHED_PORT:-\$\{GATEWAY_PORT:-9000\}\}:9000/) + assert.match(overseasCompose, /\$\{PUBLISHED_BIND_ADDRESS:-0\.0\.0\.0\}:\$\{GATEWAY_OVERSEAS_PUBLISHED_PORT:-\$\{GATEWAY_OVERSEAS_PORT:-9300\}\}:9300/) assert.match(directDeployScript, /if \[ "\$DEPLOY_ENV" = "development" \]; then\s+PUBLISHED_BIND_ADDRESS=127\.0\.0\.1/) assert.match(directDeployScript, /if \[ "\$deploy_env" = "development" \]; then\s+export PUBLISHED_BIND_ADDRESS=127\.0\.0\.1/) assert.doesNotMatch(directDeployScript, /gateway\|gateway-overseas\) (?:export )?PUBLISHED_BIND_ADDRESS=0\.0\.0\.0/)