单体仓库中依赖模块变更时,如何仅构建受影响的Java服务WAR包并通过GitHub Actions部署至AWS
刚接触Java和Monorepo的话,这个需求确实是很常见的痛点——既要避免无意义的全量构建,又要保证依赖变更的服务能及时更新。我给你几个实用的思路,一步步来解决:
思路一:结合Maven依赖检测+GitHub Actions动态脚本
这是最适合新手的方案,不需要引入额外工具,用Maven自带的命令和简单的Shell脚本就能实现。
步骤1:先明确依赖关系
首先你需要确认每个服务到底依赖哪些lib模块,本地可以用Maven的dependency:tree命令快速排查:
# 检查foo服务是否依赖moduleA mvn dependency:tree -f services/foo/pom.xml | grep "moduleA"
如果输出包含moduleA的坐标,就说明这个服务依赖该模块。
步骤2:编写Shell脚本自动筛选受影响的服务
在GitHub Actions中,我们可以写一个脚本,自动遍历所有服务,检查哪些服务依赖了变更的lib模块,然后收集这些服务的路径:
#!/bin/bash # 1. 确定变更的lib模块(从Git变更文件中提取所属的lib模块) CHANGED_LIB=$(git diff --name-only HEAD^ HEAD | grep -E "^lib/" | cut -d'/' -f2 | uniq) # 2. 初始化存储受影响服务的数组 SERVICES_TO_BUILD=() # 3. 遍历所有服务,检查是否依赖变更的lib模块 for SERVICE in services/*; do if [ -d "$SERVICE" ]; then # 检查当前服务是否依赖目标lib模块 if mvn dependency:tree -f "$SERVICE/pom.xml" | grep -q "$CHANGED_LIB"; then SERVICES_TO_BUILD+=("$SERVICE") fi fi done # 4. 将结果输出到GitHub环境变量,供后续步骤使用 echo "SERVICES_TO_BUILD=${SERVICES_TO_BUILD[*]}" >> $GITHUB_ENV
步骤3:在GitHub Actions中配置构建流程
把脚本集成到你的Workflow里,先构建变更的lib模块,再构建受影响的服务:
name: Deploy Affected Services on: push: paths: - 'lib/**' - 'services/**' jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 2 # 需要拉取上一个commit来做diff - name: Set up JDK uses: actions/setup-java@v4 with: java-version: '17' # 替换成你的Java版本 distribution: 'temurin' - name: Determine changed lib module and affected services run: | # 上面的Shell脚本内容 - name: Build changed lib module first run: | mvn clean install -Dmaven.clean.failOnError=false -f lib/${{ env.CHANGED_LIB }}/pom.xml - name: Build and deploy affected services run: | for SERVICE in ${{ env.SERVICES_TO_BUILD }}; do # 构建当前服务的WAR包 mvn clean install -Dmaven.clean.failOnError=false -f "$SERVICE/pom.xml" # 这里添加部署到AWS的命令,比如上传WAR包到S3或者部署到ECS/Beanstalk # aws s3 cp target/*.war s3://your-bucket/path/ # 或者其他AWS部署命令 done
思路二:使用Maven Reactor的依赖追踪(进阶)
Maven的Reactor有个-amd参数(--also-make-dependents),可以构建指定模块的所有依赖模块。不过这个参数是“正向”的——如果你指定构建moduleA,它会同时构建所有依赖moduleA的模块。但需要先确认你的Monorepo根目录有一个父pom.xml(把所有lib和services模块都声明为子模块),这样Maven才能识别完整的依赖树。
比如根目录的pom.xml可以这样配置:
<modules> <module>lib/moduleA</module> <module>lib/moduleB</module> <module>services/foo</module> <module>services/bar</module> <module>services/gamma</module> </modules>
然后当moduleA变更时,直接运行:
mvn clean install -Dmaven.clean.failOnError=false -pl lib/moduleA -amd
这个命令会自动构建moduleA和所有依赖它的服务(foo、gamma),省去手动筛选的步骤。不过这个方案要求根目录有统一的父pom,适合已经规范了pom结构的项目。
思路三:引入Monorepo专用工具(大规模项目)
如果你的项目规模越来越大,服务和模块越来越多,可以考虑用专门的Monorepo构建工具,比如Turborepo(虽然常用于前端,但也支持Java项目)。它可以自动缓存构建结果,智能追踪依赖关系,当某个模块变更时,只构建受影响的模块。你只需要在根目录配置turbo.json,定义每个模块的构建命令和依赖关系即可。
内容的提问来源于stack exchange,提问作者boomchickawawa

