Skip to content

Commit 20af2bd

Browse files
authored
fix(local-only), do not auto-tag/snap local-only, block export when some ids are local-only (#9059)
also, remove the --include-local-only flag. This is related to the recently-merged local-only feature: #9057. The original implementation has two issues: 1. It's possible to snap/tag local-only components with `--include-local-only` flag. Then, the export filters them out. However, what if it has been unset later on, snapped again and exported. The remote doesn't have part of the history which was local-only back then. 2. when on a lane and some local-only components were snapped with the flag above, the export sends a lane object to the remote with some components that exist only locally. This PR handles the issues above by the follwoing: 1. removes the `--include-local-only` flag, so it's impossible to snap/tag local-only components. 2. block `bit local-only set` from running on staged components. 3. just in case somehow a component was managed to be local-only when it's staged, `bit export` blocks the export and suggests running `bit reset` or `bit local-only unset`.
1 parent e164e44 commit 20af2bd

File tree

6 files changed

+52
-31
lines changed

6 files changed

+52
-31
lines changed

scopes/component/snapping/snapping.main.runtime.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,6 @@ export class SnappingMain {
159159
incrementBy = 1,
160160
disableTagAndSnapPipelines = false,
161161
failFast = false,
162-
includeLocalOnly,
163162
}: {
164163
ids?: string[];
165164
all?: boolean | string;
@@ -193,8 +192,7 @@ export class SnappingMain {
193192
persist,
194193
ids,
195194
snapped,
196-
unmerged,
197-
includeLocalOnly
195+
unmerged
198196
);
199197
if (!bitIds.length) return null;
200198

@@ -550,7 +548,6 @@ if you're willing to lose the history from the head to the specified version, us
550548
rebuildDepsGraph,
551549
unmodified = false,
552550
exitOnFirstFailedTask = false,
553-
includeLocalOnly,
554551
}: Partial<BasicTagSnapParams> & {
555552
pattern?: string;
556553
legacyBitIds?: ComponentIdList;
@@ -619,7 +616,7 @@ if you're willing to lose the history from the head to the specified version, us
619616
if (unmerged) {
620617
return componentsList.listDuringMergeStateComponents();
621618
}
622-
const tagPendingComponentsIds = await self.getTagPendingComponentsIds(unmodified, includeLocalOnly);
619+
const tagPendingComponentsIds = await self.getTagPendingComponentsIds(unmodified);
623620
if (!tagPendingComponentsIds.length) return null;
624621
// when unmodified, we ask for all components, throw if no matching. if not unmodified and no matching, see error
625622
// below, suggesting to use --unmodified flag.
@@ -1178,13 +1175,10 @@ another option, in case this dependency is not in main yet is to remove all refe
11781175
component.config.extensions.push(extension);
11791176
}
11801177

1181-
private async getTagPendingComponentsIds(includeUnmodified = false, includeLocalOnly = false) {
1178+
private async getTagPendingComponentsIds(includeUnmodified = false) {
11821179
const ids = includeUnmodified
11831180
? await this.workspace.listPotentialTagIds()
11841181
: await this.workspace.listTagPendingIds();
1185-
if (includeLocalOnly) {
1186-
return ids;
1187-
}
11881182
const localOnlyIds = this.workspace.filter.byLocalOnly(ids);
11891183
if (!localOnlyIds.length) {
11901184
return ids;
@@ -1199,8 +1193,7 @@ another option, in case this dependency is not in main yet is to remove all refe
11991193
persist: boolean,
12001194
ids: string[],
12011195
snapped: boolean,
1202-
unmerged: boolean,
1203-
includeLocalOnly = false
1196+
unmerged: boolean
12041197
): Promise<{ bitIds: ComponentID[]; warnings: string[] }> {
12051198
const warnings: string[] = [];
12061199
const componentsList = new ComponentsList(this.workspace.consumer);
@@ -1209,13 +1202,13 @@ another option, in case this dependency is not in main yet is to remove all refe
12091202
return { bitIds: softTaggedComponents, warnings: [] };
12101203
}
12111204

1212-
const tagPendingComponentsIds = await this.getTagPendingComponentsIds(includeUnmodified, includeLocalOnly);
1205+
const tagPendingComponentsIds = await this.getTagPendingComponentsIds(includeUnmodified);
12131206

12141207
const snappedComponentsIds = (await this.workspace.filter.bySnappedOnMain()).map((id) =>
12151208
id.changeVersion(undefined)
12161209
);
12171210

1218-
if (snappedComponentsIds.length && !includeLocalOnly) {
1211+
if (snappedComponentsIds.length) {
12191212
const localOnlyIds = this.workspace.filter.byLocalOnly(snappedComponentsIds);
12201213
const localOnlyListIds = ComponentIdList.fromArray(localOnlyIds);
12211214
snappedComponentsIds.forEach((id) => {

scopes/component/snapping/snapping.spec.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,24 @@ describe('Snapping aspect', function () {
183183
const taggedNames = tagResults?.snappedComponents.map((c) => c.name);
184184
expect(taggedNames).to.not.include('comp1');
185185
});
186-
it('should never be exported even when tagged with --include-local-only', async () => {
186+
it('should be ignored when it is an auto-tag candidate', async () => {
187187
const snapping = harmony.get<SnappingMain>(SnappingAspect.id);
188-
const tagResults = await snapping.tag({ unmodified: true, includeLocalOnly: true });
189-
expect(tagResults?.taggedComponents).to.have.lengthOf(3);
190-
191-
const exportAspect = harmony.get<ExportMain>(ExportAspect.id);
192-
const result = await exportAspect.export();
193-
expect(result.componentsIds).to.have.lengthOf(2);
188+
await snapping.tag({ unmodified: true });
189+
const tagResults = await snapping.tag({ ids: ['comp3'], unmodified: true });
190+
expect(tagResults?.autoTaggedResults).to.have.lengthOf(1); // only comp3 should be auto-tagged
191+
const taggedNames = tagResults?.autoTaggedResults.map((c) => c.component.name);
192+
expect(taggedNames).to.not.include('comp1');
193+
});
194+
it('should block setting local-only when a component is staged', async () => {
195+
const snapping = harmony.get<SnappingMain>(SnappingAspect.id);
196+
await snapping.tag({ unmodified: true });
197+
const comp2Id = await workspace.idsByPattern('comp2');
198+
try {
199+
await workspace.setLocalOnly(comp2Id);
200+
expect.fail('should have thrown an error');
201+
} catch (err: any) {
202+
expect(err.message).to.include('unable to set the following component(s) as local-only');
203+
}
194204
});
195205
});
196206
});

scopes/component/snapping/tag-cmd.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ to ignore multiple issues, separate them by a comma and wrap with quotes. to ign
9393
'stop pipeline execution on the first failed task (by default a task is skipped only when its dependency failed)',
9494
],
9595
['b', 'build', 'locally run the build pipeline (i.e. not via rippleCI) and complete the tag'],
96-
['', 'include-local-only', 'include local-only components in the tag process'],
9796
] as CommandOptions;
9897
remoteOp = true; // In case a compiler / tester is not installed
9998
examples = [{ cmd: 'tag --ver 1.0.0', description: 'tag all components to version 1.0.0' }];
@@ -130,7 +129,6 @@ to ignore multiple issues, separate them by a comma and wrap with quotes. to ign
130129
rebuildDepsGraph,
131130
failFast = false,
132131
incrementBy = 1,
133-
includeLocalOnly,
134132
}: {
135133
snapped?: boolean;
136134
unmerged?: boolean;
@@ -234,7 +232,6 @@ To undo local tag use the "bit reset" command.`
234232
incrementBy,
235233
version: ver,
236234
failFast,
237-
includeLocalOnly,
238235
};
239236

240237
const results = await this.snapping.tag(params);

scopes/component/snapping/tag-model-component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export type BasicTagSnapParams = {
3939
build?: boolean;
4040
ignoreBuildErrors?: boolean;
4141
rebuildDepsGraph?: boolean;
42-
includeLocalOnly?: boolean;
4342
};
4443

4544
export type BasicTagParams = BasicTagSnapParams & {
@@ -243,8 +242,12 @@ export async function tagModelComponent({
243242
// ids without versions are new. it's impossible that tagged (and not-modified) components has
244243
// them as dependencies.
245244
const idsToTriggerAutoTag = idsToTag.filter((id) => id.hasVersion());
246-
const autoTagData =
245+
const autoTagDataWithLocalOnly =
247246
skipAutoTag || !consumer ? [] : await getAutoTagInfo(consumer, ComponentIdList.fromArray(idsToTriggerAutoTag));
247+
const localOnly = workspace?.listLocalOnly();
248+
const autoTagData = localOnly
249+
? autoTagDataWithLocalOnly.filter((autoTagItem) => !localOnly.hasWithoutVersion(autoTagItem.component.id))
250+
: autoTagDataWithLocalOnly;
248251
const autoTagComponents = autoTagData.map((autoTagItem) => autoTagItem.component);
249252
const autoTagComponentsFiltered = autoTagComponents.filter((c) => !idsToTag.has(c.id));
250253
const autoTagIds = ComponentIdList.fromArray(autoTagComponentsFiltered.map((autoTag) => autoTag.id));

scopes/scope/export/export.main.runtime.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -761,12 +761,17 @@ if the export fails with missing objects/versions/components, run "bit fetch --l
761761
const consumer = this.workspace.consumer;
762762
const componentsList = new ComponentsList(consumer);
763763
const idsHaveWildcard = hasWildcard(ids);
764-
const filterLocalOnlyIfNeeded = async (
764+
const throwForLocalOnlyIfNeeded = async (
765765
bitIds: ComponentIdList
766766
): Promise<{ idsToExport: ComponentIdList; missingScope: ComponentID[]; idsWithFutureScope: ComponentIdList }> => {
767767
const localOnlyComponents = this.workspace.listLocalOnly();
768-
const idsToExport = bitIds.filter((id) => !localOnlyComponents.hasWithoutScopeAndVersion(id));
769-
return { idsToExport: ComponentIdList.fromArray(idsToExport), missingScope: [], idsWithFutureScope: bitIds };
768+
const localOnlyExportPending = bitIds.filter((id) => localOnlyComponents.hasWithoutScopeAndVersion(id));
769+
if (localOnlyExportPending.length) {
770+
throw new BitError(`unable to export the following components as they are local only:
771+
(either bit-reset them or run "bit local-only unset" to make them non local only)
772+
${localOnlyExportPending.map((c) => c.toString()).join('\n')}`);
773+
}
774+
return { idsToExport: ComponentIdList.fromArray(bitIds), missingScope: [], idsWithFutureScope: bitIds };
770775
};
771776
if (isUserTryingToExportLanes(consumer)) {
772777
if (ids.length) {
@@ -775,7 +780,7 @@ if the export fails with missing objects/versions/components, run "bit fetch --l
775780
const { componentsToExport, laneObject } = await this.getLaneCompIdsToExport(consumer, includeNonStaged);
776781
const loaderMsg = componentsToExport.length > 1 ? BEFORE_EXPORTS : BEFORE_EXPORT;
777782
loader.start(loaderMsg);
778-
const filtered = await filterLocalOnlyIfNeeded(componentsToExport);
783+
const filtered = await throwForLocalOnlyIfNeeded(componentsToExport);
779784
return { ...filtered, laneObject };
780785
}
781786
if (!ids.length || idsHaveWildcard) {
@@ -788,14 +793,14 @@ if the export fails with missing objects/versions/components, run "bit fetch --l
788793
: exportPendingComponents;
789794
const loaderMsg = componentsToExport.length > 1 ? BEFORE_EXPORTS : BEFORE_EXPORT;
790795
loader.start(loaderMsg);
791-
return filterLocalOnlyIfNeeded(componentsToExport);
796+
return throwForLocalOnlyIfNeeded(componentsToExport);
792797
}
793798
loader.start(BEFORE_EXPORT); // show single export
794799
const parsedIds = await Promise.all(ids.map((id) => getParsedId(consumer, id)));
795800
// load the components for fixing any out-of-sync issues.
796801
await consumer.loadComponents(ComponentIdList.fromArray(parsedIds));
797802

798-
return filterLocalOnlyIfNeeded(ComponentIdList.fromArray(parsedIds));
803+
return throwForLocalOnlyIfNeeded(ComponentIdList.fromArray(parsedIds));
799804
}
800805

801806
private async getLaneCompIdsToExport(

scopes/workspace/workspace/workspace.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,19 @@ the following envs are used in this workspace: ${availableEnvs.join(', ')}`);
21602160
}
21612161

21622162
async setLocalOnly(ids: ComponentID[]) {
2163+
const staged = compact(
2164+
await mapSeries(ids, async (id) => {
2165+
const componentStatus = await this.getComponentStatusById(id);
2166+
if (componentStatus.staged) return id;
2167+
})
2168+
);
2169+
if (staged.length) {
2170+
throw new BitError(
2171+
`unable to set the following component(s) as local-only because they have local snaps/tags: ${staged.join(
2172+
', '
2173+
)}`
2174+
);
2175+
}
21632176
this.bitMap.setLocalOnly(ids);
21642177
await this.bitMap.write('setLocalOnly');
21652178
}

0 commit comments

Comments
 (0)