diff --git a/AGENTS.md b/AGENTS.md index 6ad6df887..331982177 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,6 +37,7 @@ | 海外 API 路由、海外 Token、Audience/JWKS | `gateway-overseas/` | `gateway-overseas/AGENTS.md` | | 尾程订单、海外入仓计划、换标、库存、采购、本土卡车 | `lastleg-service/` | `lastleg-service/AGENTS.md`,再按 `inboundplan/`、`relabel/`、`inventory/`、`procurement/`、`trucking/` 定位 | | 海外入仓分页、FBA 预约、PDA 卸柜照片 | `lastleg-service/` | `inboundplan/controller/InboundPlanController.java`、`InboundPlanQueryMapper.java`、`InboundFbaAppointmentService.java`、`inboundplan/pda/` | +| 海外仓排车查询、候选与智能预排 | `lastleg-service/` | `dispatch/controller/DispatchQueryController.java`、`SmartDispatchController.java`;只读排车和预排创建独立开关,不自动开放编辑、取消、发车、PDA 出库或派送 | | 海外仓 PDA 到仓、卸柜、实收、上架、合托 | `lastleg-service/`、`warehouse-overseas-service/` | 业务流程 `lastleg-service/inboundplan/pda/`;库位、托盘和资源锁归 `warehouse-overseas-service/`;设备端 `../apps/pda-overseas-uniapp/`,公网入口 `gateway-overseas/` | | 海外仓库、海外库位、海外托盘、资源锁 | `warehouse-overseas-service/` | `warehouse-overseas-service/AGENTS.md` | | PDF 标签、审计、跨区双向事件 | `print/`、`audit-service/`、`event-bridge-service/` | 事件模型先读 `common-event-contract/AGENTS.md`;可靠投递与 CN/OVERSEAS 目标路由读 `event-bridge-service/AGENTS.md` | @@ -75,7 +76,7 @@ - 数据库只访问所属服务库。模块表结构位于 `/src/main/resources/db/`;当前开发阶段直接更新最新模块基线和 `deploy/sql/头程数据库统一增量-MySQL8.sql`,不为历史开发数据新增兼容迁移。已有名称含 `migration` 的脚本仅作现有结构来源和开发追溯,部署说明见 `deploy/sql/README.md`。 - 通用配置在 `config/nacos/{local,cn}/`;服务日志、上传文件、`target/`、`reports/` 不属于源码。 - Java 签名变更的最低编译门禁是 `mvn -pl -am test-compile`,它必须在部署前完成并覆盖测试源码;有行为变化时继续运行 `mvn -pl -am test`。`-DskipTests` 可跳过测试执行但仍须保留测试编译,`-Dmaven.test.skip=true` 会跳过测试编译,不得作为签名变更的交付验证。全量验证仍使用 `mvn -DskipTests validate`、`mvn test`;不要在本地启动 Java/Nacos/数据库,除非用户明确要求。 -- `AGENTS.md` 索引维护完成后运行 `./scripts/agents-index-contract.ps1`;脚本只读源码目录和指定指引,验证部署单元、模块覆盖、关键入口与排除目录,不访问 HTTP、数据库或部署环境。 +- `AGENTS.md` 索引维护完成后运行 `./scripts/agents-index-contract.ps1`;脚本只读源码目录和指定指引,验证根快速表唯一性、部署单元、模块覆盖、关键入口与排除目录,不访问 HTTP、数据库或部署环境。 ## 全量 HTTP 验收脚本路由 diff --git a/deploy/scripts/deploy-built-service.sh b/deploy/scripts/deploy-built-service.sh index e41e3719c..d10d16685 100644 --- a/deploy/scripts/deploy-built-service.sh +++ b/deploy/scripts/deploy-built-service.sh @@ -214,25 +214,28 @@ run_scp() { run_ssh install -d -m 750 "$REMOTE_DIR" SUFFIX="${IMAGE_TAG}.$$.${SERVICE}" -REMOTE_ENV_TMP="$REMOTE_DIR/.env.$SUFFIX.tmp" -REMOTE_COMPOSE_TMP="$REMOTE_DIR/docker-compose.yml.$SUFFIX.tmp" +REMOTE_ENV_NAME=".env.$PROJECT_NAME" +REMOTE_COMPOSE_NAME="docker-compose.$PROJECT_NAME.yml" +REMOTE_ENV_TMP="$REMOTE_DIR/$REMOTE_ENV_NAME.$SUFFIX.tmp" +REMOTE_COMPOSE_TMP="$REMOTE_DIR/$REMOTE_COMPOSE_NAME.$SUFFIX.tmp" run_scp "$ENV_FILE" "$SSH_TARGET:$REMOTE_ENV_TMP" run_scp "$COMPOSE_FILE" "$SSH_TARGET:$REMOTE_COMPOSE_TMP" -run_ssh sh -s -- "$REMOTE_DIR" "$REMOTE_ENV_TMP" "$REMOTE_COMPOSE_TMP" <<'REMOTE_PREPARE' +run_ssh sh -s -- "$REMOTE_ENV_TMP" "$REMOTE_COMPOSE_TMP" "$REMOTE_DIR/$REMOTE_ENV_NAME" "$REMOTE_DIR/$REMOTE_COMPOSE_NAME" <<'REMOTE_PREPARE' set -eu -remote_dir="$1" -env_tmp="$2" -compose_tmp="$3" +env_tmp="$1" +compose_tmp="$2" +env_target="$3" +compose_target="$4" chmod 600 "$env_tmp" chmod 644 "$compose_tmp" -mv -f "$env_tmp" "$remote_dir/.env" -mv -f "$compose_tmp" "$remote_dir/docker-compose.yml" +mv -f "$env_tmp" "$env_target" +mv -f "$compose_tmp" "$compose_target" REMOTE_PREPARE echo "传输镜像到 $SSH_TARGET: $IMAGE" docker save "$IMAGE" | gzip -1 | run_ssh 'gzip -dc | docker load' -run_ssh sh -s -- "$REMOTE_DIR" "$DEPLOY_ENV" "$IMAGE_REPOSITORY" "$IMAGE_TAG" "$SERVICE" "$PROJECT_NAME" <<'REMOTE_DEPLOY' +run_ssh sh -s -- "$REMOTE_DIR" "$DEPLOY_ENV" "$IMAGE_REPOSITORY" "$IMAGE_TAG" "$SERVICE" "$PROJECT_NAME" "$REMOTE_ENV_NAME" "$REMOTE_COMPOSE_NAME" <<'REMOTE_DEPLOY' set -eu remote_dir="$1" deploy_env="$2" @@ -240,18 +243,20 @@ image_repository="$3" image_tag="$4" service="$5" project_name="$6" +env_file="$7" +compose_file="$8" cd "$remote_dir" export DEPLOY_ENV="$deploy_env" export IMAGE_REPOSITORY="$image_repository" export IMAGE_TAG="$image_tag" docker compose \ --project-name "$project_name" \ - --env-file .env \ - -f docker-compose.yml \ + --env-file "$env_file" \ + -f "$compose_file" \ up -d --remove-orphans --pull never "$service" docker compose \ --project-name "$project_name" \ - --env-file .env \ - -f docker-compose.yml \ + --env-file "$env_file" \ + -f "$compose_file" \ ps "$service" REMOTE_DEPLOY diff --git a/lastleg-service/AGENTS.md b/lastleg-service/AGENTS.md index a4e5e1f4b..e16e1f071 100644 --- a/lastleg-service/AGENTS.md +++ b/lastleg-service/AGENTS.md @@ -10,7 +10,8 @@ | 功能 | 入口 | |---|---| -| 事件接收、订单列表/详情/导出、目的仓调整 | `src/main/java/com/baiyue/lastleg/controller/LastlegOrderController.java` | +| 事件接收、平台订单列表/详情/导出、目的仓调整 | `src/main/java/com/baiyue/lastleg/controller/LastlegOrderController.java` | +| 客户尾程订单列表与详情 | `controller/MerchantLastlegOrderController.java`;客户范围只取认证身份,稳定类型/状态见 `model/LastlegOrderType.java`、`LastlegOrderStatus.java` | | 装柜完成事件消费 | `service/LoadingCompletedConsumer.java`;当前生产信封为 `FIRST_LEG_LOADING_PLAN_COMPLETED` V2,消费端按冻结字段校验后建单 | | 尾程单号生成 | `service/LastlegOrderNoGenerator.java` | | 订单查询与更新 | `service/LastlegOrderService.java` | @@ -24,6 +25,7 @@ | PDA 卸柜照片文件上传与归属校验 | `InboundPdaController.java` 的 `/unload-photos/files`、`inboundplan/pda/gateway/UnloadPhotoFileGateway.java`、`HttpUnloadPhotoFileGateway.java`;File 类型固定 `OVERSEAS_UNLOAD_PHOTO` | | 卸柜照片与海外卸柜完成 Outbox | `inboundplan/pda/controller/InboundPdaController.java`、`inboundplan/pda/service/InboundUnloadCompletedOutboxService.java`、`entity/LastlegEventOutbox.java`、`mapper/LastlegEventOutboxMapper.java`;事件模型见 `../common-event-contract/AGENTS.md` | | 入仓后 SKU 库存余额、库位和流水 | `inventory/`;平台只读查询与非出库库存命令分别从 `inventory/query/controller/InventoryQueryController.java`、`inventory/command/controller/InventoryCommandController.java` 进入 | +| 平台排车列表/详情与智能预排 | `dispatch/controller/DispatchQueryController.java`、`SmartDispatchController.java`、`dispatch/mapper/DispatchCandidateMapper.java`、`dispatch/service/DispatchService.java`;入口 `/api/platform/v1/last-leg/dispatches`,范围开关与禁用写操作见“四期开发范围路由” | | 仓储资源调用 | `inboundplan/pda/gateway/WarehouseResourceGateway.java`、`HttpWarehouseResourceGateway.java` | | 仓储转运、历史订单/入仓绑定、可选换标联动 | `transfer/controller/WarehouseTransferController.java`、`transfer/service/WarehouseTransferService.java` | | 一件代发接收、SKU/箱唛库存 | `dropship/service/DropshipOrderService.java`、`dropship/entity/`、`dropship/mapper/` | diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanController.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanController.java index f294b6fc7..3bdb1f82d 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanController.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanController.java @@ -33,7 +33,7 @@ public class InboundPlanController { /** 按入仓计划明细分页查询可预约的入库订单行。 */ @GetMapping - @PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_CREATE + "')") + @PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_LIST + "')") @Operation(summary = "分页查询入库订单", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_LIST) public ApiResponse> page( @AuthenticationPrincipal AuthenticatedUser user, @@ -43,7 +43,7 @@ public class InboundPlanController { } @PostMapping("/convert") - @PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_LIST + "')") + @PreAuthorize("hasAuthority('" + OverseasPermissions.INBOUND_ORDER_CREATE + "')") /** 将入仓单转换为入仓计划。 */ @Operation(summary = "入仓单转入仓计划", description = "Required authority: " + OverseasPermissions.INBOUND_ORDER_CREATE) public ApiResponse convert(@AuthenticationPrincipal AuthenticatedUser user, diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanPermissionContractTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanPermissionContractTest.java index d73111a9b..8336749d1 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanPermissionContractTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/controller/InboundPlanPermissionContractTest.java @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; class InboundPlanPermissionContractTest { private static final String LIST = "platform:overseas-warehouse:inbound-order:list"; + private static final String CREATE = "platform:overseas-warehouse:inbound-order:create"; private static final String FBA_APPOINTMENT = "platform:overseas-warehouse:inbound-order:fba-appointment"; @@ -28,6 +29,17 @@ class InboundPlanPermissionContractTest { } } + @Test + void conversionUsesCanonicalInboundOrderCreateAuthority() { + Method method = Arrays.stream(InboundPlanController.class.getDeclaredMethods()) + .filter(candidate -> candidate.getName().equals("convert")) + .findFirst() + .orElseThrow(); + + assertEquals("hasAuthority('" + CREATE + "')", method.getAnnotation(PreAuthorize.class).value()); + assertEquals("Required authority: " + CREATE, method.getAnnotation(Operation.class).description()); + } + @Test void fbaAppointmentWritesUseDedicatedAuthority() { Map methods = Arrays.stream(InboundPlanController.class.getDeclaredMethods()) diff --git a/scripts/README.md b/scripts/README.md index 06dc4f67f..ee7075b31 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -31,7 +31,7 @@ node scripts/create-pending-review-orders.mjs | Admin/Client 本地测试与类型检查 | `scripts/frontend-full-regression.ps1` | | PDA 本地页面和状态测试 | `../apps/pda-uniapp/tests/*.test.mjs` | | 本地数据库 SQL 静态/受控验收 | `scripts/local-db-full-acceptance.ps1` | -| AGENTS 快速索引一致性 | `scripts/agents-index-contract.ps1`;只读核对部署单元、业务模块、PDA 注册页面、Maven 模块、关键入口与排除目录 | +| AGENTS 快速索引一致性 | `scripts/agents-index-contract.ps1`;只读核对根快速表唯一性、部署单元、业务模块、PDA 注册页面、Maven 模块、关键入口与排除目录 | Admin/Client HTTP 脚本的离线合同只验证脚本结构、参数和模块边界,不访问测试环境: diff --git a/scripts/agents-index-contract.ps1 b/scripts/agents-index-contract.ps1 index 70e839089..cc5e214f9 100644 --- a/scripts/agents-index-contract.ps1 +++ b/scripts/agents-index-contract.ps1 @@ -36,6 +36,27 @@ function Assert-Path([string]$relativePath) { } } +function Assert-MarkdownTableUnique([string]$path, [string]$heading, [int]$keyColumn, [string]$scope) { + $script:checked++ + $lines = Get-Content -LiteralPath $path + $inside = $false + $keys = [System.Collections.Generic.List[string]]::new() + foreach ($line in $lines) { + if ($line -eq $heading) { + $inside = $true + continue + } + if ($inside -and $line.StartsWith('## ')) { break } + if (-not $inside -or -not $line.StartsWith('|')) { continue } + $cells = @($line.Trim('|').Split('|') | ForEach-Object { $_.Trim() }) + if ($keyColumn -ge $cells.Count -or $cells[$keyColumn] -match '^:?-{3,}:?$') { continue } + $keys.Add($cells[$keyColumn]) + } + $keys | Group-Object | Where-Object Count -gt 1 | ForEach-Object { + Add-Failure "$scope 存在重复索引键: $($_.Name)($($_.Count) 次)" + } +} + $rootAgentsPath = Join-Path $workspaceRoot 'AGENTS.md' $adminAgentsPath = Join-Path $workspaceRoot 'apps/admin-web/AGENTS.md' $clientAgentsPath = Join-Path $workspaceRoot 'apps/client-web/AGENTS.md' @@ -55,6 +76,10 @@ Get-ChildItem -LiteralPath (Join-Path $workspaceRoot 'apps') -Directory | Sort-Object Name | ForEach-Object { Assert-Contains $rootAgents "apps/$($_.Name)/" '根 AGENTS.md' } Assert-Contains $rootAgents 'baiyue-firstleg/' '根 AGENTS.md' +Assert-MarkdownTableUnique $rootAgentsPath '## 项目快速入口' 0 '根项目表' +Assert-MarkdownTableUnique $rootAgentsPath '## 项目快速入口' 1 '根项目目录' +Assert-MarkdownTableUnique $rootAgentsPath '## 业务快速路由表' 0 '根业务路由表' +Assert-MarkdownTableUnique $rootAgentsPath '## 原型入口索引' 0 '根原型表' # 前端当前全部业务模块必须能从应用级索引命中。 $applications = @( @@ -95,6 +120,9 @@ $criticalPaths = @( 'apps/admin-web/src/modules/overseas-warehouse/api/inboundPlans.ts', 'apps/admin-web/src/modules/overseas-warehouse/api/inventory.ts', 'apps/admin-web/src/modules/overseas-warehouse/api/pallets.ts', + 'apps/admin-web/src/modules/overseas-warehouse/api/dispatches.ts', + 'apps/admin-web/src/modules/overseas-warehouse/pages/DispatchesView.vue', + 'apps/admin-web/src/modules/overseas-warehouse/components/SmartDispatchDialog.vue', 'apps/admin-web/src/modules/overseas-business/api/platformOverseasBusiness.ts', 'apps/admin-web/src/modules/overseas-master-data/api/overseasMasterData.ts', 'apps/pda-uniapp/pages/tasks/tasks.vue', @@ -111,8 +139,19 @@ $criticalPaths = @( 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/mapper/InboundPlanQueryMapper.java', 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/InboundFbaAppointmentService.java', 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/pda/gateway/UnloadPhotoFileGateway.java', + 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/controller/DispatchQueryController.java', + 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/controller/SmartDispatchController.java', + 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/controller/MerchantLastlegOrderController.java', + 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/model/LastlegOrderType.java', + 'baiyue-firstleg/lastleg-service/src/main/java/com/baiyue/lastleg/model/LastlegOrderStatus.java', + 'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/portschedule/service/CarrierScheduleQuerySlots.java', + 'baiyue-firstleg/firstleg/src/main/java/com/baiyue/firstleg/portschedule/entity/CarrierScheduleExceptionFactEntity.java', + 'baiyue-firstleg/integration-service/src/main/java/com/baiyue/integration/domain/CarrierScheduleStandardNode.java', + 'baiyue-firstleg/integration-service/src/main/java/com/baiyue/integration/domain/CarrierScheduleExceptionCode.java', + 'baiyue-firstleg/gateway-overseas/src/test/java/com/baiyue/gateway/config/OverseasLastlegRouteContractTest.java', 'baiyue-firstleg/event-bridge-service/src/main/java/com/baiyue/eventbridge/service/FirstlegEventClient.java', - 'baiyue-firstleg/common-event-contract/src/main/java/com/baiyue/common/event/FirstLegLoadingPlanCompletedV2.java' + 'baiyue-firstleg/common-event-contract/src/main/java/com/baiyue/common/event/FirstLegLoadingPlanCompletedV2.java', + 'docs/四期/船节点标准化契约.md' ) $criticalPaths | ForEach-Object { Assert-Path $_ } @@ -164,5 +203,5 @@ if ($failures.Count -gt 0) { exit 1 } -Write-Host "AGENTS 索引合同通过:$checked 项检查,覆盖部署单元、前端模块、PDA 注册页面、Maven 模块、关键入口和排除目录。" -ForegroundColor Green +Write-Host "AGENTS 索引合同通过:$checked 项检查,覆盖快速表唯一性、部署单元、前端模块、PDA 注册页面、Maven 模块、关键入口和排除目录。" -ForegroundColor Green exit 0 diff --git a/scripts/db/incremental.sql b/scripts/db/incremental.sql index 2011ee141..678e02e2e 100644 --- a/scripts/db/incremental.sql +++ b/scripts/db/incremental.sql @@ -3442,6 +3442,8 @@ SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE tab PREPARE schema_comments_025 FROM @ddl; EXECUTE schema_comments_025; DEALLOCATE PREPARE schema_comments_025; SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='wh_pallet' AND column_name='remark' AND COALESCE(column_comment,'') <> '备注'), 'ALTER TABLE wh_pallet MODIFY COLUMN remark VARCHAR(500) DEFAULT NULL COMMENT ''备注''', 'SELECT 1')); PREPARE schema_comments_025 FROM @ddl; EXECUTE schema_comments_025; DEALLOCATE PREPARE schema_comments_025; +SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='wh_pallet' AND column_name='fba_warehouse_code' AND COALESCE(column_comment,'') <> '所属FBA仓编码'), 'ALTER TABLE wh_pallet MODIFY COLUMN fba_warehouse_code VARCHAR(128) DEFAULT NULL COMMENT ''所属FBA仓编码''', 'SELECT 1')); +PREPARE schema_comments_025 FROM @ddl; EXECUTE schema_comments_025; DEALLOCATE PREPARE schema_comments_025; SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='wh_pallet' AND column_name='created_by' AND COALESCE(column_comment,'') <> '创建人ID'), 'ALTER TABLE wh_pallet MODIFY COLUMN created_by BIGINT UNSIGNED DEFAULT NULL COMMENT ''创建人ID''', 'SELECT 1')); PREPARE schema_comments_025 FROM @ddl; EXECUTE schema_comments_025; DEALLOCATE PREPARE schema_comments_025; SET @ddl = (SELECT IF(EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='wh_pallet' AND column_name='created_at' AND COALESCE(column_comment,'') <> '创建时间'), 'ALTER TABLE wh_pallet MODIFY COLUMN created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT ''创建时间''', 'SELECT 1')); diff --git a/scripts/overseas-deployment-contract.test.mjs b/scripts/overseas-deployment-contract.test.mjs index e37d7e708..575df9e41 100644 --- a/scripts/overseas-deployment-contract.test.mjs +++ b/scripts/overseas-deployment-contract.test.mjs @@ -70,6 +70,10 @@ test('CN and overseas event bridges have explicit opposite-region consumers', as assert.match(scripts, /env_file_value REGION_CODE/) assert.match(scripts, /EVENT_BRIDGE_REGION_OVERRIDE/) assert.match(scripts, /FIRSTLEG_CONTAINER_EVENT_CONSUMER_URL/) + assert.match(scripts, /COMPOSE_FILE="deploy\/docker-compose\.overseas\.yml"/) + assert.match(scripts, /PROJECT_NAME="firstleg-\$DEPLOY_ENV-overseas"/) + assert.match(scripts, /REMOTE_ENV_NAME="\.env\.\$PROJECT_NAME"/) + assert.match(scripts, /REMOTE_COMPOSE_NAME="docker-compose\.\$PROJECT_NAME\.yml"/) for (const key of [ 'NACOS_SERVER_ADDR', 'OVERSEAS_NACOS_NAMESPACE', 'INTERNAL_SERVICE_TOKEN', 'JWT_ISSUER', 'OVERSEAS_JWT_AUDIENCE', 'JWT_JWK_SET_URI' diff --git a/scripts/test-environment-pipeline-contract.test.mjs b/scripts/test-environment-pipeline-contract.test.mjs index e76beaf5b..e3463170f 100644 --- a/scripts/test-environment-pipeline-contract.test.mjs +++ b/scripts/test-environment-pipeline-contract.test.mjs @@ -39,6 +39,25 @@ test('Java builds use the shared builder pool and deploy on the target environme assert.match(directDeployScript, /--pull never/) }) +test('parallel remote deployments keep env and compose assets isolated by project', () => { + assert.match(directDeployScript, /REMOTE_ENV_NAME="\.env\.\$PROJECT_NAME"/) + assert.match(directDeployScript, /REMOTE_COMPOSE_NAME="docker-compose\.\$PROJECT_NAME\.yml"/) + assert.match(directDeployScript, /REMOTE_ENV_TMP="\$REMOTE_DIR\/\$REMOTE_ENV_NAME\.\$SUFFIX\.tmp"/) + assert.match(directDeployScript, /REMOTE_COMPOSE_TMP="\$REMOTE_DIR\/\$REMOTE_COMPOSE_NAME\.\$SUFFIX\.tmp"/) + assert.match(directDeployScript, /env_target="\$3"/) + assert.match(directDeployScript, /compose_target="\$4"/) + assert.match(directDeployScript, /mv -f "\$env_tmp" "\$env_target"/) + assert.match(directDeployScript, /mv -f "\$compose_tmp" "\$compose_target"/) + assert.match(directDeployScript, /env_file="\$7"/) + assert.match(directDeployScript, /compose_file="\$8"/) + assert.match(directDeployScript, /--env-file "\$env_file"/) + assert.match(directDeployScript, /-f "\$compose_file"/) + assert.doesNotMatch(directDeployScript, /mv -f "\$env_tmp" "\$remote_dir\/\.env"/) + assert.doesNotMatch(directDeployScript, /mv -f "\$compose_tmp" "\$remote_dir\/docker-compose\.yml"/) + assert.doesNotMatch(directDeployScript, /--env-file \.env/) + assert.doesNotMatch(directDeployScript, /-f docker-compose\.yml/) +}) + test('shared builders use a reachable public Maven mirror instead of the retired private proxy', () => { assert.match(pipeline, /MAVEN_SETTINGS = 'deploy\/maven\/settings-ci\.xml'/) assert.match(ciMavenSettings, /https:\/\/maven\.aliyun\.com\/repository\/public/)