基于Monorepo目录变更触发对应Dockerfile构建工作流
Monorepo按需构建Docker镜像的实现方案
核心思路
通过Git的文件变更对比能力,识别本次提交(push或PR)中包含Dockerfile的改动文件夹,仅针对这些文件夹执行镜像构建流程,避免无意义的全量构建。
GitHub Actions实现
拉取完整代码历史
使用actions/checkout时设置fetch-depth: 0,确保能获取完整提交记录用于对比:- uses: actions/checkout@v4 with: fetch-depth: 0检测变更文件夹
根据事件类型(PR/push)执行不同的Git diff命令,筛选出包含Dockerfile的改动文件夹:if [ "${{ github.event_name }}" = "pull_request" ]; then DIFF_CMD="git diff --name-only ${{ github.base_ref }} ${{ github.head_ref }}" else DIFF_CMD="git diff --name-only HEAD^ HEAD" fi # 提取Dockerfile所在文件夹并去重 CHANGED_DIRS=$($DIFF_CMD | grep -E 'Dockerfile$' | xargs dirname | sort | uniq | jq -R . | jq -s .) echo "changed_dirs=$CHANGED_DIRS" >> $GITHUB_OUTPUT动态生成构建任务
将变更文件夹列表转为JSON数组,作为矩阵参数触发多任务并行构建:jobs: detect-changes: runs-on: ubuntu-latest outputs: changed_dirs: ${{ steps.set-dirs.outputs.changed_dirs }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - id: set-dirs run: | # 上面的检测命令 build-images: needs: detect-changes if: ${{ needs.detect-changes.outputs.changed_dirs != '[]' }} runs-on: ubuntu-latest strategy: matrix: dir: ${{ fromJSON(needs.detect-changes.outputs.changed_dirs) }} steps: - uses: actions/checkout@v4 - name: Build image for ${{ matrix.dir }} run: | cd ${{ matrix.dir }} docker build -t my-image-${{ matrix.dir }} . # 可选:添加镜像推送步骤
GitLab CI实现
获取变更文件列表
根据CI环境变量判断是PR还是push场景,执行Git diff筛选目标文件夹:if [ -n "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ]; then DIFF_CMD="git diff --name-only $CI_MERGE_REQUEST_TARGET_BRANCH_NAME...$CI_COMMIT_BRANCH" else DIFF_CMD="git diff --name-only HEAD^ HEAD" fi CHANGED_DIRS=$($DIFF_CMD | grep -E 'Dockerfile$' | xargs dirname | sort | uniq) echo "CHANGED_DIRS=$CHANGED_DIRS" > changed_dirs.env传递变量并执行构建
通过dotenv制品传递变更列表,在构建阶段循环处理每个文件夹:stages: - detect-changes - build detect-changes: stage: detect-changes script: # 上面的检测命令 artifacts: reports: dotenv: changed_dirs.env build: stage: build script: - | for dir in $CHANGED_DIRS; do echo "Building image for $dir" cd $dir docker build -t my-image-$dir . # 可选:添加镜像推送步骤 done only: variables: - $CHANGED_DIRS != ""
Jenkins实现
- 检测变更文件夹
在流水线中通过Groovy脚本处理Git变更,筛选出目标文件夹:pipeline { agent any stages { stage('Detect Changes') { steps { script { def changedFiles if (env.CHANGE_ID) { // PR场景对比分支 changedFiles = sh(script: "git diff --name-only origin/${env.CHANGE_TARGET} origin/${env.CHANGE_BRANCH}", returnStdout: true).trim() } else { // push场景对比提交 changedFiles = sh(script: "git diff --name-only HEAD^ HEAD", returnStdout: true).trim() } def changedDirs = changedFiles.split('\n').findAll { it.endsWith('Dockerfile') }.collect { new File(it).parent }.unique() env.CHANGED_DIRS = changedDirs.join(' ') } } } stage('Build Images') { when { expression { env.CHANGED_DIRS != '' } } steps { script { env.CHANGED_DIRS.split(' ').each { dir -> echo "Building image for ${dir}" dir(dir) { sh "docker build -t my-image-${dir} ." // 可选:添加镜像推送步骤 } } } } } } }
通用注意事项
- 确保Git仓库能获取完整提交历史,否则diff命令无法正确对比变更;
- 若需要文件夹内任何文件变更都触发构建,可修改筛选规则,比如用
grep -E '^services/api/'匹配整个文件夹的变更; - 对于嵌套目录结构,
dirname命令会自动获取Dockerfile所在的直接父目录,无需额外处理。
内容的提问来源于stack exchange,提问作者Memphis Meng
相关产品推荐
相关产品推荐

