Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: avoid throwing ComponentNotFound when .bitmap has a non-exist version on the scope #6496

Merged
merged 7 commits into from
Oct 5, 2022
3 changes: 1 addition & 2 deletions e2e/flows/out-of-sync-componets.e2e.3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,7 @@ describe('components that are not synced between the scope and the consumer', fu
});
});
});
// @TODO: FIX ON HARMONY!
describe.skip('when the remote component does not exist or does not have this missing version', () => {
describe('when the remote component does not exist or does not have this missing version', () => {
let scopeAfterV1;
let scopeOutOfSync;
before(() => {
Expand Down
3 changes: 1 addition & 2 deletions e2e/harmony/lanes/lanes.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1240,8 +1240,7 @@ describe('bit lane command', function () {
});
});
});
// @todo: fix!
describe.skip('head on the lane is not in the filesystem', () => {
describe('head on the lane is not in the filesystem', () => {
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopesHarmony();
helper.bitJsonc.setupDefault();
Expand Down
5 changes: 3 additions & 2 deletions scopes/workspace/workspace/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -806,11 +806,12 @@ the following envs are used in this workspace: ${availableEnvs.join(', ')}`);
if (!lane) {
return;
}
this.logger.debug(`current lane ${laneId.toString()} is missing, importing it`);
this.logger.info(`current lane ${laneId.toString()} is missing, importing it`);
await this.scope.legacyScope.objects.writeObjectsToTheFS([lane]);
const scopeComponentsImporter = ScopeComponentsImporter.getInstance(this.scope.legacyScope);
const ids = BitIds.fromArray(lane.toBitIds().filter((id) => id.hasScope()));
await scopeComponentsImporter.importManyDeltaWithoutDeps(ids, true, lane);
await scopeComponentsImporter.importMany({ ids, lanes: lane ? [lane] : undefined });
await scopeComponentsImporter.importMany({ ids, lanes: [lane] });
}

/**
Expand Down
52 changes: 32 additions & 20 deletions src/consumer/component/component-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Consumer from '../consumer';
import { ComponentFsCache } from './component-fs-cache';
import { updateDependenciesVersions } from './dependencies/dependency-resolver';
import { DependenciesLoader } from './dependencies/dependency-resolver/dependencies-loader';
import ComponentMap from '../bit-map/component-map';

export type ComponentLoadOptions = {
loadDocs?: boolean;
Expand Down Expand Up @@ -143,7 +144,7 @@ export default class ComponentLoader {
removedComponents: Component[],
loadOpts?: ComponentLoadOptions
) {
const componentMap = this.consumer.bitMap.getComponent(id);
let componentMap = this.consumer.bitMap.getComponent(id);
if (componentMap.isRemoved()) {
const fromModel = await this.consumer.scope.getConsumerComponentIfExist(id);
if (!fromModel) {
Expand All @@ -161,7 +162,6 @@ export default class ComponentLoader {
removedComponents.push(fromModel);
return null;
}
const bitDir = path.join(this.consumer.getPath(), componentMap.rootDir);
let component: Component;
const handleError = (error) => {
if (throwOnFailure) throw error;
Expand All @@ -175,20 +175,25 @@ export default class ComponentLoader {
}
throw error;
};
const newId = await this._handleOutOfSyncScenarios(componentMap);
if (newId) {
componentMap = this.consumer.bitMap.getComponent(newId);
}
const updatedId = newId || id;

try {
component = await Component.loadFromFileSystem({
bitDir,
componentMap,
id,
id: updatedId,
consumer: this.consumer,
});
} catch (err: any) {
return handleError(err);
}
component.loadedFromFileSystem = true;
// reload component map as it may be changed after calling Component.loadFromFileSystem()
component.componentMap = this.consumer.bitMap.getComponent(id);
await this._handleOutOfSyncScenarios(component);
component.componentMap = this.consumer.bitMap.getComponent(updatedId);
await this._handleOutOfSyncWithDefaultScope(component);

const loadDependencies = async () => {
await this.invalidateDependenciesCacheIfNeeded();
Expand Down Expand Up @@ -226,11 +231,10 @@ export default class ComponentLoader {
});
}

private async _handleOutOfSyncScenarios(component: Component) {
const { componentFromModel, componentMap } = component;
// @ts-ignore componentMap is set here
private async _handleOutOfSyncScenarios(componentMap: ComponentMap): Promise<BitId | undefined> {
const currentId: BitId = componentMap.id;
let newId: BitId | null | undefined;
const componentFromModel = await this.consumer.loadComponentFromModelIfExist(currentId);
let newId: BitId | undefined;
if (componentFromModel && !currentId.hasVersion()) {
// component is in the scope but .bitmap doesn't have version, sync .bitmap with the scope data
newId = currentId.changeVersion(componentFromModel.version);
Expand All @@ -248,30 +252,38 @@ export default class ComponentLoader {
// latest version from the scope
await this._throwPendingImportIfNeeded(currentId);
newId = currentId.changeVersion(modelComponent.latest());
component.componentFromModel = await this.consumer.loadComponentFromModelIfExist(newId);
} else if (!currentId.hasScope()) {
// the scope doesn't have this component and .bitmap doesn't have scope, assume it's new
newId = currentId.changeVersion(undefined);
}
}
if (!componentFromModel && !currentId.hasVersion() && component.defaultScope) {

if (newId) {
this.consumer.bitMap.updateComponentId(newId);
}
return newId;
}

private async _handleOutOfSyncWithDefaultScope(component: Component) {
const { componentFromModel, componentMap } = component;
// @ts-ignore componentMap is set here
const currentId: BitId = componentMap.id;
if (!componentFromModel && !currentId.hasVersion()) {
// for Harmony, we know ahead the defaultScope, so even then .bitmap shows it as new and
// there is nothing in the scope, we can check if there is a component with the same
// default-scope in the objects
const modelComponent = await this.consumer.scope.getModelComponentIfExist(
currentId.changeScope(component.defaultScope)
);
if (modelComponent) {
newId = currentId.changeVersion(modelComponent.latest()).changeScope(modelComponent.scope);
const newId = currentId.changeVersion(modelComponent.latest()).changeScope(modelComponent.scope);
component.componentFromModel = await this.consumer.loadComponentFromModelIfExist(newId);
}
}

if (newId) {
component.version = newId.version;
component.scope = newId.scope;
this.consumer.bitMap.updateComponentId(newId);
component.componentMap = this.consumer.bitMap.getComponent(newId);
component.version = newId.version;
component.scope = newId.scope;
this.consumer.bitMap.updateComponentId(newId);
component.componentMap = this.consumer.bitMap.getComponent(newId);
}
}
}

Expand Down
18 changes: 5 additions & 13 deletions src/consumer/component/consumer-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import ComponentWithDependencies from '../../scope/component-dependencies';
import { ScopeListItem } from '../../scope/models/model-component';
import Version, { Log } from '../../scope/models/version';
import { pathNormalizeToLinux } from '../../utils';
import { PathLinux, PathOsBased, PathOsBasedAbsolute, PathOsBasedRelative } from '../../utils/path';
import { PathLinux, PathOsBased, PathOsBasedRelative } from '../../utils/path';
import ComponentMap from '../bit-map/component-map';
import { IgnoredDirectory } from '../component-ops/add-components/exceptions/ignored-directory';
import ComponentsPendingImport from '../component-ops/exceptions/components-pending-import';
Expand Down Expand Up @@ -496,19 +496,15 @@ export default class Component {
return this.fromObject(object);
}

// eslint-disable-next-line complexity
static async loadFromFileSystem({
bitDir,
componentMap,
id,
consumer,
}: {
bitDir: PathOsBasedAbsolute;
componentMap: ComponentMap;
id: BitId;
consumer: Consumer;
}): Promise<Component> {
const consumerPath = consumer.getPath();
const workspaceConfig: ILegacyWorkspaceConfig = consumer.config;
const modelComponent = await consumer.scope.getModelComponentIfExist(id);
const componentFromModel = await consumer.loadComponentFromModelIfExist(id);
Expand All @@ -518,20 +514,16 @@ export default class Component {
if (!inScopeWithAnyVersion) throw new ComponentsPendingImport();
}
const deprecated = componentFromModel ? componentFromModel.deprecated : false;
const componentDir = componentMap.getComponentDir();
if (!fs.existsSync(bitDir)) throw new ComponentNotFoundInPath(bitDir);
const compDirAbs = path.join(consumer.getPath(), componentMap.getComponentDir());
if (!fs.existsSync(compDirAbs)) throw new ComponentNotFoundInPath(compDirAbs);

// Load the base entry from the root dir in map file in case it was imported using -path
// Or created using bit create so we don't want all the path but only the relative one
// Check that bitDir isn't the same as consumer path to make sure we are not loading global stuff into component
// (like dependencies)
logger.trace(`consumer-component.loadFromFileSystem, start loading config ${id.toString()}`);
const componentConfig = await ComponentConfig.load({
consumer,
componentId: id,
componentDir,
workspaceDir: consumerPath,
workspaceConfig,
});
logger.trace(`consumer-component.loadFromFileSystem, finish loading config ${id.toString()}`);
// by default, imported components are not written with bit.json file.
Expand All @@ -556,7 +548,7 @@ export default class Component {
);
const packageJsonFile = (componentConfig && componentConfig.packageJsonFile) || undefined;
const packageJsonChangedProps = componentFromModel ? componentFromModel.packageJsonChangedProps : undefined;
const files = await getLoadedFilesHarmony(consumer, componentMap, id, bitDir);
const files = await getLoadedFiles(consumer, componentMap, id, compDirAbs);
const docsP = _getDocsForFiles(files, consumer.componentFsCache);
const docs = await Promise.all(docsP);
const flattenedDocs = docs ? R.flatten(docs) : [];
Expand Down Expand Up @@ -593,7 +585,7 @@ export default class Component {
}
}

async function getLoadedFilesHarmony(
async function getLoadedFiles(
consumer: Consumer,
componentMap: ComponentMap,
id: BitId,
Expand Down
Loading