<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[下游触发分支规则（FANOUT_BRANCH 单原则）]]></title><description><![CDATA[<h1>下游触发分支规则（FANOUT_BRANCH 单原则）</h1>
<h2>1. 背景</h2>
<p dir="auto">在多仓库、多级触发（A -&gt; B -&gt; C）的场景中，如果每一跳都“按当前项目的运行分支”去触发下游，很容易发生分支漂移。</p>
<p dir="auto">本规则的目标：</p>
<ul>
<li>200+ 项目尽量不改各自的 <code>.gitlab-ci.yml</code></li>
<li>不使用 GitLab API Token</li>
<li>触发链路稳定、可预测</li>
</ul>
<h2>2. 唯一原则：FANOUT_BRANCH 贯穿</h2>
<p dir="auto">我们定义一个变量：<code>FANOUT_BRANCH</code>，表示“本次触发链路从最上游开始的分支”。</p>
<p dir="auto">生成/继承规则：</p>
<ul>
<li>若上游已传入 <code>FANOUT_BRANCH</code>，则下游沿用该值</li>
<li>否则默认 <code>FANOUT_BRANCH = CI_COMMIT_BRANCH</code></li>
</ul>
<p dir="auto">每次触发下游时，都必须继续传递 <code>FANOUT_BRANCH</code>，从而保证多级触发不会漂移。</p>
<h2>3. 下游触发分支的决定规则</h2>
<h3>3.1 显式指定优先（最重要）</h3>
<p dir="auto">如果项目在 <code>variables:</code> 中显式写了 <code>DOWNSTREAM_BRANCH</code>，例如：</p>
<pre><code class="language-yaml">variables:
  DOWNSTREAM_PROJECT: platform/deploy/blazor-app
  DOWNSTREAM_BRANCH: master
</code></pre>
<p dir="auto">则表现为：</p>
<ul>
<li>永远触发下游的 <code>master</code> 分支</li>
<li>即使当前上游在 <code>feature/LT-49</code>、<code>bugfix/*</code> 等分支上运行，也不会触发同名分支</li>
<li><code>FANOUT_BRANCH</code> 仍会被传递到下游，但仅用于链路追踪/后续再触发，不参与本次分支选择</li>
</ul>
<h3>3.2 未显式指定时的默认行为</h3>
<p dir="auto">如果未显式设置 <code>DOWNSTREAM_BRANCH</code>，则默认：</p>
<ul>
<li><code>DOWNSTREAM_BRANCH = FANOUT_BRANCH</code></li>
</ul>
<p dir="auto">即：上游在 <code>feature/LT-49</code> 运行时触发下游 <code>feature/LT-49</code>；上游在 <code>master</code> 运行时触发下游 <code>master</code>。</p>
<h2>4. 关于 master/main</h2>
<p dir="auto">在“不使用 Token、不调用 API”的前提下，我们无法在触发前自动判断下游默认分支是 <code>master</code> 还是 <code>main</code>。</p>
<p dir="auto">治理建议：</p>
<ul>
<li>公司内尽量统一主干命名为 <code>master</code></li>
<li>若少数仓库历史上使用 <code>main</code>，建议一次性调整为 <code>master</code>，之后无需在 200+ 项目侧维护差异</li>
</ul>
<h2>5. 适用范围</h2>
<ul>
<li>NuGet / IIS / Docker 等流水线，只要通过 common-ci 的 downstream trigger 模板触发下游，本规则均适用。</li>
</ul>
<h2>6. 常见问题（FAQ）</h2>
<h3>6.1 上游是 develop（或 feature/*），下游只有 master，会怎么样？</h3>
<p dir="auto">在未显式配置 <code>DOWNSTREAM_BRANCH</code> 的情况下，默认行为是：</p>
<ul>
<li><code>DOWNSTREAM_BRANCH = FANOUT_BRANCH</code></li>
<li><code>FANOUT_BRANCH</code> 默认等于当前上游的 <code>CI_COMMIT_BRANCH</code></li>
</ul>
<p dir="auto">因此：</p>
<ul>
<li>上游在 <code>develop</code> 跑时，会尝试触发下游的 <code>develop</code></li>
<li>若下游仓库没有 <code>develop</code> 分支（只有 <code>master</code>），则 <code>trigger:</code> 作业会失败</li>
<li>因为我们使用 <code>strategy: depend</code>，上游流水线会在“触发下游”阶段失败</li>
</ul>
<p dir="auto">治理方式（二选一）：</p>
<ul>
<li>方式 A（推荐，稳定）
<ul>
<li>对“只有主干分支”的下游仓库，在上游项目显式配置 <code>DOWNSTREAM_BRANCH: master</code></li>
<li>这样不管上游在哪个分支跑，下游都只触发 <code>master</code></li>
</ul>
</li>
<li>方式 B（统一分支体系）
<ul>
<li>在下游仓库创建对应分支（例如创建 <code>develop</code>），保证分支存在</li>
<li>适用于需要严格“同分支联动”的仓库</li>
</ul>
</li>
</ul>
]]></description><link>https://talk.loda.net/topic/43/下游触发分支规则-fanout_branch-单原则</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 08:18:02 GMT</lastBuildDate><atom:link href="https://talk.loda.net/topic/43.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 08 Jan 2026 10:53:48 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to 下游触发分支规则（FANOUT_BRANCH 单原则） on Fri, 09 Jan 2026 17:37:10 GMT]]></title><description><![CDATA[<h1>下游触发分支选择：现象、根因与最终方案（仅 develop/master）</h1>
<h2>背景与目标</h2>
<p dir="auto">我们在 NuGet 组件发布流水线中使用下游触发（trigger downstream pipeline）来联动发布/同步多个项目。</p>
<p dir="auto">核心目标是：</p>
<ul>
<li>让下游触发分支选择<strong>可预测、可维护、可审计</strong>。</li>
<li>避免因为 GitLab Token 权限差异导致的“误判分支不存在”“偶发失败”等不稳定行为。</li>
<li>适配跨项目、跨实例同步（common-ci 同步到多个 GitLab 实例）的场景。</li>
</ul>
<h2>现象（问题表现）</h2>
<p dir="auto">在 <code>trigger_downstream_pipeline</code> 作业中出现以下典型问题：</p>
<ul>
<li>
<p dir="auto"><strong>现象 A：作业过早触发</strong></p>
<ul>
<li>流水线还未完成 push/update 等步骤，下游就被触发。</li>
</ul>
</li>
<li>
<p dir="auto"><strong>现象 B：下游明明存在分支，却提示分支不存在</strong></p>
<ul>
<li>例如下游有 <code>develop</code>，但 job 日志显示“分支不存在”。</li>
</ul>
</li>
<li>
<p dir="auto"><strong>现象 C：HTTP 401 Unauthorized</strong></p>
<ul>
<li>使用 <code>CI_JOB_TOKEN</code> 调用 GitLab API 触发下游 pipeline 时返回 401。</li>
<li>说明默认情况下 <code>CI_JOB_TOKEN</code> <strong>不具备跨项目触发</strong>的权限。</li>
</ul>
</li>
<li>
<p dir="auto"><strong>现象 D：Reference not found / downstream pipeline can not be created</strong></p>
<ul>
<li>trigger 使用的 <code>branch</code> 变量为空或未按预期注入，导致 GitLab 认为 ref 不存在。</li>
</ul>
</li>
</ul>
<h2>根因分析</h2>
<h3>1. GitLab 原生 <code>trigger:</code> 的能力边界</h3>
<ul>
<li>原生 <code>trigger:</code> 只能触发一个确定的 <code>project + branch</code>。</li>
<li>它<strong>不会</strong>做“分支不存在则 fallback”这种动态选择。</li>
</ul>
<p dir="auto">因此，如果要做“某项目没有分支就改触发另一个分支、后续再恢复原分支”的行为，必须：</p>
<ul>
<li>预先知道下游是否存在该分支（需要 API），或</li>
<li>直接尝试触发并解析失败原因（需要 API），或</li>
<li>人工/静态配置规则（每项目一个开关），或</li>
<li>制定组织规范（所有项目都具备统一分支）。</li>
</ul>
<h3>2. 跨项目权限与 Token 复杂度</h3>
<p dir="auto">如果使用 API 触发下游（<code>/api/v4/projects/:id/pipeline</code>）：</p>
<ul>
<li><code>CI_JOB_TOKEN</code> 在多数场景下对跨项目 API <strong>权限不足</strong>（401）。</li>
<li>使用 <code>GITLAB_API_TOKEN</code>（Project/Group/Personal Access Token）可以解决，但引入：
<ul>
<li>Token 分发与轮换成本</li>
<li>安全风险与合规审计成本</li>
<li>多实例同步时的配置复杂度</li>
</ul>
</li>
</ul>
<h3>3. 变量注入顺序与 artifacts 依赖</h3>
<p dir="auto">我们使用 <code>dotenv</code> artifacts 在 setup 阶段写入变量（如 <code>FANOUT_BRANCH</code>、<code>DOWNSTREAM_BRANCH</code>）。</p>
<p dir="auto">如果 trigger job 没有正确 <code>needs</code> 对应的 setup job（且 <code>artifacts: true</code>），就可能拿不到变量，导致：</p>
<ul>
<li><code>$DOWNSTREAM_BRANCH</code> 为空</li>
<li>trigger 触发时报 <code>Reference not found</code></li>
</ul>
<h2>方案对比（做过的选择）</h2>
<h3>方案 1：智能分支（API + fallback）</h3>
<ul>
<li>思路：优先触发“根分支”，失败再 fallback 到 <code>develop/master</code>。</li>
<li>优点：理论上能实现更复杂的链路行为。</li>
<li>缺点：
<ul>
<li>需要跨项目 API 权限，<code>CI_JOB_TOKEN</code> 往往不够（401）</li>
<li>Token 配置与安全治理成本高</li>
<li>失败原因多样（401/404/网络/权限策略），可维护性差</li>
</ul>
</li>
</ul>
<h3>方案 2：自建服务（分支查询/代理触发）</h3>
<ul>
<li>思路：将 GitLab token 收拢到内部服务，CI 只调用内部服务。</li>
<li>优点：可控、安全面可收敛。</li>
<li>缺点：引入额外系统：
<ul>
<li>部署与运维成本</li>
<li>高可用/限流/审计等需求</li>
<li>增加链路复杂度</li>
</ul>
</li>
</ul>
<h3>方案 3（最终采用）：最简单方案（无 API，仅 develop/master）</h3>
<p dir="auto"><strong>结论：采用方案 3。</strong></p>
<ul>
<li>只在 <code>develop/master</code> 两者之间选择分支。</li>
<li>在 setup 阶段一次性确定“触发链根分支”，并通过 <code>dotenv</code> 跨项目传递。</li>
<li>trigger 使用 GitLab 原生 <code>trigger:</code>，不使用 API，不依赖 token。</li>
</ul>
<p dir="auto"><strong>该方案的关键前提：所有参与链路的项目必须同时存在 <code>develop</code> 和 <code>master</code> 分支。</strong></p>
<p dir="auto">这项前提换来的是：</p>
<ul>
<li>不需要任何额外权限与 token</li>
<li>行为稳定、可预测</li>
<li>维护成本最低</li>
</ul>
<h2>最终方案设计</h2>
<h3>1. 根分支归一化（setup 阶段）</h3>
<p dir="auto">在 <code>setup_version</code>（<code>.version_setup</code>）阶段：</p>
<ul>
<li>读取触发源：<code>FANOUT_BRANCH</code>（上游传递）或 <code>CI_COMMIT_BRANCH</code>（当前）</li>
<li>归一化为：
<ul>
<li><code>master/main/hotfix/*</code> → <code>master</code></li>
<li>其他 → <code>develop</code></li>
</ul>
</li>
<li>写入 dotenv：
<ul>
<li><code>FANOUT_BRANCH=&lt;develop|master&gt;</code></li>
<li><code>DOWNSTREAM_BRANCH=&lt;develop|master&gt;</code>（如果用户未显式指定）</li>
</ul>
</li>
</ul>
<h3>2. 下游触发（trigger 阶段）</h3>
<p dir="auto">在 <code>.trigger-downstream</code> 中：</p>
<ul>
<li>使用 GitLab 原生：
<ul>
<li><code>trigger.project: $DOWNSTREAM_PROJECT</code></li>
<li><code>trigger.branch: $DOWNSTREAM_BRANCH</code></li>
</ul>
</li>
<li>同时向下游继续传递：
<ul>
<li><code>FANOUT_BRANCH</code></li>
<li><code>FANOUT_FROM</code></li>
<li><code>STOP_FANOUT</code></li>
</ul>
</li>
</ul>
<p dir="auto">并通过 <code>needs</code> 确保拿到 setup 阶段的 dotenv：</p>
<ul>
<li><code>needs: setup_global_var (artifacts: true)</code></li>
</ul>
<h2>代码改动点（common-ci）</h2>
<ul>
<li>
<p dir="auto"><code>shared/version-setup.yml</code></p>
<ul>
<li>在 <code>version.env</code> 中写入归一化后的 <code>FANOUT_BRANCH</code>，并默认填充 <code>DOWNSTREAM_BRANCH</code>。</li>
</ul>
</li>
<li>
<p dir="auto"><code>nuget/7-trigger-downstream.yml</code></p>
<ul>
<li>恢复为原生 <code>trigger:</code></li>
<li><code>needs</code> 增加 <code>setup_global_var</code>（带 <code>artifacts: true</code>）以确保 <code>DOWNSTREAM_BRANCH</code> 可用</li>
</ul>
</li>
</ul>
<h2>使用与约束</h2>
<ul>
<li>
<p dir="auto"><strong>组织规范（必须执行）</strong></p>
<ul>
<li>所有参与链路的仓库必须同时存在 <code>develop</code> 与 <code>master</code> 分支。</li>
</ul>
</li>
<li>
<p dir="auto"><strong>项目侧配置（可选）</strong></p>
<ul>
<li>在 <code>.gitlab-ci.yml</code> 中配置：
<ul>
<li><code>DOWNSTREAM_PROJECT</code>：下游项目路径</li>
</ul>
</li>
<li>一般无需配置 <code>DOWNSTREAM_BRANCH</code>，默认由上游归一化规则决定。</li>
</ul>
</li>
</ul>
<h2>常见问题（FAQ）</h2>
<h3>Q1：为什么不继续做“智能分支 + fallback”？</h3>
<p dir="auto">因为需要 API 触发或分支判断，而跨项目 token 权限与安全治理成本过高，且多实例同步场景下不可控因素更多。</p>
<h3>Q2：为什么会出现 Reference not found？</h3>
<p dir="auto">多数情况下是 trigger job 没有拿到 setup 阶段的 dotenv（变量未注入），导致 <code>branch</code> 为空/错误。</p>
<h3>Q3：这个方案还能实现“某项目只有 master，但后续又想回到 develop”吗？</h3>
<p dir="auto">不能。</p>
<p dir="auto">不使用 API 的前提下无法判断下游是否存在某分支，因此无法对“每一跳”做动态选择。</p>
<p dir="auto">要支持这种行为，必须回到 API 方案或引入项目级配置。</p>
]]></description><link>https://talk.loda.net/post/50</link><guid isPermaLink="true">https://talk.loda.net/post/50</guid><dc:creator><![CDATA[admin]]></dc:creator><pubDate>Fri, 09 Jan 2026 17:37:10 GMT</pubDate></item></channel></rss>