@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.
@@ -11,6 +11,7 @@ import {
11
11
  deriveOwnerRepo,
12
12
  renderInitChecklist,
13
13
  } from './init-scaffold';
14
+ import type { OwnerRepo } from './init-scaffold';
14
15
 
15
16
  const yamlLoad = (src: string) => YAML.parse(src);
16
17
 
@@ -99,10 +100,11 @@ describe('init-scaffold', () => {
99
100
  });
100
101
 
101
102
  describe('deriveOwnerRepo', () => {
102
- const REMOTES: Array<[string | undefined, { owner: string; repo: string } | undefined]> = [
103
- ['git@github.com:acme/shop.git', { owner: 'acme', repo: 'shop' }],
104
- ['https://github.com/acme/shop.git', { owner: 'acme', repo: 'shop' }],
105
- ['https://github.com/acme/shop', { owner: 'acme', repo: 'shop' }],
103
+ const SHOP = { owner: 'acme', repo: 'shop', host: 'github.com' };
104
+ const REMOTES: Array<[string | undefined, OwnerRepo | undefined]> = [
105
+ ['git@github.com:acme/shop.git', SHOP],
106
+ ['https://github.com/acme/shop.git', SHOP],
107
+ ['https://github.com/acme/shop', SHOP],
106
108
  ['https://gitlab.com/acme/shop.git', undefined],
107
109
  // a local/bare remote has no host at all
108
110
  ['/path/to/bare.git', undefined],
@@ -117,12 +119,67 @@ describe('init-scaffold', () => {
117
119
 
118
120
  describe('renderInitChecklist', () => {
119
121
  it('renders a real dispatch URL when owner/repo were derived, a placeholder when not', () => {
120
- const derived = renderInitChecklist({ owner: 'acme', repo: 'shop' });
122
+ const derived = renderInitChecklist({ owner: 'acme', repo: 'shop', host: 'github.com' });
121
123
  expect(derived).to.contain('https://api.github.com/repos/acme/shop/dispatches');
122
124
  expect(derived).to.not.contain('<owner>/<repo>');
123
125
  expect(renderInitChecklist(undefined)).to.contain('https://api.github.com/repos/<owner>/<repo>/dispatches');
124
126
  });
125
127
 
128
+ it('derives the repository and the dispatch URL from a GitHub Enterprise Server remote', () => {
129
+ const saved = { api: process.env.GITHUB_API_URL, server: process.env.GITHUB_SERVER_URL };
130
+ process.env.GITHUB_API_URL = 'https://ghe.corp.example/api/v3';
131
+ process.env.GITHUB_SERVER_URL = 'https://ghe.corp.example';
132
+ try {
133
+ const ownerRepo = deriveOwnerRepo('git@ghe.corp.example:acme/store.git');
134
+ expect(ownerRepo).to.deep.equal({ owner: 'acme', repo: 'store', host: 'ghe.corp.example' });
135
+ expect(renderInitChecklist(ownerRepo)).to.contain(
136
+ 'https://ghe.corp.example/api/v3/repos/acme/store/dispatches'
137
+ );
138
+ } finally {
139
+ if (saved.api === undefined) delete process.env.GITHUB_API_URL;
140
+ else process.env.GITHUB_API_URL = saved.api;
141
+ if (saved.server === undefined) delete process.env.GITHUB_SERVER_URL;
142
+ else process.env.GITHUB_SERVER_URL = saved.server;
143
+ }
144
+ });
145
+
146
+ describe('on a GHES-configured runner', () => {
147
+ const saved: Record<string, string | undefined> = {};
148
+ beforeEach(() => {
149
+ saved.api = process.env.GITHUB_API_URL;
150
+ saved.server = process.env.GITHUB_SERVER_URL;
151
+ process.env.GITHUB_SERVER_URL = 'https://ghe.corp.example';
152
+ });
153
+ afterEach(() => {
154
+ if (saved.api === undefined) delete process.env.GITHUB_API_URL;
155
+ else process.env.GITHUB_API_URL = saved.api;
156
+ if (saved.server === undefined) delete process.env.GITHUB_SERVER_URL;
157
+ else process.env.GITHUB_SERVER_URL = saved.server;
158
+ });
159
+
160
+ it('names api.github.com for a github.com origin', () => {
161
+ process.env.GITHUB_API_URL = 'https://ghe.corp.example/api/v3';
162
+ const checklist = renderInitChecklist(deriveOwnerRepo('git@github.com:acme/shop.git'));
163
+ expect(checklist).to.contain('https://api.github.com/repos/acme/shop/dispatches');
164
+ expect(checklist).to.not.contain('ghe.corp.example');
165
+ });
166
+
167
+ it('derives <server>/api/v3 for an enterprise origin when GITHUB_API_URL is unset', () => {
168
+ delete process.env.GITHUB_API_URL;
169
+ expect(renderInitChecklist(deriveOwnerRepo('https://ghe.corp.example/acme/store.git'))).to.contain(
170
+ 'https://ghe.corp.example/api/v3/repos/acme/store/dispatches'
171
+ );
172
+ });
173
+
174
+ it('names no API for an enterprise origin whose GITHUB_API_URL is on another host', () => {
175
+ process.env.GITHUB_API_URL = 'https://api.github.com';
176
+ const checklist = renderInitChecklist(deriveOwnerRepo('https://ghe.corp.example/acme/store.git'));
177
+ expect(checklist).to.contain('<GitHub API URL>/repos/acme/store/dispatches');
178
+ expect(checklist).to.contain('GITHUB_API_URL "https://api.github.com"');
179
+ expect(checklist).to.not.contain('https://api.github.com/repos');
180
+ });
181
+ });
182
+
126
183
  it('always includes the secrets, permissions, webhook and fetch-depth checklist items', () => {
127
184
  const checklist = renderInitChecklist(undefined);
128
185
  [
@@ -1,6 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { parseGitHubRepo, isGitHubRemote } from './github-client';
3
+ import { parseGitHubRepo, isGitHubRemote, gitHubApiUrl, remoteHost } from './github-client';
4
4
 
5
5
  /**
6
6
  * `bit ci sync --init` — pure scaffolding logic, unit-testable without a live workspace/git checkout.
@@ -273,6 +273,8 @@ export function scaffoldWorkflowFiles(workspaceDir: string, defaultBranch: strin
273
273
  export interface OwnerRepo {
274
274
  owner: string;
275
275
  repo: string;
276
+ /** The remote's host — github.com or the GITHUB_SERVER_URL one — which picks the API the checklist names. */
277
+ host: string;
276
278
  }
277
279
 
278
280
  /**
@@ -283,10 +285,11 @@ export interface OwnerRepo {
283
285
  export function deriveOwnerRepo(remoteUrl: string | undefined): OwnerRepo | undefined {
284
286
  if (!remoteUrl || !isGitHubRemote(remoteUrl)) return undefined;
285
287
  const ownerSlashRepo = parseGitHubRepo(remoteUrl);
286
- if (!ownerSlashRepo) return undefined;
288
+ const host = remoteHost(remoteUrl);
289
+ if (!ownerSlashRepo || !host) return undefined;
287
290
  const [owner, repo] = ownerSlashRepo.split('/');
288
291
  if (!owner || !repo) return undefined;
289
- return { owner, repo };
292
+ return { owner, repo, host };
290
293
  }
291
294
 
292
295
  /**
@@ -302,7 +305,8 @@ const WEBHOOK_PAYLOAD_TEMPLATE =
302
305
  */
303
306
  export function renderInitChecklist(ownerRepo: OwnerRepo | undefined): string {
304
307
  const repoSegment = ownerRepo ? `${ownerRepo.owner}/${ownerRepo.repo}` : '<owner>/<repo>';
305
- const dispatchUrl = `https://api.github.com/repos/${repoSegment}/dispatches`;
308
+ const api = gitHubApiUrl(ownerRepo?.host);
309
+ const dispatchUrl = `${'error' in api ? '<GitHub API URL>' : api.url}/repos/${repoSegment}/dispatches`;
306
310
  return [
307
311
  '',
308
312
  'Remaining manual steps (bit ci sync --init only scaffolds files -- these need a human):',
@@ -322,6 +326,7 @@ export function renderInitChecklist(ownerRepo: OwnerRepo | undefined): string {
322
326
  '3. bit.cloud webhook -- create ONE webhook (org Settings > Webhooks) on the "Components > Export',
323
327
  ' succeeded" event:',
324
328
  ` URL: ${dispatchUrl}`,
329
+ ...('error' in api ? [` (${api.error})`] : []),
325
330
  ' Headers: Authorization: Bearer <PAT>',
326
331
  ' Accept: application/vnd.github+json',
327
332
  ' Custom payload template:',