@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,256 @@
1
+ import type { Logger } from '@teambit/logger';
2
+ import type { LanesMain } from '@teambit/lanes';
3
+ import type { LaneData } from '@teambit/legacy.scope';
4
+ import type { CiMain } from '../ci.main.runtime';
5
+ import type { CiSyncConfig, LaneTarget } from './sync-config';
6
+ import type { GitHostProvider } from './git-host-provider';
7
+ import type { BranchKeepReason, LaneSyncAction } from './sync-planner';
8
+ /**
9
+ * Prefix of the summary line for a lane that could not be reconciled. `syncLane` deliberately does
10
+ * NOT throw on halt — one unreconcilable lane must not abort the rest of the run; the command layer
11
+ * scans for this prefix to decide the exit code.
12
+ */
13
+ export declare const HALT_SUMMARY_PREFIX = "HALTED";
14
+ /**
15
+ * Prefix for a target the reconciler refuses. Unlike a halt, nothing is mid-flight and no PR is
16
+ * annotated — the run exits non-zero, but the command layer reports a plain refusal rather than a
17
+ * sync conflict.
18
+ */
19
+ export declare const REFUSED_SUMMARY_PREFIX = "REFUSED";
20
+ export type LaneSyncDeps = {
21
+ lanes: LanesMain;
22
+ /** for snapPrCommit + getDefaultBranchName + switchToLaneForSync */
23
+ ci: CiMain;
24
+ logger: Logger;
25
+ /** undefined => PR operations are logged and skipped; the git half of the sync still runs. */
26
+ gitHost?: GitHostProvider;
27
+ cfg: Required<CiSyncConfig>;
28
+ defaultScope: string;
29
+ };
30
+ /**
31
+ * Fingerprint of a lane's content — same primitive as `branchStateFingerprint`, so "equal" means
32
+ * exactly "the branch records every lane component at the lane's head".
33
+ *
34
+ * We deliberately do NOT use `LaneData.hash`: that hash is minted randomly at lane-creation time
35
+ * and does not change when the lane's components advance. This is derived from content instead.
36
+ */
37
+ export declare function laneHeadFingerprint(components: LaneData['components']): string;
38
+ /**
39
+ * The lane's components that do not belong to `defaultScope`. A lane may span scopes, and the
40
+ * reconciler acts on a lane whole (fingerprint, materialize, snap, export), so foreign content is
41
+ * refused rather than written into this repository. Soft-deleted components and `updateDependents`
42
+ * entries are deliberately not read — the reconciler never materializes or exports them either.
43
+ */
44
+ export declare function foreignLaneComponents(components: LaneData['components'], defaultScope: string): string[];
45
+ /**
46
+ * `entries`, capped at `max`, with a final "…and K more" entry. The bound is on entries, not rendered
47
+ * length, so a markdown list is never cut mid-line. `overflowPrefix` carries the caller's list marker
48
+ * onto the summary line.
49
+ */
50
+ export declare function capEntries(entries: string[], overflowPrefix?: string, max?: number): string[];
51
+ /** The shared clause describing why a lane is cross-scope, used by the skip/refusal/halt outcomes. */
52
+ export declare function crossScopeDescription(foreignIds: string[], defaultScope: string): string;
53
+ /**
54
+ * A cross-scope lane that was merely enumerated: a skip, and the run stays green — a standing, valid
55
+ * lane must not turn every scheduled run permanently red.
56
+ */
57
+ export declare function crossScopeSkipSummary(laneName: string, foreignIds: string[], defaultScope: string): string;
58
+ /**
59
+ * A cross-scope lane the user named explicitly: exits non-zero with the explanation. A refusal, not a
60
+ * halt — no PR is labelled, no branch is written. `existingBranch` keeps the closing sentence truthful
61
+ * when a branch of that name already exists.
62
+ */
63
+ export declare function crossScopeRefusal(foreignIds: string[], defaultScope: string, existingBranch?: string): string;
64
+ /**
65
+ * Halt reason for a branch that is the live mirror of a different lane — two lanes with the same name
66
+ * in different scopes map to the same branch. Names both ids so the human can tell which owns it.
67
+ */
68
+ export declare function branchMirrorsOtherLaneReason(branch: string, mirroredLaneIdStr: string, laneIdStr: string): string;
69
+ /**
70
+ * PR comment for the branch-aliasing halt — the one halt whose PR belongs to a DIFFERENT lane. It must
71
+ * not carry the default "import the lane" steps, which would perform the overwrite the halt prevented.
72
+ */
73
+ export declare function branchMirrorsOtherLaneNote(mirroredLaneIdStr: string, laneIdStr: string): string;
74
+ /**
75
+ * A lane that became cross-scope after this repository had already mirrored it onto a branch — the one
76
+ * cross-scope shape that is a genuine halt: the pair is mid-flight and can no longer converge.
77
+ */
78
+ export declare function crossScopeMidFlightHaltReason(branch: string, foreignIds: string[], defaultScope: string): string;
79
+ /**
80
+ * Branches `executeClosePr` refuses to delete whatever the ownership evidence concluded — an
81
+ * unconditional guard at the one site that deletes remote branches.
82
+ */
83
+ export declare function isProtectedBranch(branch: string, defaultBranch: string, mainSyncBranch: string): boolean;
84
+ /**
85
+ * The summary line a dry run reports for a planned action. A planned halt carries the same prefix the
86
+ * real run would: `summarizeSync` recognizes that prefix and nothing else, so without it a `--dry-run`
87
+ * exits 0 on a plan that needs a human. Refusals never reach here — they are returned before planning.
88
+ */
89
+ export declare function dryRunSummaryLine(laneName: string, action: LaneSyncAction): string;
90
+ /** Why each `BranchKeepReason` withheld the deletion — one wording for the PR comment and the summary. */
91
+ export declare const KEPT_BECAUSE: Record<BranchKeepReason, string>;
92
+ export declare class LaneSyncExecutor {
93
+ private deps;
94
+ constructor(deps: LaneSyncDeps);
95
+ /** Every `origin/<ref>` read assumes current remote refs; fetch once per executor, not per lane. */
96
+ private fetched;
97
+ /**
98
+ * Reconcile one lane with its git branch/PR. Returns a summary line (HALT/REFUSED prefixed on
99
+ * failure) and NEVER throws — the contract the `--all` loop is written against, for every failure
100
+ * mode: the steps route expected failures to `executeHalt`, this wrapper covers the unexpected ones.
101
+ */
102
+ syncLane(target: LaneTarget, opts?: {
103
+ dryRun?: boolean;
104
+ /** The user named this lane on the command line; decides how a cross-scope lane is reported. */
105
+ explicitTarget?: boolean;
106
+ }): Promise<string>;
107
+ private reconcileLane;
108
+ /**
109
+ * What a cross-scope lane means for this repository: a mid-flight halt when the branch is this
110
+ * lane's live mirror (checked first — the problem is the state of the pair, not the phrasing of the
111
+ * request); a refusal when the user named the lane explicitly; otherwise an enumerated skip that
112
+ * stays green — one standing cross-scope lane must not fail every scheduled run forever.
113
+ */
114
+ private crossScopeOutcome;
115
+ /**
116
+ * Mirror the remote lane onto the branch: check the branch out, materialize the lane into the
117
+ * workspace, and commit the result. The committed `.bitmap` IS the record of the branch's state.
118
+ */
119
+ private executeImportLane;
120
+ /**
121
+ * Push the branch's dev commits back onto the lane: snap+export the branch's tree, then commit the
122
+ * resulting `.bitmap` back onto the branch so the next run sees the two sides as converged.
123
+ */
124
+ private executeExportBranch;
125
+ /**
126
+ * Both sides moved since the last sync. Order is load-bearing: (1) merge the lane into the branch's
127
+ * working tree (`bit checkout head`; conflicts halt or auto-resolve per `sync.onConflict`), (2) snap
128
+ * + export the merged tree — the snap IS the merge, (3) commit the resulting `.bitmap` back onto the
129
+ * branch. The merge cannot be skipped: the export path's switch runs under `forceOurs`, which never
130
+ * touches the filesystem, so a bare export would silently revert every lane-side file edit and then
131
+ * assert convergence over the loss. Never throws; anything unexpected halts.
132
+ */
133
+ private executeMergeDiverged;
134
+ /**
135
+ * Snap the workspace's current tree onto the lane and export it; returns the error so the caller can
136
+ * halt. Snaps WHATEVER is in the workspace (the switch uses `forceOurs` and never merges files), so
137
+ * a diverged tree must already hold the merged content. `keepLane` preserves the lane's history,
138
+ * `skipCleanup` keeps the snap's `.bitmap` for the caller to commit, `noDestructiveRecovery` turns
139
+ * stale-lane recovery (delete + re-fork the remote lane) into a throw. The lane object must be
140
+ * imported BEFORE delegating: a switch onto the lane the workspace is already on no-ops before any
141
+ * fetch, so it never warms a cold scope.
142
+ */
143
+ private snapAndExportOntoLane;
144
+ /**
145
+ * Record on the branch which lane state it now mirrors: re-query the lane (the export just moved it,
146
+ * so any earlier fingerprint is stale), commit — crucially the `.bitmap` the snap rewrote — and push.
147
+ * Returns undefined when the lane can no longer be read, in which case the caller halts.
148
+ */
149
+ private recordLaneHeadOnBranch;
150
+ /**
151
+ * Merge the lane's snaps into the branch's working tree via `bit checkout head` (three-way merge,
152
+ * base = the branch's `.bitmap` version; `workspaceOnly` must stay false so lane-only components are
153
+ * added). Returns conflicted ids + file count rather than throwing. Not `mergeLanes.mergeLane`: both
154
+ * sides here are the same lane id, which `validateMergeFlags` rejects, and `mergeLane` refuses to
155
+ * run with modified components — exactly the branch's state here.
156
+ */
157
+ private mergeLaneIntoBranchWorkingTree;
158
+ /**
159
+ * Put the working tree on `branch` at a pristine copy of `startPoint` and reload the `.bitmap` it
160
+ * brings (see `checkoutPristine`), so no leftover from a previous lane leaks into this lane's sync
161
+ * commit and the following bit operation resolves against this branch's `.bitmap`. Also the recovery
162
+ * after a merge attempt: resetting to `origin/<branch>` puts both sides back on the fetched tip.
163
+ */
164
+ private checkoutFromRemote;
165
+ /**
166
+ * The reachability half of `LaneOwnershipEvidence`; the attribution half lives at the call site.
167
+ * An unanswerable question resolves to `inherited-or-none` — every other answer permits deleting a
168
+ * branch, and a failed `merge-base` is not evidence of anything. A foreign-hosted lane's branch
169
+ * reached by branch name attributes as `inherited-or-none` (scope mismatch), which is the safe
170
+ * direction: left alone, never retired.
171
+ */
172
+ private assessBranchOwnership;
173
+ /**
174
+ * The lane is gone from bit.cloud — close its PR, and retire the branch if the claim on it allows.
175
+ * Deleting the branch is the one irreversible thing this command does; `keepReason` distinguishes
176
+ * "work that exists nowhere else" from "we did not write the tip".
177
+ */
178
+ private executeClosePr;
179
+ /** Seams for the retirement path, so the one irreversible outcome is unit-testable without a remote. */
180
+ private currentBranchTip;
181
+ private pushBranchDeletion;
182
+ /**
183
+ * Record a halt without touching the PR on a dry run — `--dry-run` promises no PR writes, and
184
+ * labelling freezes a lane's syncs. The line stays HALT-prefixed either way. Every pre-planning halt
185
+ * goes through here so the guard cannot be forgotten at one site.
186
+ */
187
+ private haltOrReport;
188
+ /**
189
+ * Hand the lane back to a human: label the PR so subsequent runs skip it, and comment the resolution
190
+ * steps. `commentNote` replaces the default steps for halts where they would be wrong.
191
+ */
192
+ private executeHalt;
193
+ /**
194
+ * Drive the workspace's filesystem to the remote lane's content; returns the error so the caller can
195
+ * halt. Two traps make a plain `switchToLane` insufficient: (1) it defaults to `forceOurs`, which
196
+ * never touches the filesystem — the commit would claim the branch mirrors the lane over the default
197
+ * branch's files — so `forceOurs` is cleared and `forceTheirs` set; (2) switching onto the lane the
198
+ * workspace already sits on throws "already checked out", which `switchToLane` swallows as success
199
+ * and nothing materializes — so step off to main first.
200
+ */
201
+ private materializeLane;
202
+ private fetchOnce;
203
+ /**
204
+ * The remote lane's data, or undefined when the lane no longer exists. The remote queried must be
205
+ * the lane's HOSTING scope: asking `defaultScope` for a lane it does not host answers "was not
206
+ * found" — the input that drives `close-pr`, which deletes branches.
207
+ */
208
+ private getRemoteLane;
209
+ /**
210
+ * The open PR for the branch, if any. A git-host API hiccup degrades to "no PR known" rather than
211
+ * failing the lane — the git side of the sync is still correct and the next run re-reads the PR.
212
+ */
213
+ private findPr;
214
+ /**
215
+ * The subject of the newest commit on the branch that isn't one of our own sync commits — it's
216
+ * the developer's own description of the change, and becomes the snap message on the lane.
217
+ */
218
+ private lastNonSyncCommitMessage;
219
+ /**
220
+ * Stage everything, commit with the annotated sync message, and push. NEVER force-pushes: a rejected
221
+ * push means someone pushed concurrently and the next run should re-plan rather than clobber.
222
+ * `--allow-empty` is only insurance against `git commit` failing the lane outright.
223
+ */
224
+ private commitAllAndPush;
225
+ private openPrForLane;
226
+ /**
227
+ * Put the workspace back on main with the default branch checked out. Best-effort, warn-only, so a
228
+ * restore hiccup can't throw out of a `finally` and mask the real error. The checkout is forced
229
+ * (switching back to main rewrites `.bitmap`); the scoped clean discards files a halted lane
230
+ * materialized but never committed, which would otherwise be staged onto the NEXT lane's branch;
231
+ * the reload makes the next lane resolve against the right checkout. This is a checkout, never a
232
+ * push — and `MainSyncExecutor.restoreWorkspace` must not diverge from it.
233
+ */
234
+ private restoreWorkspace;
235
+ }
236
+ /**
237
+ * The body of the pull request that mirrors a lane onto a branch. The component list is capped so the
238
+ * body stays inside the git host's size limit however large the lane is; the total is not.
239
+ */
240
+ export declare function laneSyncPrBody({ laneIdStr, laneUrl, branch, laneHead, components, }: {
241
+ laneIdStr: string;
242
+ laneUrl: string;
243
+ branch: string;
244
+ laneHead: string;
245
+ components: LaneData['components'];
246
+ }): string;
247
+ /**
248
+ * Resolution instructions posted on a halted PR. `note` replaces the default steps for halts where
249
+ * they do not apply (the branch-aliasing halt, whose PR belongs to a different lane).
250
+ */
251
+ export declare function haltCommentBody({ reason, branch, laneId, note, }: {
252
+ reason: string;
253
+ branch: string;
254
+ laneId: string;
255
+ note?: string;
256
+ }): string;