Skip to content

Fetch issues from gitlab.com as well as github.com - #25

Merged
aaroncoville merged 2 commits into
mainfrom
upstream/gitlab-issues
Aug 26, 2026
Merged

aaroncoville merged 2 commits into
mainfrom
upstream/gitlab-issues

Conversation

@aaroncoville

Copy link
Copy Markdown
Owner

What & why

"Fetch issues" in the Monitor panel ran gh issue list unconditionally. On a
repository hosted anywhere other than GitHub that is not a degraded result, it
is a misleading one: gh reports that the repository points at no known GitHub
host, so a GitLab user is told their project does not exist rather than that the
wrong tool was asked.

The forge is now chosen from the origin remote's host. github.com keeps the
existing gh path unchanged; gitlab.com goes to glab, GitLab's own CLI,
arranged exactly as the GitHub path already is — ask the CLI for JSON, let it
own authentication, store no token. glab must be installed and logged in for
the GitLab path to work, which is the same assumption the gh path has always
made.
No new credential storage or token plumbing is introduced.

Any other host — a self-hosted GitLab, GitHub Enterprise, Bitbucket, or a repo
with no origin — is refused by name rather than guessed at. Pointing gh at a
self-hosted GitLab fails in terms that describe neither the repository nor the
real problem, so the message names the supported hosts instead. It reports the
parsed host and never the remote URL, because a remote can carry a personal
access token and this string is rendered in the app.

The renderer and the IPC channel are untouched: the normalized issue shape is
unchanged, and the button already read "Fetch issues" rather than naming a
forge. Scope is issues only — the separate CI-runs path is GitHub Actions
specific and is deliberately left alone.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Docs
  • Build / CI

Evidence

A terminal capture, since the change is in the main process behind an existing
button whose label and markup do not change.

Before

main runs gh regardless of host. In a checkout whose origin is a
gitlab.com project (https://gitlab.com/gitlab-org/cli.git), that is the exact
command the old listIssues issued:

$ gh issue list --json number,title,body,assignees,labels,url,state --limit 30
none of the git remotes configured for this repository point to a known GitHub
host. To tell gh about a new GitHub host, please use `gh auth login`

The panel surfaces that string. Nothing in it suggests the project is fine and
the client was wrong.

After

The same checkout, same command path, with this change applied — listIssues()
called against that gitlab.com repo, printing what the renderer receives:

ok:true  30 issues
  #8513  Harden the keyring write probe: per-process ID instead of
     url:       https://gitlab.com/gitlab-org/cli/-/work_items/8513
     labels:    ["category:gitlab cli","devops::ai coding","group::code review"]
     assignees: ["jay_mccure"]
     body:      "Two robustness follow-ups raised by @timofurrer wh"
  #8511  feat: wire `internal/loader` into interactive `glab mr lis
     url:       https://gitlab.com/gitlab-org/cli/-/work_items/8511
     labels:    ["automation:ml","feature::enhancement","type::feature"]
     assignees: ["dankparth"]

The GitHub path is unchanged — this repository's own origin, same call:

ok:true  30 issues
  #334  Usage cache keyed by (file, sessionId) causes O(agents × f
     url:       https://github.com/chaitanyagiri/munder-difflin/issues/334

An unsupported host is named rather than guessed at, and a token embedded in the
remote is not echoed back:

origin = https://gitlab.mycompany.internal/team/app.git
ok:false  error: Fetching issues supports github.com and gitlab.com; the
                 'origin' remote points at 'gitlab.mycompany.internal'.

origin = https://oauth2:glpat-SECRETVALUE123@gitlab.mycompany.internal/team/app.git
ok:false  error: Fetching issues supports github.com and gitlab.com; the
                 'origin' remote points at 'gitlab.mycompany.internal'.

Test suite, before and after:

$ node --test test/git-remote-host.test.cjs test/issue-host-routing.test.cjs   # before
# Error: ENOENT: no such file or directory, open 'src/main/gitHost.ts'
# pass 0
# fail 1

$ npm run test:focused                                                        # after
# tests 563
# pass 563
# fail 0

How I tested it

  • OS: macOS 15 (Darwin 25.6.0, arm64)
  • Steps:
    1. npm run typecheck — 0 errors (node + web).
    2. npm run test:focused — 563 of 563 pass. The baseline on the commit
      this branches from is 552 of 552, so the 11 added tests are the whole
      difference and there are no pre-existing failures.
    3. npm run build — succeeds; out/main/index.js contains the new routing.
    4. Ran listIssues() end to end against live gitlab.com
      (gitlab-org/cli, via glab 1.114.0) and against live github.com
      (this repository, via gh), plus a self-hosted-looking host and a
      token-bearing remote. All four outputs are quoted above.
    5. The GitLab JSON field names were read off real glab issue list --output json output rather than assumed from the GitHub shape. Four of them
      differ in ways that fail silently: iid is the per-project number users
      see (id is global and would render a number matching nothing), the body
      is description and is null rather than '' when unset, assignees carry
      username not login, and labels is an array of plain strings where
      gh returns objects with a name — mapping that one as objects yields
      empty labels and no error.
    6. Each test was checked by mutation: the label shape, the iid/id choice,
      the description field, the assignee key, both routing branches, the
      unsupported-host refusal, and the credential redaction were each broken in
      turn and confirmed to turn the suite red.

Not covered: clicking the button in a running Electron window, and any
self-hosted GitLab or GitHub Enterprise instance (explicitly out of scope — such
a host is refused, not guessed at).

Checklist

  • Before and after evidence is attached above, under both headings.
  • npm run typecheck passes.
  • npm run test:focused passes.
  • npm run build succeeds.
  • This PR is one change. Unrelated fixes belong in their own PR.
  • I read the diff myself before opening this, and there is no debug output,
    commented-out code, or unrelated formatting churn in it.
  • Any new UI derives from DESIGN.md / tokens.ts — no ad-hoc colors,
    spacing, or fonts. (No UI changed.)
  • If I added art, it's my own or compatibly licensed, and listed in
    ATTRIBUTION.md. (No art.)

Aaron Coville added 2 commits August 26, 2026 14:06
Anything that talks to a code-hosting service on the user's behalf has to
decide which service it is talking to, and the only evidence always present
in a checkout is the URL of its `origin` remote.

Reading that URL is a one-liner, but interpreting it is not: git uses two
unrelated syntaxes for the same repository depending on how it was cloned —
`https://host/owner/repo.git` from an HTTPS clone and the scp-like
`git@host:owner/repo.git` from an SSH one. Comparing raw URLs, or handling
only the HTTPS form, routes the same project two different ways depending on
which one a given contributor happened to use.

`parseGitRemoteHost` reduces both forms (plus `ssh://`, `git://`, embedded
credentials, and a port) to a lowercased hostname. `issueHostFor` maps that
hostname to a forge, matching the public hostnames exactly: a self-hosted
instance is not inferred from a hostname that merely reads like one, because
guessing wrong points the wrong client at the wrong API, and that is harder
to act on than being told the host is unsupported.

Note that `git remote get-url` reports the *effective* URL, so a user's own
`url.<base>.insteadOf` rewrite rules change its answer. That is the desired
behaviour — the rewritten URL is the host git actually contacts — but it does
mean the tests empty out the git config they run against, or they would pass
or fail depending on whose machine ran them.
"Fetch issues" ran `gh issue list` unconditionally. In a checkout hosted
anywhere other than GitHub that is not a degraded result, it is a confusing
one: `gh` reports that it cannot resolve the repository, so a GitLab user is
told their project does not exist rather than that the wrong tool was asked.

The forge is now chosen from the `origin` remote's host. github.com keeps the
existing `gh` path unchanged; gitlab.com goes to `glab`, GitLab's own CLI,
arranged exactly as the GitHub path is — ask the CLI for JSON, let it own
authentication, store no token. `glab` must be installed and logged in for
that path to work, the same assumption the `gh` path has always made.

Any other host is refused by name instead of guessed at. Pointing `gh` at a
self-hosted GitLab, or `glab` at GitHub Enterprise, fails in terms that
describe neither the repository nor the real problem; "supports github.com
and gitlab.com" is at least actionable. The message names the parsed host and
never the remote URL, because a remote can carry a personal access token and
this text is rendered in the app.

The GitLab JSON is mapped from field names verified against `glab issue list
--output json` rather than assumed from the GitHub shape, which differs in
four places that would otherwise fail silently: `iid` is the per-project
number users see (`id` is global), `description` holds the body and is null
rather than empty when unset, assignees carry `username` not `login`, and
`labels` is an array of plain strings where `gh` returns objects with a
`name` — reading that one as objects yields empty labels and no error.

The renderer and the IPC channel are untouched: the normalized issue shape is
the same, and the button already said "Fetch issues" rather than naming a
forge.
@github-actions

Copy link
Copy Markdown

🚫 This PR is missing its before/after evidence

Every pull request here has to show its work. Screenshots or a short screen recording, before the change and after it.

  • Before — no image or video under that heading
  • After — no image or video under that heading

How to fix it: edit the description, keep the ### Before and ### After headings from the template, and drag an image or video under each. GitHub uploads it inline. This check re-runs the moment you save.

A bug fix with no visible surface still needs it: show the failing behaviour, then the same steps passing. A terminal recording is fine.

Genuinely nothing to show — a CI tweak, a typo, a dependency bump? A maintainer can apply the no-visual-change label. Please don't ask unless it truly has no observable effect.

📖 CONTRIBUTING.md → Evidence is mandatory

@aaroncoville
aaroncoville merged commit 3a75554 into main Aug 26, 2026
2 of 3 checks passed
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.

1 participant