Skip to content

Commit 0c2c53e

Browse files
committed
feat: add getOctokit factory with deep merge, stripUndefined, and plugin dedup
1 parent 652783f commit 0c2c53e

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {getOctokit} from '@actions/github'
2+
3+
/**
4+
* Strip keys whose value is `undefined` so they don't clobber defaults
5+
* during object spread (e.g. `{baseUrl: undefined}` would wipe a GHES URL).
6+
*/
7+
function stripUndefined(obj: Record<string, unknown>): Record<string, unknown> {
8+
return Object.fromEntries(
9+
Object.entries(obj).filter(([, v]) => v !== undefined)
10+
)
11+
}
12+
13+
/**
14+
* Creates a wrapped getOctokit that inherits default options and plugins.
15+
* Secondary clients created via the wrapper get the same retry, logging,
16+
* orchestration ID, and retries count as the pre-built `github` client.
17+
*
18+
* - `request` and `retry` are deep-merged so partial overrides
19+
* (e.g. `{request: {timeout: 5000}}`) don't clobber inherited values.
20+
* - `undefined` values in both default and user options are stripped to prevent
21+
* accidental clobbering (e.g. GHES `baseUrl`, or `log: undefined` from defaults).
22+
* - Default plugins (retry, requestLog) are always included; duplicates are skipped.
23+
*/
24+
export function createConfiguredGetOctokit(
25+
rawGetOctokit: typeof getOctokit,
26+
defaultOptions: Record<string, unknown>,
27+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28+
...defaultPlugins: any[]
29+
): typeof getOctokit {
30+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
31+
return ((token: string, options?: any, ...plugins: any[]) => {
32+
const cleanDefaults = stripUndefined(defaultOptions)
33+
const userOpts = stripUndefined(options ?? {})
34+
35+
const defaultRequest =
36+
(cleanDefaults.request as Record<string, unknown> | undefined) ?? {}
37+
const userRequest = stripUndefined(
38+
(userOpts.request as Record<string, unknown> | undefined) ?? {}
39+
)
40+
41+
const defaultRetry =
42+
(cleanDefaults.retry as Record<string, unknown> | undefined) ?? {}
43+
const userRetry = stripUndefined(
44+
(userOpts.retry as Record<string, unknown> | undefined) ?? {}
45+
)
46+
47+
const merged = {
48+
...cleanDefaults,
49+
...userOpts,
50+
request: {...defaultRequest, ...userRequest},
51+
retry: {...defaultRetry, ...userRetry}
52+
}
53+
54+
// Deduplicate: default plugins first, then user plugins that aren't already present
55+
const allPlugins = [...defaultPlugins]
56+
for (const plugin of plugins) {
57+
if (!allPlugins.includes(plugin)) {
58+
allPlugins.push(plugin)
59+
}
60+
}
61+
62+
return rawGetOctokit(token, merged, ...allPlugins)
63+
}) as typeof getOctokit
64+
}

0 commit comments

Comments
 (0)