@teambit/ci 1.0.486 → 1.0.488
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 +8 -1
- package/dist/ci.main.runtime.js +36 -7
- package/dist/ci.main.runtime.js.map +1 -1
- package/dist/{preview-1785864931304.js → preview-1785951580001.js} +1 -1
- package/dist/sync/adopt-lane-new-components.d.ts +36 -0
- package/dist/sync/adopt-lane-new-components.js +146 -0
- package/dist/sync/adopt-lane-new-components.js.map +1 -0
- package/dist/sync/lane-sync-executor.js +2 -1
- package/dist/sync/lane-sync-executor.js.map +1 -1
- package/package.json +13 -13
- package/sync/adopt-lane-new-components.ts +128 -0
- package/sync/lane-sync-executor.ts +5 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import type { CheckoutMain } from '@teambit/checkout';
|
|
3
|
+
import type { ComponentID } from '@teambit/component-id';
|
|
4
|
+
import { ComponentIdList } from '@teambit/component-id';
|
|
5
|
+
import type { LanesMain } from '@teambit/lanes';
|
|
6
|
+
import type { Logger } from '@teambit/logger';
|
|
7
|
+
import type { Workspace } from '@teambit/workspace';
|
|
8
|
+
import { capEntries } from './lane-sync-executor';
|
|
9
|
+
|
|
10
|
+
export type AdoptLaneNewComponentsDeps = {
|
|
11
|
+
workspace: Workspace;
|
|
12
|
+
lanes: LanesMain;
|
|
13
|
+
logger: Logger;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type AdoptRetryDeps = AdoptLaneNewComponentsDeps & {
|
|
17
|
+
checkout: CheckoutMain;
|
|
18
|
+
reloadWorkspace: () => Promise<void>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The lane merge (`sources.mergeLane` in the legacy scope) throws a plain Error with this wording
|
|
23
|
+
* when a lane component's objects are absent. A typed error needs a change outside the ci aspect;
|
|
24
|
+
* the stale-lane recovery of `bit ci pr` matches the same text.
|
|
25
|
+
*/
|
|
26
|
+
export function isLaneMissingComponentError(err: unknown): boolean {
|
|
27
|
+
const msg = (err as Error)?.message ?? String(err ?? '');
|
|
28
|
+
return msg.includes('unable to merge lane') && msg.includes('was not found');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A versionless `.bitmap` entry reads as local and unexported, so a lane fetch drops its id and
|
|
33
|
+
* the lane merge fails with `the component … was not found`. Adopt the lane's version into each
|
|
34
|
+
* such entry, and import the adopted objects (`includeUnexported`, because the entries made them
|
|
35
|
+
* look local). The mutation is in memory; the caller owns the rollback on a failed retry.
|
|
36
|
+
* The returned ids already record the lane's version, so a retried switch skips their files; an
|
|
37
|
+
* importing caller must write them (`checkout --reset`).
|
|
38
|
+
*/
|
|
39
|
+
export async function adoptNewComponentsTheLaneProvides(
|
|
40
|
+
laneName: string,
|
|
41
|
+
{ workspace, lanes, logger }: AdoptLaneNewComponentsDeps
|
|
42
|
+
): Promise<ComponentID[]> {
|
|
43
|
+
const newIds = await workspace.newComponentIds();
|
|
44
|
+
if (!newIds.length) return [];
|
|
45
|
+
const laneId = await lanes.parseLaneId(laneName);
|
|
46
|
+
if (laneId.isDefault()) return [];
|
|
47
|
+
const lane = await lanes.importLaneObject(laneId);
|
|
48
|
+
const newIdsList = ComponentIdList.fromArray(newIds);
|
|
49
|
+
const adoptedIds = lane
|
|
50
|
+
.toComponentIdsIncludeUpdateDependents()
|
|
51
|
+
.filter((laneCompId) => newIdsList.hasWithoutVersion(laneCompId));
|
|
52
|
+
if (!adoptedIds.length) return [];
|
|
53
|
+
logger.console(
|
|
54
|
+
chalk.blue(
|
|
55
|
+
`Adopting the lane's version for component(s) this workspace tracks as new: ` +
|
|
56
|
+
capEntries(adoptedIds.map((id) => id.toStringWithoutVersion())).join(', ')
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
const bitMap = workspace.consumer.bitMap;
|
|
60
|
+
adoptedIds.forEach((id) => {
|
|
61
|
+
bitMap.updateComponentId(id);
|
|
62
|
+
bitMap.setOnLanesOnly(id, true);
|
|
63
|
+
});
|
|
64
|
+
// cached component lists still reflect the pre-adoption `.bitmap`
|
|
65
|
+
workspace.clearAllComponentsCache();
|
|
66
|
+
await workspace.scope.legacyScope.scopeImporter.importMany({
|
|
67
|
+
ids: ComponentIdList.fromArray(adoptedIds),
|
|
68
|
+
lane,
|
|
69
|
+
includeUnexported: true,
|
|
70
|
+
includeUpdateDependents: true,
|
|
71
|
+
reason: `lane ${laneId.toString()} provides component(s) this workspace tracks as new`,
|
|
72
|
+
});
|
|
73
|
+
return adoptedIds;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Runs after a switch failed with the missing-component marker: adopt the lane's versions, then
|
|
78
|
+
* retry the switch once. Returns undefined on success, and the error to report on failure. A
|
|
79
|
+
* failed retry reloads the workspace from disk — the failed switch never wrote to it — so the
|
|
80
|
+
* in-memory adoption disappears completely (including `updatedIds` and the changed flag).
|
|
81
|
+
*/
|
|
82
|
+
export async function adoptAndRetrySwitch(
|
|
83
|
+
laneName: string,
|
|
84
|
+
originalErr: Error,
|
|
85
|
+
doSwitch: () => Promise<unknown>,
|
|
86
|
+
writeAdoptedFiles: boolean | undefined,
|
|
87
|
+
deps: AdoptRetryDeps
|
|
88
|
+
): Promise<Error | undefined> {
|
|
89
|
+
const { logger, reloadWorkspace } = deps;
|
|
90
|
+
const rollback = async (err: Error) => {
|
|
91
|
+
await reloadWorkspace().catch((reloadErr) => {
|
|
92
|
+
logger.console(
|
|
93
|
+
chalk.yellow(`Failed to reload the workspace after the adoption retry: ${reloadErr?.toString() ?? reloadErr}`)
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
return err;
|
|
97
|
+
};
|
|
98
|
+
const adopted = await adoptNewComponentsTheLaneProvides(laneName, deps).catch((adoptErr) => {
|
|
99
|
+
logger.console(chalk.yellow(`The adoption retry failed: ${adoptErr?.toString() ?? adoptErr}`));
|
|
100
|
+
return [];
|
|
101
|
+
});
|
|
102
|
+
if (!adopted.length) return rollback(originalErr);
|
|
103
|
+
try {
|
|
104
|
+
await doSwitch();
|
|
105
|
+
// the retried switch skips an id already at the lane's version, so its files are still unwritten
|
|
106
|
+
if (writeAdoptedFiles) await writeFilesKeepingConfig(adopted, deps);
|
|
107
|
+
return undefined;
|
|
108
|
+
} catch (retryErr: any) {
|
|
109
|
+
return rollback(retryErr);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* `checkout --reset` writes the files of an entry that already records the target version, but its
|
|
115
|
+
* `resetConfig` also deletes the entry's `.bitmap` config — keep the config across the call.
|
|
116
|
+
*/
|
|
117
|
+
async function writeFilesKeepingConfig(adopted: ComponentID[], { workspace, checkout }: AdoptRetryDeps) {
|
|
118
|
+
const bitMap = workspace.consumer.bitMap;
|
|
119
|
+
const entryConfig = (id: ComponentID) => bitMap.getComponentIfExist(id, { ignoreVersion: true })?.config;
|
|
120
|
+
const configs = adopted.map((id) => ({ id, config: entryConfig(id) }));
|
|
121
|
+
await checkout.checkout({ ids: adopted, reset: true, skipNpmInstall: true });
|
|
122
|
+
configs.forEach(({ id, config }) => {
|
|
123
|
+
const entry = bitMap.getComponentIfExist(id, { ignoreVersion: true });
|
|
124
|
+
if (!config || !entry || entry.config) return;
|
|
125
|
+
entry.config = config;
|
|
126
|
+
bitMap.markAsChanged();
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -1142,7 +1142,11 @@ export class LaneSyncExecutor {
|
|
|
1142
1142
|
}
|
|
1143
1143
|
}
|
|
1144
1144
|
|
|
1145
|
-
const switchErr = await this.deps.ci.switchToLaneForSync(laneIdStr, {
|
|
1145
|
+
const switchErr = await this.deps.ci.switchToLaneForSync(laneIdStr, {
|
|
1146
|
+
forceOurs: false,
|
|
1147
|
+
forceTheirs: true,
|
|
1148
|
+
writeAdoptedFiles: true,
|
|
1149
|
+
});
|
|
1146
1150
|
if (switchErr) return switchErr;
|
|
1147
1151
|
|
|
1148
1152
|
// Verify before the caller commits a `.bitmap` asserting this lane's content.
|