@teambit/ci 1.0.632 → 1.0.634
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ci.docs.mdx +8 -0
- package/dist/ci.docs.mdx +8 -0
- package/dist/ci.main.runtime.js +5 -1
- package/dist/ci.main.runtime.js.map +1 -1
- package/dist/{preview-1792168769587.js → preview-1792418223123.js} +1 -1
- package/dist/source-branch-detector.d.ts +4 -2
- package/dist/source-branch-detector.js +44 -30
- package/dist/source-branch-detector.js.map +1 -1
- package/dist/source-branch-detector.spec.d.ts +1 -0
- package/dist/source-branch-detector.spec.js +141 -0
- package/dist/source-branch-detector.spec.js.map +1 -0
- package/dist/sync/git-host-provider.spec.js +28 -0
- package/dist/sync/git-host-provider.spec.js.map +1 -1
- package/dist/sync/github-client.d.ts +41 -5
- package/dist/sync/github-client.js +131 -20
- package/dist/sync/github-client.js.map +1 -1
- package/dist/sync/github-client.spec.js +351 -0
- package/dist/sync/github-client.spec.js.map +1 -1
- package/dist/sync/init-scaffold.d.ts +2 -0
- package/dist/sync/init-scaffold.js +7 -4
- package/dist/sync/init-scaffold.js.map +1 -1
- package/dist/sync/init-scaffold.spec.js +56 -10
- package/dist/sync/init-scaffold.spec.js.map +1 -1
- package/package.json +11 -11
- package/sync/git-host-provider.spec.ts +29 -0
- package/sync/github-client.spec.ts +334 -1
- package/sync/github-client.ts +130 -26
- package/sync/init-scaffold.spec.ts +62 -5
- package/sync/init-scaffold.ts +9 -4
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
import type { GitHostProvider, PrInfo } from './git-host-provider';
|
|
2
2
|
/**
|
|
3
|
-
* The
|
|
3
|
+
* The host of the GitHub instance this run belongs to: the one `GITHUB_SERVER_URL` names (Actions sets
|
|
4
|
+
* it on every runner — `https://github.com`, or the GitHub Enterprise Server URL), else github.com.
|
|
5
|
+
*/
|
|
6
|
+
export declare function gitHubServerHost(): string;
|
|
7
|
+
/** A resolved REST API base, or why the environment's API cannot be trusted with the repository's token. */
|
|
8
|
+
export type GitHubApiUrl = {
|
|
9
|
+
url: string;
|
|
10
|
+
} | {
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The REST API base for a repository on `host` (the `GITHUB_SERVER_URL` instance's host when omitted).
|
|
15
|
+
* Every request to it carries that instance's token, so:
|
|
16
|
+
* - github.com (and any host that is not the enterprise one) is always `https://api.github.com`.
|
|
17
|
+
* - the enterprise host takes `GITHUB_API_URL` only when it lives on that host or on `api.<host>`
|
|
18
|
+
* (GHE.com data residency: `https://<sub>.ghe.com` serves its API at `https://api.<sub>.ghe.com`);
|
|
19
|
+
* unset, it is the GHES convention `<GITHUB_SERVER_URL origin>/api/v3`. An https server's API must be
|
|
20
|
+
* https too, so the token never travels in plaintext. Any other API is refused — never replaced by
|
|
21
|
+
* api.github.com, which must not receive an enterprise token.
|
|
22
|
+
*/
|
|
23
|
+
export declare function gitHubApiUrl(host?: string): GitHubApiUrl;
|
|
24
|
+
/** The host a git remote URL points at, lower-cased, or undefined when the URL has no authority. */
|
|
25
|
+
export declare function remoteHost(remoteUrl: string): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The `owner/repo` out of a GitHub remote URL, anchored to the URL authority. `:digits` is a port
|
|
4
28
|
* only under a scheme; in the scp-like form (`git@github.com:12345/repo`) the colon starts the PATH
|
|
5
29
|
* and an all-digits owner is a legal GitHub username.
|
|
6
30
|
*/
|
|
7
31
|
export declare function parseGitHubRepo(remoteUrl: string): string | undefined;
|
|
8
32
|
/**
|
|
9
|
-
* Does this remote point at GitHub
|
|
10
|
-
* GitHub's to claim. Host equality, not
|
|
33
|
+
* Does this remote point at GitHub — github.com, or the GitHub Enterprise Server host `GITHUB_SERVER_URL`
|
|
34
|
+
* names? A host test only — an unparseable GitHub remote is still GitHub's to claim. Host equality, not
|
|
35
|
+
* substring: `github.com` can be a path segment on another host.
|
|
11
36
|
*/
|
|
12
37
|
export declare function isGitHubRemote(remoteUrl: string): boolean;
|
|
13
38
|
/** Warning sink; a plain callback rather than a `Logger` so this module needs no logger aspect. */
|
|
@@ -17,10 +42,16 @@ export declare class GitHubClient implements GitHostProvider {
|
|
|
17
42
|
private token;
|
|
18
43
|
/** `owner/repo`. Readable so callers (and tests) can see *which* repository a resolved client speaks to. */
|
|
19
44
|
readonly repo: string;
|
|
45
|
+
/**
|
|
46
|
+
* The REST API base, e.g. `https://api.github.com` (the default) or a GitHub Enterprise Server's
|
|
47
|
+
* `https://<host>/api/v3`; `fromEnv` resolves it for the remote's host.
|
|
48
|
+
*/
|
|
49
|
+
readonly apiUrl: string;
|
|
20
50
|
private fetchImpl;
|
|
21
51
|
constructor(opts: {
|
|
22
52
|
token: string;
|
|
23
53
|
repo: string;
|
|
54
|
+
apiUrl?: string;
|
|
24
55
|
fetchImpl?: typeof fetch;
|
|
25
56
|
});
|
|
26
57
|
/**
|
|
@@ -72,15 +103,19 @@ export declare class GitHubClient implements GitHostProvider {
|
|
|
72
103
|
}
|
|
73
104
|
/**
|
|
74
105
|
* The registrable GitHub provider: resolves its `GitHubClient` from the environment lazily, since
|
|
75
|
-
* registration happens at aspect load, before credentials are known. A resolved client is memoized
|
|
76
|
-
* an unresolved one is retried so a later call that does carry the remote URL can still succeed.
|
|
106
|
+
* registration happens at aspect load, before credentials are known. A resolved client is memoized per
|
|
107
|
+
* remote; an unresolved one is retried so a later call that does carry the remote URL can still succeed.
|
|
77
108
|
*/
|
|
78
109
|
export declare class GitHubHostProvider implements GitHostProvider {
|
|
79
110
|
private onWarning;
|
|
80
111
|
readonly name = "github";
|
|
81
112
|
private client;
|
|
113
|
+
/** The remote `client` was resolved for. */
|
|
114
|
+
private clientRemote;
|
|
82
115
|
/** The last `origin` URL seen, so a PR method can still recover `owner/repo` from it. */
|
|
83
116
|
private remoteHint;
|
|
117
|
+
/** Warnings already emitted: an unresolved client is retried on every call, its warning is not. */
|
|
118
|
+
private warned;
|
|
84
119
|
constructor(onWarning?: WarnFn);
|
|
85
120
|
matchesRemote(remoteUrl: string): boolean;
|
|
86
121
|
/**
|
|
@@ -103,5 +138,6 @@ export declare class GitHubHostProvider implements GitHostProvider {
|
|
|
103
138
|
createIfAbsent?: boolean;
|
|
104
139
|
}): Promise<void>;
|
|
105
140
|
private resolveClient;
|
|
141
|
+
private warnOnce;
|
|
106
142
|
private requireClient;
|
|
107
143
|
}
|
|
@@ -4,49 +4,133 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.GitHubHostProvider = exports.GitHubClient = void 0;
|
|
7
|
+
exports.gitHubApiUrl = gitHubApiUrl;
|
|
8
|
+
exports.gitHubServerHost = gitHubServerHost;
|
|
7
9
|
exports.isGitHubRemote = isGitHubRemote;
|
|
8
10
|
exports.parseGitHubRepo = parseGitHubRepo;
|
|
11
|
+
exports.remoteHost = remoteHost;
|
|
9
12
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
10
13
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
11
14
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
15
|
+
const GITHUB_DOT_COM = 'github.com';
|
|
16
|
+
const GITHUB_DOT_COM_API = 'https://api.github.com';
|
|
17
|
+
const WEB_PROTOCOLS = ['http:', 'https:'];
|
|
18
|
+
|
|
19
|
+
/** The GitHub instance `GITHUB_SERVER_URL` names, or undefined when it is unset or malformed. */
|
|
20
|
+
function gitHubServerUrl() {
|
|
21
|
+
try {
|
|
22
|
+
if (process.env.GITHUB_SERVER_URL) return new URL(process.env.GITHUB_SERVER_URL);
|
|
23
|
+
} catch {
|
|
24
|
+
// a malformed value names no instance
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The host of the GitHub instance this run belongs to: the one `GITHUB_SERVER_URL` names (Actions sets
|
|
31
|
+
* it on every runner — `https://github.com`, or the GitHub Enterprise Server URL), else github.com.
|
|
32
|
+
*/
|
|
33
|
+
function gitHubServerHost() {
|
|
34
|
+
return gitHubServerUrl()?.hostname.toLowerCase() || GITHUB_DOT_COM;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The hosts whose remotes are GitHub's: github.com, plus a GitHub Enterprise Server host. */
|
|
38
|
+
function gitHubHosts() {
|
|
39
|
+
const serverHost = gitHubServerHost();
|
|
40
|
+
return serverHost === GITHUB_DOT_COM ? [GITHUB_DOT_COM] : [GITHUB_DOT_COM, serverHost];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** A resolved REST API base, or why the environment's API cannot be trusted with the repository's token. */
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The REST API base for a repository on `host` (the `GITHUB_SERVER_URL` instance's host when omitted).
|
|
47
|
+
* Every request to it carries that instance's token, so:
|
|
48
|
+
* - github.com (and any host that is not the enterprise one) is always `https://api.github.com`.
|
|
49
|
+
* - the enterprise host takes `GITHUB_API_URL` only when it lives on that host or on `api.<host>`
|
|
50
|
+
* (GHE.com data residency: `https://<sub>.ghe.com` serves its API at `https://api.<sub>.ghe.com`);
|
|
51
|
+
* unset, it is the GHES convention `<GITHUB_SERVER_URL origin>/api/v3`. An https server's API must be
|
|
52
|
+
* https too, so the token never travels in plaintext. Any other API is refused — never replaced by
|
|
53
|
+
* api.github.com, which must not receive an enterprise token.
|
|
54
|
+
*/
|
|
55
|
+
function gitHubApiUrl(host) {
|
|
56
|
+
const serverUrl = gitHubServerUrl();
|
|
57
|
+
const serverHost = gitHubServerHost();
|
|
58
|
+
if (!serverUrl || serverHost === GITHUB_DOT_COM || host && host.toLowerCase() !== serverHost) {
|
|
59
|
+
return {
|
|
60
|
+
url: GITHUB_DOT_COM_API
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const refuse = reason => ({
|
|
64
|
+
error: `${reason} — refusing to send the token of the GITHUB_SERVER_URL instance "${process.env.GITHUB_SERVER_URL}" to it`
|
|
65
|
+
});
|
|
66
|
+
if (!WEB_PROTOCOLS.includes(serverUrl.protocol)) return refuse(`GITHUB_SERVER_URL is not an http(s) URL`);
|
|
67
|
+
const apiUrl = process.env.GITHUB_API_URL;
|
|
68
|
+
if (!apiUrl) return {
|
|
69
|
+
url: `${serverUrl.origin}/api/v3`
|
|
70
|
+
};
|
|
71
|
+
let api;
|
|
72
|
+
try {
|
|
73
|
+
api = new URL(apiUrl);
|
|
74
|
+
} catch {
|
|
75
|
+
// a malformed value is refused below like any other foreign API
|
|
76
|
+
}
|
|
77
|
+
if (!api || ![serverHost, `api.${serverHost}`].includes(api.hostname.toLowerCase())) {
|
|
78
|
+
return refuse(`GITHUB_API_URL "${apiUrl}" is not on ${serverHost} or api.${serverHost}`);
|
|
79
|
+
}
|
|
80
|
+
// the token travels at least as securely as the server is reached: never in plaintext from an https server
|
|
81
|
+
const protocols = serverUrl.protocol === 'https:' ? ['https:'] : WEB_PROTOCOLS;
|
|
82
|
+
if (!protocols.includes(api.protocol)) {
|
|
83
|
+
return refuse(`GITHUB_API_URL "${apiUrl}" is not ${protocols.map(p => p.slice(0, -1)).join(' or ')}`);
|
|
84
|
+
}
|
|
85
|
+
// the base is a path prefix: `/repos/...` appended after a query or fragment would never reach the API
|
|
86
|
+
if (api.search || api.hash) return refuse(`GITHUB_API_URL "${apiUrl}" carries a query or fragment`);
|
|
87
|
+
return {
|
|
88
|
+
url: apiUrl.replace(/\/+$/, '')
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** The host a git remote URL points at, lower-cased, or undefined when the URL has no authority. */
|
|
93
|
+
function remoteHost(remoteUrl) {
|
|
94
|
+
return remoteUrl.trim().match(/^(?:[a-z+]+:\/\/)?(?:[^@/]+@)?([^:/]+)/i)?.[1]?.toLowerCase();
|
|
95
|
+
}
|
|
96
|
+
|
|
12
97
|
/**
|
|
13
|
-
* The `owner/repo` out of a
|
|
98
|
+
* The `owner/repo` out of a GitHub remote URL, anchored to the URL authority. `:digits` is a port
|
|
14
99
|
* only under a scheme; in the scp-like form (`git@github.com:12345/repo`) the colon starts the PATH
|
|
15
100
|
* and an all-digits owner is a legal GitHub username.
|
|
16
101
|
*/
|
|
17
102
|
function parseGitHubRepo(remoteUrl) {
|
|
18
103
|
const url = remoteUrl.trim();
|
|
104
|
+
const host = remoteHost(url);
|
|
105
|
+
if (!host || !gitHubHosts().includes(host)) return undefined;
|
|
106
|
+
const hostPattern = host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
19
107
|
const hasScheme = /^[a-z+]+:\/\//i.test(url);
|
|
20
|
-
const match = hasScheme ? url.match(
|
|
108
|
+
const match = hasScheme ? url.match(new RegExp(`(?:\\/\\/|@)${hostPattern}(?::\\d+)?\\/([^/]+\\/[^/]+?)(?:\\.git)?$`, 'i')) : url.match(new RegExp(`(?:^|@)${hostPattern}[:/]([^/]+\\/[^/]+?)(?:\\.git)?$`, 'i'));
|
|
21
109
|
return match?.[1];
|
|
22
110
|
}
|
|
23
111
|
|
|
24
|
-
/** The host a git remote URL points at, lower-cased, or undefined when the URL has no authority. */
|
|
25
|
-
function remoteHost(remoteUrl) {
|
|
26
|
-
return remoteUrl.trim().match(/^(?:[a-z+]+:\/\/)?(?:[^@/]+@)?([^:/]+)/i)?.[1]?.toLowerCase();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
112
|
/**
|
|
30
|
-
* Does this remote point at GitHub
|
|
31
|
-
* GitHub's to claim. Host equality, not
|
|
113
|
+
* Does this remote point at GitHub — github.com, or the GitHub Enterprise Server host `GITHUB_SERVER_URL`
|
|
114
|
+
* names? A host test only — an unparseable GitHub remote is still GitHub's to claim. Host equality, not
|
|
115
|
+
* substring: `github.com` can be a path segment on another host.
|
|
32
116
|
*/
|
|
33
117
|
function isGitHubRemote(remoteUrl) {
|
|
34
|
-
|
|
118
|
+
const host = remoteHost(remoteUrl);
|
|
119
|
+
return Boolean(host && gitHubHosts().includes(host));
|
|
35
120
|
}
|
|
36
|
-
const API = 'https://api.github.com';
|
|
37
121
|
|
|
38
122
|
/**
|
|
39
123
|
* The `rel="next"` URL out of a GitHub `Link` response header, or undefined on the last page. The
|
|
40
124
|
* header is response data, and `requestRaw` attaches the bearer token to whatever URL it is handed —
|
|
41
125
|
* so a next link whose origin is not the API's reads as "last page", never as a request target.
|
|
42
126
|
*/
|
|
43
|
-
function nextPageUrl(linkHeader) {
|
|
127
|
+
function nextPageUrl(linkHeader, apiUrl) {
|
|
44
128
|
if (!linkHeader) return undefined;
|
|
45
129
|
const next = linkHeader.split(',').find(part => part.includes('rel="next"'));
|
|
46
130
|
const url = next?.match(/<([^>]+)>/)?.[1];
|
|
47
131
|
if (!url) return undefined;
|
|
48
132
|
try {
|
|
49
|
-
return new URL(url).origin ===
|
|
133
|
+
return new URL(url).origin === new URL(apiUrl).origin ? url : undefined;
|
|
50
134
|
} catch {
|
|
51
135
|
return undefined;
|
|
52
136
|
}
|
|
@@ -61,9 +145,15 @@ class GitHubClient {
|
|
|
61
145
|
_defineProperty(this, "token", void 0);
|
|
62
146
|
/** `owner/repo`. Readable so callers (and tests) can see *which* repository a resolved client speaks to. */
|
|
63
147
|
_defineProperty(this, "repo", void 0);
|
|
148
|
+
/**
|
|
149
|
+
* The REST API base, e.g. `https://api.github.com` (the default) or a GitHub Enterprise Server's
|
|
150
|
+
* `https://<host>/api/v3`; `fromEnv` resolves it for the remote's host.
|
|
151
|
+
*/
|
|
152
|
+
_defineProperty(this, "apiUrl", void 0);
|
|
64
153
|
_defineProperty(this, "fetchImpl", void 0);
|
|
65
154
|
this.token = opts.token;
|
|
66
155
|
this.repo = opts.repo;
|
|
156
|
+
this.apiUrl = opts.apiUrl ?? GITHUB_DOT_COM_API;
|
|
67
157
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
68
158
|
}
|
|
69
159
|
|
|
@@ -86,9 +176,16 @@ class GitHubClient {
|
|
|
86
176
|
if (repoFromRemote && repoFromEnv && repoFromRemote.toLowerCase() !== repoFromEnv.toLowerCase()) {
|
|
87
177
|
warn(`bit ci sync: GITHUB_REPOSITORY is "${repoFromEnv}" but the "origin" remote points at ` + `"${repoFromRemote}" — using "${repoFromRemote}", the repository this run pushes its branches to.`);
|
|
88
178
|
}
|
|
179
|
+
// Without a remote, the GITHUB_REPOSITORY repository lives on the GITHUB_SERVER_URL instance.
|
|
180
|
+
const api = gitHubApiUrl(gitRemoteUrl ? remoteHost(gitRemoteUrl) : undefined);
|
|
181
|
+
if ('error' in api) {
|
|
182
|
+
warn(`bit ci sync: ${api.error}. The GitHub provider is not configured.`);
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
89
185
|
return new GitHubClient({
|
|
90
186
|
token,
|
|
91
|
-
repo
|
|
187
|
+
repo,
|
|
188
|
+
apiUrl: api.url
|
|
92
189
|
});
|
|
93
190
|
}
|
|
94
191
|
matchesRemote(remoteUrl) {
|
|
@@ -102,7 +199,7 @@ class GitHubClient {
|
|
|
102
199
|
|
|
103
200
|
/** `pathOrUrl` may be a path relative to this repo, or an absolute URL (a `Link` header's next page). */
|
|
104
201
|
async requestRaw(method, pathOrUrl, body) {
|
|
105
|
-
const url = /^https?:\/\//i.test(pathOrUrl) ? pathOrUrl : `${
|
|
202
|
+
const url = /^https?:\/\//i.test(pathOrUrl) ? pathOrUrl : `${this.apiUrl}/repos/${this.repo}${pathOrUrl}`;
|
|
106
203
|
const res = await this.fetchImpl(url, {
|
|
107
204
|
method,
|
|
108
205
|
headers: {
|
|
@@ -176,7 +273,7 @@ class GitHubClient {
|
|
|
176
273
|
if (match) return {
|
|
177
274
|
id: match.id
|
|
178
275
|
};
|
|
179
|
-
next = nextPageUrl(res.headers.get('link'));
|
|
276
|
+
next = nextPageUrl(res.headers.get('link'), this.apiUrl);
|
|
180
277
|
}
|
|
181
278
|
return next ? 'unknown' : 'absent';
|
|
182
279
|
}
|
|
@@ -211,8 +308,8 @@ class GitHubClient {
|
|
|
211
308
|
|
|
212
309
|
/**
|
|
213
310
|
* The registrable GitHub provider: resolves its `GitHubClient` from the environment lazily, since
|
|
214
|
-
* registration happens at aspect load, before credentials are known. A resolved client is memoized
|
|
215
|
-
* an unresolved one is retried so a later call that does carry the remote URL can still succeed.
|
|
311
|
+
* registration happens at aspect load, before credentials are known. A resolved client is memoized per
|
|
312
|
+
* remote; an unresolved one is retried so a later call that does carry the remote URL can still succeed.
|
|
216
313
|
*/
|
|
217
314
|
exports.GitHubClient = GitHubClient;
|
|
218
315
|
class GitHubHostProvider {
|
|
@@ -220,8 +317,17 @@ class GitHubHostProvider {
|
|
|
220
317
|
this.onWarning = onWarning;
|
|
221
318
|
_defineProperty(this, "name", 'github');
|
|
222
319
|
_defineProperty(this, "client", void 0);
|
|
320
|
+
/** The remote `client` was resolved for. */
|
|
321
|
+
_defineProperty(this, "clientRemote", void 0);
|
|
223
322
|
/** The last `origin` URL seen, so a PR method can still recover `owner/repo` from it. */
|
|
224
323
|
_defineProperty(this, "remoteHint", void 0);
|
|
324
|
+
/** Warnings already emitted: an unresolved client is retried on every call, its warning is not. */
|
|
325
|
+
_defineProperty(this, "warned", new Set());
|
|
326
|
+
_defineProperty(this, "warnOnce", message => {
|
|
327
|
+
if (this.warned.has(message)) return;
|
|
328
|
+
this.warned.add(message);
|
|
329
|
+
this.onWarning(message);
|
|
330
|
+
});
|
|
225
331
|
}
|
|
226
332
|
matchesRemote(remoteUrl) {
|
|
227
333
|
const matches = isGitHubRemote(remoteUrl);
|
|
@@ -260,13 +366,18 @@ class GitHubHostProvider {
|
|
|
260
366
|
return this.requireClient().upsertComment(prNumber, marker, body, options);
|
|
261
367
|
}
|
|
262
368
|
resolveClient(remoteUrl) {
|
|
263
|
-
|
|
369
|
+
const remote = remoteUrl ?? this.remoteHint;
|
|
370
|
+
// The client pins the remote's repository and API, so another remote needs its own client.
|
|
371
|
+
if (!this.client || this.clientRemote !== remote) {
|
|
372
|
+
this.client = GitHubClient.fromEnv(remote, this.warnOnce);
|
|
373
|
+
this.clientRemote = remote;
|
|
374
|
+
}
|
|
264
375
|
return this.client;
|
|
265
376
|
}
|
|
266
377
|
requireClient() {
|
|
267
378
|
const client = this.resolveClient();
|
|
268
379
|
if (!client) {
|
|
269
|
-
throw new Error(`the github host provider is not configured: set BIT_GITHUB_TOKEN (or GITHUB_TOKEN), and either ` + `GITHUB_REPOSITORY or a
|
|
380
|
+
throw new Error(`the github host provider is not configured: set BIT_GITHUB_TOKEN (or GITHUB_TOKEN), and either ` + `GITHUB_REPOSITORY or a GitHub "origin" remote (github.com, or the GITHUB_SERVER_URL host)`);
|
|
270
381
|
}
|
|
271
382
|
return client;
|
|
272
383
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["parseGitHubRepo","remoteUrl","url","trim","hasScheme","test","match","remoteHost","toLowerCase","isGitHubRemote","API","nextPageUrl","linkHeader","undefined","next","split","find","part","includes","URL","origin","noopWarn","GitHubClient","constructor","opts","_defineProperty","token","repo","fetchImpl","fetch","fromEnv","gitRemoteUrl","warn","process","env","BIT_GITHUB_TOKEN","GITHUB_TOKEN","repoFromRemote","repoFromEnv","GITHUB_REPOSITORY","matchesRemote","isConfigured","Boolean","requestRaw","method","pathOrUrl","body","res","headers","authorization","accept","JSON","stringify","ok","text","catch","Error","status","request","path","json","toPrInfo","raw","number","state","labels","map","l","name","headRef","head","ref","htmlUrl","html_url","findPrByBranch","branch","owner","list","encodeURIComponent","length","createPr","closePr","prNumber","comment","addLabel","label","findMarkedComment","marker","page","c","id","get","upsertComment","options","existing","createIfAbsent","exports","GitHubHostProvider","onWarning","matches","remoteHint","resolveClient","requireClient","client"],"sources":["github-client.ts"],"sourcesContent":["import type { GitHostProvider, PrInfo } from './git-host-provider';\n\n/**\n * The `owner/repo` out of a github.com remote URL, anchored to the URL authority. `:digits` is a port\n * only under a scheme; in the scp-like form (`git@github.com:12345/repo`) the colon starts the PATH\n * and an all-digits owner is a legal GitHub username.\n */\nexport function parseGitHubRepo(remoteUrl: string): string | undefined {\n const url = remoteUrl.trim();\n const hasScheme = /^[a-z+]+:\\/\\//i.test(url);\n const match = hasScheme\n ? url.match(/(?:\\/\\/|@)github\\.com(?::\\d+)?\\/([^/]+\\/[^/]+?)(?:\\.git)?$/i)\n : url.match(/(?:^|@)github\\.com[:/]([^/]+\\/[^/]+?)(?:\\.git)?$/i);\n return match?.[1];\n}\n\n/** The host a git remote URL points at, lower-cased, or undefined when the URL has no authority. */\nfunction remoteHost(remoteUrl: string): string | undefined {\n return remoteUrl\n .trim()\n .match(/^(?:[a-z+]+:\\/\\/)?(?:[^@/]+@)?([^:/]+)/i)?.[1]\n ?.toLowerCase();\n}\n\n/**\n * Does this remote point at GitHub? A host test only — an unparseable github.com remote is still\n * GitHub's to claim. Host equality, not substring: `github.com` can be a path segment on another host.\n */\nexport function isGitHubRemote(remoteUrl: string): boolean {\n return remoteHost(remoteUrl) === 'github.com';\n}\n\nconst API = 'https://api.github.com';\n\n/**\n * The `rel=\"next\"` URL out of a GitHub `Link` response header, or undefined on the last page. The\n * header is response data, and `requestRaw` attaches the bearer token to whatever URL it is handed —\n * so a next link whose origin is not the API's reads as \"last page\", never as a request target.\n */\nfunction nextPageUrl(linkHeader: string | null): string | undefined {\n if (!linkHeader) return undefined;\n const next = linkHeader.split(',').find((part) => part.includes('rel=\"next\"'));\n const url = next?.match(/<([^>]+)>/)?.[1];\n if (!url) return undefined;\n try {\n return new URL(url).origin === API ? url : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Warning sink; a plain callback rather than a `Logger` so this module needs no logger aspect. */\nexport type WarnFn = (message: string) => void;\n\nconst noopWarn: WarnFn = () => {};\n\nexport class GitHubClient implements GitHostProvider {\n readonly name = 'github';\n\n private token: string;\n /** `owner/repo`. Readable so callers (and tests) can see *which* repository a resolved client speaks to. */\n readonly repo: string;\n private fetchImpl: typeof fetch;\n\n constructor(opts: { token: string; repo: string; fetchImpl?: typeof fetch }) {\n this.token = opts.token;\n this.repo = opts.repo;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n }\n\n /**\n * The client this environment describes, or undefined when it describes none. `origin` outranks\n * `GITHUB_REPOSITORY`: every PR operation names a branch that only exists on `origin`, so if the two\n * disagree the origin-parsed repository wins and the mismatch is warned about once.\n */\n static fromEnv(gitRemoteUrl?: string, warn: WarnFn = noopWarn): GitHubClient | undefined {\n // BIT_GITHUB_TOKEN is the override; GITHUB_TOKEN is the ambient default Actions injects everywhere.\n const token = process.env.BIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;\n // A remote that is demonstrably not GitHub's is a hard no, whatever the environment says —\n // GITHUB_REPOSITORY is set on every Actions job, including ones whose origin is another host.\n if (gitRemoteUrl && !isGitHubRemote(gitRemoteUrl)) return undefined;\n const repoFromRemote = gitRemoteUrl ? parseGitHubRepo(gitRemoteUrl) : undefined;\n const repoFromEnv = process.env.GITHUB_REPOSITORY;\n const repo = repoFromRemote || repoFromEnv;\n if (!token || !repo) return undefined;\n // GitHub owner/repo names are case-insensitive.\n if (repoFromRemote && repoFromEnv && repoFromRemote.toLowerCase() !== repoFromEnv.toLowerCase()) {\n warn(\n `bit ci sync: GITHUB_REPOSITORY is \"${repoFromEnv}\" but the \"origin\" remote points at ` +\n `\"${repoFromRemote}\" — using \"${repoFromRemote}\", the repository this run pushes its branches to.`\n );\n }\n return new GitHubClient({ token, repo });\n }\n\n matchesRemote(remoteUrl: string): boolean {\n return isGitHubRemote(remoteUrl);\n }\n\n /** A constructed client always has both halves — it cannot be built without them. */\n isConfigured(): boolean {\n return Boolean(this.token && this.repo);\n }\n\n /** `pathOrUrl` may be a path relative to this repo, or an absolute URL (a `Link` header's next page). */\n private async requestRaw(method: string, pathOrUrl: string, body?: unknown): Promise<Response> {\n const url = /^https?:\\/\\//i.test(pathOrUrl) ? pathOrUrl : `${API}/repos/${this.repo}${pathOrUrl}`;\n const res = await this.fetchImpl(url, {\n method,\n headers: {\n authorization: `Bearer ${this.token}`,\n accept: 'application/vnd.github+json',\n 'content-type': 'application/json',\n 'x-github-api-version': '2022-11-28',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`GitHub API ${method} ${pathOrUrl} failed: ${res.status} ${text}`);\n }\n return res;\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<any> {\n const res = await this.requestRaw(method, path, body);\n return res.status === 204 ? undefined : res.json();\n }\n\n private toPrInfo(raw: any): PrInfo {\n return {\n number: raw.number,\n state: raw.state,\n labels: (raw.labels ?? []).map((l: any) => (typeof l === 'string' ? l : l.name)),\n headRef: raw.head?.ref,\n htmlUrl: raw.html_url,\n };\n }\n\n async findPrByBranch(branch: string): Promise<PrInfo | undefined> {\n const owner = this.repo.split('/')[0];\n const list = await this.request('GET', `/pulls?state=open&head=${encodeURIComponent(`${owner}:${branch}`)}`);\n return list.length ? this.toPrInfo(list[0]) : undefined;\n }\n\n async createPr(opts: { head: string; base: string; title: string; body: string }): Promise<PrInfo> {\n return this.toPrInfo(await this.request('POST', '/pulls', opts));\n }\n\n async closePr(prNumber: number, comment?: string): Promise<void> {\n if (comment) await this.comment(prNumber, comment);\n await this.request('PATCH', `/pulls/${prNumber}`, { state: 'closed' });\n }\n\n async comment(prNumber: number, body: string): Promise<void> {\n await this.request('POST', `/issues/${prNumber}/comments`, { body });\n }\n\n async addLabel(prNumber: number, label: string): Promise<void> {\n await this.request('POST', `/issues/${prNumber}/labels`, { labels: [label] });\n }\n\n /**\n * The marker-bearing comment on the PR, `'absent'` when every page was searched, or `'unknown'`\n * when the defensive page cap cut the search short. GitHub defaults `per_page` to 30 and orders\n * ascending, and the maintained comment is posted once, early in a PR's life — so it lives in the\n * first pages and the search returns as soon as it is seen. `'unknown'` is distinct from `'absent'`\n * because running out of budget must not read as \"not there\": the caller would post a duplicate\n * report on every later push.\n */\n private async findMarkedComment(prNumber: number, marker: string): Promise<{ id: number } | 'absent' | 'unknown'> {\n let next: string | undefined = `/issues/${prNumber}/comments?per_page=100`;\n // Defensive cap: 20 pages (2,000 comments) is far past any real PR; without it a malformed or\n // adversarial `Link` header could loop this call forever.\n for (let page = 0; next && page < 20; page += 1) {\n const res: Response = await this.requestRaw('GET', next);\n const body = (await res.json()) as any[];\n const match = body.find((c: any) => (c.body ?? '').includes(marker));\n if (match) return { id: match.id };\n next = nextPageUrl(res.headers.get('link'));\n }\n return next ? 'unknown' : 'absent';\n }\n\n /**\n * Find the comment carrying `marker` (an HTML comment embedded in the body) and replace its body,\n * or post `body` as a new comment when none exists and `options.createIfAbsent` is not false.\n * GitHub's comment PATCH endpoint is `/issues/comments/{id}`, not `/issues/{pr}/comments/{id}` —\n * a comment id is unique per repository, not scoped under the issue/PR that carries it.\n *\n * Named to match `GitHostProvider.upsertComment` exactly (not e.g. `upsertIssueComment`): a\n * `GitHubClient` registered directly as a provider must satisfy the interface's optional method\n * under its real name, or callers that feature-test via `gitHost.upsertComment` would silently\n * treat a fully-capable client as unsupported.\n */\n async upsertComment(\n prNumber: number,\n marker: string,\n body: string,\n options: { createIfAbsent?: boolean } = {}\n ): Promise<void> {\n const existing = await this.findMarkedComment(prNumber, marker);\n if (existing === 'unknown') {\n // Surfaces through the caller's warn-and-skip path; posting blind would duplicate the report.\n throw new Error(\n `PR #${prNumber} has too many comments to establish whether the \"${marker}\" comment already exists`\n );\n }\n if (existing !== 'absent') {\n await this.request('PATCH', `/issues/comments/${existing.id}`, { body });\n return;\n }\n if (options.createIfAbsent === false) return;\n await this.comment(prNumber, body);\n }\n}\n\n/**\n * The registrable GitHub provider: resolves its `GitHubClient` from the environment lazily, since\n * registration happens at aspect load, before credentials are known. A resolved client is memoized;\n * an unresolved one is retried so a later call that does carry the remote URL can still succeed.\n */\nexport class GitHubHostProvider implements GitHostProvider {\n readonly name = 'github';\n\n private client: GitHubClient | undefined;\n\n /** The last `origin` URL seen, so a PR method can still recover `owner/repo` from it. */\n private remoteHint: string | undefined;\n\n constructor(private onWarning: WarnFn = noopWarn) {}\n\n matchesRemote(remoteUrl: string): boolean {\n const matches = isGitHubRemote(remoteUrl);\n // Only remember a remote that is ours — a foreign hint could later pair with GITHUB_REPOSITORY\n // into a client for the wrong repository.\n if (matches) this.remoteHint = remoteUrl;\n return matches;\n }\n\n /**\n * A known non-GitHub remote can never count as configured, however complete the environment looks:\n * on Actions the env is always set, so answering \"configured\" for a remote this provider declined to\n * claim would make it the sole-configured fallback and aim PRs at the wrong host's repository.\n */\n isConfigured(remoteUrl?: string): boolean {\n if (remoteUrl && !isGitHubRemote(remoteUrl)) return false;\n if (remoteUrl) this.remoteHint = remoteUrl;\n return Boolean(this.resolveClient(remoteUrl));\n }\n\n async findPrByBranch(branch: string): Promise<PrInfo | undefined> {\n return this.requireClient().findPrByBranch(branch);\n }\n\n async createPr(opts: { head: string; base: string; title: string; body: string }): Promise<PrInfo> {\n return this.requireClient().createPr(opts);\n }\n\n async closePr(prNumber: number, comment?: string): Promise<void> {\n return this.requireClient().closePr(prNumber, comment);\n }\n\n async comment(prNumber: number, body: string): Promise<void> {\n return this.requireClient().comment(prNumber, body);\n }\n\n async addLabel(prNumber: number, label: string): Promise<void> {\n return this.requireClient().addLabel(prNumber, label);\n }\n\n async upsertComment(\n prNumber: number,\n marker: string,\n body: string,\n options?: { createIfAbsent?: boolean }\n ): Promise<void> {\n return this.requireClient().upsertComment(prNumber, marker, body, options);\n }\n\n private resolveClient(remoteUrl?: string): GitHubClient | undefined {\n if (!this.client) this.client = GitHubClient.fromEnv(remoteUrl ?? this.remoteHint, this.onWarning);\n return this.client;\n }\n\n private requireClient(): GitHubClient {\n const client = this.resolveClient();\n if (!client) {\n throw new Error(\n `the github host provider is not configured: set BIT_GITHUB_TOKEN (or GITHUB_TOKEN), and either ` +\n `GITHUB_REPOSITORY or a github.com \"origin\" remote`\n );\n }\n return client;\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["GITHUB_DOT_COM","GITHUB_DOT_COM_API","WEB_PROTOCOLS","gitHubServerUrl","process","env","GITHUB_SERVER_URL","URL","undefined","gitHubServerHost","hostname","toLowerCase","gitHubHosts","serverHost","gitHubApiUrl","host","serverUrl","url","refuse","reason","error","includes","protocol","apiUrl","GITHUB_API_URL","origin","api","protocols","map","p","slice","join","search","hash","replace","remoteHost","remoteUrl","trim","match","parseGitHubRepo","hostPattern","hasScheme","test","RegExp","isGitHubRemote","Boolean","nextPageUrl","linkHeader","next","split","find","part","noopWarn","GitHubClient","constructor","opts","_defineProperty","token","repo","fetchImpl","fetch","fromEnv","gitRemoteUrl","warn","BIT_GITHUB_TOKEN","GITHUB_TOKEN","repoFromRemote","repoFromEnv","GITHUB_REPOSITORY","matchesRemote","isConfigured","requestRaw","method","pathOrUrl","body","res","headers","authorization","accept","JSON","stringify","ok","text","catch","Error","status","request","path","json","toPrInfo","raw","number","state","labels","l","name","headRef","head","ref","htmlUrl","html_url","findPrByBranch","branch","owner","list","encodeURIComponent","length","createPr","closePr","prNumber","comment","addLabel","label","findMarkedComment","marker","page","c","id","get","upsertComment","options","existing","createIfAbsent","exports","GitHubHostProvider","onWarning","Set","message","warned","has","add","matches","remoteHint","resolveClient","requireClient","remote","client","clientRemote","warnOnce"],"sources":["github-client.ts"],"sourcesContent":["import type { GitHostProvider, PrInfo } from './git-host-provider';\n\nconst GITHUB_DOT_COM = 'github.com';\nconst GITHUB_DOT_COM_API = 'https://api.github.com';\nconst WEB_PROTOCOLS = ['http:', 'https:'];\n\n/** The GitHub instance `GITHUB_SERVER_URL` names, or undefined when it is unset or malformed. */\nfunction gitHubServerUrl(): URL | undefined {\n try {\n if (process.env.GITHUB_SERVER_URL) return new URL(process.env.GITHUB_SERVER_URL);\n } catch {\n // a malformed value names no instance\n }\n return undefined;\n}\n\n/**\n * The host of the GitHub instance this run belongs to: the one `GITHUB_SERVER_URL` names (Actions sets\n * it on every runner — `https://github.com`, or the GitHub Enterprise Server URL), else github.com.\n */\nexport function gitHubServerHost(): string {\n return gitHubServerUrl()?.hostname.toLowerCase() || GITHUB_DOT_COM;\n}\n\n/** The hosts whose remotes are GitHub's: github.com, plus a GitHub Enterprise Server host. */\nfunction gitHubHosts(): string[] {\n const serverHost = gitHubServerHost();\n return serverHost === GITHUB_DOT_COM ? [GITHUB_DOT_COM] : [GITHUB_DOT_COM, serverHost];\n}\n\n/** A resolved REST API base, or why the environment's API cannot be trusted with the repository's token. */\nexport type GitHubApiUrl = { url: string } | { error: string };\n\n/**\n * The REST API base for a repository on `host` (the `GITHUB_SERVER_URL` instance's host when omitted).\n * Every request to it carries that instance's token, so:\n * - github.com (and any host that is not the enterprise one) is always `https://api.github.com`.\n * - the enterprise host takes `GITHUB_API_URL` only when it lives on that host or on `api.<host>`\n * (GHE.com data residency: `https://<sub>.ghe.com` serves its API at `https://api.<sub>.ghe.com`);\n * unset, it is the GHES convention `<GITHUB_SERVER_URL origin>/api/v3`. An https server's API must be\n * https too, so the token never travels in plaintext. Any other API is refused — never replaced by\n * api.github.com, which must not receive an enterprise token.\n */\nexport function gitHubApiUrl(host?: string): GitHubApiUrl {\n const serverUrl = gitHubServerUrl();\n const serverHost = gitHubServerHost();\n if (!serverUrl || serverHost === GITHUB_DOT_COM || (host && host.toLowerCase() !== serverHost)) {\n return { url: GITHUB_DOT_COM_API };\n }\n const refuse = (reason: string): GitHubApiUrl => ({\n error: `${reason} — refusing to send the token of the GITHUB_SERVER_URL instance \"${process.env.GITHUB_SERVER_URL}\" to it`,\n });\n if (!WEB_PROTOCOLS.includes(serverUrl.protocol)) return refuse(`GITHUB_SERVER_URL is not an http(s) URL`);\n const apiUrl = process.env.GITHUB_API_URL;\n if (!apiUrl) return { url: `${serverUrl.origin}/api/v3` };\n let api: URL | undefined;\n try {\n api = new URL(apiUrl);\n } catch {\n // a malformed value is refused below like any other foreign API\n }\n if (!api || ![serverHost, `api.${serverHost}`].includes(api.hostname.toLowerCase())) {\n return refuse(`GITHUB_API_URL \"${apiUrl}\" is not on ${serverHost} or api.${serverHost}`);\n }\n // the token travels at least as securely as the server is reached: never in plaintext from an https server\n const protocols = serverUrl.protocol === 'https:' ? ['https:'] : WEB_PROTOCOLS;\n if (!protocols.includes(api.protocol)) {\n return refuse(`GITHUB_API_URL \"${apiUrl}\" is not ${protocols.map((p) => p.slice(0, -1)).join(' or ')}`);\n }\n // the base is a path prefix: `/repos/...` appended after a query or fragment would never reach the API\n if (api.search || api.hash) return refuse(`GITHUB_API_URL \"${apiUrl}\" carries a query or fragment`);\n return { url: apiUrl.replace(/\\/+$/, '') };\n}\n\n/** The host a git remote URL points at, lower-cased, or undefined when the URL has no authority. */\nexport function remoteHost(remoteUrl: string): string | undefined {\n return remoteUrl\n .trim()\n .match(/^(?:[a-z+]+:\\/\\/)?(?:[^@/]+@)?([^:/]+)/i)?.[1]\n ?.toLowerCase();\n}\n\n/**\n * The `owner/repo` out of a GitHub remote URL, anchored to the URL authority. `:digits` is a port\n * only under a scheme; in the scp-like form (`git@github.com:12345/repo`) the colon starts the PATH\n * and an all-digits owner is a legal GitHub username.\n */\nexport function parseGitHubRepo(remoteUrl: string): string | undefined {\n const url = remoteUrl.trim();\n const host = remoteHost(url);\n if (!host || !gitHubHosts().includes(host)) return undefined;\n const hostPattern = host.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const hasScheme = /^[a-z+]+:\\/\\//i.test(url);\n const match = hasScheme\n ? url.match(new RegExp(`(?:\\\\/\\\\/|@)${hostPattern}(?::\\\\d+)?\\\\/([^/]+\\\\/[^/]+?)(?:\\\\.git)?$`, 'i'))\n : url.match(new RegExp(`(?:^|@)${hostPattern}[:/]([^/]+\\\\/[^/]+?)(?:\\\\.git)?$`, 'i'));\n return match?.[1];\n}\n\n/**\n * Does this remote point at GitHub — github.com, or the GitHub Enterprise Server host `GITHUB_SERVER_URL`\n * names? A host test only — an unparseable GitHub remote is still GitHub's to claim. Host equality, not\n * substring: `github.com` can be a path segment on another host.\n */\nexport function isGitHubRemote(remoteUrl: string): boolean {\n const host = remoteHost(remoteUrl);\n return Boolean(host && gitHubHosts().includes(host));\n}\n\n/**\n * The `rel=\"next\"` URL out of a GitHub `Link` response header, or undefined on the last page. The\n * header is response data, and `requestRaw` attaches the bearer token to whatever URL it is handed —\n * so a next link whose origin is not the API's reads as \"last page\", never as a request target.\n */\nfunction nextPageUrl(linkHeader: string | null, apiUrl: string): string | undefined {\n if (!linkHeader) return undefined;\n const next = linkHeader.split(',').find((part) => part.includes('rel=\"next\"'));\n const url = next?.match(/<([^>]+)>/)?.[1];\n if (!url) return undefined;\n try {\n return new URL(url).origin === new URL(apiUrl).origin ? url : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Warning sink; a plain callback rather than a `Logger` so this module needs no logger aspect. */\nexport type WarnFn = (message: string) => void;\n\nconst noopWarn: WarnFn = () => {};\n\nexport class GitHubClient implements GitHostProvider {\n readonly name = 'github';\n\n private token: string;\n /** `owner/repo`. Readable so callers (and tests) can see *which* repository a resolved client speaks to. */\n readonly repo: string;\n /**\n * The REST API base, e.g. `https://api.github.com` (the default) or a GitHub Enterprise Server's\n * `https://<host>/api/v3`; `fromEnv` resolves it for the remote's host.\n */\n readonly apiUrl: string;\n private fetchImpl: typeof fetch;\n\n constructor(opts: { token: string; repo: string; apiUrl?: string; fetchImpl?: typeof fetch }) {\n this.token = opts.token;\n this.repo = opts.repo;\n this.apiUrl = opts.apiUrl ?? GITHUB_DOT_COM_API;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n }\n\n /**\n * The client this environment describes, or undefined when it describes none. `origin` outranks\n * `GITHUB_REPOSITORY`: every PR operation names a branch that only exists on `origin`, so if the two\n * disagree the origin-parsed repository wins and the mismatch is warned about once.\n */\n static fromEnv(gitRemoteUrl?: string, warn: WarnFn = noopWarn): GitHubClient | undefined {\n // BIT_GITHUB_TOKEN is the override; GITHUB_TOKEN is the ambient default Actions injects everywhere.\n const token = process.env.BIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;\n // A remote that is demonstrably not GitHub's is a hard no, whatever the environment says —\n // GITHUB_REPOSITORY is set on every Actions job, including ones whose origin is another host.\n if (gitRemoteUrl && !isGitHubRemote(gitRemoteUrl)) return undefined;\n const repoFromRemote = gitRemoteUrl ? parseGitHubRepo(gitRemoteUrl) : undefined;\n const repoFromEnv = process.env.GITHUB_REPOSITORY;\n const repo = repoFromRemote || repoFromEnv;\n if (!token || !repo) return undefined;\n // GitHub owner/repo names are case-insensitive.\n if (repoFromRemote && repoFromEnv && repoFromRemote.toLowerCase() !== repoFromEnv.toLowerCase()) {\n warn(\n `bit ci sync: GITHUB_REPOSITORY is \"${repoFromEnv}\" but the \"origin\" remote points at ` +\n `\"${repoFromRemote}\" — using \"${repoFromRemote}\", the repository this run pushes its branches to.`\n );\n }\n // Without a remote, the GITHUB_REPOSITORY repository lives on the GITHUB_SERVER_URL instance.\n const api = gitHubApiUrl(gitRemoteUrl ? remoteHost(gitRemoteUrl) : undefined);\n if ('error' in api) {\n warn(`bit ci sync: ${api.error}. The GitHub provider is not configured.`);\n return undefined;\n }\n return new GitHubClient({ token, repo, apiUrl: api.url });\n }\n\n matchesRemote(remoteUrl: string): boolean {\n return isGitHubRemote(remoteUrl);\n }\n\n /** A constructed client always has both halves — it cannot be built without them. */\n isConfigured(): boolean {\n return Boolean(this.token && this.repo);\n }\n\n /** `pathOrUrl` may be a path relative to this repo, or an absolute URL (a `Link` header's next page). */\n private async requestRaw(method: string, pathOrUrl: string, body?: unknown): Promise<Response> {\n const url = /^https?:\\/\\//i.test(pathOrUrl) ? pathOrUrl : `${this.apiUrl}/repos/${this.repo}${pathOrUrl}`;\n const res = await this.fetchImpl(url, {\n method,\n headers: {\n authorization: `Bearer ${this.token}`,\n accept: 'application/vnd.github+json',\n 'content-type': 'application/json',\n 'x-github-api-version': '2022-11-28',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`GitHub API ${method} ${pathOrUrl} failed: ${res.status} ${text}`);\n }\n return res;\n }\n\n private async request(method: string, path: string, body?: unknown): Promise<any> {\n const res = await this.requestRaw(method, path, body);\n return res.status === 204 ? undefined : res.json();\n }\n\n private toPrInfo(raw: any): PrInfo {\n return {\n number: raw.number,\n state: raw.state,\n labels: (raw.labels ?? []).map((l: any) => (typeof l === 'string' ? l : l.name)),\n headRef: raw.head?.ref,\n htmlUrl: raw.html_url,\n };\n }\n\n async findPrByBranch(branch: string): Promise<PrInfo | undefined> {\n const owner = this.repo.split('/')[0];\n const list = await this.request('GET', `/pulls?state=open&head=${encodeURIComponent(`${owner}:${branch}`)}`);\n return list.length ? this.toPrInfo(list[0]) : undefined;\n }\n\n async createPr(opts: { head: string; base: string; title: string; body: string }): Promise<PrInfo> {\n return this.toPrInfo(await this.request('POST', '/pulls', opts));\n }\n\n async closePr(prNumber: number, comment?: string): Promise<void> {\n if (comment) await this.comment(prNumber, comment);\n await this.request('PATCH', `/pulls/${prNumber}`, { state: 'closed' });\n }\n\n async comment(prNumber: number, body: string): Promise<void> {\n await this.request('POST', `/issues/${prNumber}/comments`, { body });\n }\n\n async addLabel(prNumber: number, label: string): Promise<void> {\n await this.request('POST', `/issues/${prNumber}/labels`, { labels: [label] });\n }\n\n /**\n * The marker-bearing comment on the PR, `'absent'` when every page was searched, or `'unknown'`\n * when the defensive page cap cut the search short. GitHub defaults `per_page` to 30 and orders\n * ascending, and the maintained comment is posted once, early in a PR's life — so it lives in the\n * first pages and the search returns as soon as it is seen. `'unknown'` is distinct from `'absent'`\n * because running out of budget must not read as \"not there\": the caller would post a duplicate\n * report on every later push.\n */\n private async findMarkedComment(prNumber: number, marker: string): Promise<{ id: number } | 'absent' | 'unknown'> {\n let next: string | undefined = `/issues/${prNumber}/comments?per_page=100`;\n // Defensive cap: 20 pages (2,000 comments) is far past any real PR; without it a malformed or\n // adversarial `Link` header could loop this call forever.\n for (let page = 0; next && page < 20; page += 1) {\n const res: Response = await this.requestRaw('GET', next);\n const body = (await res.json()) as any[];\n const match = body.find((c: any) => (c.body ?? '').includes(marker));\n if (match) return { id: match.id };\n next = nextPageUrl(res.headers.get('link'), this.apiUrl);\n }\n return next ? 'unknown' : 'absent';\n }\n\n /**\n * Find the comment carrying `marker` (an HTML comment embedded in the body) and replace its body,\n * or post `body` as a new comment when none exists and `options.createIfAbsent` is not false.\n * GitHub's comment PATCH endpoint is `/issues/comments/{id}`, not `/issues/{pr}/comments/{id}` —\n * a comment id is unique per repository, not scoped under the issue/PR that carries it.\n *\n * Named to match `GitHostProvider.upsertComment` exactly (not e.g. `upsertIssueComment`): a\n * `GitHubClient` registered directly as a provider must satisfy the interface's optional method\n * under its real name, or callers that feature-test via `gitHost.upsertComment` would silently\n * treat a fully-capable client as unsupported.\n */\n async upsertComment(\n prNumber: number,\n marker: string,\n body: string,\n options: { createIfAbsent?: boolean } = {}\n ): Promise<void> {\n const existing = await this.findMarkedComment(prNumber, marker);\n if (existing === 'unknown') {\n // Surfaces through the caller's warn-and-skip path; posting blind would duplicate the report.\n throw new Error(\n `PR #${prNumber} has too many comments to establish whether the \"${marker}\" comment already exists`\n );\n }\n if (existing !== 'absent') {\n await this.request('PATCH', `/issues/comments/${existing.id}`, { body });\n return;\n }\n if (options.createIfAbsent === false) return;\n await this.comment(prNumber, body);\n }\n}\n\n/**\n * The registrable GitHub provider: resolves its `GitHubClient` from the environment lazily, since\n * registration happens at aspect load, before credentials are known. A resolved client is memoized per\n * remote; an unresolved one is retried so a later call that does carry the remote URL can still succeed.\n */\nexport class GitHubHostProvider implements GitHostProvider {\n readonly name = 'github';\n\n private client: GitHubClient | undefined;\n\n /** The remote `client` was resolved for. */\n private clientRemote: string | undefined;\n\n /** The last `origin` URL seen, so a PR method can still recover `owner/repo` from it. */\n private remoteHint: string | undefined;\n\n /** Warnings already emitted: an unresolved client is retried on every call, its warning is not. */\n private warned = new Set<string>();\n\n constructor(private onWarning: WarnFn = noopWarn) {}\n\n matchesRemote(remoteUrl: string): boolean {\n const matches = isGitHubRemote(remoteUrl);\n // Only remember a remote that is ours — a foreign hint could later pair with GITHUB_REPOSITORY\n // into a client for the wrong repository.\n if (matches) this.remoteHint = remoteUrl;\n return matches;\n }\n\n /**\n * A known non-GitHub remote can never count as configured, however complete the environment looks:\n * on Actions the env is always set, so answering \"configured\" for a remote this provider declined to\n * claim would make it the sole-configured fallback and aim PRs at the wrong host's repository.\n */\n isConfigured(remoteUrl?: string): boolean {\n if (remoteUrl && !isGitHubRemote(remoteUrl)) return false;\n if (remoteUrl) this.remoteHint = remoteUrl;\n return Boolean(this.resolveClient(remoteUrl));\n }\n\n async findPrByBranch(branch: string): Promise<PrInfo | undefined> {\n return this.requireClient().findPrByBranch(branch);\n }\n\n async createPr(opts: { head: string; base: string; title: string; body: string }): Promise<PrInfo> {\n return this.requireClient().createPr(opts);\n }\n\n async closePr(prNumber: number, comment?: string): Promise<void> {\n return this.requireClient().closePr(prNumber, comment);\n }\n\n async comment(prNumber: number, body: string): Promise<void> {\n return this.requireClient().comment(prNumber, body);\n }\n\n async addLabel(prNumber: number, label: string): Promise<void> {\n return this.requireClient().addLabel(prNumber, label);\n }\n\n async upsertComment(\n prNumber: number,\n marker: string,\n body: string,\n options?: { createIfAbsent?: boolean }\n ): Promise<void> {\n return this.requireClient().upsertComment(prNumber, marker, body, options);\n }\n\n private resolveClient(remoteUrl?: string): GitHubClient | undefined {\n const remote = remoteUrl ?? this.remoteHint;\n // The client pins the remote's repository and API, so another remote needs its own client.\n if (!this.client || this.clientRemote !== remote) {\n this.client = GitHubClient.fromEnv(remote, this.warnOnce);\n this.clientRemote = remote;\n }\n return this.client;\n }\n\n private warnOnce: WarnFn = (message) => {\n if (this.warned.has(message)) return;\n this.warned.add(message);\n this.onWarning(message);\n };\n\n private requireClient(): GitHubClient {\n const client = this.resolveClient();\n if (!client) {\n throw new Error(\n `the github host provider is not configured: set BIT_GITHUB_TOKEN (or GITHUB_TOKEN), and either ` +\n `GITHUB_REPOSITORY or a GitHub \"origin\" remote (github.com, or the GITHUB_SERVER_URL host)`\n );\n }\n return client;\n }\n}\n"],"mappings":"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","ignoreList":[]}
|