5.2 KiB
Plan: Refine Propagation Workflow to Enforce Strict Hierarchy (Pittsburgh Model)
1. Introduction
This plan outlines the update of the .github/workflows/propagate-changes.yml workflow. The goal is to enforce a strict hierarchical propagation strategy ("The Pittsburgh Model") where changes flow downstream from main to development, and then from development to leaf branches (feature/*, hotfix/*). This explicitly prevents "loop-backs" and direct updates from main to feature branches.
2. Methodology & Rules
The Pittsburgh Model (Strict Hierarchy):
-
Rule 1 (The Ohio River):
mainONLY propagates todevelopment.- Logic:
mainis the stable release branch. Changes here (hotfixes, releases) must flow intodevelopmentfirst. - Constraint:
mainmust NEVER propagate directly tofeature/*orhotfix/*.
- Logic:
-
Rule 2 (The Point):
developmentis the ONLY branch that propagates to leaf branches.- Logic:
developmentis the source of truth for active work. It aggregatesmainchanges plus ongoing development. - Targets:
feature/*andhotfix/*.
- Logic:
-
Rule 3 (Loop Prevention): Determine the "source" PR to prevent re-propagation.
- Problem: When
feature/Amerges intodevelopment, we must not open a PR fromdevelopmentback tofeature/A. - Mechanism: Identify the source branch of the commit triggering the workflow and exclude it from targets.
- Problem: When
3. Workflow Design
3.1. Branching Strategy Logic
| Trigger Branch | Source | Target(s) | Logic |
|---|---|---|---|
main |
main |
development |
Create PR main -> development |
development |
development |
feature/*, hotfix/* |
Create PR development -> [leaf] (Excluding changes source) |
feature/* |
- | - | No action (Triggers CI only) |
hotfix/* |
- | - | No action (Triggers CI only) |
3.2. Logic Updates Needed
A. Strict Main Enforcement
- Current logic likely does this, but we will explicitly verify
if (currentBranch === 'main') { propagate('development'); }and nothing else.
B. Development Distribution & Hotfix Inclusion
- Update the branch listing logic to find both
feature/*ANDhotfix/*branches. - Current code only looks for
feature/*.
C. Loop Prevention (The "Source Branch" Check)
- Trigger: Script runs on push to
development. - Action:
- Retrieve the Pull Request associated with the commit sha using the GitHub API.
- If a merged PR exists for this commit, extract the source branch name (
head.ref). - Exclude this source branch from the list of propagation targets.
3.3. Technical Implementation Details
- File:
.github/workflows/propagate-changes.yml - Action:
actions/github-script
Pseudo-Code Update:
// 1. Get current branch
const branch = context.ref.replace('refs/heads/', '');
// 2. Rule 1: Main -> Development
if (branch === 'main') {
await createPR('main', 'development');
return;
}
// 3. Rule 2: Development -> Leafs
if (branch === 'development') {
// 3a. Identify Source (Rule 3 Loop Prevention)
// NOTE: This runs on push, so context.sha is the commit sha.
let excludedBranch = null;
try {
const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.repo.owner,
repo: context.repo.repo,
commit_sha: context.sha,
});
// Find the PR that was merged
const mergedPr = prs.data.find(pr => pr.merged_at);
if (mergedPr) {
excludedBranch = mergedPr.head.ref;
core.info(`Commit derived from merged PR #${mergedPr.number} (Source: ${excludedBranch}). Skipping back-propagation.`);
}
} catch (e) {
core.info('Could not check associated PRs: ' + e.message);
}
// 3b. Find Targets
const branches = await github.paginate(github.rest.repos.listBranches, {
owner: context.repo.owner,
repo: context.repo.repo,
});
const targets = branches
.map(b => b.name)
.filter(b => (b.startsWith('feature/') || b.startsWith('hotfix/')))
.filter(b => b !== excludedBranch); // Exclude source
// 3c. Propagate
core.info(`Propagating to ${targets.length} branches: ${targets.join(', ')}`);
for (const target of targets) {
await createPR('development', target);
}
}
4. Implementation Steps
- Refactor
mainlogic: Ensure it returns immediately after propagating todevelopmentto prevent any fall-through. - Update
developmentlogic:- Add
hotfix/to the filter regex. - Implement the
listPullRequestsAssociatedWithCommitcall to identify the exclusion. - Apply the exclusion to the target list.
- Add
- Verify Hierarchy:
- Confirm no path exists for
main->feature/*.
- Confirm no path exists for
5. Acceptance Criteria
- Push to
maincreates a PR ONLY todevelopment. - Push to
developmentcreates PRs to all downstreamfeature/*ANDhotfix/*branches. - Push to
development(caused by merge offeature/A) does NOT create a PR back tofeature/A. - A hotfix merged to
mainflows:main->development, thendevelopment->hotfix/active-work(if any exist).