diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..42c9e24 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + - run: npm ci + - run: npm run typecheck + - run: npm run build + - run: npm run test diff --git a/src/index.ts b/src/index.ts index d933748..bce346b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,15 +7,6 @@ const API_BASE = process.env["MCP_BUGZILLA_BASE_URL"] ?? "https://bugzilla.mozilla.org/rest" const API_KEY = process.env["MCP_BUGZILLA_API_KEY"] -export class BugzillaError extends Error { - constructor( - public status: number, - public body: string, - ) { - super(`Bugzilla API error ${status}: ${body}`) - } -} - export const apiFetch = async ( path: string, options: RequestInit = {}, @@ -26,12 +17,18 @@ export const apiFetch = async ( } if (API_KEY) headers["X-BUGZILLA-API-KEY"] = API_KEY - const response = await fetch(`${API_BASE}${path}`, { ...options, headers }) - if (!response.ok) { - const body = await response.text() - throw new BugzillaError(response.status, body) + try { + const response = await fetch(`${API_BASE}${path}`, { ...options, headers }) + if (!response.ok) { + const body = await response.text() + console.error(`Bugzilla API error ${response.status}: ${body} — ${path}`) + return null + } + return (await response.json()) as T + } catch (error) { + console.error(`Fetch failed: ${path}`, error) + return null } - return (await response.json()) as T } export const ok = (data: unknown) => ({ @@ -90,15 +87,11 @@ export const createBug = async (params: { target_milestone?: string }) => { if (!API_KEY) return err("MCP_BUGZILLA_API_KEY is required to create bugs") - try { - const data = await apiFetch<{ id: number }>("/bug", { - method: "POST", - body: JSON.stringify(params), - }) - return data?.id ? ok({ id: data.id }) : err("failed to create bug") - } catch (e) { - return err(e instanceof Error ? e.message : "failed to create bug") - } + const data = await apiFetch<{ id: number }>("/bug", { + method: "POST", + body: JSON.stringify(params), + }) + return data?.id ? ok({ id: data.id }) : err("failed to create bug") } export const updateBug = async (params: { @@ -117,15 +110,11 @@ export const updateBug = async (params: { }) => { if (!API_KEY) return err("MCP_BUGZILLA_API_KEY is required to update bugs") const { id, ...fields } = params - try { - const data = await apiFetch<{ bugs: unknown[] }>(`/bug/${id}`, { - method: "PUT", - body: JSON.stringify(fields), - }) - return data?.bugs ? ok(data.bugs) : err(`failed to update bug ${id}`) - } catch (e) { - return err(e instanceof Error ? e.message : `failed to update bug ${id}`) - } + const data = await apiFetch<{ bugs: unknown[] }>(`/bug/${id}`, { + method: "PUT", + body: JSON.stringify(fields), + }) + return data?.bugs ? ok(data.bugs) : err(`failed to update bug ${id}`) } export const getBugHistory = async ({ id }: { id: string }) => { @@ -159,15 +148,11 @@ export const createComment = async (params: { }) => { if (!API_KEY) return err("MCP_BUGZILLA_API_KEY is required to post comments") const { id, ...body } = params - try { - const data = await apiFetch<{ id: number }>(`/bug/${id}/comment`, { - method: "POST", - body: JSON.stringify(body), - }) - return data?.id ? ok({ id: data.id }) : err("failed to create comment") - } catch (e) { - return err(e instanceof Error ? e.message : "failed to create comment") - } + const data = await apiFetch<{ id: number }>(`/bug/${id}/comment`, { + method: "POST", + body: JSON.stringify(body), + }) + return data?.id ? ok({ id: data.id }) : err("failed to create comment") } export const searchCommentTags = async ({ @@ -224,17 +209,13 @@ export const createAttachment = async (params: { if (!API_KEY) return err("MCP_BUGZILLA_API_KEY is required to create attachments") const { id, ...body } = params - try { - const result = await apiFetch<{ ids: number[] }>(`/bug/${id}/attachment`, { - method: "POST", - body: JSON.stringify({ ...body, ids: [id] }), - }) - return result?.ids - ? ok({ ids: result.ids }) - : err("failed to create attachment") - } catch (e) { - return err(e instanceof Error ? e.message : "failed to create attachment") - } + const result = await apiFetch<{ ids: number[] }>(`/bug/${id}/attachment`, { + method: "POST", + body: JSON.stringify({ ...body, ids: [id] }), + }) + return result?.ids + ? ok({ ids: result.ids }) + : err("failed to create attachment") } export const updateAttachment = async (params: {