Skip to content

Commit

Permalink
feat: support project sync
Browse files Browse the repository at this point in the history
  • Loading branch information
pedro-gomes-92 committed Nov 2, 2024
1 parent ee4e1bb commit 709deb3
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 158 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,34 +1,147 @@
import { getProjectMismatches, queryProject } from '../utilities/project';
import { getProjectDependencies, getProjectMismatches, queryProject } from '../utilities/project';
import { VersionMismatchFinderEntity } from '@rushstack/rush-sdk/lib/logic/versionMismatch/VersionMismatchFinderEntity';
import Console from '../providers/console';
import { Colorize } from '@rushstack/terminal';
import { syncDependencies } from './syncDependencies';
import { IRushConfigurationProjectJson } from '@rushstack/rush-sdk/lib/api/RushConfigurationProject';
import { chooseDependencyPrompt, chooseVersionPrompt, enterVersionPrompt } from '../prompts/dependency';
import { updateProjectDependency } from './updateProjectDependency';
import { getRecommendedVersion } from '../utilities/dependency';
import { IPackageJsonDependencyTable, JsonFile, JsonObject } from '@rushstack/node-core-library';
import { getRushSubspaceConfigurationFolderPath, getSubspaceDependencies } from '../utilities/subspace';
import { updateSubspaceAlternativeVersions } from './updateSubspace';
import { RushConstants } from '@rushstack/rush-sdk';
import { confirmProjectSyncVersions } from '../prompts/project';

export const syncProjectDependencies = async (projectName: string): Promise<boolean> => {
const addVersionToCommonVersionConfiguration = (
subspaceName: string,
dependencyName: string,
selectedVersion: string
): void => {
const subspaceCommonVersionsPath: string = `${getRushSubspaceConfigurationFolderPath(subspaceName)}/${
RushConstants.commonVersionsFilename
}`;
const subspaceCommonVersionsJson: JsonObject = JsonFile.load(subspaceCommonVersionsPath);

Console.debug(
`Adding ${Colorize.bold(selectedVersion)} to allowedAlternativeVersions of ${Colorize.bold(
subspaceCommonVersionsPath
)}`
);

subspaceCommonVersionsJson.allowedAlternativeVersions[dependencyName] = updateSubspaceAlternativeVersions(
subspaceCommonVersionsJson.allowedAlternativeVersions[dependencyName],
[selectedVersion]
);

JsonFile.save(subspaceCommonVersionsJson, subspaceCommonVersionsPath, {
updateExistingFile: true
});
};

const syncDependencyVersion = async (dependencyToUpdate: string, projectToUpdate: string): Promise<void> => {
const project: IRushConfigurationProjectJson | undefined = queryProject(projectToUpdate);
if (!project || !project.subspaceName) {
return;
}

const subspaceDependencies: Map<string, Map<string, string[]>> = getSubspaceDependencies(
project.subspaceName
);
const availableVersionsMap: Map<string, string[]> = subspaceDependencies.get(dependencyToUpdate) as Map<
string,
string[]
>;
const availableVersions: string[] = Array.from(availableVersionsMap.keys());
const projectDependencies: IPackageJsonDependencyTable | undefined =
getProjectDependencies(projectToUpdate);
const currentVersion: string | undefined = projectDependencies?.[dependencyToUpdate];
if (!currentVersion) {
Console.error(
`Dependency ${Colorize.bold(dependencyToUpdate)} doesn't exist in the project ${Colorize.bold(
projectToUpdate
)}! Skipping...`
);
return;
}

const recommendedVersion: string | undefined = currentVersion
? getRecommendedVersion(currentVersion, availableVersions)
: undefined;

const versionToSync: string = await chooseVersionPrompt(
availableVersionsMap,
currentVersion,
recommendedVersion
);

if (versionToSync === 'skip') {
return;
} else if (versionToSync === 'manual') {
const newVersion: string = (await enterVersionPrompt(dependencyToUpdate)).trim();
addVersionToCommonVersionConfiguration(project.subspaceName, dependencyToUpdate, newVersion);
await updateProjectDependency(projectToUpdate, dependencyToUpdate, newVersion);
} else if (versionToSync === 'alternative') {
addVersionToCommonVersionConfiguration(project.subspaceName, dependencyToUpdate, currentVersion);
} else {
await updateProjectDependency(projectToUpdate, dependencyToUpdate, versionToSync);
}
};

export const syncProjectDependencies = async (
dependencies: string[],
projectName: string
): Promise<boolean> => {
const dependenciesToSync: string[] = [...dependencies];
do {
const selectedDependency: string = await chooseDependencyPrompt(dependenciesToSync);
await syncDependencyVersion(selectedDependency, projectName);
dependenciesToSync.splice(dependenciesToSync.indexOf(selectedDependency), 1);
} while (dependenciesToSync.length > 0);

return dependenciesToSync.length === 0;
};

export const syncProjectMismatchedDependencies = async (
projectName: string,
forceSync: boolean = false
): Promise<boolean> => {
Console.title(`🔄 Syncing version mismatches for project ${Colorize.bold(projectName)}...`);

const projectMismatches: ReadonlyMap<
string,
ReadonlyMap<string, readonly VersionMismatchFinderEntity[]>
> = getProjectMismatches(projectName);

if (projectMismatches.size === 0) {
const mismatchedDependencies: string[] = Array.from(projectMismatches.keys());

if (mismatchedDependencies.length === 0) {
Console.success(`No mismatches found in the project ${Colorize.bold(projectName)}!`);
return true;
}

Console.warn(
`There are ${Colorize.bold(
`${projectMismatches.size}`
`${mismatchedDependencies.length}`
)} mismatched dependencies for the project ${Colorize.bold(projectName)}...`
);

const confirmSync: boolean = forceSync || (await confirmProjectSyncVersions());
if (!confirmSync) {
return true;
}

const project: IRushConfigurationProjectJson | undefined = queryProject(projectName);
if (!project || !project.subspaceName) {
Console.error(`Project ${Colorize.bold(projectName)} is not part of a subspace!`);
return false;
}

return await syncDependencies(projectMismatches, project.subspaceName, project.packageName);
if (await syncProjectDependencies(mismatchedDependencies, project.packageName)) {
Console.success(
`All mismatched dependencies for the subspace ${Colorize.bold(projectName)} have been synced!`
);
return true;
}

return false;
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import Console from '../providers/console';
import { Colorize } from '@rushstack/terminal';
import { RushConstants } from '@rushstack/rush-sdk';
import { RushNameConstants } from '../constants/paths';
import { satisfies } from 'semver';
import { intersects } from 'semver';
import { sortVersions } from '../utilities/dependency';

export const updateSubspaceAlternativeVersions = (
availableVersions: string[],
versionsToUpdate: string[]
): string[] => {
const newVersions: string[] = [...availableVersions, ...versionsToUpdate];

// Remove duplicates
const validVersions: string[] = newVersions.filter((version, index, currValidVersions) => {
return currValidVersions.some((validVersion) => satisfies(version, validVersion));
// Remove duplicates & unnecessary versions
const validVersions: string[] = sortVersions(newVersions).filter((version, index, currValidVersions) => {
const compareVersions: string[] = [...currValidVersions];
compareVersions.splice(index, 1);
return !compareVersions.some((validVersion) => intersects(version, validVersion));
});

return validVersions;
Expand Down
19 changes: 11 additions & 8 deletions rush-plugins/rush-migrate-subspace-plugin/src/migrateProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
querySubspaces
} from './utilities/repository';
import { RushConstants } from '@rushstack/rush-sdk';
import { syncProjectDependencies } from './functions/syncProjectDependencies';
import { syncProjectMismatchedDependencies } from './functions/syncProjectDependencies';

export const migrateProject = async (): Promise<void> => {
Console.debug('Executing project migration command...');
Expand Down Expand Up @@ -87,14 +87,17 @@ export const migrateProject = async (): Promise<void> => {
return;
}

const sourceProjectToMigrate: IRushConfigurationProjectJson = await chooseProjectPrompt(
sourceAvailableProjects
const sourceProjectNameToMigrate: string = await chooseProjectPrompt(
sourceAvailableProjects.map(({ packageName }) => packageName)
);
Console.title(
`🏃 Migrating project ${sourceProjectToMigrate.packageName} to subspace ${Colorize.bold(
targetSubspace
)}...`
const sourceProjectToMigrate: IRushConfigurationProjectJson | undefined = sourceAvailableProjects.find(
({ packageName }) => packageName === sourceProjectNameToMigrate
);
if (!sourceProjectToMigrate) {
return;
}

Console.title(`🏃 Migrating project ${sourceProjectToMigrate} to subspace ${targetSubspace}...`);

if (sourceProjectToMigrate.subspaceName) {
const sourceSubspaceConfigurationFolderPath: string = getRushSubspaceConfigurationFolderPath(
Expand All @@ -110,7 +113,7 @@ export const migrateProject = async (): Promise<void> => {
}

await addProjectToSubspace(sourceProjectToMigrate, targetSubspace, sourceMonorepoPath);
await syncProjectDependencies(sourceProjectToMigrate.packageName);
await syncProjectMismatchedDependencies(sourceProjectToMigrate.packageName);
} while (await confirmNextProjectPrompt(targetSubspace));

Console.warn(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
import inquirer from 'inquirer';

export const requestVersionTypePrompt = async (
availableVersions: { name: string; value: string }[]
export const chooseVersionPrompt = async (
availableVersionsMap: Map<string, string[]>,
currentVersion: string,
recommendedVersion?: string
): Promise<string> => {
const availableVersions: string[] = Array.from(availableVersionsMap.keys());
const { versionToSync } = await inquirer.prompt([
{
type: 'list',
name: 'versionToSync',
message: 'Which version would you like to use?',
message: `Which version would you like to use?`,
suffix: `(Current version: ${currentVersion})`,
choices: [
...availableVersions,
...(recommendedVersion
? [
{ type: 'separator', line: '==== Recommended version:' },
{ name: recommendedVersion, value: recommendedVersion }
]
: []),
{ type: 'separator', line: '==== All versions:' },
...availableVersions.map((version) => {
const projects: string[] = availableVersionsMap.get(version) || [];

return {
name: `${version} - used by ${
projects.length > 4 ? `${projects.length} projects` : projects.join(',')
}`,
value: version
};
}),
{ type: 'separator', line: '==== Other options:' },
{ name: 'Add current to allowedAlternativeVersions', value: 'alternative' },
{ name: 'Add manual version', value: 'manual' },
{ name: 'Skip this dependency', value: 'skip' },
{ name: 'Create exception (allowedAlternativeVersions)', value: 'alternative' }
{ name: 'Skip this dependency', value: 'skip' }
]
}
]);
Expand Down
Loading

0 comments on commit 709deb3

Please sign in to comment.