基于文件夹变更触发Github Actions构建指定.NET项目Docker镜像
实现按需构建变更项目的Docker镜像
要实现仅构建变更文件夹对应的.NET项目,核心思路是先检测代码变更路径,再动态生成过滤后的构建矩阵(matrix),避免全量执行所有项目的构建任务。以下是具体实现方案和优化建议:
方案一:动态过滤构建矩阵(推荐)
通过修改你的prep任务,先检测变更路径,再过滤原始矩阵中未变更的项目,只输出需要构建的项目矩阵。
1. 修改prep任务,添加变更检测与矩阵过滤
prep: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: 检出代码 uses: actions/checkout@v4 with: fetch-depth: 0 # 必须拉取完整历史,才能对比所有变更 - name: 检测变更的项目路径 id: changed-files uses: tj-actions/changed-files@v41 with: files: | src/ServiceA/** src/ServiceB/** src/ServiceC/** src/Shared/** # 若有共享类库,需加入检测 - name: 生成过滤后的构建矩阵 id: set-matrix run: | # 定义原始的全量项目矩阵(替换成你实际的项目配置) original_matrix='{ "include": [ {"name": "ServiceA", "context": "src/ServiceA", "dockerfile": "src/ServiceA/Dockerfile", "docker-image-name": "${{ env.REGISTRY }}/servicea"}, {"name": "ServiceB", "context": "src/ServiceB", "dockerfile": "src/ServiceB/Dockerfile", "docker-image-name": "${{ env.REGISTRY }}/serviceb"}, {"name": "ServiceC", "context": "src/ServiceC", "dockerfile": "src/ServiceC/Dockerfile", "docker-image-name": "${{ env.REGISTRY }}/servicec"} ] }' # 将变更路径转为数组 changed_paths=(${{ steps.changed-files.outputs.all_changed_files }}) # 标记是否有共享类库变更(若有) shared_changed=false for path in "${changed_paths[@]}"; do if [[ "$path" == "src/Shared"/* ]]; then shared_changed=true break fi done # 处理过滤逻辑:共享类库变更则全量构建,否则仅构建变更项目 if [ "$shared_changed" = true ] || [ ${#changed_paths[@]} -eq 0 ]; then # 无变更或共享类库变更时,使用全量矩阵 filtered_matrix="$original_matrix" else # 过滤出变更路径对应的项目 filtered_include=() for item in $(echo "$original_matrix" | jq -r '.include[] | @base64'); do decoded=$(echo "$item" | base64 --decode) context=$(echo "$decoded" | jq -r '.context') # 检查当前项目路径是否在变更列表中 for path in "${changed_paths[@]}"; do if [[ "$path" == "$context"/* ]]; then filtered_include+=("$decoded") break fi done done filtered_matrix=$(printf '%s\n' "${filtered_include[@]}" | jq -s '{include: .}') fi # 输出过滤后的矩阵 echo "matrix=$filtered_matrix" >> "$GITHUB_OUTPUT"
2. 优化原构建任务
原build-and-push-image任务无需大幅修改,但可以优化依赖还原步骤,仅针对当前项目还原,提升速度:
build-and-push-image: name: "BUILD: ${{ matrix.name }}" runs-on: ubuntu-latest needs: prep strategy: matrix: ${{fromJson(needs.prep.outputs.matrix)}} permissions: contents: read packages: write steps: - name: 检出代码 uses: actions/checkout@v4 - name: 登录容器仓库 uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: 提取Docker元数据(标签、标签) id: meta uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 with: images: ${{ matrix.docker-image-name }} flavor: latest=true tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} - name: 还原当前项目依赖 shell: bash run: dotnet restore ${{ matrix.context }} - name: 构建并推送Docker镜像 uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: context: ${{matrix.context }} file: ${{ matrix.dockerfile }} push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max
方案二:为单个任务添加路径过滤(简单但效率略低)
如果不想修改矩阵生成逻辑,也可以直接在构建任务中添加路径条件,仅当对应项目路径有变更时才执行任务:
build-and-push-image: name: "BUILD: ${{ matrix.name }}" runs-on: ubuntu-latest needs: prep strategy: matrix: ${{fromJson(needs.prep.outputs.matrix)}} permissions: contents: read packages: write # 仅当项目路径有变更或共享类库变更时执行 if: | contains(steps.changed-files.outputs.all_changed_files, matrix.context) || contains(steps.changed-files.outputs.all_changed_files, 'src/Shared') steps: - name: 检出代码 uses: actions/checkout@v4 with: fetch-depth: 0 - name: 检测变更路径 id: changed-files uses: tj-actions/changed-files@v41 with: files: | src/** # 后续步骤同原任务...
注意:此方案会创建所有矩阵任务,但仅执行符合条件的任务,资源利用率略低于动态过滤矩阵方案。
额外优化建议
Dockerfile缓存优化:将
dotnet restore步骤移入Dockerfile,利用Docker层缓存减少重复还原时间:FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY ["ServiceA.csproj", "."] RUN dotnet restore "./ServiceA.csproj" COPY . . RUN dotnet build "ServiceA.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "ServiceA.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "ServiceA.dll"]手动全量构建支持:添加
workflow_dispatch触发方式,允许手动触发全量构建:on: push: branches: [ main ] pull_request: branches: [ main ] workflow_dispatch: inputs: full_build: description: '是否执行全量构建' required: false default: 'false' type: boolean然后在
prep任务的过滤逻辑中添加:if [ "${{ github.event.inputs.full_build }}" = "true" ]; then filtered_matrix="$original_matrix" elif [ "$shared_changed" = true ] || [ ${#changed_paths[@]} -eq 0 ]; then # 原逻辑... fi
内容的提问来源于stack exchange,提问作者Niranjan
相关产品推荐
相关产品推荐

