Skip to content

fix: honor project qualifiers and repository remotes when resolving issue forks - #372

Open
mglaman wants to merge 4 commits into
mainfrom
work-item-fork-resolution
Open

fix: honor project qualifiers and repository remotes when resolving issue forks#372
mglaman wants to merge 4 commits into
mainfrom
work-item-fork-resolution

Conversation

@mglaman

@mglaman mglaman commented Aug 28, 2026

Copy link
Copy Markdown
Owner

What changed

issue:setup-remote, issue:checkout, and issue:get-fork now resolve the project that owns an issue fork through a new IssueProjectResolver instead of calling Client::getNode() directly. An explicit qualifier (campaign#3615648, project/campaign#3615648, or a work-item URL) skips the Drupal.org lookup entirely. Inside a checkout whose remote points at project/<name>, that project is used for bare ids and a Drupal.org node that names a different project is a hard error. The resolver never returns an empty machine name, so git never sees a -<nid> remote again.

Builds on #370 — merge that first; this PR's diff shrinks to the resolver once it lands.

New helpers: src/Api/IssueProjectResolver.php, ProjectRemote::detect() / fromRemotes() on #370's src/Api/ProjectRemote.php, and WorkItemRef::projectMachineName().

Why

Closes #359.

Projects that migrated to GitLab work items keep issuing iids from the same number range as Drupal.org's global node sequence. api-d7/node/<id> either answers with a forwarding stub (now rejected by #368) or with a live, unrelated project_issue such as node 3615648 (sdx), which built issue/sdx-3615648 for a campaign work item. Passing campaign#3615648 did not help because the qualifier was ignored and the node lookup ran anyway.

How

Resolution order in IssueProjectResolver::resolve():

  1. Explicit project qualifier from the nid argument. No Drupal.org request.
  2. Project machine name from the repository's remotes (origin first, then any remote matching git.drupal.org:project/<name>.git or https://git.drupalcode.org/project/<name>.git). The Drupal.org node is still fetched: a missing or non-issue node falls back to the repository project, a matching project confirms it, and a different project throws a collision error that names both campaign#<nid> and <other>#<nid>.
  3. Drupal.org node lookup. An empty machine name or a missing node throws with the hint to pass project#<nid>.

Only issue:setup-remote and issue:checkout pass the repository project (they mutate the repository). issue:get-fork is read-only, can run outside a checkout, and honors the qualifier only.

Testing

$ vendor/bin/phpcs src tests && vendor/bin/phpstan analyse src && vendor/bin/phpunit
OK (201 tests, 602 assertions)

$ php drupalorg issue:get-fork campaign#3615648
Remote name: campaign-3615648
SSH URL:     git@git.drupal.org:issue/campaign-3615648.git
GitLab path: issue/campaign-3615648
Branches:
  3615648-issue-branch
  3615648-work-item-migration-docs
  ...

# With HTTPS blocked, the qualifier still resolves (no Drupal.org request); a bare id does not.
$ HTTPS_PROXY=http://127.0.0.1:9 php drupalorg issue:get-fork campaign#3615648
Remote name: campaign-3615648
$ HTTPS_PROXY=http://127.0.0.1:9 php drupalorg issue:get-fork 3615648
cURL error 7: Failed to connect to www.drupal.org port 443 ...

Inside a throwaway git clone --depth 1 https://git.drupalcode.org/project/campaign.git:

$ drupalorg issue:setup-remote campaign#3615648
Remote campaign-3615648 added.
$ git remote -v | grep 3615648
campaign-3615648	git@git.drupal.org:issue/campaign-3615648.git (fetch)

$ drupalorg issue:setup-remote 3615648
Issue 3615648 belongs to project "sdx" on Drupal.org, but this repository is project "campaign".
Pass campaign#3615648 for the GitLab work item or sdx#3615648 for the Drupal.org issue.

$ drupalorg issue:setup-remote 3615635
Remote campaign-3615635 added.

🤖 Generated with Claude Code

mglaman and others added 4 commits August 28, 2026 11:21
…tted

ProjectCommandBase checked InputInterface::hasArgument('project'), which only reports whether the argument is defined on the command, not whether the user passed it. It was always true, so the git remote fallback never ran and the null argument became an empty project name. The parsing now lives in ProjectRemote so it can be unit tested against SSH, HTTPS, and .git-less remote forms, and a missing derivation reports a clear error.

Closes #346

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ssue forks

GitLab work-item ids on migrated projects share the number space with Drupal.org node ids, so a bare id can resolve to an unrelated project and build the wrong fork path. IssueProjectResolver now prefers an explicit project qualifier, then the project/<name> remote of the current repository, and only then the Drupal.org node lookup, failing loudly when the node and the repository disagree. A qualifier such as campaign#3615648 no longer triggers any Drupal.org request.

Closes #359

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops the duplicate URL parser in src/Api/Git and moves fromRemotes() and detect() onto ProjectRemote so a single parser handles project remotes. Callers read ->machineName from the value object.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

issue:setup-remote builds invalid remote name for GitLab work-item ids

1 participant