From 1391a86b1df3a5f626f8ef1de5fc55e051324702 Mon Sep 17 00:00:00 2001 From: Oskar Nyc Date: Sun, 30 Aug 2026 23:21:01 +0200 Subject: [PATCH 1/2] Drop the port from remote-derived hosts so glab --hostname accepts them parseRemoteUrl used URL.host for ssh:// and https:// remotes, which includes the port (ssh://git@gitlab.example.com:2222/group/project.git -> gitlab.example.com:2222). That value is passed to `glab api --hostname`, which rejects any host:port, so `codiff mr ` failed with "Unable to read repository" on GitLab instances whose remotes carry a custom port. Use URL.hostname instead. No behaviour change for remotes without a port, and the scp-style branch already had none. Adds regression tests for ssh/https remotes with ports. Co-Authored-By: Claude Fable 5 --- electron/__tests__/review-source.test.ts | 36 +++++++++++++++++++++++- electron/review-source.cjs | 5 +++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/electron/__tests__/review-source.test.ts b/electron/__tests__/review-source.test.ts index d7a0e4be..3916a62d 100644 --- a/electron/__tests__/review-source.test.ts +++ b/electron/__tests__/review-source.test.ts @@ -3,7 +3,12 @@ import { expect, test } from 'vite-plus/test'; const require = createRequire(import.meta.url); -const { parseReviewUrl } = require('../review-source.cjs') as { +const { parseRemoteUrl, parseReviewUrl } = require('../review-source.cjs') as { + parseRemoteUrl: (value: string) => { + host: string; + projectPath: string; + provider: 'github' | 'gitlab'; + } | null; parseReviewUrl: (value: string) => { host: string; number: number; @@ -129,3 +134,32 @@ test('parseReviewUrl rejects values that are not review URLs', () => { expect(parseReviewUrl(value)).toBe(null); } }); + +test('parseRemoteUrl drops a custom port from `ssh://` remotes', () => { + expect(parseRemoteUrl('ssh://git@gitlab.example.com:2222/group/project.git')).toEqual({ + host: 'gitlab.example.com', + projectPath: 'group/project', + provider: 'gitlab', + }); +}); + +test('parseRemoteUrl drops a custom port from `https://` remotes', () => { + expect(parseRemoteUrl('https://gitlab.example.com:8443/group/subgroup/project.git')).toEqual({ + host: 'gitlab.example.com', + projectPath: 'group/subgroup/project', + provider: 'gitlab', + }); +}); + +test('parseRemoteUrl reads scp-style and GitHub remotes', () => { + expect(parseRemoteUrl('git@gitlab.example.com:group/project.git')).toEqual({ + host: 'gitlab.example.com', + projectPath: 'group/project', + provider: 'gitlab', + }); + expect(parseRemoteUrl('https://github.com/nkzw-tech/codiff.git')).toEqual({ + host: 'github.com', + projectPath: 'nkzw-tech/codiff', + provider: 'github', + }); +}); diff --git a/electron/review-source.cjs b/electron/review-source.cjs index 8e7f6536..6be3ff06 100644 --- a/electron/review-source.cjs +++ b/electron/review-source.cjs @@ -106,7 +106,9 @@ const parseRemoteUrl = (value) => { const projectPath = url.pathname.replaceAll(/^\/+|\.git$/gi, ''); return projectPath ? { - host: url.host.toLowerCase(), + // `url.host` includes the port (e.g. a custom SSH port in `ssh://` remotes); + // glab's `--hostname` rejects `host:port`, so use the bare hostname. + host: url.hostname.toLowerCase(), projectPath, provider: /** @type {ReviewProvider} */ ( url.hostname.toLowerCase() === 'github.com' ? 'github' : 'gitlab' @@ -175,6 +177,7 @@ const resolveReviewUrl = (repositoryPath, number, provider) => { }; module.exports = { + parseRemoteUrl, parseReviewUrl, readReviewRemotes, resolveReviewUrl, From cde19d1fed6fb56da247b63072cf90e34fb9023d Mon Sep 17 00:00:00 2001 From: Oskar Nyc Date: Mon, 31 Aug 2026 14:42:16 +0200 Subject: [PATCH 2/2] Limit port stripping to ssh:// remotes An explicit port on https:// remotes is part of the instance identifier and must be kept; only the SSH transport port is dropped. Co-Authored-By: Claude Fable 5 --- electron/__tests__/review-source.test.ts | 4 ++-- electron/review-source.cjs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/electron/__tests__/review-source.test.ts b/electron/__tests__/review-source.test.ts index 3916a62d..9b2f078f 100644 --- a/electron/__tests__/review-source.test.ts +++ b/electron/__tests__/review-source.test.ts @@ -143,9 +143,9 @@ test('parseRemoteUrl drops a custom port from `ssh://` remotes', () => { }); }); -test('parseRemoteUrl drops a custom port from `https://` remotes', () => { +test('parseRemoteUrl keeps an explicit port on `https://` remotes', () => { expect(parseRemoteUrl('https://gitlab.example.com:8443/group/subgroup/project.git')).toEqual({ - host: 'gitlab.example.com', + host: 'gitlab.example.com:8443', projectPath: 'group/subgroup/project', provider: 'gitlab', }); diff --git a/electron/review-source.cjs b/electron/review-source.cjs index 6be3ff06..0e51d270 100644 --- a/electron/review-source.cjs +++ b/electron/review-source.cjs @@ -106,9 +106,10 @@ const parseRemoteUrl = (value) => { const projectPath = url.pathname.replaceAll(/^\/+|\.git$/gi, ''); return projectPath ? { - // `url.host` includes the port (e.g. a custom SSH port in `ssh://` remotes); - // glab's `--hostname` rejects `host:port`, so use the bare hostname. - host: url.hostname.toLowerCase(), + // A custom SSH port (`ssh://git@host:2222/...`) is transport detail, not part + // of the instance identifier, and glab's `--hostname` rejects `host:port` — + // strip it. An explicit port on `https://` remotes stays: it identifies the instance. + host: (url.protocol === 'ssh:' ? url.hostname : url.host).toLowerCase(), projectPath, provider: /** @type {ReviewProvider} */ ( url.hostname.toLowerCase() === 'github.com' ? 'github' : 'gitlab'