@teambit/ci 1.0.507 → 1.0.512
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/dist/ci.main.runtime.d.ts +3 -2
- package/dist/ci.main.runtime.js +5 -6
- package/dist/ci.main.runtime.js.map +1 -1
- package/dist/{preview-1787147173446.js → preview-1787168213927.js} +1 -1
- package/dist/sync/git-host-provider.d.ts +9 -0
- package/dist/sync/git-host-provider.js.map +1 -1
- package/dist/sync/github-client.d.ts +28 -0
- package/dist/sync/github-client.js +81 -3
- package/dist/sync/github-client.js.map +1 -1
- package/dist/sync/github-client.spec.js +249 -0
- package/dist/sync/github-client.spec.js.map +1 -1
- package/dist/sync/lane-sync-executor.d.ts +47 -3
- package/dist/sync/lane-sync-executor.js +191 -39
- package/dist/sync/lane-sync-executor.js.map +1 -1
- package/dist/sync/lane-sync-executor.spec.js +336 -0
- package/dist/sync/lane-sync-executor.spec.js.map +1 -1
- package/dist/sync/sync-planner.d.ts +12 -2
- package/dist/sync/sync-planner.js +17 -8
- package/dist/sync/sync-planner.js.map +1 -1
- package/dist/sync/sync-planner.spec.js +64 -0
- package/dist/sync/sync-planner.spec.js.map +1 -1
- package/dist/sync/sync-state.d.ts +34 -3
- package/dist/sync/sync-state.js +61 -3
- package/dist/sync/sync-state.js.map +1 -1
- package/dist/sync/sync-state.spec.js +54 -0
- package/dist/sync/sync-state.spec.js.map +1 -1
- package/package.json +15 -15
- package/sync/git-host-provider.ts +8 -0
- package/sync/github-client.spec.ts +160 -0
- package/sync/github-client.ts +90 -3
- package/sync/lane-sync-executor.spec.ts +291 -0
- package/sync/lane-sync-executor.ts +233 -50
- package/sync/sync-planner.spec.ts +41 -0
- package/sync/sync-planner.ts +31 -10
- package/sync/sync-state.spec.ts +48 -0
- package/sync/sync-state.ts +104 -4
package/sync/sync-state.ts
CHANGED
|
@@ -92,9 +92,10 @@ export function hasSyncMarker(message: string): boolean {
|
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* Strict probe for "we wrote this commit": the marker alone on its own line. This is an input to branch
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
95
|
+
* DELETION only — the one decision no content probe can stand in for, because the lane it would compare
|
|
96
|
+
* against is gone. It must never decide convergence: a squash-merge's body quotes the squashed ledger
|
|
97
|
+
* messages verbatim, marker included, so a message can claim authorship the reconciler never had.
|
|
98
|
+
* `\r?` tolerates CRLF; a recognition failure errs toward keeping the branch.
|
|
98
99
|
*/
|
|
99
100
|
export function isSyncAuthoredMessage(message: string): boolean {
|
|
100
101
|
return new RegExp(`^${SYNC_COMMIT_MARKER.replace(/[[\]]/g, '\\$&')}\\r?$`, 'm').test(message);
|
|
@@ -160,6 +161,14 @@ export type BranchSyncState = {
|
|
|
160
161
|
bitmap?: BranchBitmapState;
|
|
161
162
|
/** Whether the branch carries commits on top of its bit state — work bit has not seen. */
|
|
162
163
|
hasDevCommits: boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Whether the state commit also touched files besides `.bitmap`. SUSPECTED work only: those files
|
|
166
|
+
* may already be inside the snap the `.bitmap` records, which git cannot tell — the executor's
|
|
167
|
+
* read-only status probe is what distinguishes real work. True for ANY other path (`docs/`, CI
|
|
168
|
+
* config, this reconciler's own source-bundling ledger commits): a spurious `true` re-probes, which
|
|
169
|
+
* writes nothing, so it can repeat without consequence.
|
|
170
|
+
*/
|
|
171
|
+
stateCommitBundlesSources: boolean;
|
|
163
172
|
/** The branch tip's full commit message (subject + body), for the sync-marker loop-guard probe. */
|
|
164
173
|
tipMessage: string;
|
|
165
174
|
/**
|
|
@@ -169,6 +178,40 @@ export type BranchSyncState = {
|
|
|
169
178
|
tipSha?: string;
|
|
170
179
|
};
|
|
171
180
|
|
|
181
|
+
/**
|
|
182
|
+
* The pure half of `hasUnsyncedWorkChanges` (ci.main.runtime.ts): whether a `bit status` result holds
|
|
183
|
+
* anything a snap/export would push. The unloadable categories count as work: `invalidComponents` AND
|
|
184
|
+
* `importPendingComponents` (`StatusMain` splits pending-import errors out of the invalid list) are
|
|
185
|
+
* both UNKNOWN — their sources may hold the branch's edits — and this answer gates a "converged,
|
|
186
|
+
* write nothing" exit, so not knowing must route to the snap (which fails loudly) rather than
|
|
187
|
+
* converge. Incoming-change categories (outdated, pending updates from main) deliberately do NOT
|
|
188
|
+
* count: they are the lane's or main's movement, which the planner routes, not branch work.
|
|
189
|
+
*/
|
|
190
|
+
export function statusReportsUnsyncedWork(status: {
|
|
191
|
+
newComponents: unknown[];
|
|
192
|
+
modifiedComponents: unknown[];
|
|
193
|
+
stagedComponents: unknown[];
|
|
194
|
+
locallySoftRemoved: unknown[];
|
|
195
|
+
pendingUpdateDependents: unknown[];
|
|
196
|
+
mergePendingComponents: unknown[];
|
|
197
|
+
componentsDuringMergeState: unknown[];
|
|
198
|
+
invalidComponents: unknown[];
|
|
199
|
+
importPendingComponents: unknown[];
|
|
200
|
+
}): boolean {
|
|
201
|
+
const divergence = [
|
|
202
|
+
status.newComponents,
|
|
203
|
+
status.modifiedComponents,
|
|
204
|
+
status.stagedComponents,
|
|
205
|
+
status.locallySoftRemoved,
|
|
206
|
+
status.pendingUpdateDependents,
|
|
207
|
+
status.mergePendingComponents,
|
|
208
|
+
status.componentsDuringMergeState,
|
|
209
|
+
status.invalidComponents,
|
|
210
|
+
status.importPendingComponents,
|
|
211
|
+
];
|
|
212
|
+
return divergence.some((components) => components.length > 0);
|
|
213
|
+
}
|
|
214
|
+
|
|
172
215
|
/**
|
|
173
216
|
* Whether a `rev-list --count` output reports commits. An unreadable count answers `true`: `false` is an
|
|
174
217
|
* input to branch retirement, so not knowing must withhold a deletion rather than license one. Empty
|
|
@@ -200,7 +243,64 @@ export async function readBranchSyncState(
|
|
|
200
243
|
const range = stateCommit ? `${stateCommit}..${revision}` : `origin/${defaultBranch}..${revision}`;
|
|
201
244
|
const count = await git.raw(['rev-list', range, '--count']);
|
|
202
245
|
|
|
203
|
-
|
|
246
|
+
// Source edits can HIDE AT OR BELOW the state commit — bundled into it (the conflict-halt comment's
|
|
247
|
+
// resolve-by-hand recipe), or in an earlier commit that a later `.bitmap`-only commit covers — and
|
|
248
|
+
// `hasDevCommits` starts counting only after it, so they were invisible. Git file names alone cannot
|
|
249
|
+
// tell whether those sources are already inside the snap the `.bitmap` records (a dev who snapped,
|
|
250
|
+
// exported, and committed everything at once) or were never snapped at all — so this is SUSPECTED
|
|
251
|
+
// work, computed over the whole range since the PREVIOUS state commit (the last point a `.bitmap`
|
|
252
|
+
// write could have accounted for sources) and reported separately for the planner to probe.
|
|
253
|
+
// Deliberately content-only, per this module's header: this reconciler's own ledger commits have the
|
|
254
|
+
// same shape (import-lane and merge-diverged bundle already-exported sources) and its probe is a
|
|
255
|
+
// read, so a spurious `true` costs one status check — while consulting the tip's MESSAGE here read a
|
|
256
|
+
// squash-merge (whose body quotes the squashed ledger messages, marker included) as machine-authored
|
|
257
|
+
// and silently dropped the bundled work.
|
|
258
|
+
const stateCommitBundlesSources =
|
|
259
|
+
!parseDevCommitCount(count) &&
|
|
260
|
+
stateCommit !== undefined &&
|
|
261
|
+
stateCommit === tipSha &&
|
|
262
|
+
(await sourcesTouchedSincePreviousState(revision, defaultBranch));
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
stateCommit,
|
|
266
|
+
bitmap,
|
|
267
|
+
hasDevCommits: parseDevCommitCount(count),
|
|
268
|
+
stateCommitBundlesSources,
|
|
269
|
+
tipMessage,
|
|
270
|
+
tipSha,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Whether any file besides `.bitmap` changed between the PREVIOUS `.bitmap`-writing commit (or, on a
|
|
276
|
+
* branch with only one, its fork point off the default branch) and the tip. A tree diff over the
|
|
277
|
+
* whole range, not a per-commit walk: a source edit in one commit followed by a `.bitmap`-only commit
|
|
278
|
+
* would be invisible to a single-commit check, and a later revert legitimately cancels out.
|
|
279
|
+
* Unreadable answers `true`: this feeds `stateCommitBundlesSources`, where not knowing must plan the
|
|
280
|
+
* probe, never declare convergence.
|
|
281
|
+
*/
|
|
282
|
+
async function sourcesTouchedSincePreviousState(revision: string, defaultBranch: string): Promise<boolean> {
|
|
283
|
+
try {
|
|
284
|
+
const previousStateCommit = (
|
|
285
|
+
await git.raw(['log', revision, '--first-parent', '-n', '1', '--skip', '1', '--format=%H', '--', BIT_MAP])
|
|
286
|
+
).trim();
|
|
287
|
+
const base = previousStateCommit || (await git.raw(['merge-base', `origin/${defaultBranch}`, revision])).trim();
|
|
288
|
+
if (!base) return true;
|
|
289
|
+
const names = await git.raw(['diff', '--name-only', `${base}..${revision}`]);
|
|
290
|
+
// Empty is legitimate here (a range whose edits cancel out), unlike a single state commit's diff.
|
|
291
|
+
return touchesBeyondBitmap(names);
|
|
292
|
+
} catch {
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** The pure half of `commitTouchesBeyondBitmap`, split out so it is testable without a real git log. */
|
|
298
|
+
export function touchesBeyondBitmap(rawNames: string): boolean {
|
|
299
|
+
return rawNames
|
|
300
|
+
.split('\n')
|
|
301
|
+
.map((name) => name.trim())
|
|
302
|
+
.filter(Boolean)
|
|
303
|
+
.some((name) => name !== BIT_MAP);
|
|
204
304
|
}
|
|
205
305
|
|
|
206
306
|
/**
|