@teambit/ci 1.0.637 → 1.0.639
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 +13 -7
- package/commands/sync.cmd.ts +5 -2
- package/dist/ci.docs.mdx +13 -7
- package/dist/ci.main.runtime.d.ts +6 -4
- package/dist/ci.main.runtime.js +19 -13
- package/dist/ci.main.runtime.js.map +1 -1
- package/dist/commands/sync.cmd.js +5 -2
- package/dist/commands/sync.cmd.js.map +1 -1
- package/dist/{preview-1792532220087.js → preview-1792595323307.js} +1 -1
- package/dist/sync/init-scaffold.d.ts +89 -12
- package/dist/sync/init-scaffold.js +208 -21
- package/dist/sync/init-scaffold.js.map +1 -1
- package/dist/sync/init-scaffold.spec.js +295 -45
- package/dist/sync/init-scaffold.spec.js.map +1 -1
- package/dist/sync/sync-orchestrator.d.ts +3 -1
- package/dist/sync/sync-orchestrator.js +84 -14
- package/dist/sync/sync-orchestrator.js.map +1 -1
- package/package.json +19 -19
- package/sync/init-scaffold.spec.ts +314 -52
- package/sync/init-scaffold.ts +341 -55
- package/sync/sync-orchestrator.ts +89 -22
|
@@ -9,7 +9,13 @@ import {
|
|
|
9
9
|
renderBitReleaseWorkflow,
|
|
10
10
|
scaffoldWorkflowFiles,
|
|
11
11
|
deriveOwnerRepo,
|
|
12
|
-
|
|
12
|
+
renderInitReport,
|
|
13
|
+
shellQuote,
|
|
14
|
+
chooseInitDefaultBranch,
|
|
15
|
+
suggestedServerUrl,
|
|
16
|
+
isLocalPathRemote,
|
|
17
|
+
redactRemoteUrl,
|
|
18
|
+
escapeControlChars,
|
|
13
19
|
} from './init-scaffold';
|
|
14
20
|
import type { OwnerRepo } from './init-scaffold';
|
|
15
21
|
|
|
@@ -56,6 +62,20 @@ describe('init-scaffold', () => {
|
|
|
56
62
|
expect(renderBitSyncWorkflow("it's/a-branch")).to.contain("'it''s/a-branch'");
|
|
57
63
|
expect(yamlLoad(renderBitSyncWorkflow('release/main')).jobs).to.be.an('object');
|
|
58
64
|
});
|
|
65
|
+
|
|
66
|
+
it('set the action ws-dir for a workspace below the repository root, and leave it commented at the root', () => {
|
|
67
|
+
const withInput = (doc: string) =>
|
|
68
|
+
Object.values(yamlLoad(doc).jobs as Record<string, any>)
|
|
69
|
+
.flatMap((job) => job.steps)
|
|
70
|
+
.find((s: any) => String(s.uses).startsWith('teambit/bit-git-sync')).with;
|
|
71
|
+
[renderBitSyncWorkflow, renderBitReleaseWorkflow].forEach((render) => {
|
|
72
|
+
expect(withInput(render('main', 'packages/app'))['ws-dir']).to.equal('packages/app');
|
|
73
|
+
expect(withInput(render('main', "it's dir"))['ws-dir']).to.equal("it's dir");
|
|
74
|
+
expect(withInput(render('main'))).to.not.have.property('ws-dir');
|
|
75
|
+
expect(render('main')).to.contain('# ws-dir: packages/my-workspace');
|
|
76
|
+
expect(render('main', '.')).to.equal(render('main'));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
59
79
|
});
|
|
60
80
|
|
|
61
81
|
describe('scaffoldWorkflowFiles', () => {
|
|
@@ -117,33 +137,281 @@ describe('init-scaffold', () => {
|
|
|
117
137
|
});
|
|
118
138
|
});
|
|
119
139
|
|
|
120
|
-
describe('
|
|
121
|
-
it('
|
|
122
|
-
|
|
123
|
-
expect(
|
|
124
|
-
expect(
|
|
125
|
-
expect(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
describe('chooseInitDefaultBranch', () => {
|
|
141
|
+
it('takes the detected branch, else main or master when checked out, else main', () => {
|
|
142
|
+
expect(chooseInitDefaultBranch('trunk', 'feature/x')).to.equal('trunk');
|
|
143
|
+
expect(chooseInitDefaultBranch(undefined, 'master')).to.equal('master');
|
|
144
|
+
expect(chooseInitDefaultBranch(undefined, 'main')).to.equal('main');
|
|
145
|
+
expect(chooseInitDefaultBranch(undefined, 'feature/x')).to.equal('main');
|
|
146
|
+
expect(chooseInitDefaultBranch(undefined, undefined)).to.equal('main');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('renderInitReport', () => {
|
|
151
|
+
const files = [
|
|
152
|
+
{
|
|
153
|
+
relativePath: WORKFLOW_RELATIVE_PATHS.sync,
|
|
154
|
+
status: 'written' as const,
|
|
155
|
+
displayPath: '.github/workflows/bit-sync.yml',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
relativePath: WORKFLOW_RELATIVE_PATHS.release,
|
|
159
|
+
status: 'written' as const,
|
|
160
|
+
displayPath: '.github/workflows/bit-release.yml',
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
// eslint-disable-next-line no-control-regex
|
|
164
|
+
const plain = (text: string) => text.replace(/\u001b\[[0-9;]*m/g, '');
|
|
165
|
+
const report = (remote: string | undefined, extra: Partial<Parameters<typeof renderInitReport>[0]> = {}) =>
|
|
166
|
+
plain(
|
|
167
|
+
renderInitReport({
|
|
168
|
+
scopeId: 'acme.shop',
|
|
169
|
+
ownerRepo: deriveOwnerRepo(remote),
|
|
170
|
+
defaultBranch: 'main',
|
|
171
|
+
files,
|
|
172
|
+
configAdded: true,
|
|
173
|
+
...extra,
|
|
174
|
+
})
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
it('prints each step once, as commands for the derived repository', () => {
|
|
178
|
+
const out = report('git@github.com:acme/shop.git');
|
|
179
|
+
expect(out).to.contain('for acme.shop, mirrored to acme/shop (default branch: main)');
|
|
180
|
+
expect(out).to.contain('git push -u origin HEAD');
|
|
181
|
+
expect(out).to.contain('gh secret set BIT_CONFIG_ACCESS_TOKEN -R acme/shop');
|
|
182
|
+
expect(out).to.contain('gh api -X PUT repos/acme/shop/actions/permissions/workflow');
|
|
183
|
+
expect(out.match(/Next: 3 steps to go live/g)).to.have.length(1);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('points github.com repositories at the GitHub App and the relay, not at a personal token', () => {
|
|
187
|
+
const out = report('https://github.com/acme/shop.git');
|
|
188
|
+
expect(out).to.contain('https://github.com/apps/bit-git-sync');
|
|
189
|
+
expect(out).to.contain('/setup');
|
|
190
|
+
expect(out).to.not.contain('/dispatches');
|
|
191
|
+
expect(out).to.not.contain('Authorization: Bearer');
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('prints no placeholder commands when there is no GitHub remote yet', () => {
|
|
195
|
+
const out = report(undefined);
|
|
196
|
+
expect(out).to.contain('no GitHub remote yet');
|
|
197
|
+
expect(out).to.contain('gh repo create --private --source . --remote origin --push');
|
|
198
|
+
expect(out).to.not.contain('GITHUB_SERVER_URL');
|
|
199
|
+
expect(out).to.contain('Run bit ci sync --init again');
|
|
200
|
+
expect(out).to.contain('Next: 2 steps to go live');
|
|
201
|
+
expect(out).to.not.contain('<owner>');
|
|
202
|
+
expect(out).to.not.contain('gh secret set');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('commits and pushes in separate commands, so a re-run with nothing to commit still pushes', () => {
|
|
206
|
+
const out = report('git@github.com:acme/shop.git');
|
|
207
|
+
expect(out).to.match(/git commit -m 'Mirror acme\.shop' -- \\\n\s+[^\n]+\n\s+git push -u origin HEAD/);
|
|
208
|
+
expect(out).to.not.contain('&& git');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('stages only the files --init touched, so unrelated local changes stay out of the commit', () => {
|
|
212
|
+
const out = report('git@github.com:acme/shop.git');
|
|
213
|
+
expect(out).to.contain(
|
|
214
|
+
'git add -- .github/workflows/bit-sync.yml .github/workflows/bit-release.yml workspace.jsonc\n'
|
|
215
|
+
);
|
|
216
|
+
expect(out).to.not.contain('git add -A');
|
|
217
|
+
// the paths are the ones shown, as seen from where the user stands
|
|
218
|
+
expect(
|
|
219
|
+
report('git@github.com:acme/shop.git', {
|
|
220
|
+
files: files.map((f) => ({ ...f, displayPath: `/repo root/${f.displayPath}` })),
|
|
221
|
+
configDisplayPath: 'workspace.jsonc',
|
|
222
|
+
})
|
|
223
|
+
).to.contain("git add -- '/repo root/.github/workflows/bit-sync.yml'");
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('stages exactly the files git shows as new or changed', () => {
|
|
227
|
+
const out = report('git@github.com:acme/shop.git', { toStage: ['.github/workflows/bit-sync.yml'], ignored: [] });
|
|
228
|
+
expect(out).to.contain('git add -- .github/workflows/bit-sync.yml\n');
|
|
229
|
+
expect(out).to.not.contain('bit-release.yml workspace.jsonc');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('force-adds a workflow file that .gitignore matches, so the rest of the staging still runs', () => {
|
|
233
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
234
|
+
toStage: ['workspace.jsonc'],
|
|
235
|
+
ignored: ['.github/workflows/bit-release.yml'],
|
|
236
|
+
});
|
|
237
|
+
expect(out).to.contain('git add -- workspace.jsonc\n');
|
|
238
|
+
expect(out).to.contain('git add -f -- .github/workflows/bit-release.yml\n');
|
|
239
|
+
expect(out).to.contain('.gitignore matches these');
|
|
240
|
+
expect(out).to.contain("git commit -m 'Mirror acme.shop'");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('commits only the files it stages, so changes staged before stay out of the commit', () => {
|
|
244
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
245
|
+
toStage: ['workspace.jsonc'],
|
|
246
|
+
ignored: ['.github/workflows/bit-release.yml'],
|
|
247
|
+
});
|
|
248
|
+
expect(out).to.match(
|
|
249
|
+
/git commit -m 'Mirror acme\.shop' -- \\\n\s+workspace\.jsonc \.github\/workflows\/bit-release\.yml\n/
|
|
250
|
+
);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('force-adds an ignored workflow file also for the first commit, which git add -A would skip', () => {
|
|
254
|
+
const out = report(undefined, { repoHasCommits: false, ignored: ['.github/workflows/bit-sync.yml'] });
|
|
255
|
+
expect(out).to.match(/git add -A\n\s+git add -f -- \.github\/workflows\/bit-sync\.yml\n/);
|
|
256
|
+
// the first commit takes the whole workspace, so it names no paths
|
|
257
|
+
expect(out).to.match(/git commit -m 'Mirror acme\.shop'\n/);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('prints no commit when nothing is left to commit, and still pushes', () => {
|
|
261
|
+
const out = report('git@github.com:acme/shop.git', { toStage: [], ignored: [] });
|
|
262
|
+
expect(out).to.not.contain('git add');
|
|
263
|
+
expect(out).to.not.contain('git commit');
|
|
264
|
+
expect(out).to.contain('nothing to commit');
|
|
265
|
+
expect(out).to.contain('git push -u origin HEAD');
|
|
144
266
|
});
|
|
145
267
|
|
|
146
|
-
|
|
268
|
+
it('starts with git init when the workspace is not in a git repository', () => {
|
|
269
|
+
const out = report(undefined, { inGitRepo: false, repoHasCommits: false });
|
|
270
|
+
expect(out).to.match(/git init -b main\n\s+git add -A\n\s+git commit -m/);
|
|
271
|
+
expect(report(undefined, { inGitRepo: false, repoRootFromCwd: '..' })).to.contain('git init -b main ..\n');
|
|
272
|
+
// the branch the workflows use, not git's own default (master when init.defaultBranch is unset)
|
|
273
|
+
expect(report(undefined, { inGitRepo: false, defaultBranch: 'trunk' })).to.contain('git init -b trunk\n');
|
|
274
|
+
expect(report(undefined)).to.not.contain('git init');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('stages the whole workspace for the first commit of a new repository', () => {
|
|
278
|
+
expect(report(undefined, { repoHasCommits: false })).to.contain('git add -A\n');
|
|
279
|
+
expect(report(undefined, { repoHasCommits: false, repoRootFromCwd: '../..' })).to.contain('git add -A :/\n');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('shell-quotes values it puts in commands', () => {
|
|
283
|
+
expect(shellQuote('main')).to.equal('main');
|
|
284
|
+
expect(shellQuote('feature/x-1.2')).to.equal('feature/x-1.2');
|
|
285
|
+
expect(shellQuote('$(id)')).to.equal("'$(id)'");
|
|
286
|
+
expect(shellQuote("it's")).to.equal("'it'\\''s'");
|
|
287
|
+
// the push names HEAD, so a branch name never reaches a command
|
|
288
|
+
expect(report('git@github.com:acme/shop.git', { defaultBranch: '$(id)' })).to.contain('git push -u origin HEAD');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('names the token-account exemption, the PR setting and the optional CI token', () => {
|
|
292
|
+
const out = report('git@github.com:acme/shop.git');
|
|
293
|
+
expect(out).to.contain('exempt the token');
|
|
294
|
+
expect(out).to.contain('Allow GitHub Actions to create and approve pull requests');
|
|
295
|
+
// one command sets both settings guide step 4 names
|
|
296
|
+
expect(out).to.contain('-f default_workflow_permissions=write -F can_approve_pull_request_reviews=true');
|
|
297
|
+
expect(out).to.contain('Read and write permissions');
|
|
298
|
+
// the command line ends at the command, so it runs when pasted
|
|
299
|
+
expect(out).to.match(/gh secret set BIT_SYNC_GH_TOKEN -R acme\/shop\n\s+optional: a GitHub token/);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('states the schedule the scaffolded workflow installs', () => {
|
|
303
|
+
expect(renderBitSyncWorkflow('main')).to.contain("cron: '*/30 * * * *'");
|
|
304
|
+
expect(report('git@github.com:acme/shop.git')).to.contain('every 30 minutes');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('names an origin it does not recognize, and says how to mark a GitHub Enterprise Server', () => {
|
|
308
|
+
const origin = 'git@github.corp.example:acme/store.git';
|
|
309
|
+
const out = report(origin, { originUrl: origin });
|
|
310
|
+
expect(out).to.contain(`origin ${origin}`);
|
|
311
|
+
expect(out).to.contain('GITHUB_SERVER_URL=https://github.corp.example bit ci sync --init');
|
|
312
|
+
expect(out).to.contain('git push -u origin HEAD');
|
|
313
|
+
expect(out).to.not.contain('no GitHub remote yet');
|
|
314
|
+
// the host is unknown, so no github.com steps
|
|
315
|
+
expect(out).to.not.contain('github.com/apps/bit-git-sync');
|
|
316
|
+
expect(out).to.not.contain('gh secret set');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('marks files that already existed and a config block that was already there', () => {
|
|
320
|
+
const out = report('git@github.com:acme/shop.git', {
|
|
321
|
+
files: [{ ...files[0], status: 'skipped' }, files[1]],
|
|
322
|
+
configAdded: false,
|
|
323
|
+
});
|
|
324
|
+
expect(out).to.match(/\.github\/workflows\/bit-sync\.yml\s+already exists, left unchanged/);
|
|
325
|
+
expect(out).to.match(/\.github\/workflows\/bit-release\.yml\s+releases the scope/);
|
|
326
|
+
expect(out).to.match(/workspace\.jsonc\s+already configures sync/);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('creates the repository from the repository root when the workspace is a subdirectory', () => {
|
|
330
|
+
expect(report(undefined, { repoRootFromCwd: '../..' })).to.contain(
|
|
331
|
+
'gh repo create --private --source ../.. --remote origin --push'
|
|
332
|
+
);
|
|
333
|
+
expect(report(undefined, { repoRootFromCwd: '/my repo' })).to.contain("--source '/my repo' --remote");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it('flags a guessed default branch', () => {
|
|
337
|
+
const origin = 'git@github.com:acme/shop.git';
|
|
338
|
+
expect(report(origin, { originUrl: origin, defaultBranchGuessed: true })).to.contain(
|
|
339
|
+
"the remote didn't name its default branch"
|
|
340
|
+
);
|
|
341
|
+
expect(report(undefined, { defaultBranchGuessed: true })).to.contain('no remote names the default branch');
|
|
342
|
+
expect(report(origin)).to.not.contain('default branch, so');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('suggests no server for an origin that is a path on this machine', () => {
|
|
346
|
+
[
|
|
347
|
+
'/tmp/e2e/remote.git',
|
|
348
|
+
'../remote.git',
|
|
349
|
+
'remote.git',
|
|
350
|
+
'repos/remote.git',
|
|
351
|
+
'file:///srv/remote.git',
|
|
352
|
+
'C:\\repos\\remote.git',
|
|
353
|
+
].forEach((origin) => {
|
|
354
|
+
expect(isLocalPathRemote(origin), origin).to.be.true;
|
|
355
|
+
const out = report(origin, { originUrl: origin });
|
|
356
|
+
expect(out, origin).to.contain(`origin ${origin}`);
|
|
357
|
+
expect(out, origin).to.not.contain('GITHUB_SERVER_URL');
|
|
358
|
+
expect(out, origin).to.not.contain('<your-server>');
|
|
359
|
+
expect(out, origin).to.contain('git push -u origin HEAD');
|
|
360
|
+
});
|
|
361
|
+
expect(isLocalPathRemote('git@ghe.example:acme/store.git')).to.be.false;
|
|
362
|
+
expect(isLocalPathRemote('https://ghe.example/acme/store.git')).to.be.false;
|
|
363
|
+
expect(isLocalPathRemote('ssh://git@ghe.example:2222/acme/store.git')).to.be.false;
|
|
364
|
+
expect(isLocalPathRemote('ghe.example:acme/store.git')).to.be.false;
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('prints control characters in an origin as inert text, and suggests no server for it', () => {
|
|
368
|
+
expect(escapeControlChars('a\nb\u001b[2Jc')).to.equal('a\\x0ab\\x1b[2Jc');
|
|
369
|
+
const origin = 'https://ghe.example/acme/store.git\n\u001b[32m✔ all set';
|
|
370
|
+
const out = renderInitReport({
|
|
371
|
+
scopeId: 'acme.shop',
|
|
372
|
+
originUrl: origin,
|
|
373
|
+
defaultBranch: 'main',
|
|
374
|
+
files,
|
|
375
|
+
configAdded: true,
|
|
376
|
+
});
|
|
377
|
+
expect(out).to.not.contain('\u001b[32m');
|
|
378
|
+
expect(out).to.not.contain('store.git\n');
|
|
379
|
+
expect(out).to.contain('origin https://ghe.example/acme/store.git\\x0a\\x1b[32m');
|
|
380
|
+
expect(out).to.not.contain('GITHUB_SERVER_URL');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
it('never prints the credentials an origin URL carries', () => {
|
|
384
|
+
expect(redactRemoteUrl('https://user:ghp_secret@git.corp.example/acme/store.git')).to.equal(
|
|
385
|
+
'https://git.corp.example/acme/store.git'
|
|
386
|
+
);
|
|
387
|
+
expect(redactRemoteUrl('https://ghp_secret@git.corp.example/acme/store.git')).to.equal(
|
|
388
|
+
'https://git.corp.example/acme/store.git'
|
|
389
|
+
);
|
|
390
|
+
expect(redactRemoteUrl('ssh://git:secret@git.corp.example/acme/store.git')).to.equal(
|
|
391
|
+
'ssh://git@git.corp.example/acme/store.git'
|
|
392
|
+
);
|
|
393
|
+
expect(redactRemoteUrl('ssh://git@git.corp.example/acme/store.git')).to.equal(
|
|
394
|
+
'ssh://git@git.corp.example/acme/store.git'
|
|
395
|
+
);
|
|
396
|
+
expect(redactRemoteUrl('git@git.corp.example:acme/store.git')).to.equal('git@git.corp.example:acme/store.git');
|
|
397
|
+
const origin = 'https://user:ghp_secret@git.corp.example:8443/acme/store.git';
|
|
398
|
+
const out = report(origin, { originUrl: origin });
|
|
399
|
+
expect(out).to.not.contain('ghp_secret');
|
|
400
|
+
expect(out).to.contain('origin https://git.corp.example:8443/acme/store.git');
|
|
401
|
+
expect(out).to.contain('GITHUB_SERVER_URL=https://git.corp.example:8443 bit ci sync --init');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('suggests a GITHUB_SERVER_URL that keeps an https port, and drops an ssh one', () => {
|
|
405
|
+
expect(suggestedServerUrl('https://ghe.example:8443/acme/store.git')).to.equal('https://ghe.example:8443');
|
|
406
|
+
expect(suggestedServerUrl('git@ghe.example:acme/store.git')).to.equal('https://ghe.example');
|
|
407
|
+
expect(suggestedServerUrl('ssh://git@ghe.example:2222/acme/store.git')).to.equal('https://ghe.example');
|
|
408
|
+
const origin = 'https://ghe.example:8443/acme/store.git';
|
|
409
|
+
expect(report(origin, { originUrl: origin })).to.contain(
|
|
410
|
+
'GITHUB_SERVER_URL=https://ghe.example:8443 bit ci sync --init'
|
|
411
|
+
);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
describe('on GitHub Enterprise Server', () => {
|
|
147
415
|
const saved: Record<string, string | undefined> = {};
|
|
148
416
|
beforeEach(() => {
|
|
149
417
|
saved.api = process.env.GITHUB_API_URL;
|
|
@@ -157,43 +425,37 @@ describe('init-scaffold', () => {
|
|
|
157
425
|
else process.env.GITHUB_SERVER_URL = saved.server;
|
|
158
426
|
});
|
|
159
427
|
|
|
160
|
-
it('
|
|
428
|
+
it('uses the dispatch webhook on the server API, and gh commands that name the host', () => {
|
|
161
429
|
process.env.GITHUB_API_URL = 'https://ghe.corp.example/api/v3';
|
|
162
|
-
const
|
|
163
|
-
expect(
|
|
164
|
-
expect(
|
|
430
|
+
const out = report('git@ghe.corp.example:acme/store.git');
|
|
431
|
+
expect(out).to.contain('https://ghe.corp.example/api/v3/repos/acme/store/dispatches');
|
|
432
|
+
expect(out).to.contain('"event_type":"bit-export"');
|
|
433
|
+
expect(out).to.contain('gh secret set BIT_CONFIG_ACCESS_TOKEN -R ghe.corp.example/acme/store');
|
|
434
|
+
expect(out).to.contain('gh api --hostname ghe.corp.example -X PUT repos/acme/store/');
|
|
435
|
+
expect(out).to.not.contain('github.com/apps/bit-git-sync');
|
|
165
436
|
});
|
|
166
437
|
|
|
167
|
-
it('derives <server>/api/v3
|
|
438
|
+
it('derives <server>/api/v3 when GITHUB_API_URL is unset', () => {
|
|
168
439
|
delete process.env.GITHUB_API_URL;
|
|
169
|
-
expect(
|
|
440
|
+
expect(report('https://ghe.corp.example/acme/store.git')).to.contain(
|
|
170
441
|
'https://ghe.corp.example/api/v3/repos/acme/store/dispatches'
|
|
171
442
|
);
|
|
172
443
|
});
|
|
173
444
|
|
|
174
|
-
it('names no API
|
|
445
|
+
it('names no API when GITHUB_API_URL is on another host', () => {
|
|
175
446
|
process.env.GITHUB_API_URL = 'https://api.github.com';
|
|
176
|
-
const
|
|
177
|
-
expect(
|
|
178
|
-
expect(
|
|
179
|
-
expect(
|
|
447
|
+
const out = report('https://ghe.corp.example/acme/store.git');
|
|
448
|
+
expect(out).to.contain('<GitHub API URL>/repos/acme/store/dispatches');
|
|
449
|
+
expect(out).to.contain('GITHUB_API_URL "https://api.github.com"');
|
|
450
|
+
expect(out).to.not.contain('https://api.github.com/repos');
|
|
180
451
|
});
|
|
181
|
-
});
|
|
182
452
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
'
|
|
187
|
-
'
|
|
188
|
-
|
|
189
|
-
'Components > Export',
|
|
190
|
-
'Authorization: Bearer <PAT>',
|
|
191
|
-
'Accept: application/vnd.github+json',
|
|
192
|
-
'"event_type":"bit-export"',
|
|
193
|
-
'{{laneId}}',
|
|
194
|
-
'drops its custom headers',
|
|
195
|
-
'fetch-depth: 0',
|
|
196
|
-
].forEach((item) => expect(checklist, item).to.contain(item));
|
|
453
|
+
it('keeps the App path for a github.com origin', () => {
|
|
454
|
+
process.env.GITHUB_API_URL = 'https://ghe.corp.example/api/v3';
|
|
455
|
+
const out = report('git@github.com:acme/shop.git');
|
|
456
|
+
expect(out).to.contain('https://github.com/apps/bit-git-sync');
|
|
457
|
+
expect(out).to.not.contain('ghe.corp.example');
|
|
458
|
+
});
|
|
197
459
|
});
|
|
198
460
|
});
|
|
199
461
|
});
|