如何解决Github Actions矩阵中多环境Git推送冲突问题
解决GitHub Actions矩阵作业Git推送冲突问题
问题根源
你的矩阵作业并行执行时,每个作业启动时拉取的是仓库初始状态,修改对应环境文件后直接提交推送。当第一个作业成功更新远程分支后,后续作业的本地分支已落后于远程版本,此时推送就会触发cannot lock ref冲突——远程分支的HEAD已不是作业拉取时的版本。随机sleep仅能降低冲突概率,无法从根本上解决问题。
解决方案
方案1:将矩阵作业改为串行执行
通过GitHub Actions的concurrency配置,强制同工作流内的矩阵任务按顺序执行,彻底避免并行推送冲突。
修改工作流的作业配置,添加concurrency字段:
jobs: shutdown-envs: runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false strategy: matrix: unprotected_environment: ${{ steps.generate-matrix.outputs.environments }} steps: # 你的现有步骤...
上述配置会让整个工作流的矩阵任务串行执行,若需按环境分组串行,可在group后追加-${{ matrix.unprotected_environment }}。
方案2:推送前拉取最新代码并合并
在git push前执行拉取操作,确保本地分支同步远程最新状态后再推送。由于每个作业修改的是不同环境的独立目录,合并时不会出现内容冲突。
修改你的git-push步骤脚本:
set -ex -o pipefail git config --global user.name github-actions-workflows git config --global user.email devops+github-actions-workflows@company env_name=${{ matrix.unprotected_environment }} # 修改对应环境文件 cd ${{ github.workspace }}/${{ env.qa_processing_tg_dir }}/${{ env.qa_envs_region}}/${env_name} git add ecs-service git add autoscaling/product git add ecs-capacity-provider/service cd ${{ github.workspace }}/${{ env.qa_commercial_tg_dir }}/${{ env.qa_envs_region}}/${env_name} git add ecs-service git add ecs-capacity-provider/service git commit -m "Shutting down for the night environment ${env_name}" # 推送前拉取最新代码,用rebase保持提交历史整洁 git pull --rebase origin master # 若rebase失败(理论上不会,因为修改的是独立目录),可改用普通pull # git pull origin master git push export COMMIT_ID=$(git rev-parse HEAD) echo "::set-output name=commit_id::${COMMIT_ID}" echo "COMMIT_ID: ${COMMIT_ID}"
方案3:批量修改所有环境,单次提交推送
放弃矩阵并行,一次性收集所有需停机的环境,批量修改文件后单次提交推送,从根源上消除冲突。
示例脚本:
set -ex -o pipefail git config --global user.name github-actions-workflows git config --global user.email devops+github-actions-workflows@company # 假设已通过前置步骤获取需停机的环境列表 envs=(${{ steps.generate-matrix.outputs.environments }}) for env_name in "${envs[@]}"; do # 修改processing目录下的环境文件 cd ${{ github.workspace }}/${{ env.qa_processing_tg_dir }}/${{ env.qa_envs_region}}/${env_name} git add ecs-service git add autoscaling/product git add ecs-capacity-provider/service # 修改commercial目录下的环境文件 cd ${{ github.workspace }}/${{ env.qa_commercial_tg_dir }}/${{ env.qa_envs_region}}/${env_name} git add ecs-service git add ecs-capacity-provider/service done # 一次性提交所有变更 git commit -m "Shutting down QA environments for the night: ${envs[*]}" git push export COMMIT_ID=$(git rev-parse HEAD) echo "::set-output name=commit_id::${COMMIT_ID}"
方案选择建议
- 若需保留并行效率(比如每个环境的Terragrunt执行需单独运行),优先选方案2,既保留并行优势,又能避免冲突。
- 若环境处理逻辑简单,无需并行,方案3更简洁,提交历史也更清晰。
- 方案1适合必须串行处理的场景,但会增加整体工作流的执行时间。
内容的提问来源于stack exchange,提问作者Itai Ganot
相关产品推荐
相关产品推荐

