@teambit/ci 1.0.648 → 1.0.650
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 +6 -1
- package/dist/ci.docs.mdx +6 -1
- package/dist/{preview-1792708253611.js → preview-1792772346990.js} +1 -1
- package/dist/sync/lane-sync-executor.d.ts +1 -1
- package/dist/sync/lane-sync-executor.js.map +1 -1
- package/dist/sync/sync-orchestrator.d.ts +7 -1
- package/dist/sync/sync-orchestrator.js +47 -21
- package/dist/sync/sync-orchestrator.js.map +1 -1
- package/dist/sync/sync-state.d.ts +2 -0
- package/dist/sync/sync-state.js +6 -0
- package/dist/sync/sync-state.js.map +1 -1
- package/package.json +20 -20
- package/sync/lane-sync-executor.ts +1 -1
- package/sync/sync-orchestrator.ts +35 -22
- package/sync/sync-state.ts +5 -0
|
@@ -9,7 +9,7 @@ import type { CheckoutMain } from '@teambit/checkout';
|
|
|
9
9
|
import { CiAspect } from '../ci.aspect';
|
|
10
10
|
import type { CiMain, CiWorkspaceConfig } from '../ci.main.runtime';
|
|
11
11
|
import { git } from '../git';
|
|
12
|
-
import type { CiSyncConfig } from './sync-config';
|
|
12
|
+
import type { CiSyncConfig, LaneTarget } from './sync-config';
|
|
13
13
|
import {
|
|
14
14
|
branchToLaneName,
|
|
15
15
|
isValidGitBranchName,
|
|
@@ -23,6 +23,7 @@ import { HALT_SUMMARY_PREFIX, REFUSED_SUMMARY_PREFIX, LaneSyncExecutor } from '.
|
|
|
23
23
|
import { MainSyncExecutor } from './main-sync-executor';
|
|
24
24
|
import { selectGitHostProvider } from './git-host-provider';
|
|
25
25
|
import { gitRepoRoot, isNonContentPath, listRemoteBranches } from './git-ops';
|
|
26
|
+
import { readBranchLanePointer } from './sync-state';
|
|
26
27
|
import { chooseInitDefaultBranch, deriveOwnerRepo, renderInitReport, scaffoldWorkflowFiles } from './init-scaffold';
|
|
27
28
|
import { UNKNOWN_REPO, scaffoldAgentsMd } from './agents-md';
|
|
28
29
|
|
|
@@ -171,11 +172,8 @@ export class SyncOrchestrator {
|
|
|
171
172
|
if (!laneName) return `branch ${branch} does not map to a valid lane name; nothing to do`;
|
|
172
173
|
const skipReason = this.laneNotSyncableReason(laneName, cfg, mainLaneName, defaultBranch);
|
|
173
174
|
if (skipReason) return `${skipReason} (branch ${branch})`;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
return this.summarizeSync([
|
|
177
|
-
await laneSync.syncLane({ hostScope: defaultScope, name: laneName }, { dryRun: opts.dryRun }),
|
|
178
|
-
]);
|
|
175
|
+
const target = await this.branchLaneTarget(laneSync, branch, laneName);
|
|
176
|
+
return this.summarizeSync([await laneSync.syncLane(target, { dryRun: opts.dryRun })]);
|
|
179
177
|
}
|
|
180
178
|
|
|
181
179
|
if (opts.lane) {
|
|
@@ -190,19 +188,17 @@ export class SyncOrchestrator {
|
|
|
190
188
|
// `--all` (also the no-arguments default): every mapped lane, then the main scope. Sequential on
|
|
191
189
|
// purpose — the lanes share one workspace and one git checkout.
|
|
192
190
|
const lines: string[] = [];
|
|
193
|
-
const { lanes: lanesToSync, errors } = await this.listLanesToSync(cfg, mainLaneName, defaultBranch);
|
|
191
|
+
const { lanes: lanesToSync, errors } = await this.listLanesToSync(cfg, mainLaneName, defaultBranch, laneSync);
|
|
194
192
|
lines.push(...errors);
|
|
195
193
|
this.deps.logger.console(
|
|
196
194
|
formatTitle(
|
|
197
195
|
`Reconciling ${lanesToSync.length} mapped lane(s) of ${defaultScope}` +
|
|
198
|
-
`${lanesToSync.length ? `: ${lanesToSync.join(', ')}` : ''}`
|
|
196
|
+
`${lanesToSync.length ? `: ${lanesToSync.map(({ name }) => name).join(', ')}` : ''}`
|
|
199
197
|
)
|
|
200
198
|
);
|
|
201
|
-
for (const
|
|
202
|
-
// Every enumerated target is hosted on `defaultScope` by construction; foreign-hosted lanes are
|
|
203
|
-
// explicit-target only.
|
|
199
|
+
for (const target of lanesToSync) {
|
|
204
200
|
// oxlint-disable-next-line no-await-in-loop
|
|
205
|
-
lines.push(await laneSync.syncLane(
|
|
201
|
+
lines.push(await laneSync.syncLane(target, { dryRun: opts.dryRun }));
|
|
206
202
|
}
|
|
207
203
|
lines.push(await mainSync.syncMain({ dryRun: opts.dryRun }));
|
|
208
204
|
return this.summarizeSync(lines);
|
|
@@ -378,7 +374,7 @@ export class SyncOrchestrator {
|
|
|
378
374
|
}
|
|
379
375
|
|
|
380
376
|
/**
|
|
381
|
-
* The
|
|
377
|
+
* The lanes to reconcile on an `--all` run (sorted by name), plus one HALTED line per enumeration
|
|
382
378
|
* failure. The list is the union of the remote's lanes AND the lane-mapped branches on `origin` —
|
|
383
379
|
* a lane deleted on bit.cloud is gone from the first source, and only the branch side can make
|
|
384
380
|
* `close-pr` fire for it. An enumeration failure is reported rather than thrown, so the half that
|
|
@@ -387,8 +383,9 @@ export class SyncOrchestrator {
|
|
|
387
383
|
private async listLanesToSync(
|
|
388
384
|
cfg: Required<CiSyncConfig>,
|
|
389
385
|
mainLaneName: string,
|
|
390
|
-
defaultBranch: string
|
|
391
|
-
|
|
386
|
+
defaultBranch: string,
|
|
387
|
+
laneSync: LaneSyncExecutor
|
|
388
|
+
): Promise<{ lanes: LaneTarget[]; errors: string[] }> {
|
|
392
389
|
if (!cfg.lanes.length) {
|
|
393
390
|
this.deps.logger.console(
|
|
394
391
|
formatWarningSummary('sync.lanes is empty — lane mirroring is disabled, reconciling the main scope only')
|
|
@@ -396,14 +393,15 @@ export class SyncOrchestrator {
|
|
|
396
393
|
return { lanes: [], errors: [] };
|
|
397
394
|
}
|
|
398
395
|
const errors: string[] = [];
|
|
399
|
-
const
|
|
396
|
+
const targets = new Map<string, LaneTarget>();
|
|
397
|
+
const add = (target: LaneTarget) => targets.set(`${target.hostScope}/${target.name}`, target);
|
|
400
398
|
const remote = this.deps.workspace.defaultScope;
|
|
401
399
|
|
|
402
400
|
try {
|
|
403
401
|
const lanes = await this.deps.lanes.getLanes({ remote });
|
|
404
402
|
lanes.forEach((lane) => {
|
|
405
403
|
const name = lane.id?.name ?? lane.name;
|
|
406
|
-
if (name)
|
|
404
|
+
if (name) add({ hostScope: remote, name });
|
|
407
405
|
});
|
|
408
406
|
} catch (e: any) {
|
|
409
407
|
const msg = e?.toString() ?? '';
|
|
@@ -431,7 +429,8 @@ export class SyncOrchestrator {
|
|
|
431
429
|
);
|
|
432
430
|
continue;
|
|
433
431
|
}
|
|
434
|
-
|
|
432
|
+
// oxlint-disable-next-line no-await-in-loop
|
|
433
|
+
add(await this.branchLaneTarget(laneSync, branch, laneName));
|
|
435
434
|
}
|
|
436
435
|
} catch (e: any) {
|
|
437
436
|
errors.push(
|
|
@@ -440,13 +439,27 @@ export class SyncOrchestrator {
|
|
|
440
439
|
);
|
|
441
440
|
}
|
|
442
441
|
|
|
443
|
-
const lanes = [...
|
|
444
|
-
.filter((name) => name !== mainLaneName)
|
|
445
|
-
.filter((name) => shouldSyncLane(name, cfg))
|
|
446
|
-
.sort();
|
|
442
|
+
const lanes = [...targets.values()]
|
|
443
|
+
.filter(({ name }) => name !== mainLaneName)
|
|
444
|
+
.filter(({ name }) => shouldSyncLane(name, cfg))
|
|
445
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
447
446
|
return { lanes, errors };
|
|
448
447
|
}
|
|
449
448
|
|
|
449
|
+
/**
|
|
450
|
+
* The lane a lane-mapped branch mirrors. A branch name carries no scope, so a lane that another scope
|
|
451
|
+
* hosts is found only through the lane pointer in the branch's `.bitmap`. A pointer to a lane of
|
|
452
|
+
* another name comes from a branch cut from that lane's branch, and must not redirect this one.
|
|
453
|
+
*/
|
|
454
|
+
private async branchLaneTarget(laneSync: LaneSyncExecutor, branch: string, laneName: string): Promise<LaneTarget> {
|
|
455
|
+
const defaultScope = this.deps.workspace.defaultScope;
|
|
456
|
+
// a failed fetch must not throw here: syncLane fetches again and turns the failure into a halt
|
|
457
|
+
await laneSync.fetchOnce().catch(() => undefined);
|
|
458
|
+
const pointer = await readBranchLanePointer(branch, defaultScope);
|
|
459
|
+
const target = pointer ? parseLaneTarget(pointer, defaultScope) : undefined;
|
|
460
|
+
return target?.name === laneName ? target : { hostScope: defaultScope, name: laneName };
|
|
461
|
+
}
|
|
462
|
+
|
|
450
463
|
/**
|
|
451
464
|
* Join the per-target summary lines, and turn any halt into a non-zero exit — the executors never
|
|
452
465
|
* throw, so this is the single place "needs a human" becomes visible to CI. `BitError` rather than
|
package/sync/sync-state.ts
CHANGED
|
@@ -222,6 +222,11 @@ export function parseDevCommitCount(raw: string): boolean {
|
|
|
222
222
|
return Number.isNaN(count) ? true : count > 0;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
/** The lane id in `origin/<branch>`'s `.bitmap`, if it has an exported one. Assumes `git fetch origin` already ran. */
|
|
226
|
+
export async function readBranchLanePointer(branch: string, defaultScope: string): Promise<string | undefined> {
|
|
227
|
+
return parseBranchBitmap(await readFileAtRef(`origin/${branch}`, BIT_MAP), defaultScope)?.laneIdStr;
|
|
228
|
+
}
|
|
229
|
+
|
|
225
230
|
/**
|
|
226
231
|
* Read a remote branch's sync state from its committed `.bitmap`. Assumes `git fetch origin` already ran.
|
|
227
232
|
* `--first-parent` is a correctness requirement: `git log` orders across all parents, so a `.bitmap`
|