Forgejo Auth Routing
Added host-scoped routing for gh auth status and gh auth token when GH_HOST, --hostname, or the current repo resolves to an allowlisted Forgejo host.
The shim now answers Forgejo-scoped gh auth status, gh auth token, and gh api user probes itself, while GitHub repositories still delegate to the real GitHub CLI.
Codex.app was again reporting GitHub CLI unavailable for Forgejo and GitHub workflows. The repo and machine checks showed the GUI PATH and installed shim were visible, but a newer availability-style probe fails when GH_HOST points at a Forgejo host and the shim delegates auth/API commands to the real gh. This change makes those host-scoped auth probes part of the Forgejo compatibility surface.
Added host-scoped routing for gh auth status and gh auth token when GH_HOST, --hostname, or the current repo resolves to an allowlisted Forgejo host.
Added support for the common gh api user probe, including --jq .login, so tools can confirm the active Forgejo identity without the real GitHub CLI rejecting the host.
Implemented gh auth status --json hosts with a GitHub-shaped hosts envelope and a shim-specific token source.
Bumped package metadata from 0.1.0 to 0.1.1 so the installed gfj version can distinguish this regression fix.
The important failing repro was not basic PATH discovery. gfj doctor already reported a good GUI PATH, installed shim, real gh, fj, and Forgejo auth. The red path was a constrained invocation such as GH_HOST=git.dirtydishes.dev gh auth status: before this change, the shim delegated auth to the real GitHub CLI, which tried to validate Forgejo auth as if it were GitHub Enterprise auth and returned a failure.
RouteDecision now carries an optional host, because auth and identity checks can be host-scoped without needing a repository owner/name pair.--hostname first, then GH_HOST, then the current repo detection path.gh auth status reports discoverable shim auth without live validation. Live validation still happens when commands call Forgejo APIs.gh api endpoints continue to delegate; only user and /user are claimed by the shim for Forgejo hosts.Config.is_forgejo_host("github.com") is still false.Focused unified diff fallback. It shows the routing surface, auth/API handlers, version bump, and regression tests without embedding the full file diff.
diff --git a/src/gh_forgejo_shim/routing.py b/src/gh_forgejo_shim/routing.py
@@
-from .config import Config, load_config
+from .config import Config, load_config, normalize_host
@@
SUPPORTED_PR_COMMANDS = {"checks", "checkout", "co", "comment", "create", "diff", "list", "new", "status", "view"}
SUPPORTED_ISSUE_COMMANDS = {"create", "list", "ls", "new", "view"}
SUPPORTED_REPO_COMMANDS = {"view"}
+SUPPORTED_AUTH_COMMANDS = {"status", "token"}
@@
class RouteDecision:
kind: str
reason: str
repo: RepoRef | None = None
+ host: str | None = None
@@
+def _is_supported_auth_command(argv: list[str]) -> bool:
+ return len(argv) >= 2 and argv[0] == "auth" and argv[1] in SUPPORTED_AUTH_COMMANDS
+
+def _is_supported_api_command(argv: list[str]) -> bool:
+ return len(argv) >= 2 and argv[0] == "api" and _api_endpoint(argv[1:]) in {"user", "/user"}
@@
+ if _is_supported_auth_command(argv):
+ return _decide_host_route(
+ argv,
+ config=config,
+ env=values,
+ cwd=cwd,
+ host=_hostname_arg(argv[2:], allow_short=True) or values.get("GH_HOST"),
+ )
+
+ if _is_supported_api_command(argv):
+ return _decide_host_route(
+ argv,
+ config=config,
+ env=values,
+ cwd=cwd,
+ host=_hostname_arg(argv[1:], allow_short=False) or values.get("GH_HOST"),
+ )
@@
+def run_forgejo_auth(
+ argv: list[str],
+ host: str,
+ token: str | None,
+ *,
+ stdout: TextIO,
+ stderr: TextIO,
+) -> int:
+ command = argv[1]
+ rest = argv[2:]
+ if command == "status":
+ parsed = _parse_auth_status_args(rest)
+ if token is None:
+ _print_missing_auth_status(host, stderr)
+ return 1
+ data = _auth_status_data(host, token, show_token=parsed.show_token)
+ if parsed.json_fields:
+ _print_json_or_jq(_filter_selected_fields(data, parsed.json_fields), parsed.jq, stdout, template=parsed.template)
+ else:
+ _print_auth_status_text(host, token if parsed.show_token else None, stdout)
+ return 0
@@
+def run_forgejo_api(
+ argv: list[str],
+ host: str,
+ client: ForgejoClient,
+ *,
+ stdout: TextIO,
+) -> int:
+ parsed = _parse_api_args(argv[1:])
+ if parsed.endpoint not in {"user", "/user"}:
+ raise ValueError(f"unsupported Forgejo api endpoint: {parsed.endpoint}")
+ user = client.get_current_user(host)
+ if parsed.silent:
+ return 0
+ _print_json_or_jq(user, parsed.jq, stdout, template=parsed.template)
+ return 0
diff --git a/tests/test_routing.py b/tests/test_routing.py
@@
+ def test_forgejo_auth_status_uses_shim_auth_instead_of_real_gh(self) -> None:
+ out = io.StringIO()
+ code = run_gh(
+ ["auth", "status"],
+ env={
+ "FJ_SHIM_HOSTS": "git.example.com",
+ "FJ_SHIM_REAL_GH": "/missing/gh",
+ "FJ_SHIM_TOKEN": "secret-token",
+ "GH_HOST": "git.example.com",
+ "PATH": "",
+ },
+ stdout=out,
+ stderr=io.StringIO(),
+ )
+
+ self.assertEqual(code, 0)
+ self.assertIn("git.example.com", out.getvalue())
+ self.assertIn("Logged in", out.getvalue())
@@
+ def test_forgejo_api_user_probe_uses_forgejo_client(self) -> None:
+ out = io.StringIO()
+ code = run_gh(
+ ["api", "user", "--jq", ".login"],
+ env={
+ "FJ_SHIM_HOSTS": "git.example.com",
+ "FJ_SHIM_REAL_GH": "/missing/gh",
+ "FJ_SHIM_TOKEN": "secret-token",
+ "GH_HOST": "git.example.com",
+ "PATH": "",
+ },
+ stdout=out,
+ stderr=io.StringIO(),
+ client_factory=lambda token: FakeClient(),
+ )
+
+ self.assertEqual(code, 0)
+ self.assertEqual(out.getvalue(), "alice\n")
diff --git a/pyproject.toml b/pyproject.toml
@@
-version = "0.1.0"
+version = "0.1.1"
Codex.app should stop treating Forgejo-hosted workspaces as having an unavailable GitHub CLI when it probes auth or identity through GH_HOST. GitHub repositories should continue to use the real GitHub CLI and should keep reporting normal GitHub auth and repository metadata.
python3 -m unittest ran 98 tests on the branch based on origin/main.PYTHONPATH=/Users/kell/dev/gh-forgejo-shim/src GH_HOST=git.dirtydishes.dev python3 -m gh_forgejo_shim gh auth status.PYTHONPATH=/Users/kell/dev/gh-forgejo-shim/src GH_HOST=git.dirtydishes.dev python3 -m gh_forgejo_shim gh api user --jq .login.pipx install --force /Users/kell/dev/gh-forgejo-shim and gfj install-shim.gfj doctor reports real gh, fj, Forgejo auth, shim PATH, and macOS GUI PATH all ok.gh auth status is a token-discovery compatibility response, not a live validation request. Actual API commands still validate by calling Forgejo.gh api support remains intentionally narrow. Only the identity probe is implemented; broader API emulation should be added only when a concrete tool probe requires it.No new follow-up issue is required for this fix. A useful future improvement would be a dedicated compatibility test script that runs the common Codex probes against the installed shim, but the current regression tests cover the failing auth/API seams.