mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-08-22 03:18:54 +08:00
The REST client honors GITHUB_API_URL, but the GraphQL client derived its base URL from GITHUB_API_URL as well and ignored the standard GITHUB_GRAPHQL_URL variable that GitHub Actions provides. On standard GitHub Enterprise Server this still worked because @octokit/graphql rewrites a ".../api/v3" REST base to ".../api/graphql", but any deployment whose GraphQL endpoint is not derivable from the REST base (custom proxy, separate host) sent GraphQL requests to the wrong URL. Honor GITHUB_GRAPHQL_URL independently and fall back to GITHUB_API_URL when it is unset, so behavior is unchanged for github.com and standard GHES. A single trailing "/graphql" is stripped because @octokit/graphql appends its own. Add wire-level regression tests that run the real client factory in a fresh process and assert the final request URLs and Authorization headers; constructor-option assertions are insufficient because @octokit/graphql rewrites the path after the client is constructed.
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
// Wire-level probe for the GitHub API client's endpoint routing.
|
|
//
|
|
// `src/github/api/config.ts` reads GITHUB_API_URL / GITHUB_GRAPHQL_URL at module
|
|
// load time, so each endpoint configuration has to be exercised in its own fresh
|
|
// process (the companion test spawns this file once per case with the relevant
|
|
// env vars set). We stub global fetch to capture the FINAL request URL and
|
|
// Authorization header — asserting constructor options is not enough because
|
|
// @octokit/graphql rewrites/append the path (".../api/v3" -> ".../api/graphql",
|
|
// otherwise it appends "/graphql") after the client is constructed.
|
|
import { createOctokit } from "../../src/github/api/client";
|
|
|
|
type Captured = { url: string; auth: string | null };
|
|
const captured: Captured[] = [];
|
|
|
|
globalThis.fetch = (async (input: any, init?: any) => {
|
|
const url: string =
|
|
typeof input === "string" ? input : (input?.url ?? String(input));
|
|
const headers = new Headers(init?.headers ?? input?.headers);
|
|
captured.push({ url, auth: headers.get("authorization") });
|
|
return new Response(JSON.stringify({ data: {} }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}) as typeof fetch;
|
|
|
|
const octokits = createOctokit("test-token");
|
|
|
|
await octokits.graphql(`query { viewer { login } }`);
|
|
const graphql = captured[captured.length - 1]!;
|
|
|
|
await octokits.rest.request("GET /meta");
|
|
const rest = captured[captured.length - 1]!;
|
|
|
|
process.stdout.write(
|
|
JSON.stringify({
|
|
graphqlUrl: graphql.url,
|
|
graphqlAuth: graphql.auth,
|
|
restUrl: rest.url,
|
|
restAuth: rest.auth,
|
|
}),
|
|
);
|