From fb875ca2a1c6f0a3e770492c0065d6824469d430 Mon Sep 17 00:00:00 2001 From: David First Date: Sun, 22 Sep 2024 12:54:04 -0400 Subject: [PATCH] fix(tag-from-scope), merge package.json props from the last snap (#9207) --- .bitmap | 2 +- .../isolator/isolator.main.runtime.ts | 87 +++++++++++++++---- .../snapping/snapping.main.runtime.ts | 2 + .../component/snapping/tag-from-scope.cmd.ts | 4 + .../component/snapping/tag-model-component.ts | 4 +- scopes/lanes/merge-lanes/merge-lane.cmd.ts | 2 +- 6 files changed, 83 insertions(+), 18 deletions(-) diff --git a/.bitmap b/.bitmap index ce588b78a5c..4a87641e34c 100644 --- a/.bitmap +++ b/.bitmap @@ -19,7 +19,7 @@ "api-reference": { "name": "api-reference", "scope": "teambit.api-reference", - "version": "1.0.411", + "version": "1.0.410", "mainFile": "index.ts", "rootDir": "scopes/api-reference/api-reference", "config": { diff --git a/scopes/component/isolator/isolator.main.runtime.ts b/scopes/component/isolator/isolator.main.runtime.ts index 4d2fc3329e5..7b4d18352d7 100644 --- a/scopes/component/isolator/isolator.main.runtime.ts +++ b/scopes/component/isolator/isolator.main.runtime.ts @@ -210,6 +210,13 @@ export type IsolateComponentsOptions = CreateGraphOptions & { */ populateArtifactsFrom?: ComponentID[]; + /** + * relevant when populateArtifactsFrom is set. + * by default, it uses the package.json created in the previous snap as a base and make the necessary changes. + * if this is set to true, it will ignore the package.json from the previous snap. + */ + populateArtifactsIgnorePkgJson?: boolean; + /** * Force specific host to get the component from. */ @@ -910,6 +917,13 @@ export class IsolatorMain { const legacyComponents = [...legacyUnmodifiedComps, ...legacyModifiedComps]; if (legacyScope && unmodifiedComps.length) await importMultipleDistsArtifacts(legacyScope, legacyUnmodifiedComps); const allIds = ComponentIdList.fromArray(legacyComponents.map((c) => c.id)); + const getParentsComp = () => { + const artifactsFrom = opts?.populateArtifactsFrom; + if (!artifactsFrom) return undefined; + if (!legacyScope) throw new Error('populateArtifactsFrom is set but legacyScope is not defined'); + return Promise.all(artifactsFrom.map((id) => legacyScope.getConsumerComponent(id))); + }; + const populateArtifactsFromComps = await getParentsComp(); await Promise.all( components.map(async (component) => { const capsule = capsuleList.getCapsule(component.id); @@ -918,7 +932,13 @@ export class IsolatorMain { (await CapsuleList.capsuleUsePreviouslySavedDists(component)) || opts?.populateArtifactsFrom ? legacyScope : undefined; - const dataToPersist = await this.populateComponentsFilesToWriteForCapsule(component, allIds, scope, opts); + const dataToPersist = await this.populateComponentsFilesToWriteForCapsule( + component, + allIds, + scope, + opts, + populateArtifactsFromComps + ); await dataToPersist.persistAllToCapsule(capsule, { keepExistingCapsule: true }); }) ); @@ -1128,11 +1148,12 @@ export class IsolatorMain { return packageJson; } - async populateComponentsFilesToWriteForCapsule( + private async populateComponentsFilesToWriteForCapsule( component: Component, ids: ComponentIdList, legacyScope?: Scope, - opts?: IsolateComponentsOptions + opts?: IsolateComponentsOptions, + populateArtifactsFromComps?: ConsumerComponent[] ): Promise { const legacyComp: ConsumerComponent = component.state._consumer; const dataToPersist = new DataToPersist(); @@ -1153,12 +1174,55 @@ export class IsolatorMain { await PackageJsonTransformer.applyTransformers(component, packageJson); const valuesToMerge = legacyComp.overrides.componentOverridesPackageJsonData; packageJson.mergePackageJsonObject(valuesToMerge); + if (populateArtifactsFromComps && !opts?.populateArtifactsIgnorePkgJson) { + const compParent = this.getCompForArtifacts(component, populateArtifactsFromComps); + this.mergePkgJsonFromLastBuild(compParent, packageJson); + } dataToPersist.addFile(packageJson.toVinylFile()); - const artifacts = await this.getArtifacts(component, legacyScope, opts?.populateArtifactsFrom); + const artifacts = await this.getArtifacts(component, legacyScope, populateArtifactsFromComps); dataToPersist.addManyFiles(artifacts); return dataToPersist; } + private mergePkgJsonFromLastBuild(component: ConsumerComponent, packageJson: PackageJsonFile) { + const suffix = `for ${component.id.toString()}. to workaround this, use --ignore-last-pkg-json flag`; + const aspectsData = component.extensions.findExtension('teambit.pipelines/builder')?.data?.aspectsData; + if (!aspectsData) throw new Error(`getPkgJsonFromLastBuild, unable to find builder aspects data ${suffix}`); + const data = aspectsData?.find((aspectData) => aspectData.aspectId === 'teambit.pkg/pkg'); + if (!data) throw new Error(`getPkgJsonFromLastBuild, unable to find pkg aspect data ${suffix}`); + const pkgJsonLastBuild = data?.data?.pkgJson; + if (!pkgJsonLastBuild) throw new Error(`getPkgJsonFromLastBuild, unable to find pkgJson of pkg aspect ${suffix}`); + const current = packageJson.packageJsonObject; + pkgJsonLastBuild.componentId = current.componentId; + pkgJsonLastBuild.version = current.version; + const mergeDeps = (currentDeps?: Record, depsFromLastBuild?: Record) => { + if (!depsFromLastBuild) return; + if (!currentDeps) return depsFromLastBuild; + Object.keys(depsFromLastBuild).forEach((depName) => { + if (!currentDeps[depName]) return; + depsFromLastBuild[depName] = currentDeps[depName]; + }); + return depsFromLastBuild; + }; + pkgJsonLastBuild.dependencies = mergeDeps(current.dependencies, pkgJsonLastBuild.dependencies); + pkgJsonLastBuild.devDependencies = mergeDeps(current.devDependencies, pkgJsonLastBuild.devDependencies); + pkgJsonLastBuild.peerDependencies = mergeDeps(current.peerDependencies, pkgJsonLastBuild.peerDependencies); + packageJson.mergePackageJsonObject(pkgJsonLastBuild); + } + + private getCompForArtifacts( + component: Component, + populateArtifactsFromComps: ConsumerComponent[] + ): ConsumerComponent { + const compParent = populateArtifactsFromComps.find((comp) => + comp.id.isEqual(component.id, { ignoreVersion: true }) + ); + if (!compParent) { + throw new Error(`isolator, unable to find where to populate the artifacts from for ${component.id.toString()}`); + } + return compParent; + } + private preparePackageJsonToWrite( component: Component, bitDir: string, @@ -1208,28 +1272,21 @@ export class IsolatorMain { private async getArtifacts( component: Component, legacyScope?: Scope, - populateArtifactsFrom?: ComponentID[] + populateArtifactsFromComps?: ConsumerComponent[] ): Promise { const legacyComp: ConsumerComponent = component.state._consumer; if (!legacyScope) { - if (populateArtifactsFrom) throw new Error(`unable to fetch from parent, the legacyScope was not provided`); // when capsules are written via the workspace, do not write artifacts, they get created by // build-pipeline. when capsules are written via the scope, we do need the dists. return []; } - if (legacyComp.buildStatus !== 'succeed' && !populateArtifactsFrom) { + if (legacyComp.buildStatus !== 'succeed' && !populateArtifactsFromComps) { // this is important for "bit sign" when on lane to not go to the original scope return []; } const artifactFilesToFetch = async () => { - if (populateArtifactsFrom) { - const found = populateArtifactsFrom.find((id) => id.isEqual(component.id, { ignoreVersion: true })); - if (!found) { - throw new Error( - `getArtifacts: unable to find where to populate the artifacts from for ${component.id.toString()}` - ); - } - const compParent = await legacyScope.getConsumerComponent(found); + if (populateArtifactsFromComps) { + const compParent = this.getCompForArtifacts(component, populateArtifactsFromComps); return getArtifactFilesExcludeExtension(compParent.extensions, 'teambit.pkg/pkg'); } const extensionsNamesForArtifacts = ['teambit.compilation/compiler']; diff --git a/scopes/component/snapping/snapping.main.runtime.ts b/scopes/component/snapping/snapping.main.runtime.ts index e4791334f2f..7e5184bf1de 100644 --- a/scopes/component/snapping/snapping.main.runtime.ts +++ b/scopes/component/snapping/snapping.main.runtime.ts @@ -260,6 +260,7 @@ export class SnappingMain { ignoreIssues?: string; incrementBy?: number; rebuildArtifacts?: boolean; + ignoreLastPkgJson?: boolean; } & Partial ): Promise { if (this.workspace) { @@ -340,6 +341,7 @@ if you're willing to lose the history from the head to the specified version, us consumerComponents, tagDataPerComp, populateArtifactsFrom: shouldUsePopulateArtifactsFrom ? components.map((c) => c.id) : undefined, + populateArtifactsIgnorePkgJson: params.ignoreLastPkgJson, copyLogFromPreviousSnap: true, snapping: this, builder: this.builder, diff --git a/scopes/component/snapping/tag-from-scope.cmd.ts b/scopes/component/snapping/tag-from-scope.cmd.ts index bbc9f90f623..bf1d958d9fd 100644 --- a/scopes/component/snapping/tag-from-scope.cmd.ts +++ b/scopes/component/snapping/tag-from-scope.cmd.ts @@ -37,6 +37,7 @@ an example of the final data: '[{"componentId":"ci.remote2/comp-b","dependencies options = [ ['', 'push', 'export the updated objects to the original scopes once done'], ['', 'rebuild-artifacts', 'run the full build pipeline. do not use the saved artifacts from the last snap'], + ['', 'ignore-last-pkg-json', 'ignore the package.json created by the last snap'], ...tagCmdOptions.filter((o) => !excludeOptions.includes(o[1])), ] as CommandOptions; remoteOp = true; // In case a compiler / tester is not installed @@ -49,6 +50,7 @@ an example of the final data: '[{"componentId":"ci.remote2/comp-b","dependencies options: { push?: boolean; rebuildArtifacts?: boolean; + ignoreLastPkgJson?: boolean; } & Partial ): Promise { const { releaseType, preReleaseId } = validateOptions(options); @@ -64,6 +66,7 @@ an example of the final data: '[{"componentId":"ci.remote2/comp-b","dependencies disableTagPipeline = false, ignoreBuildErrors = false, rebuildArtifacts, + ignoreLastPkgJson, rebuildDepsGraph, incrementBy = 1, } = options; @@ -85,6 +88,7 @@ an example of the final data: '[{"componentId":"ci.remote2/comp-b","dependencies incrementBy, version: ver, rebuildArtifacts, + ignoreLastPkgJson, }; const tagDataPerCompRaw = this.parseData(data); diff --git a/scopes/component/snapping/tag-model-component.ts b/scopes/component/snapping/tag-model-component.ts index 22885468c1b..c5728934522 100644 --- a/scopes/component/snapping/tag-model-component.ts +++ b/scopes/component/snapping/tag-model-component.ts @@ -180,6 +180,7 @@ export async function tagModelComponent({ ids, tagDataPerComp, populateArtifactsFrom, + populateArtifactsIgnorePkgJson, message, editor, exactVersion, @@ -211,6 +212,7 @@ export async function tagModelComponent({ ids: ComponentIdList; tagDataPerComp?: TagDataPerComp[]; populateArtifactsFrom?: ComponentID[]; + populateArtifactsIgnorePkgJson?: boolean; copyLogFromPreviousSnap?: boolean; exactVersion?: string | null | undefined; releaseType?: ReleaseType; @@ -355,7 +357,7 @@ export async function tagModelComponent({ }; const skipTasksParsed = skipTasks ? skipTasks.split(',').map((t) => t.trim()) : undefined; const seedersOnly = !workspace; // if tag from scope, build only the given components - const isolateOptions = { packageManagerConfigRootDir, seedersOnly }; + const isolateOptions = { packageManagerConfigRootDir, seedersOnly, populateArtifactsIgnorePkgJson }; const builderOptions = { exitOnFirstFailedTask, skipTests, skipTasks: skipTasksParsed }; const componentsToBuild = allComponentsToTag.filter((c) => !c.isRemoved()); diff --git a/scopes/lanes/merge-lanes/merge-lane.cmd.ts b/scopes/lanes/merge-lanes/merge-lane.cmd.ts index cac7b860fcf..4fa6919c9e0 100644 --- a/scopes/lanes/merge-lanes/merge-lane.cmd.ts +++ b/scopes/lanes/merge-lanes/merge-lane.cmd.ts @@ -78,7 +78,7 @@ Component pattern format: ${COMPONENT_PATTERN_HELP}`, [ '', 'include-deps', - 'relevant for "--pattern" and "--workspace". merge also dependencies of the specified components', + 'relevant for "pattern" and "--workspace". merge also dependencies of the specified components', ], [ '',