@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,396 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.SyncOrchestrator = void 0;
7
+ exports.assertCleanForDryRun = assertCleanForDryRun;
8
+ function _cli() {
9
+ const data = require("@teambit/cli");
10
+ _cli = function () {
11
+ return data;
12
+ };
13
+ return data;
14
+ }
15
+ function path() {
16
+ const data = _interopRequireWildcard(require("path"));
17
+ path = function () {
18
+ return data;
19
+ };
20
+ return data;
21
+ }
22
+ function _bitError() {
23
+ const data = require("@teambit/bit-error");
24
+ _bitError = function () {
25
+ return data;
26
+ };
27
+ return data;
28
+ }
29
+ function _ci() {
30
+ const data = require("../ci.aspect");
31
+ _ci = function () {
32
+ return data;
33
+ };
34
+ return data;
35
+ }
36
+ function _git() {
37
+ const data = require("../git");
38
+ _git = function () {
39
+ return data;
40
+ };
41
+ return data;
42
+ }
43
+ function _syncConfig() {
44
+ const data = require("./sync-config");
45
+ _syncConfig = function () {
46
+ return data;
47
+ };
48
+ return data;
49
+ }
50
+ function _laneSyncExecutor() {
51
+ const data = require("./lane-sync-executor");
52
+ _laneSyncExecutor = function () {
53
+ return data;
54
+ };
55
+ return data;
56
+ }
57
+ function _mainSyncExecutor() {
58
+ const data = require("./main-sync-executor");
59
+ _mainSyncExecutor = function () {
60
+ return data;
61
+ };
62
+ return data;
63
+ }
64
+ function _gitHostProvider() {
65
+ const data = require("./git-host-provider");
66
+ _gitHostProvider = function () {
67
+ return data;
68
+ };
69
+ return data;
70
+ }
71
+ function _gitOps() {
72
+ const data = require("./git-ops");
73
+ _gitOps = function () {
74
+ return data;
75
+ };
76
+ return data;
77
+ }
78
+ function _initScaffold() {
79
+ const data = require("./init-scaffold");
80
+ _initScaffold = function () {
81
+ return data;
82
+ };
83
+ return data;
84
+ }
85
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
86
+ /**
87
+ * Refuse a `--dry-run` whose plan would be computed by force-checking-out branches over uncommitted
88
+ * work: the main-scope drift is a diff, so the tree IS written before the dry-run return. A dry run
89
+ * promises to write nothing, so it may not discard either — with a clean tree the write-then-restore
90
+ * loses nothing.
91
+ */
92
+ function assertCleanForDryRun(dirtyFiles) {
93
+ if (!dirtyFiles.length) return;
94
+ const named = `${dirtyFiles.slice(0, 10).join(', ')}${dirtyFiles.length > 10 ? ', …' : ''}`;
95
+ throw new (_bitError().BitError)(`bit ci sync --dry-run refuses to run: the working tree has ${dirtyFiles.length} uncommitted change(s), ` + `and computing the plan force-checkouts branches and removes untracked files, which would discard ` + `them. Commit or stash them first: ${named}`);
96
+ }
97
+
98
+ /** Everything the sync orchestration needs from `CiMain`, passed explicitly. */
99
+
100
+ /**
101
+ * Routing and aggregation for `bit ci sync`: decides which targets a run visits and how their
102
+ * summaries combine. What reconciling a target means lives in the two executors, both stateless and
103
+ * idempotent, so the trigger only ever decides when the reconciler runs.
104
+ */
105
+ class SyncOrchestrator {
106
+ constructor(deps) {
107
+ this.deps = deps;
108
+ }
109
+
110
+ /**
111
+ * `bit ci sync` — reconcile Bit lanes and the main scope with git branches and pull requests.
112
+ * Returns the per-target summary lines; throws the joined summary after the loop when any target
113
+ * halted or was refused, so CI exits non-zero while every other target was still attempted. A skip
114
+ * is neither — it reports itself and leaves the run green.
115
+ */
116
+ async sync(opts = {}) {
117
+ if (opts.init) {
118
+ if (opts.lane || opts.branch || opts.all || opts.main || opts.dryRun) {
119
+ throw new (_bitError().BitError)('bit ci sync --init cannot be combined with a lane argument, --branch, --all, --main or ' + '--dry-run: it only scaffolds onboarding files (workflows + workspace.jsonc config) and exits ' + '— it never reconciles');
120
+ }
121
+ return this.syncInit();
122
+ }
123
+
124
+ // Refuse contradictory flag combinations rather than silently letting one target win.
125
+ const narrower = opts.lane ? `a lane argument ("${opts.lane}")` : opts.branch && `--branch ("${opts.branch}")` || opts.main && '--main' || undefined;
126
+ if (opts.all && narrower) {
127
+ throw new (_bitError().BitError)(`--all cannot be combined with ${narrower}: --all reconciles every mapped lane plus the main ` + `scope, and is what runs when no target is given`);
128
+ }
129
+
130
+ // A lane argument, `--branch` and `--main` each select the single target; any two contradict.
131
+ const selectors = [opts.lane && `a lane argument ("${opts.lane}")`, opts.branch && `--branch ("${opts.branch}")`, opts.main && '--main'].filter(selector => Boolean(selector));
132
+ if (selectors.length > 1) {
133
+ const winner = opts.main ? '--main' : `--branch ("${opts.branch}")`;
134
+ throw new (_bitError().BitError)(`${selectors[0]} cannot be combined with ${selectors.slice(1).join(' or with ')}: each selects ` + `the single target this run reconciles, and only ${winner} would have run — the rest would be ` + `silently dropped, leaving a run that reports success while a target you named was never visited`);
135
+ }
136
+ const cfg = (0, _syncConfig().resolveSyncConfig)(this.deps.config.sync);
137
+ const defaultScope = this.deps.workspace.defaultScope;
138
+ const defaultBranch = await this.deps.ci.getDefaultBranchName();
139
+ const mainLaneName = this.deps.lanes.getDefaultLaneId().name;
140
+
141
+ // The executors force-checkout branches and remove untracked files; warn up front, naming the
142
+ // files at stake, before an interactive run discards anything.
143
+ const statusAtStart = await _git().git.status().catch(() => undefined);
144
+ const dirtyFiles = (statusAtStart?.files ?? []).map(file => file.path).filter(file => !(0, _gitOps().isNonContentPath)(file));
145
+ if (opts.dryRun) assertCleanForDryRun(dirtyFiles);
146
+ if (dirtyFiles.length) {
147
+ this.deps.logger.consoleWarning(`the working tree has ${dirtyFiles.length} uncommitted change(s). "bit ci sync" force-checkouts branches ` + `and removes untracked files (except .bit/ and node_modules/), so these will be discarded: ` + `${dirtyFiles.slice(0, 10).join(', ')}${dirtyFiles.length > 10 ? ', …' : ''}`);
148
+ }
149
+ const remoteUrl = await _git().git.remote(['get-url', 'origin']).catch(() => undefined);
150
+ const {
151
+ provider: gitHost,
152
+ reason: noGitHostReason
153
+ } = (0, _gitHostProvider().selectGitHostProvider)(this.deps.ci.listGitHostProviders(), typeof remoteUrl === 'string' ? remoteUrl.trim() : undefined);
154
+ if (gitHost) this.deps.logger.debug(`bit ci sync: using the "${gitHost.name}" git host provider`);
155
+ // Warn once, up front — the executors' per-action lines can only say PRs were skipped, not why.
156
+ else if (noGitHostReason) this.deps.logger.consoleWarning(noGitHostReason);
157
+ const laneSync = new (_laneSyncExecutor().LaneSyncExecutor)({
158
+ lanes: this.deps.lanes,
159
+ ci: this.deps.ci,
160
+ logger: this.deps.logger,
161
+ gitHost,
162
+ cfg,
163
+ defaultScope
164
+ });
165
+ const mainSync = new (_mainSyncExecutor().MainSyncExecutor)({
166
+ checkout: this.deps.checkout,
167
+ lanes: this.deps.lanes,
168
+ ci: this.deps.ci,
169
+ logger: this.deps.logger,
170
+ gitHost,
171
+ cfg,
172
+ defaultBranch,
173
+ defaultScope
174
+ });
175
+ if (opts.main) {
176
+ return this.summarizeSync([await mainSync.syncMain({
177
+ dryRun: opts.dryRun
178
+ })]);
179
+ }
180
+ if (opts.branch) {
181
+ const branch = opts.branch;
182
+ // Under the default `branchPrefix: ''` these two would resolve to the bogus lanes "main" /
183
+ // "bit-sync/main"; both are reconciled by the main-scope path, never as lanes.
184
+ if (branch === defaultBranch) {
185
+ return `branch ${branch} is the default branch — reconcile it with "bit ci sync --main"; nothing to do`;
186
+ }
187
+ if (branch === cfg.mainSyncBranch) {
188
+ return `branch ${branch} is the main sync branch maintained by this command; nothing to do`;
189
+ }
190
+ const laneName = (0, _syncConfig().syncableLaneNameForBranch)(branch, cfg);
191
+ if (!laneName) return `branch ${branch} does not map to a valid lane name; nothing to do`;
192
+ const skipReason = this.laneNotSyncableReason(laneName, cfg, mainLaneName, defaultBranch);
193
+ if (skipReason) return `${skipReason} (branch ${branch})`;
194
+ // A branch name carries no scope, so this path can only resolve the lane against `defaultScope`;
195
+ // resolving a foreign-hosted lane to the wrong scope is safe (no-op, never a deletion).
196
+ return this.summarizeSync([await laneSync.syncLane({
197
+ hostScope: defaultScope,
198
+ name: laneName
199
+ }, {
200
+ dryRun: opts.dryRun
201
+ })]);
202
+ }
203
+ if (opts.lane) {
204
+ // Every guard below is asked about the NAME: "main" is not a lane whichever scope hosts it, and
205
+ // the `lanes` patterns match lane names, not lane ids.
206
+ const target = (0, _syncConfig().parseLaneTarget)(opts.lane, defaultScope);
207
+ const skipReason = this.laneNotSyncableReason(target.name, cfg, mainLaneName, defaultBranch);
208
+ if (skipReason) return skipReason;
209
+ return this.summarizeSync([await laneSync.syncLane(target, {
210
+ dryRun: opts.dryRun,
211
+ explicitTarget: true
212
+ })]);
213
+ }
214
+
215
+ // `--all` (also the no-arguments default): every mapped lane, then the main scope. Sequential on
216
+ // purpose — the lanes share one workspace and one git checkout.
217
+ const lines = [];
218
+ const {
219
+ lanes: lanesToSync,
220
+ errors
221
+ } = await this.listLanesToSync(cfg, mainLaneName, defaultBranch);
222
+ lines.push(...errors);
223
+ this.deps.logger.console((0, _cli().formatTitle)(`Reconciling ${lanesToSync.length} mapped lane(s) of ${defaultScope}` + `${lanesToSync.length ? `: ${lanesToSync.join(', ')}` : ''}`));
224
+ for (const laneName of lanesToSync) {
225
+ // Every enumerated target is hosted on `defaultScope` by construction; foreign-hosted lanes are
226
+ // explicit-target only.
227
+ // oxlint-disable-next-line no-await-in-loop
228
+ lines.push(await laneSync.syncLane({
229
+ hostScope: defaultScope,
230
+ name: laneName
231
+ }, {
232
+ dryRun: opts.dryRun
233
+ }));
234
+ }
235
+ lines.push(await mainSync.syncMain({
236
+ dryRun: opts.dryRun
237
+ }));
238
+ return this.summarizeSync(lines);
239
+ }
240
+
241
+ /**
242
+ * `bit ci sync --init` — one-command onboarding: scaffolds the two workflow files, adds the sync
243
+ * config block to workspace.jsonc if missing, and prints the manual-steps checklist. Safe to
244
+ * re-run: existing files are skipped, an existing config block is left alone; it never reconciles.
245
+ */
246
+ async syncInit() {
247
+ const defaultBranch = await this.deps.ci.getDefaultBranchName();
248
+
249
+ // Workflows belong to the repository, not the workspace: GitHub only discovers them at
250
+ // `<repo-root>/.github/workflows`, and a bit workspace may be a subdirectory.
251
+ const repoRoot = await (0, _gitOps().gitRepoRoot)();
252
+ const scaffoldRoot = repoRoot ?? this.deps.workspace.path;
253
+ if (!repoRoot) {
254
+ this.deps.logger.consoleWarning(`could not resolve the git repository root ("git rev-parse --show-toplevel" failed — is this a git ` + `repository?). Writing the workflow files under the workspace instead: ${scaffoldRoot}. If that is ` + `not the repository root, GitHub will not discover them — move .github/workflows there by hand.`);
255
+ }
256
+ const fileOutcomes = (0, _initScaffold().scaffoldWorkflowFiles)(scaffoldRoot, defaultBranch);
257
+ // Report the path relative to where the user is standing, not to the workspace.
258
+ const displayPath = relativePath => {
259
+ const abs = path().join(scaffoldRoot, relativePath);
260
+ const fromCwd = path().relative(process.cwd(), abs);
261
+ return fromCwd && !fromCwd.startsWith('..') ? fromCwd : abs;
262
+ };
263
+ const fileLines = fileOutcomes.map(outcome => outcome.status === 'written' ? `wrote ${displayPath(outcome.relativePath)} (default branch: ${defaultBranch})` : `skipped ${displayPath(outcome.relativePath)} (already exists — bit ci sync --init never overwrites)`);
264
+ const configAdded = await this.ensureSyncConfigBlock();
265
+ const configLine = configAdded ? 'added "teambit.git/ci": { "sync": {} } to workspace.jsonc' : 'workspace.jsonc already configures "teambit.git/ci".sync — left untouched';
266
+ const remoteUrl = await _git().git.remote(['get-url', 'origin']).catch(() => undefined);
267
+ const ownerRepo = (0, _initScaffold().deriveOwnerRepo)(typeof remoteUrl === 'string' ? remoteUrl.trim() : undefined);
268
+ const summary = ['bit ci sync --init', ...fileLines, configLine, (0, _initScaffold().renderInitChecklist)(ownerRepo)].join('\n');
269
+ this.deps.logger.console(summary);
270
+ return summary;
271
+ }
272
+
273
+ /**
274
+ * Add `"sync": {}` under `"teambit.git/ci"` in workspace.jsonc if that key isn't there yet; returns
275
+ * whether it wrote. The comment-preserving `WorkspaceConfig` API merges into existing
276
+ * `teambit.git/ci` config rather than clobbering it.
277
+ */
278
+ async ensureSyncConfigBlock() {
279
+ const wsConfig = this.deps.workspace.getWorkspaceConfig();
280
+ const existing = wsConfig.extension(_ci().CiAspect.id, true);
281
+ if (existing?.sync) return false;
282
+ wsConfig.setExtension(_ci().CiAspect.id, {
283
+ sync: {}
284
+ }, {
285
+ mergeIntoExisting: true,
286
+ ignoreVersion: true
287
+ });
288
+ await wsConfig.write({
289
+ reasonForChange: 'bit ci sync --init: add the sync config block'
290
+ });
291
+ return true;
292
+ }
293
+
294
+ /**
295
+ * Why this lane name must not be handed to the lane executor, or undefined when it's syncable. The
296
+ * mapped branch is checked too — the lane path must never write the two branches the main-scope
297
+ * path owns.
298
+ */
299
+ laneNotSyncableReason(laneName, cfg, mainLaneName, defaultBranch) {
300
+ if (laneName === mainLaneName) {
301
+ return `"${mainLaneName}" is not a lane — reconcile the main scope with "bit ci sync --main"; nothing to do`;
302
+ }
303
+ const branch = (0, _syncConfig().laneNameToBranch)(laneName, cfg);
304
+ if (branch === defaultBranch) {
305
+ return `lane ${laneName} maps to the default branch ${branch} — reconcile it with "bit ci sync --main"; ` + `nothing to do`;
306
+ }
307
+ if (branch === cfg.mainSyncBranch) {
308
+ return `lane ${laneName} maps to ${branch}, the main sync branch maintained by this command; nothing to do`;
309
+ }
310
+ if (!(0, _syncConfig().shouldSyncLane)(laneName, cfg)) {
311
+ return `lane ${laneName} is not matched by the sync config (lanes: ${JSON.stringify(cfg.lanes)}); nothing to do`;
312
+ }
313
+ return undefined;
314
+ }
315
+
316
+ /**
317
+ * The lane names to reconcile on an `--all` run (sorted), plus one HALTED line per enumeration
318
+ * failure. The list is the union of the remote's lanes AND the lane-mapped branches on `origin` —
319
+ * a lane deleted on bit.cloud is gone from the first source, and only the branch side can make
320
+ * `close-pr` fire for it. An enumeration failure is reported rather than thrown, so the half that
321
+ * did enumerate is still reconciled while the run exits non-zero.
322
+ */
323
+ async listLanesToSync(cfg, mainLaneName, defaultBranch) {
324
+ if (!cfg.lanes.length) {
325
+ this.deps.logger.console((0, _cli().formatWarningSummary)('sync.lanes is empty — lane mirroring is disabled, reconciling the main scope only'));
326
+ return {
327
+ lanes: [],
328
+ errors: []
329
+ };
330
+ }
331
+ const errors = [];
332
+ const names = new Set();
333
+ const remote = this.deps.workspace.defaultScope;
334
+ try {
335
+ const lanes = await this.deps.lanes.getLanes({
336
+ remote
337
+ });
338
+ lanes.forEach(lane => {
339
+ const name = lane.id?.name ?? lane.name;
340
+ if (name) names.add(name);
341
+ });
342
+ } catch (e) {
343
+ const msg = e?.toString() ?? '';
344
+ // A scope with no lanes at all: the remote answers "not found" rather than an empty list.
345
+ if (msg.includes('was not found') || msg.includes('not found')) {
346
+ this.deps.logger.console((0, _cli().formatWarningSummary)(`No lanes found on ${remote} — reconciling lane-mapped branches and the main scope only`));
347
+ } else {
348
+ errors.push(`${_laneSyncExecutor().HALT_SUMMARY_PREFIX} lanes -> could not list the lanes of ${remote}: ${msg}`);
349
+ }
350
+ }
351
+ try {
352
+ for (const branch of await (0, _gitOps().listRemoteBranches)()) {
353
+ // Reconciled by the main-scope path, never as lanes — under the default `branchPrefix: ''`
354
+ // they would otherwise resolve to the bogus lanes "main" / "bit-sync/main".
355
+ if (branch === defaultBranch || branch === cfg.mainSyncBranch) continue;
356
+ // Ordinary developer branches are skipped at debug level, never as a summary line.
357
+ const laneName = (0, _syncConfig().syncableLaneNameForBranch)(branch, cfg);
358
+ if (!laneName) {
359
+ this.deps.logger.debug(`bit ci sync: branch "${branch}" does not map to a name any lane could have ` + `(mapped to "${(0, _syncConfig().branchToLaneName)(branch, cfg) ?? '<not lane-mapped>'}") — skipping it`);
360
+ continue;
361
+ }
362
+ names.add(laneName);
363
+ }
364
+ } catch (e) {
365
+ errors.push(`${_laneSyncExecutor().HALT_SUMMARY_PREFIX} lanes -> could not list the branches of "origin", so a lane deleted on ` + `bit.cloud may have kept an orphan branch and an open PR: ${e?.message || e}`);
366
+ }
367
+ const lanes = [...names].filter(name => name !== mainLaneName).filter(name => (0, _syncConfig().shouldSyncLane)(name, cfg)).sort();
368
+ return {
369
+ lanes,
370
+ errors
371
+ };
372
+ }
373
+
374
+ /**
375
+ * Join the per-target summary lines, and turn any halt into a non-zero exit — the executors never
376
+ * throw, so this is the single place "needs a human" becomes visible to CI. `BitError` rather than
377
+ * `Error`: bit reports a plain `Error` as an internal failure (stack trace, Sentry FATAL).
378
+ */
379
+ summarizeSync(lines) {
380
+ const summary = lines.join('\n');
381
+ const halted = lines.filter(line => line.startsWith(_laneSyncExecutor().HALT_SUMMARY_PREFIX));
382
+ if (halted.length) {
383
+ throw new (_bitError().BitError)(`bit ci sync could not reconcile ${halted.length} target(s):\n${summary}`);
384
+ }
385
+ // A refusal is not a halt: the repository is healthy, the reconciler simply will not do what was
386
+ // asked — exit non-zero saying that, rather than reporting a sync conflict.
387
+ const refusals = lines.filter(line => line.startsWith(_laneSyncExecutor().REFUSED_SUMMARY_PREFIX));
388
+ if (refusals.length) {
389
+ throw new (_bitError().BitError)(refusals.map(line => line.slice(_laneSyncExecutor().REFUSED_SUMMARY_PREFIX.length).trim()).join('\n'));
390
+ }
391
+ return summary;
392
+ }
393
+ }
394
+ exports.SyncOrchestrator = SyncOrchestrator;
395
+
396
+ //# sourceMappingURL=sync-orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_cli","data","require","path","_interopRequireWildcard","_bitError","_ci","_git","_syncConfig","_laneSyncExecutor","_mainSyncExecutor","_gitHostProvider","_gitOps","_initScaffold","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","assertCleanForDryRun","dirtyFiles","length","named","slice","join","BitError","SyncOrchestrator","constructor","deps","sync","opts","init","lane","branch","all","main","dryRun","syncInit","narrower","undefined","selectors","filter","selector","Boolean","winner","cfg","resolveSyncConfig","config","defaultScope","workspace","defaultBranch","ci","getDefaultBranchName","mainLaneName","lanes","getDefaultLaneId","name","statusAtStart","git","status","catch","files","map","file","isNonContentPath","logger","consoleWarning","remoteUrl","remote","provider","gitHost","reason","noGitHostReason","selectGitHostProvider","listGitHostProviders","trim","debug","laneSync","LaneSyncExecutor","mainSync","MainSyncExecutor","checkout","summarizeSync","syncMain","mainSyncBranch","laneName","syncableLaneNameForBranch","skipReason","laneNotSyncableReason","syncLane","hostScope","target","parseLaneTarget","explicitTarget","lines","lanesToSync","errors","listLanesToSync","push","console","formatTitle","repoRoot","gitRepoRoot","scaffoldRoot","fileOutcomes","scaffoldWorkflowFiles","displayPath","relativePath","abs","fromCwd","relative","process","cwd","startsWith","fileLines","outcome","configAdded","ensureSyncConfigBlock","configLine","ownerRepo","deriveOwnerRepo","summary","renderInitChecklist","wsConfig","getWorkspaceConfig","existing","extension","CiAspect","id","setExtension","mergeIntoExisting","ignoreVersion","write","reasonForChange","laneNameToBranch","shouldSyncLane","JSON","stringify","formatWarningSummary","names","Set","getLanes","forEach","add","msg","toString","includes","HALT_SUMMARY_PREFIX","listRemoteBranches","branchToLaneName","message","sort","halted","line","refusals","REFUSED_SUMMARY_PREFIX","exports"],"sources":["sync-orchestrator.ts"],"sourcesContent":["import { formatTitle, formatWarningSummary } from '@teambit/cli';\nimport * as path from 'path';\nimport { BitError } from '@teambit/bit-error';\nimport type { Logger } from '@teambit/logger';\nimport type { Workspace } from '@teambit/workspace';\nimport type { LanesMain } from '@teambit/lanes';\nimport type { CheckoutMain } from '@teambit/checkout';\nimport { CiAspect } from '../ci.aspect';\nimport type { CiMain, CiWorkspaceConfig } from '../ci.main.runtime';\nimport { git } from '../git';\nimport type { CiSyncConfig } from './sync-config';\nimport {\n branchToLaneName,\n laneNameToBranch,\n parseLaneTarget,\n resolveSyncConfig,\n shouldSyncLane,\n syncableLaneNameForBranch,\n} from './sync-config';\nimport { HALT_SUMMARY_PREFIX, REFUSED_SUMMARY_PREFIX, LaneSyncExecutor } from './lane-sync-executor';\nimport { MainSyncExecutor } from './main-sync-executor';\nimport { selectGitHostProvider } from './git-host-provider';\nimport { gitRepoRoot, isNonContentPath, listRemoteBranches } from './git-ops';\nimport { deriveOwnerRepo, renderInitChecklist, scaffoldWorkflowFiles } from './init-scaffold';\n\n/**\n * Refuse a `--dry-run` whose plan would be computed by force-checking-out branches over uncommitted\n * work: the main-scope drift is a diff, so the tree IS written before the dry-run return. A dry run\n * promises to write nothing, so it may not discard either — with a clean tree the write-then-restore\n * loses nothing.\n */\nexport function assertCleanForDryRun(dirtyFiles: string[]): void {\n if (!dirtyFiles.length) return;\n const named = `${dirtyFiles.slice(0, 10).join(', ')}${dirtyFiles.length > 10 ? ', …' : ''}`;\n throw new BitError(\n `bit ci sync --dry-run refuses to run: the working tree has ${dirtyFiles.length} uncommitted change(s), ` +\n `and computing the plan force-checkouts branches and removes untracked files, which would discard ` +\n `them. Commit or stash them first: ${named}`\n );\n}\n\n/** Everything the sync orchestration needs from `CiMain`, passed explicitly. */\nexport type SyncOrchestratorDeps = {\n ci: CiMain;\n workspace: Workspace;\n logger: Logger;\n lanes: LanesMain;\n checkout: CheckoutMain;\n config: CiWorkspaceConfig;\n};\n\n/**\n * Routing and aggregation for `bit ci sync`: decides which targets a run visits and how their\n * summaries combine. What reconciling a target means lives in the two executors, both stateless and\n * idempotent, so the trigger only ever decides when the reconciler runs.\n */\nexport class SyncOrchestrator {\n constructor(private deps: SyncOrchestratorDeps) {}\n\n /**\n * `bit ci sync` — reconcile Bit lanes and the main scope with git branches and pull requests.\n * Returns the per-target summary lines; throws the joined summary after the loop when any target\n * halted or was refused, so CI exits non-zero while every other target was still attempted. A skip\n * is neither — it reports itself and leaves the run green.\n */\n async sync(\n opts: { lane?: string; branch?: string; all?: boolean; main?: boolean; dryRun?: boolean; init?: boolean } = {}\n ): Promise<string> {\n if (opts.init) {\n if (opts.lane || opts.branch || opts.all || opts.main || opts.dryRun) {\n throw new BitError(\n 'bit ci sync --init cannot be combined with a lane argument, --branch, --all, --main or ' +\n '--dry-run: it only scaffolds onboarding files (workflows + workspace.jsonc config) and exits ' +\n '— it never reconciles'\n );\n }\n return this.syncInit();\n }\n\n // Refuse contradictory flag combinations rather than silently letting one target win.\n const narrower = opts.lane\n ? `a lane argument (\"${opts.lane}\")`\n : (opts.branch && `--branch (\"${opts.branch}\")`) || (opts.main && '--main') || undefined;\n if (opts.all && narrower) {\n throw new BitError(\n `--all cannot be combined with ${narrower}: --all reconciles every mapped lane plus the main ` +\n `scope, and is what runs when no target is given`\n );\n }\n\n // A lane argument, `--branch` and `--main` each select the single target; any two contradict.\n const selectors = [\n opts.lane && `a lane argument (\"${opts.lane}\")`,\n opts.branch && `--branch (\"${opts.branch}\")`,\n opts.main && '--main',\n ].filter((selector): selector is string => Boolean(selector));\n if (selectors.length > 1) {\n const winner = opts.main ? '--main' : `--branch (\"${opts.branch}\")`;\n throw new BitError(\n `${selectors[0]} cannot be combined with ${selectors.slice(1).join(' or with ')}: each selects ` +\n `the single target this run reconciles, and only ${winner} would have run — the rest would be ` +\n `silently dropped, leaving a run that reports success while a target you named was never visited`\n );\n }\n\n const cfg = resolveSyncConfig(this.deps.config.sync);\n const defaultScope = this.deps.workspace.defaultScope;\n const defaultBranch = await this.deps.ci.getDefaultBranchName();\n const mainLaneName = this.deps.lanes.getDefaultLaneId().name;\n\n // The executors force-checkout branches and remove untracked files; warn up front, naming the\n // files at stake, before an interactive run discards anything.\n const statusAtStart = await git.status().catch(() => undefined);\n const dirtyFiles = (statusAtStart?.files ?? []).map((file) => file.path).filter((file) => !isNonContentPath(file));\n if (opts.dryRun) assertCleanForDryRun(dirtyFiles);\n if (dirtyFiles.length) {\n this.deps.logger.consoleWarning(\n `the working tree has ${dirtyFiles.length} uncommitted change(s). \"bit ci sync\" force-checkouts branches ` +\n `and removes untracked files (except .bit/ and node_modules/), so these will be discarded: ` +\n `${dirtyFiles.slice(0, 10).join(', ')}${dirtyFiles.length > 10 ? ', …' : ''}`\n );\n }\n\n const remoteUrl = await git.remote(['get-url', 'origin']).catch(() => undefined);\n const { provider: gitHost, reason: noGitHostReason } = selectGitHostProvider(\n this.deps.ci.listGitHostProviders(),\n typeof remoteUrl === 'string' ? remoteUrl.trim() : undefined\n );\n if (gitHost) this.deps.logger.debug(`bit ci sync: using the \"${gitHost.name}\" git host provider`);\n // Warn once, up front — the executors' per-action lines can only say PRs were skipped, not why.\n else if (noGitHostReason) this.deps.logger.consoleWarning(noGitHostReason);\n\n const laneSync = new LaneSyncExecutor({\n lanes: this.deps.lanes,\n ci: this.deps.ci,\n logger: this.deps.logger,\n gitHost,\n cfg,\n defaultScope,\n });\n const mainSync = new MainSyncExecutor({\n checkout: this.deps.checkout,\n lanes: this.deps.lanes,\n ci: this.deps.ci,\n logger: this.deps.logger,\n gitHost,\n cfg,\n defaultBranch,\n defaultScope,\n });\n\n if (opts.main) {\n return this.summarizeSync([await mainSync.syncMain({ dryRun: opts.dryRun })]);\n }\n\n if (opts.branch) {\n const branch = opts.branch;\n // Under the default `branchPrefix: ''` these two would resolve to the bogus lanes \"main\" /\n // \"bit-sync/main\"; both are reconciled by the main-scope path, never as lanes.\n if (branch === defaultBranch) {\n return `branch ${branch} is the default branch — reconcile it with \"bit ci sync --main\"; nothing to do`;\n }\n if (branch === cfg.mainSyncBranch) {\n return `branch ${branch} is the main sync branch maintained by this command; nothing to do`;\n }\n const laneName = syncableLaneNameForBranch(branch, cfg);\n if (!laneName) return `branch ${branch} does not map to a valid lane name; nothing to do`;\n const skipReason = this.laneNotSyncableReason(laneName, cfg, mainLaneName, defaultBranch);\n if (skipReason) return `${skipReason} (branch ${branch})`;\n // A branch name carries no scope, so this path can only resolve the lane against `defaultScope`;\n // resolving a foreign-hosted lane to the wrong scope is safe (no-op, never a deletion).\n return this.summarizeSync([\n await laneSync.syncLane({ hostScope: defaultScope, name: laneName }, { dryRun: opts.dryRun }),\n ]);\n }\n\n if (opts.lane) {\n // Every guard below is asked about the NAME: \"main\" is not a lane whichever scope hosts it, and\n // the `lanes` patterns match lane names, not lane ids.\n const target = parseLaneTarget(opts.lane, defaultScope);\n const skipReason = this.laneNotSyncableReason(target.name, cfg, mainLaneName, defaultBranch);\n if (skipReason) return skipReason;\n return this.summarizeSync([await laneSync.syncLane(target, { dryRun: opts.dryRun, explicitTarget: true })]);\n }\n\n // `--all` (also the no-arguments default): every mapped lane, then the main scope. Sequential on\n // purpose — the lanes share one workspace and one git checkout.\n const lines: string[] = [];\n const { lanes: lanesToSync, errors } = await this.listLanesToSync(cfg, mainLaneName, defaultBranch);\n lines.push(...errors);\n this.deps.logger.console(\n formatTitle(\n `Reconciling ${lanesToSync.length} mapped lane(s) of ${defaultScope}` +\n `${lanesToSync.length ? `: ${lanesToSync.join(', ')}` : ''}`\n )\n );\n for (const laneName of lanesToSync) {\n // Every enumerated target is hosted on `defaultScope` by construction; foreign-hosted lanes are\n // explicit-target only.\n // oxlint-disable-next-line no-await-in-loop\n lines.push(await laneSync.syncLane({ hostScope: defaultScope, name: laneName }, { dryRun: opts.dryRun }));\n }\n lines.push(await mainSync.syncMain({ dryRun: opts.dryRun }));\n return this.summarizeSync(lines);\n }\n\n /**\n * `bit ci sync --init` — one-command onboarding: scaffolds the two workflow files, adds the sync\n * config block to workspace.jsonc if missing, and prints the manual-steps checklist. Safe to\n * re-run: existing files are skipped, an existing config block is left alone; it never reconciles.\n */\n private async syncInit(): Promise<string> {\n const defaultBranch = await this.deps.ci.getDefaultBranchName();\n\n // Workflows belong to the repository, not the workspace: GitHub only discovers them at\n // `<repo-root>/.github/workflows`, and a bit workspace may be a subdirectory.\n const repoRoot = await gitRepoRoot();\n const scaffoldRoot = repoRoot ?? this.deps.workspace.path;\n if (!repoRoot) {\n this.deps.logger.consoleWarning(\n `could not resolve the git repository root (\"git rev-parse --show-toplevel\" failed — is this a git ` +\n `repository?). Writing the workflow files under the workspace instead: ${scaffoldRoot}. If that is ` +\n `not the repository root, GitHub will not discover them — move .github/workflows there by hand.`\n );\n }\n\n const fileOutcomes = scaffoldWorkflowFiles(scaffoldRoot, defaultBranch);\n // Report the path relative to where the user is standing, not to the workspace.\n const displayPath = (relativePath: string) => {\n const abs = path.join(scaffoldRoot, relativePath);\n const fromCwd = path.relative(process.cwd(), abs);\n return fromCwd && !fromCwd.startsWith('..') ? fromCwd : abs;\n };\n const fileLines = fileOutcomes.map((outcome) =>\n outcome.status === 'written'\n ? `wrote ${displayPath(outcome.relativePath)} (default branch: ${defaultBranch})`\n : `skipped ${displayPath(outcome.relativePath)} (already exists — bit ci sync --init never overwrites)`\n );\n\n const configAdded = await this.ensureSyncConfigBlock();\n const configLine = configAdded\n ? 'added \"teambit.git/ci\": { \"sync\": {} } to workspace.jsonc'\n : 'workspace.jsonc already configures \"teambit.git/ci\".sync — left untouched';\n\n const remoteUrl = await git.remote(['get-url', 'origin']).catch(() => undefined);\n const ownerRepo = deriveOwnerRepo(typeof remoteUrl === 'string' ? remoteUrl.trim() : undefined);\n\n const summary = ['bit ci sync --init', ...fileLines, configLine, renderInitChecklist(ownerRepo)].join('\\n');\n this.deps.logger.console(summary);\n return summary;\n }\n\n /**\n * Add `\"sync\": {}` under `\"teambit.git/ci\"` in workspace.jsonc if that key isn't there yet; returns\n * whether it wrote. The comment-preserving `WorkspaceConfig` API merges into existing\n * `teambit.git/ci` config rather than clobbering it.\n */\n private async ensureSyncConfigBlock(): Promise<boolean> {\n const wsConfig = this.deps.workspace.getWorkspaceConfig();\n const existing = wsConfig.extension(CiAspect.id, true) as CiWorkspaceConfig | undefined;\n if (existing?.sync) return false;\n wsConfig.setExtension(CiAspect.id, { sync: {} }, { mergeIntoExisting: true, ignoreVersion: true });\n await wsConfig.write({ reasonForChange: 'bit ci sync --init: add the sync config block' });\n return true;\n }\n\n /**\n * Why this lane name must not be handed to the lane executor, or undefined when it's syncable. The\n * mapped branch is checked too — the lane path must never write the two branches the main-scope\n * path owns.\n */\n private laneNotSyncableReason(\n laneName: string,\n cfg: Required<CiSyncConfig>,\n mainLaneName: string,\n defaultBranch: string\n ): string | undefined {\n if (laneName === mainLaneName) {\n return `\"${mainLaneName}\" is not a lane — reconcile the main scope with \"bit ci sync --main\"; nothing to do`;\n }\n const branch = laneNameToBranch(laneName, cfg);\n if (branch === defaultBranch) {\n return (\n `lane ${laneName} maps to the default branch ${branch} — reconcile it with \"bit ci sync --main\"; ` +\n `nothing to do`\n );\n }\n if (branch === cfg.mainSyncBranch) {\n return `lane ${laneName} maps to ${branch}, the main sync branch maintained by this command; nothing to do`;\n }\n if (!shouldSyncLane(laneName, cfg)) {\n return `lane ${laneName} is not matched by the sync config (lanes: ${JSON.stringify(cfg.lanes)}); nothing to do`;\n }\n return undefined;\n }\n\n /**\n * The lane names to reconcile on an `--all` run (sorted), plus one HALTED line per enumeration\n * failure. The list is the union of the remote's lanes AND the lane-mapped branches on `origin` —\n * a lane deleted on bit.cloud is gone from the first source, and only the branch side can make\n * `close-pr` fire for it. An enumeration failure is reported rather than thrown, so the half that\n * did enumerate is still reconciled while the run exits non-zero.\n */\n private async listLanesToSync(\n cfg: Required<CiSyncConfig>,\n mainLaneName: string,\n defaultBranch: string\n ): Promise<{ lanes: string[]; errors: string[] }> {\n if (!cfg.lanes.length) {\n this.deps.logger.console(\n formatWarningSummary('sync.lanes is empty — lane mirroring is disabled, reconciling the main scope only')\n );\n return { lanes: [], errors: [] };\n }\n const errors: string[] = [];\n const names = new Set<string>();\n const remote = this.deps.workspace.defaultScope;\n\n try {\n const lanes = await this.deps.lanes.getLanes({ remote });\n lanes.forEach((lane) => {\n const name = lane.id?.name ?? lane.name;\n if (name) names.add(name);\n });\n } catch (e: any) {\n const msg = e?.toString() ?? '';\n // A scope with no lanes at all: the remote answers \"not found\" rather than an empty list.\n if (msg.includes('was not found') || msg.includes('not found')) {\n this.deps.logger.console(\n formatWarningSummary(`No lanes found on ${remote} — reconciling lane-mapped branches and the main scope only`)\n );\n } else {\n errors.push(`${HALT_SUMMARY_PREFIX} lanes -> could not list the lanes of ${remote}: ${msg}`);\n }\n }\n\n try {\n for (const branch of await listRemoteBranches()) {\n // Reconciled by the main-scope path, never as lanes — under the default `branchPrefix: ''`\n // they would otherwise resolve to the bogus lanes \"main\" / \"bit-sync/main\".\n if (branch === defaultBranch || branch === cfg.mainSyncBranch) continue;\n // Ordinary developer branches are skipped at debug level, never as a summary line.\n const laneName = syncableLaneNameForBranch(branch, cfg);\n if (!laneName) {\n this.deps.logger.debug(\n `bit ci sync: branch \"${branch}\" does not map to a name any lane could have ` +\n `(mapped to \"${branchToLaneName(branch, cfg) ?? '<not lane-mapped>'}\") — skipping it`\n );\n continue;\n }\n names.add(laneName);\n }\n } catch (e: any) {\n errors.push(\n `${HALT_SUMMARY_PREFIX} lanes -> could not list the branches of \"origin\", so a lane deleted on ` +\n `bit.cloud may have kept an orphan branch and an open PR: ${e?.message || e}`\n );\n }\n\n const lanes = [...names]\n .filter((name) => name !== mainLaneName)\n .filter((name) => shouldSyncLane(name, cfg))\n .sort();\n return { lanes, errors };\n }\n\n /**\n * Join the per-target summary lines, and turn any halt into a non-zero exit — the executors never\n * throw, so this is the single place \"needs a human\" becomes visible to CI. `BitError` rather than\n * `Error`: bit reports a plain `Error` as an internal failure (stack trace, Sentry FATAL).\n */\n private summarizeSync(lines: string[]): string {\n const summary = lines.join('\\n');\n const halted = lines.filter((line) => line.startsWith(HALT_SUMMARY_PREFIX));\n if (halted.length) {\n throw new BitError(`bit ci sync could not reconcile ${halted.length} target(s):\\n${summary}`);\n }\n // A refusal is not a halt: the repository is healthy, the reconciler simply will not do what was\n // asked — exit non-zero saying that, rather than reporting a sync conflict.\n const refusals = lines.filter((line) => line.startsWith(REFUSED_SUMMARY_PREFIX));\n if (refusals.length) {\n throw new BitError(refusals.map((line) => line.slice(REFUSED_SUMMARY_PREFIX.length).trim()).join('\\n'));\n }\n return summary;\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,155 @@
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 _syncOrchestrator() {
11
+ const data = require("./sync-orchestrator");
12
+ _syncOrchestrator = function () {
13
+ return data;
14
+ };
15
+ return data;
16
+ }
17
+ function _syncConfig() {
18
+ const data = require("./sync-config");
19
+ _syncConfig = function () {
20
+ return data;
21
+ };
22
+ return data;
23
+ }
24
+ // The deps are never reached: every conflict case throws before the first `this.deps` access.
25
+ function orchestrator() {
26
+ return new (_syncOrchestrator().SyncOrchestrator)({});
27
+ }
28
+
29
+ /** Each row must name every selector it refused, and which one would otherwise have silently won. */
30
+ const conflicts = [[{
31
+ all: true,
32
+ branch: 'my-branch'
33
+ }, ['--all cannot be combined with', '--branch ("my-branch")']], [{
34
+ all: true,
35
+ lane: 'my-lane'
36
+ }, ['--all cannot be combined with', 'a lane argument ("my-lane")']], [{
37
+ all: true,
38
+ main: true
39
+ }, ['--all cannot be combined with', '--main']], [{
40
+ lane: 'my-lane',
41
+ branch: 'my-branch'
42
+ }, ['a lane argument ("my-lane") cannot be combined with --branch ("my-branch")', 'only --branch ("my-branch") would have run']], [{
43
+ lane: 'my-lane',
44
+ main: true
45
+ }, ['a lane argument ("my-lane") cannot be combined with --main', 'only --main would have run']], [{
46
+ branch: 'my-branch',
47
+ main: true
48
+ }, ['--branch ("my-branch") cannot be combined with --main', 'only --main would have run']], [{
49
+ lane: 'my-lane',
50
+ branch: 'my-branch',
51
+ main: true
52
+ }, ['a lane argument ("my-lane") cannot be combined with --branch ("my-branch") or with --main', 'only --main would have run']],
53
+ // --init only scaffolds, so pairing it with a target could not mean anything.
54
+ [{
55
+ init: true,
56
+ branch: 'x'
57
+ }, ['--init cannot be combined']]];
58
+ describe('SyncOrchestrator target guards', () => {
59
+ it('refuses every combination of selectors, naming the ones it saw', async () => {
60
+ for (const [options, expected] of conflicts) {
61
+ const label = JSON.stringify(options);
62
+ let message = '';
63
+ await orchestrator().sync(options).catch(err => {
64
+ message = err.message;
65
+ });
66
+ expected.forEach(fragment => (0, _chai().expect)(message, label).to.contain(fragment));
67
+ }
68
+ });
69
+ it('reports a branch whose mapped name is not a valid lane name, instead of syncing it', async () => {
70
+ const deps = {
71
+ config: {
72
+ sync: {}
73
+ },
74
+ workspace: {
75
+ defaultScope: 'org.scope'
76
+ },
77
+ ci: {
78
+ getDefaultBranchName: async () => 'main',
79
+ listGitHostProviders: () => []
80
+ },
81
+ lanes: {
82
+ getDefaultLaneId: () => ({
83
+ name: 'main'
84
+ })
85
+ },
86
+ logger: {
87
+ console() {},
88
+ consoleWarning() {},
89
+ debug() {}
90
+ }
91
+ };
92
+ const result = await new (_syncOrchestrator().SyncOrchestrator)(deps).sync({
93
+ branch: 'feature/foo'
94
+ });
95
+ (0, _chai().expect)(result).to.equal('branch feature/foo does not map to a valid lane name; nothing to do');
96
+ });
97
+ });
98
+
99
+ // A dry run computes the main-scope plan by force-checking-out the sync branch, so a dirty tree would be
100
+ // discarded by a run that promised to write nothing.
101
+ describe('assertCleanForDryRun', () => {
102
+ it('refuses a dirty tree, naming the paths at stake and how to keep them', () => {
103
+ let message = '';
104
+ try {
105
+ (0, _syncOrchestrator().assertCleanForDryRun)(['comp1/index.js', 'notes.txt']);
106
+ } catch (e) {
107
+ message = e.message;
108
+ }
109
+ (0, _chai().expect)(message).to.contain('--dry-run refuses to run');
110
+ (0, _chai().expect)(message).to.contain('2 uncommitted change(s)');
111
+ (0, _chai().expect)(message).to.contain('comp1/index.js, notes.txt');
112
+ (0, _chai().expect)(message).to.contain('Commit or stash them first');
113
+ });
114
+ it('caps the named paths so a large diff cannot produce an unreadable error', () => {
115
+ const paths = Array.from({
116
+ length: 12
117
+ }, (_, index) => `file-${index}.js`);
118
+ let message = '';
119
+ try {
120
+ (0, _syncOrchestrator().assertCleanForDryRun)(paths);
121
+ } catch (e) {
122
+ message = e.message;
123
+ }
124
+ (0, _chai().expect)(message).to.contain('12 uncommitted change(s)');
125
+ (0, _chai().expect)(message).to.contain('file-9.js');
126
+ (0, _chai().expect)(message).to.not.contain('file-10.js');
127
+ (0, _chai().expect)(message).to.contain('…');
128
+ });
129
+ it('permits a clean tree: the write-then-restore is only acceptable when nothing can be lost', () => {
130
+ (0, _chai().expect)(() => (0, _syncOrchestrator().assertCleanForDryRun)([])).to.not.throw();
131
+ });
132
+ });
133
+ describe('branch -> lane enumeration filter', () => {
134
+ it('queues only the branches whose mapped name could actually be a lane', () => {
135
+ const cfg = (0, _syncConfig().resolveSyncConfig)({});
136
+ // non-vacuity: the default mapping really is the identity, so the filter is what excludes anything
137
+ (0, _chai().expect)((0, _syncConfig().branchToLaneName)('feature/foo', cfg)).to.equal('feature/foo');
138
+ (0, _chai().expect)(['feature/foo', 'FEATURE', 'ok-lane'].map(b => (0, _syncConfig().syncableLaneNameForBranch)(b, cfg)).filter(Boolean)).to.deep.equal(['ok-lane']);
139
+ });
140
+ it('honours the prefix (stripped before the name is checked) and an explicit branches override', () => {
141
+ const prefixed = (0, _syncConfig().resolveSyncConfig)({
142
+ branchPrefix: 'lane/'
143
+ });
144
+ (0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('lane/my-lane', prefixed)).to.equal('my-lane');
145
+ (0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('feature/foo', prefixed)).to.equal(undefined);
146
+ const overridden = (0, _syncConfig().resolveSyncConfig)({
147
+ branches: {
148
+ 'my-lane': 'custom/branch'
149
+ }
150
+ });
151
+ (0, _chai().expect)((0, _syncConfig().syncableLaneNameForBranch)('custom/branch', overridden)).to.equal('my-lane');
152
+ });
153
+ });
154
+
155
+ //# sourceMappingURL=sync-orchestrator.spec.js.map