@teambit/ci 1.0.485 → 1.0.487

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.
Files changed (97) hide show
  1. package/ci.docs.mdx +344 -83
  2. package/commands/sync.cmd.ts +77 -0
  3. package/dist/ci.docs.mdx +344 -83
  4. package/dist/ci.main.runtime.d.ts +67 -6
  5. package/dist/ci.main.runtime.js +152 -25
  6. package/dist/ci.main.runtime.js.map +1 -1
  7. package/dist/commands/sync.cmd.d.ts +34 -0
  8. package/dist/commands/sync.cmd.js +57 -0
  9. package/dist/commands/sync.cmd.js.map +1 -0
  10. package/dist/git.d.ts +11 -1
  11. package/dist/git.js +11 -1
  12. package/dist/git.js.map +1 -1
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +26 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/{preview-1785858957533.js → preview-1785883495880.js} +1 -1
  17. package/dist/sync/git-host-provider.d.ts +47 -0
  18. package/dist/sync/git-host-provider.js +54 -0
  19. package/dist/sync/git-host-provider.js.map +1 -0
  20. package/dist/sync/git-host-provider.spec.d.ts +1 -0
  21. package/dist/sync/git-host-provider.spec.js +171 -0
  22. package/dist/sync/git-host-provider.spec.js.map +1 -0
  23. package/dist/sync/git-ops.d.ts +171 -0
  24. package/dist/sync/git-ops.js +354 -0
  25. package/dist/sync/git-ops.js.map +1 -0
  26. package/dist/sync/git-ops.spec.d.ts +1 -0
  27. package/dist/sync/git-ops.spec.js +476 -0
  28. package/dist/sync/git-ops.spec.js.map +1 -0
  29. package/dist/sync/github-client.d.ts +79 -0
  30. package/dist/sync/github-client.js +198 -0
  31. package/dist/sync/github-client.js.map +1 -0
  32. package/dist/sync/github-client.spec.d.ts +1 -0
  33. package/dist/sync/github-client.spec.js +205 -0
  34. package/dist/sync/github-client.spec.js.map +1 -0
  35. package/dist/sync/init-scaffold.d.ts +40 -0
  36. package/dist/sync/init-scaffold.js +326 -0
  37. package/dist/sync/init-scaffold.js.map +1 -0
  38. package/dist/sync/init-scaffold.spec.d.ts +1 -0
  39. package/dist/sync/init-scaffold.spec.js +169 -0
  40. package/dist/sync/init-scaffold.spec.js.map +1 -0
  41. package/dist/sync/lane-sync-executor.d.ts +256 -0
  42. package/dist/sync/lane-sync-executor.js +1249 -0
  43. package/dist/sync/lane-sync-executor.js.map +1 -0
  44. package/dist/sync/lane-sync-executor.spec.d.ts +1 -0
  45. package/dist/sync/lane-sync-executor.spec.js +416 -0
  46. package/dist/sync/lane-sync-executor.spec.js.map +1 -0
  47. package/dist/sync/main-sync-executor.d.ts +62 -0
  48. package/dist/sync/main-sync-executor.js +305 -0
  49. package/dist/sync/main-sync-executor.js.map +1 -0
  50. package/dist/sync/sync-config.d.ts +83 -0
  51. package/dist/sync/sync-config.js +187 -0
  52. package/dist/sync/sync-config.js.map +1 -0
  53. package/dist/sync/sync-config.spec.d.ts +1 -0
  54. package/dist/sync/sync-config.spec.js +254 -0
  55. package/dist/sync/sync-config.spec.js.map +1 -0
  56. package/dist/sync/sync-orchestrator.d.ts +76 -0
  57. package/dist/sync/sync-orchestrator.js +396 -0
  58. package/dist/sync/sync-orchestrator.js.map +1 -0
  59. package/dist/sync/sync-orchestrator.spec.d.ts +1 -0
  60. package/dist/sync/sync-orchestrator.spec.js +155 -0
  61. package/dist/sync/sync-orchestrator.spec.js.map +1 -0
  62. package/dist/sync/sync-planner.d.ts +60 -0
  63. package/dist/sync/sync-planner.js +114 -0
  64. package/dist/sync/sync-planner.js.map +1 -0
  65. package/dist/sync/sync-planner.spec.d.ts +1 -0
  66. package/dist/sync/sync-planner.spec.js +210 -0
  67. package/dist/sync/sync-planner.spec.js.map +1 -0
  68. package/dist/sync/sync-state.d.ts +94 -0
  69. package/dist/sync/sync-state.js +178 -0
  70. package/dist/sync/sync-state.js.map +1 -0
  71. package/dist/sync/sync-state.spec.d.ts +1 -0
  72. package/dist/sync/sync-state.spec.js +271 -0
  73. package/dist/sync/sync-state.spec.js.map +1 -0
  74. package/dist/sync/workspace-lane.d.ts +13 -0
  75. package/dist/sync/workspace-lane.js +27 -0
  76. package/dist/sync/workspace-lane.js.map +1 -0
  77. package/package.json +20 -15
  78. package/sync/git-host-provider.spec.ts +197 -0
  79. package/sync/git-host-provider.ts +89 -0
  80. package/sync/git-ops.spec.ts +506 -0
  81. package/sync/git-ops.ts +334 -0
  82. package/sync/github-client.spec.ts +174 -0
  83. package/sync/github-client.ts +209 -0
  84. package/sync/init-scaffold.spec.ts +142 -0
  85. package/sync/init-scaffold.ts +340 -0
  86. package/sync/lane-sync-executor.spec.ts +389 -0
  87. package/sync/lane-sync-executor.ts +1363 -0
  88. package/sync/main-sync-executor.ts +321 -0
  89. package/sync/sync-config.spec.ts +283 -0
  90. package/sync/sync-config.ts +214 -0
  91. package/sync/sync-orchestrator.spec.ts +120 -0
  92. package/sync/sync-orchestrator.ts +386 -0
  93. package/sync/sync-planner.spec.ts +126 -0
  94. package/sync/sync-planner.ts +99 -0
  95. package/sync/sync-state.spec.ts +263 -0
  96. package/sync/sync-state.ts +176 -0
  97. package/sync/workspace-lane.ts +20 -0
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.GitHubHostProvider = exports.GitHubClient = void 0;
7
+ exports.isGitHubRemote = isGitHubRemote;
8
+ exports.parseGitHubRepo = parseGitHubRepo;
9
+ 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
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
11
+ 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); }
12
+ /**
13
+ * The `owner/repo` out of a github.com remote URL, anchored to the URL authority. `:digits` is a port
14
+ * only under a scheme; in the scp-like form (`git@github.com:12345/repo`) the colon starts the PATH
15
+ * and an all-digits owner is a legal GitHub username.
16
+ */
17
+ function parseGitHubRepo(remoteUrl) {
18
+ const url = remoteUrl.trim();
19
+ const hasScheme = /^[a-z+]+:\/\//i.test(url);
20
+ const match = hasScheme ? url.match(/(?:\/\/|@)github\.com(?::\d+)?\/([^/]+\/[^/]+?)(?:\.git)?$/i) : url.match(/(?:^|@)github\.com[:/]([^/]+\/[^/]+?)(?:\.git)?$/i);
21
+ return match?.[1];
22
+ }
23
+
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
+ /**
30
+ * Does this remote point at GitHub? A host test only — an unparseable github.com remote is still
31
+ * GitHub's to claim. Host equality, not substring: `github.com` can be a path segment on another host.
32
+ */
33
+ function isGitHubRemote(remoteUrl) {
34
+ return remoteHost(remoteUrl) === 'github.com';
35
+ }
36
+ const API = 'https://api.github.com';
37
+
38
+ /** Warning sink; a plain callback rather than a `Logger` so this module needs no logger aspect. */
39
+
40
+ const noopWarn = () => {};
41
+ class GitHubClient {
42
+ constructor(opts) {
43
+ _defineProperty(this, "name", 'github');
44
+ _defineProperty(this, "token", void 0);
45
+ /** `owner/repo`. Readable so callers (and tests) can see *which* repository a resolved client speaks to. */
46
+ _defineProperty(this, "repo", void 0);
47
+ _defineProperty(this, "fetchImpl", void 0);
48
+ this.token = opts.token;
49
+ this.repo = opts.repo;
50
+ this.fetchImpl = opts.fetchImpl ?? fetch;
51
+ }
52
+
53
+ /**
54
+ * The client this environment describes, or undefined when it describes none. `origin` outranks
55
+ * `GITHUB_REPOSITORY`: every PR operation names a branch that only exists on `origin`, so if the two
56
+ * disagree the origin-parsed repository wins and the mismatch is warned about once.
57
+ */
58
+ static fromEnv(gitRemoteUrl, warn = noopWarn) {
59
+ // BIT_GITHUB_TOKEN is the override; GITHUB_TOKEN is the ambient default Actions injects everywhere.
60
+ const token = process.env.BIT_GITHUB_TOKEN || process.env.GITHUB_TOKEN;
61
+ // A remote that is demonstrably not GitHub's is a hard no, whatever the environment says —
62
+ // GITHUB_REPOSITORY is set on every Actions job, including ones whose origin is another host.
63
+ if (gitRemoteUrl && !isGitHubRemote(gitRemoteUrl)) return undefined;
64
+ const repoFromRemote = gitRemoteUrl ? parseGitHubRepo(gitRemoteUrl) : undefined;
65
+ const repoFromEnv = process.env.GITHUB_REPOSITORY;
66
+ const repo = repoFromRemote || repoFromEnv;
67
+ if (!token || !repo) return undefined;
68
+ // GitHub owner/repo names are case-insensitive.
69
+ if (repoFromRemote && repoFromEnv && repoFromRemote.toLowerCase() !== repoFromEnv.toLowerCase()) {
70
+ 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.`);
71
+ }
72
+ return new GitHubClient({
73
+ token,
74
+ repo
75
+ });
76
+ }
77
+ matchesRemote(remoteUrl) {
78
+ return isGitHubRemote(remoteUrl);
79
+ }
80
+
81
+ /** A constructed client always has both halves — it cannot be built without them. */
82
+ isConfigured() {
83
+ return Boolean(this.token && this.repo);
84
+ }
85
+ async request(method, path, body) {
86
+ const res = await this.fetchImpl(`${API}/repos/${this.repo}${path}`, {
87
+ method,
88
+ headers: {
89
+ authorization: `Bearer ${this.token}`,
90
+ accept: 'application/vnd.github+json',
91
+ 'content-type': 'application/json',
92
+ 'x-github-api-version': '2022-11-28'
93
+ },
94
+ body: body === undefined ? undefined : JSON.stringify(body)
95
+ });
96
+ if (!res.ok) {
97
+ const text = await res.text().catch(() => '');
98
+ throw new Error(`GitHub API ${method} ${path} failed: ${res.status} ${text}`);
99
+ }
100
+ return res.status === 204 ? undefined : res.json();
101
+ }
102
+ toPrInfo(raw) {
103
+ return {
104
+ number: raw.number,
105
+ state: raw.state,
106
+ labels: (raw.labels ?? []).map(l => typeof l === 'string' ? l : l.name),
107
+ headRef: raw.head?.ref,
108
+ htmlUrl: raw.html_url
109
+ };
110
+ }
111
+ async findPrByBranch(branch) {
112
+ const owner = this.repo.split('/')[0];
113
+ const list = await this.request('GET', `/pulls?state=open&head=${encodeURIComponent(`${owner}:${branch}`)}`);
114
+ return list.length ? this.toPrInfo(list[0]) : undefined;
115
+ }
116
+ async createPr(opts) {
117
+ return this.toPrInfo(await this.request('POST', '/pulls', opts));
118
+ }
119
+ async closePr(prNumber, comment) {
120
+ if (comment) await this.comment(prNumber, comment);
121
+ await this.request('PATCH', `/pulls/${prNumber}`, {
122
+ state: 'closed'
123
+ });
124
+ }
125
+ async comment(prNumber, body) {
126
+ await this.request('POST', `/issues/${prNumber}/comments`, {
127
+ body
128
+ });
129
+ }
130
+ async addLabel(prNumber, label) {
131
+ await this.request('POST', `/issues/${prNumber}/labels`, {
132
+ labels: [label]
133
+ });
134
+ }
135
+ }
136
+
137
+ /**
138
+ * The registrable GitHub provider: resolves its `GitHubClient` from the environment lazily, since
139
+ * registration happens at aspect load, before credentials are known. A resolved client is memoized;
140
+ * an unresolved one is retried so a later call that does carry the remote URL can still succeed.
141
+ */
142
+ exports.GitHubClient = GitHubClient;
143
+ class GitHubHostProvider {
144
+ constructor(onWarning = noopWarn) {
145
+ this.onWarning = onWarning;
146
+ _defineProperty(this, "name", 'github');
147
+ _defineProperty(this, "client", void 0);
148
+ /** The last `origin` URL seen, so a PR method can still recover `owner/repo` from it. */
149
+ _defineProperty(this, "remoteHint", void 0);
150
+ }
151
+ matchesRemote(remoteUrl) {
152
+ const matches = isGitHubRemote(remoteUrl);
153
+ // Only remember a remote that is ours — a foreign hint could later pair with GITHUB_REPOSITORY
154
+ // into a client for the wrong repository.
155
+ if (matches) this.remoteHint = remoteUrl;
156
+ return matches;
157
+ }
158
+
159
+ /**
160
+ * A known non-GitHub remote can never count as configured, however complete the environment looks:
161
+ * on Actions the env is always set, so answering "configured" for a remote this provider declined to
162
+ * claim would make it the sole-configured fallback and aim PRs at the wrong host's repository.
163
+ */
164
+ isConfigured(remoteUrl) {
165
+ if (remoteUrl && !isGitHubRemote(remoteUrl)) return false;
166
+ if (remoteUrl) this.remoteHint = remoteUrl;
167
+ return Boolean(this.resolveClient(remoteUrl));
168
+ }
169
+ async findPrByBranch(branch) {
170
+ return this.requireClient().findPrByBranch(branch);
171
+ }
172
+ async createPr(opts) {
173
+ return this.requireClient().createPr(opts);
174
+ }
175
+ async closePr(prNumber, comment) {
176
+ return this.requireClient().closePr(prNumber, comment);
177
+ }
178
+ async comment(prNumber, body) {
179
+ return this.requireClient().comment(prNumber, body);
180
+ }
181
+ async addLabel(prNumber, label) {
182
+ return this.requireClient().addLabel(prNumber, label);
183
+ }
184
+ resolveClient(remoteUrl) {
185
+ if (!this.client) this.client = GitHubClient.fromEnv(remoteUrl ?? this.remoteHint, this.onWarning);
186
+ return this.client;
187
+ }
188
+ requireClient() {
189
+ const client = this.resolveClient();
190
+ if (!client) {
191
+ throw new Error(`the github host provider is not configured: set BIT_GITHUB_TOKEN (or GITHUB_TOKEN), and either ` + `GITHUB_REPOSITORY or a github.com "origin" remote`);
192
+ }
193
+ return client;
194
+ }
195
+ }
196
+ exports.GitHubHostProvider = GitHubHostProvider;
197
+
198
+ //# sourceMappingURL=github-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["parseGitHubRepo","remoteUrl","url","trim","hasScheme","test","match","remoteHost","toLowerCase","isGitHubRemote","API","noopWarn","GitHubClient","constructor","opts","_defineProperty","token","repo","fetchImpl","fetch","fromEnv","gitRemoteUrl","warn","process","env","BIT_GITHUB_TOKEN","GITHUB_TOKEN","undefined","repoFromRemote","repoFromEnv","GITHUB_REPOSITORY","matchesRemote","isConfigured","Boolean","request","method","path","body","res","headers","authorization","accept","JSON","stringify","ok","text","catch","Error","status","json","toPrInfo","raw","number","state","labels","map","l","name","headRef","head","ref","htmlUrl","html_url","findPrByBranch","branch","owner","split","list","encodeURIComponent","length","createPr","closePr","prNumber","comment","addLabel","label","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/** 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 private async request(method: string, path: string, body?: unknown): Promise<any> {\n const res = await this.fetchImpl(`${API}/repos/${this.repo}${path}`, {\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} ${path} failed: ${res.status} ${text}`);\n }\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/**\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 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":[]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+
3
+ function _chai() {
4
+ const data = require("chai");
5
+ _chai = function () {
6
+ return data;
7
+ };
8
+ return data;
9
+ }
10
+ function _githubClient() {
11
+ const data = require("./github-client");
12
+ _githubClient = function () {
13
+ return data;
14
+ };
15
+ return data;
16
+ }
17
+ /**
18
+ * A claimed remote the parse then fails on silently degrades the whole run to PR-less mode, so the two
19
+ * predicates are pinned over the same remote shapes: whatever `isGitHubRemote` claims must parse.
20
+ */
21
+ const REMOTES = [
22
+ // url, parseGitHubRepo, isGitHubRemote
23
+ ['git@github.com:acme/shop.git', 'acme/shop', true], ['https://github.com/acme/shop.git', 'acme/shop', true], ['https://github.com/acme/shop', 'acme/shop', true], ['ssh://git@github.com/acme/shop.git', 'acme/shop', true],
24
+ // mixed-case host, an explicit port, and credentials in the url are all still github.com
25
+ ['https://GitHub.com/acme/shop.git', 'acme/shop', true], ['https://github.com:443/acme/shop.git', 'acme/shop', true], ['ssh://git@github.com:22/acme/shop.git', 'acme/shop', true], ['https://x-access-token:tok@github.com/acme/shop.git', 'acme/shop', true],
26
+ // an all-digits scp owner is an owner, never a port
27
+ ['git@github.com:12345/shop.git', '12345/shop', true],
28
+ // another host that merely has "github.com" in its path, in both url forms
29
+ ['https://gitlab.example.com/mirrors/github.com/acme/repo.git', undefined, false], ['git@gitlab.example.com:mirrors/github.com/acme/repo.git', undefined, false],
30
+ // look-alike and unrelated hosts
31
+ ['https://mygithub.com/acme/shop', undefined, false], ['https://github.company.com/acme/shop', undefined, false], ['git@gitlab.com:acme/shop.git', undefined, false], ['https://gitlab.com/acme/shop.git', undefined, false], ['/srv/git/github.com/acme/shop.git', undefined, false]];
32
+ describe('parseGitHubRepo / isGitHubRemote', () => {
33
+ it('agree on every remote form git accepts, and on every host that is not github.com', () => {
34
+ REMOTES.forEach(([url, repo, isGitHub]) => {
35
+ (0, _chai().expect)((0, _githubClient().parseGitHubRepo)(url), url).to.equal(repo);
36
+ (0, _chai().expect)((0, _githubClient().isGitHubRemote)(url), url).to.equal(isGitHub);
37
+ });
38
+ });
39
+ });
40
+ describe('GitHubClient.fromEnv', () => {
41
+ const envKeys = ['GITHUB_TOKEN', 'BIT_GITHUB_TOKEN', 'GITHUB_REPOSITORY'];
42
+ const saved = {};
43
+ beforeEach(() => {
44
+ envKeys.forEach(key => {
45
+ saved[key] = process.env[key];
46
+ delete process.env[key];
47
+ });
48
+ process.env.GITHUB_TOKEN = 'tok';
49
+ });
50
+ afterEach(() => {
51
+ envKeys.forEach(key => {
52
+ if (saved[key] === undefined) delete process.env[key];else process.env[key] = saved[key];
53
+ });
54
+ });
55
+ const OURS = 'git@github.com:acme/shop.git';
56
+ /** [case, GITHUB_REPOSITORY, remote, resolved repo, warning fragments]. */
57
+ const RESOLUTION = [['the remote and the environment agree', 'acme/shop', OURS, 'acme/shop'],
58
+ // the ORIGIN-parsed repository outranks GITHUB_REPOSITORY, and the disagreement must be reported
59
+ ['they disagree', 'other-org/other-repo', OURS, 'acme/shop', ['other-org/other-repo', 'acme/shop']],
60
+ // GitHub repository names are case-insensitive, so a case-only difference is not a disagreement
61
+ ['they differ only in case', 'Acme/Shop', OURS, 'acme/shop'], ['there is no remote to parse', 'acme/shop', undefined, 'acme/shop'], ['the remote is a github url nothing parses out of', 'acme/shop', 'https://github.com/', 'acme/shop'],
62
+ // a remote that is demonstrably not github stays unconfigured however complete the environment is
63
+ ['the remote is another host', 'acme/shop', 'https://gitlab.com/acme/shop.git', undefined], ['the remote is a look-alike host', 'acme/shop', 'https://mygithub.com/acme/shop', undefined]];
64
+ RESOLUTION.forEach(([name, repository, remote, repo, warns]) => {
65
+ it(`resolves the repository when ${name}`, () => {
66
+ process.env.GITHUB_REPOSITORY = repository;
67
+ const warnings = [];
68
+ const client = _githubClient().GitHubClient.fromEnv(remote, message => warnings.push(message));
69
+ (0, _chai().expect)(client?.repo).to.equal(repo);
70
+ (warns ?? []).forEach(fragment => (0, _chai().expect)(warnings.join('\n')).to.contain(fragment));
71
+ if (!warns) (0, _chai().expect)(warnings).to.deep.equal([]);
72
+ });
73
+ });
74
+ it('needs a token: a repository on its own configures nothing', () => {
75
+ delete process.env.GITHUB_TOKEN;
76
+ process.env.GITHUB_REPOSITORY = 'acme/shop';
77
+ (0, _chai().expect)(_githubClient().GitHubClient.fromEnv('git@github.com:acme/shop.git')).to.equal(undefined);
78
+ });
79
+
80
+ // Asserted through the `authorization` header, so what is pinned is the token the client actually
81
+ // sends. The fetch stub must be installed before `fromEnv` runs.
82
+ describe('token precedence', () => {
83
+ let realFetch;
84
+ let calls;
85
+ beforeEach(() => {
86
+ realFetch = globalThis.fetch;
87
+ calls = [];
88
+ globalThis.fetch = async (url, init) => {
89
+ calls.push({
90
+ url: String(url),
91
+ init
92
+ });
93
+ return new Response(JSON.stringify([]), {
94
+ status: 200,
95
+ headers: {
96
+ 'content-type': 'application/json'
97
+ }
98
+ });
99
+ };
100
+ });
101
+ afterEach(() => {
102
+ globalThis.fetch = realFetch;
103
+ });
104
+
105
+ /** The token a client built from this environment puts on the wire. */
106
+ async function sentToken() {
107
+ const client = _githubClient().GitHubClient.fromEnv('git@github.com:acme/shop.git');
108
+ await client?.findPrByBranch('lane-x');
109
+ return calls[0]?.init?.headers?.authorization;
110
+ }
111
+ const PRECEDENCE = [
112
+ // the override beats the ambient default
113
+ ['both set', {
114
+ GITHUB_TOKEN: 'workflow-token',
115
+ BIT_GITHUB_TOKEN: 'user-pat'
116
+ }, 'Bearer user-pat'],
117
+ // the ordinary Actions job
118
+ ['only GITHUB_TOKEN', {
119
+ GITHUB_TOKEN: 'workflow-token'
120
+ }, 'Bearer workflow-token'],
121
+ // any CI that is not Actions
122
+ ['only BIT_GITHUB_TOKEN', {
123
+ BIT_GITHUB_TOKEN: 'user-pat'
124
+ }, 'Bearer user-pat'], ['an EMPTY override', {
125
+ GITHUB_TOKEN: 'workflow-token',
126
+ BIT_GITHUB_TOKEN: ''
127
+ }, 'Bearer workflow-token']];
128
+ PRECEDENCE.forEach(([name, env, expected]) => {
129
+ it(`sends the right token with ${name}`, async () => {
130
+ delete process.env.GITHUB_TOKEN;
131
+ Object.entries(env).forEach(([key, value]) => {
132
+ process.env[key] = value;
133
+ });
134
+ (0, _chai().expect)(await sentToken()).to.equal(expected);
135
+ });
136
+ });
137
+ it('is not configured when neither token is set, and puts nothing on the wire', async () => {
138
+ delete process.env.GITHUB_TOKEN;
139
+ process.env.GITHUB_REPOSITORY = 'acme/shop';
140
+ (0, _chai().expect)(_githubClient().GitHubClient.fromEnv('git@github.com:acme/shop.git')).to.equal(undefined);
141
+ (0, _chai().expect)(calls).to.have.lengthOf(0);
142
+ });
143
+ });
144
+ });
145
+ describe('GitHubClient', () => {
146
+ it('createPr posts the pull-request payload to the pulls endpoint', async () => {
147
+ const calls = [];
148
+ const fakeFetch = async (url, init) => {
149
+ calls.push({
150
+ url: String(url),
151
+ init
152
+ });
153
+ return new Response(JSON.stringify({
154
+ number: 7,
155
+ state: 'open',
156
+ labels: [],
157
+ head: {
158
+ ref: 'lane-x'
159
+ },
160
+ html_url: 'http://pr/7'
161
+ }), {
162
+ status: 201,
163
+ headers: {
164
+ 'content-type': 'application/json'
165
+ }
166
+ });
167
+ };
168
+ const client = new (_githubClient().GitHubClient)({
169
+ token: 'tok',
170
+ repo: 'acme/shop',
171
+ fetchImpl: fakeFetch
172
+ });
173
+ const pr = await client.createPr({
174
+ head: 'lane-x',
175
+ base: 'main',
176
+ title: 't',
177
+ body: 'b'
178
+ });
179
+ (0, _chai().expect)(pr.number).to.equal(7);
180
+ (0, _chai().expect)(calls[0].url).to.equal('https://api.github.com/repos/acme/shop/pulls');
181
+ (0, _chai().expect)(calls[0].init.method).to.equal('POST');
182
+ (0, _chai().expect)(JSON.parse(calls[0].init.body)).to.deep.equal({
183
+ head: 'lane-x',
184
+ base: 'main',
185
+ title: 't',
186
+ body: 'b'
187
+ });
188
+ });
189
+ it('findPrByBranch returns undefined on empty list', async () => {
190
+ const fakeFetch = async () => new Response(JSON.stringify([]), {
191
+ status: 200,
192
+ headers: {
193
+ 'content-type': 'application/json'
194
+ }
195
+ });
196
+ const client = new (_githubClient().GitHubClient)({
197
+ token: 'tok',
198
+ repo: 'acme/shop',
199
+ fetchImpl: fakeFetch
200
+ });
201
+ (0, _chai().expect)(await client.findPrByBranch('lane-x')).to.equal(undefined);
202
+ });
203
+ });
204
+
205
+ //# sourceMappingURL=github-client.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_chai","data","require","_githubClient","REMOTES","undefined","describe","it","forEach","url","repo","isGitHub","expect","parseGitHubRepo","to","equal","isGitHubRemote","envKeys","saved","beforeEach","key","process","env","GITHUB_TOKEN","afterEach","OURS","RESOLUTION","name","repository","remote","warns","GITHUB_REPOSITORY","warnings","client","GitHubClient","fromEnv","message","push","fragment","join","contain","deep","realFetch","calls","globalThis","fetch","init","String","Response","JSON","stringify","status","headers","sentToken","findPrByBranch","authorization","PRECEDENCE","BIT_GITHUB_TOKEN","expected","Object","entries","value","have","lengthOf","fakeFetch","number","state","labels","head","ref","html_url","token","fetchImpl","pr","createPr","base","title","body","method","parse"],"sources":["github-client.spec.ts"],"sourcesContent":["import { expect } from 'chai';\nimport { GitHubClient, isGitHubRemote, parseGitHubRepo } from './github-client';\n\n/**\n * A claimed remote the parse then fails on silently degrades the whole run to PR-less mode, so the two\n * predicates are pinned over the same remote shapes: whatever `isGitHubRemote` claims must parse.\n */\nconst REMOTES: Array<[string, string | undefined, boolean]> = [\n // url, parseGitHubRepo, isGitHubRemote\n ['git@github.com:acme/shop.git', 'acme/shop', true],\n ['https://github.com/acme/shop.git', 'acme/shop', true],\n ['https://github.com/acme/shop', 'acme/shop', true],\n ['ssh://git@github.com/acme/shop.git', 'acme/shop', true],\n // mixed-case host, an explicit port, and credentials in the url are all still github.com\n ['https://GitHub.com/acme/shop.git', 'acme/shop', true],\n ['https://github.com:443/acme/shop.git', 'acme/shop', true],\n ['ssh://git@github.com:22/acme/shop.git', 'acme/shop', true],\n ['https://x-access-token:tok@github.com/acme/shop.git', 'acme/shop', true],\n // an all-digits scp owner is an owner, never a port\n ['git@github.com:12345/shop.git', '12345/shop', true],\n // another host that merely has \"github.com\" in its path, in both url forms\n ['https://gitlab.example.com/mirrors/github.com/acme/repo.git', undefined, false],\n ['git@gitlab.example.com:mirrors/github.com/acme/repo.git', undefined, false],\n // look-alike and unrelated hosts\n ['https://mygithub.com/acme/shop', undefined, false],\n ['https://github.company.com/acme/shop', undefined, false],\n ['git@gitlab.com:acme/shop.git', undefined, false],\n ['https://gitlab.com/acme/shop.git', undefined, false],\n ['/srv/git/github.com/acme/shop.git', undefined, false],\n];\n\ndescribe('parseGitHubRepo / isGitHubRemote', () => {\n it('agree on every remote form git accepts, and on every host that is not github.com', () => {\n REMOTES.forEach(([url, repo, isGitHub]) => {\n expect(parseGitHubRepo(url), url).to.equal(repo);\n expect(isGitHubRemote(url), url).to.equal(isGitHub);\n });\n });\n});\n\ndescribe('GitHubClient.fromEnv', () => {\n const envKeys = ['GITHUB_TOKEN', 'BIT_GITHUB_TOKEN', 'GITHUB_REPOSITORY'] as const;\n const saved: Record<string, string | undefined> = {};\n\n beforeEach(() => {\n envKeys.forEach((key) => {\n saved[key] = process.env[key];\n delete process.env[key];\n });\n process.env.GITHUB_TOKEN = 'tok';\n });\n\n afterEach(() => {\n envKeys.forEach((key) => {\n if (saved[key] === undefined) delete process.env[key];\n else process.env[key] = saved[key];\n });\n });\n\n const OURS = 'git@github.com:acme/shop.git';\n /** [case, GITHUB_REPOSITORY, remote, resolved repo, warning fragments]. */\n const RESOLUTION: Array<[string, string, string | undefined, string | undefined, string[]?]> = [\n ['the remote and the environment agree', 'acme/shop', OURS, 'acme/shop'],\n // the ORIGIN-parsed repository outranks GITHUB_REPOSITORY, and the disagreement must be reported\n ['they disagree', 'other-org/other-repo', OURS, 'acme/shop', ['other-org/other-repo', 'acme/shop']],\n // GitHub repository names are case-insensitive, so a case-only difference is not a disagreement\n ['they differ only in case', 'Acme/Shop', OURS, 'acme/shop'],\n ['there is no remote to parse', 'acme/shop', undefined, 'acme/shop'],\n ['the remote is a github url nothing parses out of', 'acme/shop', 'https://github.com/', 'acme/shop'],\n // a remote that is demonstrably not github stays unconfigured however complete the environment is\n ['the remote is another host', 'acme/shop', 'https://gitlab.com/acme/shop.git', undefined],\n ['the remote is a look-alike host', 'acme/shop', 'https://mygithub.com/acme/shop', undefined],\n ];\n\n RESOLUTION.forEach(([name, repository, remote, repo, warns]) => {\n it(`resolves the repository when ${name}`, () => {\n process.env.GITHUB_REPOSITORY = repository;\n const warnings: string[] = [];\n const client = GitHubClient.fromEnv(remote, (message) => warnings.push(message));\n expect(client?.repo).to.equal(repo);\n (warns ?? []).forEach((fragment) => expect(warnings.join('\\n')).to.contain(fragment));\n if (!warns) expect(warnings).to.deep.equal([]);\n });\n });\n\n it('needs a token: a repository on its own configures nothing', () => {\n delete process.env.GITHUB_TOKEN;\n process.env.GITHUB_REPOSITORY = 'acme/shop';\n expect(GitHubClient.fromEnv('git@github.com:acme/shop.git')).to.equal(undefined);\n });\n\n // Asserted through the `authorization` header, so what is pinned is the token the client actually\n // sends. The fetch stub must be installed before `fromEnv` runs.\n describe('token precedence', () => {\n let realFetch: typeof fetch;\n let calls: Array<{ url: string; init: any }>;\n\n beforeEach(() => {\n realFetch = globalThis.fetch;\n calls = [];\n globalThis.fetch = (async (url: any, init: any) => {\n calls.push({ url: String(url), init });\n return new Response(JSON.stringify([]), { status: 200, headers: { 'content-type': 'application/json' } });\n }) as typeof fetch;\n });\n\n afterEach(() => {\n globalThis.fetch = realFetch;\n });\n\n /** The token a client built from this environment puts on the wire. */\n async function sentToken(): Promise<string | undefined> {\n const client = GitHubClient.fromEnv('git@github.com:acme/shop.git');\n await client?.findPrByBranch('lane-x');\n return calls[0]?.init?.headers?.authorization;\n }\n\n const PRECEDENCE: Array<[string, Record<string, string | undefined>, string]> = [\n // the override beats the ambient default\n ['both set', { GITHUB_TOKEN: 'workflow-token', BIT_GITHUB_TOKEN: 'user-pat' }, 'Bearer user-pat'],\n // the ordinary Actions job\n ['only GITHUB_TOKEN', { GITHUB_TOKEN: 'workflow-token' }, 'Bearer workflow-token'],\n // any CI that is not Actions\n ['only BIT_GITHUB_TOKEN', { BIT_GITHUB_TOKEN: 'user-pat' }, 'Bearer user-pat'],\n ['an EMPTY override', { GITHUB_TOKEN: 'workflow-token', BIT_GITHUB_TOKEN: '' }, 'Bearer workflow-token'],\n ];\n\n PRECEDENCE.forEach(([name, env, expected]) => {\n it(`sends the right token with ${name}`, async () => {\n delete process.env.GITHUB_TOKEN;\n Object.entries(env).forEach(([key, value]) => {\n process.env[key] = value;\n });\n expect(await sentToken()).to.equal(expected);\n });\n });\n\n it('is not configured when neither token is set, and puts nothing on the wire', async () => {\n delete process.env.GITHUB_TOKEN;\n process.env.GITHUB_REPOSITORY = 'acme/shop';\n expect(GitHubClient.fromEnv('git@github.com:acme/shop.git')).to.equal(undefined);\n expect(calls).to.have.lengthOf(0);\n });\n });\n});\n\ndescribe('GitHubClient', () => {\n it('createPr posts the pull-request payload to the pulls endpoint', async () => {\n const calls: Array<{ url: string; init: any }> = [];\n const fakeFetch = (async (url: any, init: any) => {\n calls.push({ url: String(url), init });\n return new Response(\n JSON.stringify({ number: 7, state: 'open', labels: [], head: { ref: 'lane-x' }, html_url: 'http://pr/7' }),\n { status: 201, headers: { 'content-type': 'application/json' } }\n );\n }) as typeof fetch;\n const client = new GitHubClient({ token: 'tok', repo: 'acme/shop', fetchImpl: fakeFetch });\n const pr = await client.createPr({ head: 'lane-x', base: 'main', title: 't', body: 'b' });\n expect(pr.number).to.equal(7);\n expect(calls[0].url).to.equal('https://api.github.com/repos/acme/shop/pulls');\n expect(calls[0].init.method).to.equal('POST');\n expect(JSON.parse(calls[0].init.body)).to.deep.equal({ head: 'lane-x', base: 'main', title: 't', body: 'b' });\n });\n\n it('findPrByBranch returns undefined on empty list', async () => {\n const fakeFetch = (async () =>\n new Response(JSON.stringify([]), {\n status: 200,\n headers: { 'content-type': 'application/json' },\n })) as typeof fetch;\n const client = new GitHubClient({ token: 'tok', repo: 'acme/shop', fetchImpl: fakeFetch });\n expect(await client.findPrByBranch('lane-x')).to.equal(undefined);\n });\n});\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `bit ci sync --init` — pure scaffolding logic, unit-testable without a live workspace/git checkout.
3
+ *
4
+ * TEMPLATE SOURCE OF TRUTH: `BIT_SYNC_WORKFLOW_RAW` / `BIT_RELEASE_WORKFLOW_RAW` are byte-for-byte
5
+ * copies of the canonical templates in the `bit-git-sync` action workspace
6
+ * (git-sync/git-sync/workflows/templates/) and MUST be kept in sync with them. The ONLY departure is
7
+ * the two `DEFAULT_BRANCH_MARKER` substitution points; everything else — CHANGE-ME comments, the
8
+ * placeholder action ref, `bit-sync/main` config defaults — stays untouched.
9
+ */
10
+ /** Where `scaffoldWorkflowFiles` writes each file, relative to the workspace root. */
11
+ export declare const WORKFLOW_RELATIVE_PATHS: {
12
+ readonly sync: string;
13
+ readonly release: string;
14
+ };
15
+ /** Render `bit-sync.yml` with the repository's actual default branch substituted. */
16
+ export declare function renderBitSyncWorkflow(defaultBranch: string): string;
17
+ /** Render `bit-release.yml` with the repository's actual default branch substituted. */
18
+ export declare function renderBitReleaseWorkflow(defaultBranch: string): string;
19
+ export interface ScaffoldFileOutcome {
20
+ /** relative to the workspace root, e.g. `.github/workflows/bit-sync.yml` */
21
+ relativePath: string;
22
+ status: 'written' | 'skipped';
23
+ }
24
+ /** Scaffold both workflow files under `workspaceDir`. Never overwrites; creates `.github/workflows/` as needed. */
25
+ export declare function scaffoldWorkflowFiles(workspaceDir: string, defaultBranch: string): ScaffoldFileOutcome[];
26
+ export interface OwnerRepo {
27
+ owner: string;
28
+ repo: string;
29
+ }
30
+ /**
31
+ * Derive `{owner, repo}` from the `origin` remote when it is a parseable GitHub remote, else
32
+ * undefined (the checklist falls back to a placeholder). Reuses `github-client.ts`'s parsing so the
33
+ * two never disagree.
34
+ */
35
+ export declare function deriveOwnerRepo(remoteUrl: string | undefined): OwnerRepo | undefined;
36
+ /**
37
+ * The manual-steps checklist `bit ci sync --init` prints: everything the command itself cannot do
38
+ * (secrets live in GitHub settings; the webhook lives on bit.cloud).
39
+ */
40
+ export declare function renderInitChecklist(ownerRepo: OwnerRepo | undefined): string;