Skip to content

GitHub PR stages can't use an existing fork-network sibling (e.g. silug/puppet-selinux for pupmod-voxpupuli-selinux) #83

Description

@silug

Problem

GitHubPRForker#user_fork_of_repo finds "the user's fork" by listing the direct forks of the target repo and matching on owner. That works for normal simp/* repos, but not for repos that are themselves forks, like simp/pupmod-voxpupuli-selinux (a fork of voxpupuli/puppet-selinux, default branch simp-master).

Concretely: silug/puppet-selinux is a fork of the network root voxpupuli/puppet-selinux, which makes it a sibling of simp/pupmod-voxpupuli-selinux, not a direct fork of it — so user_fork_of_repo('simp/pupmod-voxpupuli-selinux', 'silug') returns nil even though the user has a perfectly usable fork in the network.

From there, ensure_fork POSTs /repos/simp/pupmod-voxpupuli-selinux/forks and one of two things happens:

  1. GitHub creates a brand-new direct fork (silug/pupmod-voxpupuli-selinux — the modern fork-a-fork behavior when the name is free). The pipeline then works end-to-end, but through a redundant second fork instead of the user's existing one. There's also a first-run race: fork creation is async, and with github_api_delay_seconds: 1 the push/PR stages can hit the fork before it exists (heals on re-run).
  2. GitHub returns the existing sibling fork (the legacy one-fork-per-network dedup). The push then lands in silug/puppet-selinux, but ensure_github_pr fails: existing_pr dereferences the nil fork lookup (repo_fork.full_nameNoMethodError) when the target repo has open PRs, and create_pr raises ERROR: no fork of '...' found for user ... otherwise.

Either way, the sync never uses the user's existing fork of the upstream, and in case 2 the PR stage crashes.

Proposed fix

Make user_fork_of_repo fork-network-aware. After the direct-forks check misses:

  1. Resolve the network root: upstream_repo.fork ? upstream_repo.source : upstream_repo (e.g. voxpupuli/puppet-selinux).
  2. Probe the user's candidate repos by name — #{fork_user}/#{upstream_repo.name} and #{fork_user}/#{root.name} (e.g. silug/puppet-selinux).
  3. Accept a candidate iff it's a fork whose source is the same network root.

With that, ensure_fork returns the existing sibling fork without creating anything, the feature branch is pushed there, and create_pull_request opens the PR with head user:branch against the simp repo's base branch — GitHub allows PRs between any repos in the same fork network, and the base branch is already correct (the dynamic inventory records the repo's real default branch, simp-master).

existing_pr should also be hardened against a nil fork lookup regardless (it currently NoMethodErrors instead of reporting a useful failure).

Context

Found while rolling out the 20260811-reference-md session (#58): pupmod-voxpupuli-selinux is in the dynamic inventory via the include_forks allow-list, so its GitHub stages will exercise this path as soon as the repo-side CI problems are sorted out.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions