diff --git a/docs/generated/manifests/nx-api.json b/docs/generated/manifests/nx-api.json index 5c9409fedb..d849313b09 100644 --- a/docs/generated/manifests/nx-api.json +++ b/docs/generated/manifests/nx-api.json @@ -2225,6 +2225,16 @@ } }, "migrations": { + "/nx-api/gradle/migrations/change-plugin-version-0-1-0": { + "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", + "file": "generated/packages/gradle/migrations/change-plugin-version-0-1-0.json", + "hidden": false, + "name": "change-plugin-version-0-1-0", + "version": "21.1.2-beta.1", + "originalFilePath": "/packages/gradle", + "path": "/nx-api/gradle/migrations/change-plugin-version-0-1-0", + "type": "migration" + }, "/nx-api/gradle/migrations/change-ciTargetName-to-ciTestTargetName": { "description": "Change @nx/gradle option from ciTargetName to ciTestTargetName", "file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json", diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index fb1c57d08d..4f23be655b 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -2209,6 +2209,16 @@ } ], "migrations": [ + { + "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", + "file": "generated/packages/gradle/migrations/change-plugin-version-0-1-0.json", + "hidden": false, + "name": "change-plugin-version-0-1-0", + "version": "21.1.2-beta.1", + "originalFilePath": "/packages/gradle", + "path": "gradle/migrations/change-plugin-version-0-1-0", + "type": "migration" + }, { "description": "Change @nx/gradle option from ciTargetName to ciTestTargetName", "file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json", diff --git a/docs/generated/packages/gradle/migrations/change-plugin-version-0-1-0.json b/docs/generated/packages/gradle/migrations/change-plugin-version-0-1-0.json new file mode 100644 index 0000000000..0f93b9a0eb --- /dev/null +++ b/docs/generated/packages/gradle/migrations/change-plugin-version-0-1-0.json @@ -0,0 +1,14 @@ +{ + "name": "change-plugin-version-0-1-0", + "version": "21.1.2-beta.1", + "cli": "nx", + "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", + "factory": "./src/migrations/21-1-2/change-plugin-version-0-1-0", + "implementation": "/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.ts", + "aliases": [], + "hidden": false, + "path": "/packages/gradle", + "schema": null, + "type": "migration", + "examplesFile": "#### Change dev.nx.gradle.project-graph to version 0.1.0\n\nChange dev.nx.gradle.project-graph to version 0.1.0 in build file\n\n#### Sample Code Changes\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```{% fileName=\"build.gradle\" %}\nplugins {\n\tid \"dev.nx.gradle.project-graph\" version \"0.0.5\"\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```{% fileName=\"build.gradle\" %}\nplugins {\n id \"dev.nx.gradle.project-graph\" version \"0.1.0\"\n}\n```\n\n{% /tab %}\n{% /tabs %}\n" +} diff --git a/packages/gradle/migrations.json b/packages/gradle/migrations.json index a6f4a99f7f..0ad4c2bfae 100644 --- a/packages/gradle/migrations.json +++ b/packages/gradle/migrations.json @@ -29,6 +29,12 @@ "cli": "nx", "description": "Change @nx/gradle option from ciTargetName to ciTestTargetName", "factory": "./src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName" + }, + "change-plugin-version-0-1-0": { + "version": "21.1.2-beta.1", + "cli": "nx", + "description": "Change dev.nx.gradle.project-graph to version 0.1.0 in build file", + "factory": "./src/migrations/21-1-2/change-plugin-version-0-1-0" } }, "packageJsonUpdates": {} diff --git a/packages/gradle/src/generators/init/gradle-project-graph-plugin-utils.spec.ts b/packages/gradle/src/generators/init/gradle-project-graph-plugin-utils.spec.ts new file mode 100644 index 0000000000..cc96cc88ae --- /dev/null +++ b/packages/gradle/src/generators/init/gradle-project-graph-plugin-utils.spec.ts @@ -0,0 +1,272 @@ +import { TempFs } from 'nx/src/internal-testing-utils/temp-fs'; +import { Tree } from '@nx/devkit'; +import { FsTree } from 'nx/src/generators/tree'; +import { + addNxProjectGraphPlugin, + extractNxPluginVersion, + updateNxPluginVersion, +} from './gradle-project-graph-plugin-utils'; +import { gradleProjectGraphPluginName } from '../../utils/versions'; +import * as execGradle from '../../utils/exec-gradle'; + +jest.mock('../../utils/exec-gradle', () => ({ + findGradlewFile: jest.fn(), + execGradleAsync: jest.fn(), +})); + +const mockFindGradlewFile = execGradle.findGradlewFile as jest.Mock; +const mockExecGradleAsync = execGradle.execGradleAsync as jest.Mock; + +describe('Gradle Project Graph Plugin Utils', () => { + describe('extractNxPluginVersion', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it.each([ + ['id "dev.nx.gradle.project-graph" version "+"', '+'], + ['id("dev.nx.gradle.project-graph") version("0.1.0")', '0.1.0'], + ['id(\'dev.nx.gradle.project-graph\') version("0.1.0")', '0.1.0'], + ["id('dev.nx.gradle.project-graph') version('0.1.0')", '0.1.0'], + ])('should extract version from %s', async (input, expected) => { + const version = await extractNxPluginVersion('build.gradle.kts', input); + expect(version).toBe(expected); + }); + + it('should return version from buildEnvironment fallback if inline version is missing', async () => { + const gradleEnvOutput = ` + classpath + \\--- dev.nx.gradle.project-graph:dev.nx.gradle.project-graph.gradle.plugin:0.2.3 + \\--- dev.nx.gradle:project-graph:0.2.3 + `; + + mockFindGradlewFile.mockReturnValue('./gradlew'); + mockExecGradleAsync.mockResolvedValue(gradleEnvOutput); + + const version = await extractNxPluginVersion( + 'build.gradle.kts', + 'some unrelated content' + ); + expect(version).toBe('0.2.3'); + expect(mockFindGradlewFile).toHaveBeenCalled(); + expect(mockExecGradleAsync).toHaveBeenCalled(); + }); + + it('should return null when no version is found anywhere', async () => { + mockFindGradlewFile.mockImplementation(() => { + throw new Error('not found'); + }); + + const version = await extractNxPluginVersion( + 'build.gradle.kts', + 'no version here' + ); + expect(version).toBeNull(); + }); + }); + + describe('updateNxPluginVersion', () => { + it('should update version in Groovy DSL format', () => { + const input = 'id "dev.nx.gradle.project-graph" version "1.0.0"'; + const expected = 'id "dev.nx.gradle.project-graph" version "2.0.0"'; + expect(updateNxPluginVersion(input, '2.0.0')).toBe(expected); + }); + + it('should update version in Kotlin DSL format', () => { + const input = 'id("dev.nx.gradle.project-graph") version("1.0.0")'; + const expected = 'id("dev.nx.gradle.project-graph") version("2.0.0")'; + expect(updateNxPluginVersion(input, '2.0.0')).toBe(expected); + }); + }); + + describe('addNxProjectGraphPlugin', () => { + let tempFs: TempFs; + let cwd: string; + let tree: Tree; + + beforeEach(async () => { + tempFs = new TempFs('test'); + cwd = process.cwd(); + process.chdir(tempFs.tempDir); + tree = new FsTree(tempFs.tempDir, false); + }); + + afterEach(() => { + jest.resetModules(); + process.chdir(cwd); + }); + + describe('Groovy DSL (build.gradle)', () => { + it('should add plugin to existing plugins block', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' +}`, + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toMatch( + /plugins\s*{\s*id\s*['"]dev\.nx\.gradle\.project-graph['"]\s*version\s*['"][^'"]+['"]\s*id\s*['"]java['"]/ + ); + expect(content).toMatch( + /allprojects\s*{\s*apply\s*{\s*plugin\(['"]dev\.nx\.gradle\.project-graph['"]\)\s*}\s*}/ + ); + }); + + it('should use passed in expected version', async () => { + const expectedVersion = '2.0.0'; + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' +}`, + }); + + await addNxProjectGraphPlugin(tree, expectedVersion); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toContain( + `id "${gradleProjectGraphPluginName}" version "${expectedVersion}"` + ); + expect(content).toMatch( + /allprojects\s*{\s*apply\s*{\s*plugin\(['"]dev\.nx\.gradle\.project-graph['"]\)\s*}\s*}/ + ); + }); + + it('should create plugins block if missing', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': 'apply plugin: "java"', + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toMatch( + /^plugins\s*{\s*id\s*['"]dev\.nx\.gradle\.project-graph['"]\s*version\s*['"][^'"]+['"]\s*}\s*apply plugin:/ + ); + }); + + it('should update existing plugin version', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id "dev.nx.gradle.project-graph" version "1.0.0" +}`, + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).not.toContain('version "1.0.0"'); + expect(content).toContain('allprojects {'); + }); + }); + + describe('Kotlin DSL (build.gradle.kts)', () => { + it('should add plugin to existing plugins block', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle.kts': '', + 'proj/build.gradle.kts': `plugins { + id("java") +}`, + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle.kts', 'utf-8'); + expect(content).toMatch( + /plugins\s*{\s*id\(['"]dev\.nx\.gradle\.project-graph['"]\)\s*version\(['"][^'"]+['"]\)\s*id\(['"]java['"]\)/ + ); + expect(content).toMatch( + /allprojects\s*{\s*apply\s*{\s*plugin\(['"]dev\.nx\.gradle\.project-graph['"]\)\s*}\s*}/ + ); + }); + + it('should create plugins block if missing', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle.kts': '', + 'proj/build.gradle.kts': 'apply(plugin = "java")', + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle.kts', 'utf-8'); + expect(content).toMatch( + /^plugins\s*{\s*id\(['"]dev\.nx\.gradle\.project-graph['"]\)\s*version\(['"][^'"]+['"]\)\s*}\s*apply\(plugin =/ + ); + }); + }); + + describe('Multiple projects', () => { + it('should handle multiple build.gradle files', async () => { + await tempFs.createFiles({ + 'proj1/settings.gradle': '', + 'proj1/build.gradle': 'apply plugin: "java"', + 'proj2/settings.gradle': '', + 'proj2/build.gradle': 'apply plugin: "java"', + }); + + await addNxProjectGraphPlugin(tree); + + const proj1Content = tree.read('proj1/build.gradle', 'utf-8'); + const proj2Content = tree.read('proj2/build.gradle', 'utf-8'); + + expect(proj1Content).toContain(`id "${gradleProjectGraphPluginName}"`); + expect(proj2Content).toContain(`id "${gradleProjectGraphPluginName}"`); + }); + + it('should handle mixed Groovy and Kotlin DSL projects', async () => { + await tempFs.createFiles({ + 'groovy/settings.gradle': '', + 'groovy/build.gradle': 'apply plugin: "java"', + 'kotlin/settings.gradle.kts': '', + 'kotlin/build.gradle.kts': 'apply(plugin = "java")', + }); + + await addNxProjectGraphPlugin(tree); + + const groovyContent = tree.read('groovy/build.gradle', 'utf-8'); + const kotlinContent = tree.read('kotlin/build.gradle.kts', 'utf-8'); + + expect(groovyContent).toContain(`id "${gradleProjectGraphPluginName}"`); + expect(kotlinContent).toContain( + `id("${gradleProjectGraphPluginName}")` + ); + }); + }); + + describe('Edge cases', () => { + it('should handle empty build.gradle file', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': '', + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toMatch( + /^plugins\s*{\s*id\s*['"]dev\.nx\.gradle\.project-graph['"]\s*version\s*['"][^'"]+['"]\s*}\s*allprojects/ + ); + }); + + it('should handle build.gradle with only comments', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': '// Some comments\n// More comments', + }); + + await addNxProjectGraphPlugin(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toMatch( + /^plugins\s*{\s*id\s*['"]dev\.nx\.gradle\.project-graph['"]\s*version\s*['"][^'"]+['"]\s*}\s*\/\/ Some comments/ + ); + }); + }); + }); +}); diff --git a/packages/gradle/src/generators/init/gradle-project-graph-plugin-utils.ts b/packages/gradle/src/generators/init/gradle-project-graph-plugin-utils.ts new file mode 100644 index 0000000000..4440759f88 --- /dev/null +++ b/packages/gradle/src/generators/init/gradle-project-graph-plugin-utils.ts @@ -0,0 +1,207 @@ +import { globAsync, logger, Tree, workspaceRoot } from '@nx/devkit'; +import { + gradleProjectGraphPluginName, + gradleProjectGraphVersion, +} from '../../utils/versions'; +import { dirname, join } from 'path'; +import { execGradleAsync, findGradlewFile } from '../../utils/exec-gradle'; + +/** + * Adds a `build.gradle(.kts)` file next to each `settings.gradle(.kts)` file found in the workspace. + * If the build.gradle file already exists, it reads its contents. + */ +export async function addBuildGradleFileNextToSettingsGradle( + tree: Tree +): Promise<{ filePath: string; content: string }[]> { + const settingsGradleFiles = await globAsync(tree, [ + '**/settings.gradle', + '**/settings.gradle.kts', + ]); + + return settingsGradleFiles.map((settingsGradlePath) => { + return ensureBuildGradleFile(tree, settingsGradlePath); + }); +} + +/** + * Determines the appropriate build.gradle file path based on the settings file + * and ensures it exists in the tree. Returns the path and contents. + */ +function ensureBuildGradleFile( + tree: Tree, + settingsGradlePath: string +): { filePath: string; content: string } { + const isKotlinDsl = settingsGradlePath.endsWith('.kts'); + const buildGradleFile = join( + dirname(settingsGradlePath), + isKotlinDsl ? 'build.gradle.kts' : 'build.gradle' + ); + + let content = ''; + if (tree.exists(buildGradleFile)) { + content = tree.read(buildGradleFile, 'utf-8'); + } else { + tree.write(buildGradleFile, content); + } + + return { filePath: buildGradleFile, content }; +} + +// a regex to get the version in build file in format `id "dev.nx.gradle.project-graph" version "x"` +const regex = + /(id\s*\(?["']dev\.nx\.gradle\.project-graph["']\)?\s*version\s*\(?["'])([^"']+)(["']\)?)/; + +/** + * Extract gradle plugin version from build.gradle file + */ +export async function extractNxPluginVersion( + gradleFilePath: string, + gradleContent: string +): Promise { + const match = gradleContent.match(regex); + let version = match ? match[2] : null; + if (!version) { + try { + const gradlewFile = findGradlewFile(gradleFilePath, workspaceRoot); + const buildEnvironment = ( + await execGradleAsync(join(workspaceRoot, gradlewFile), [ + 'buildEnvironment', + '--quiet', + ]) + ).toString(); + version = getPluginVersion(buildEnvironment); + } catch (e) {} // Silently ignore error, fallback remains null + } + return version; +} + +function getPluginVersion(dependencyTree: string): string | null { + const lines = dependencyTree.split('\n'); + + for (const line of lines) { + // line is dev.nx.gradle.project-graph:dev.nx.gradle.project-graph.gradle.plugin:version + const match = line.match( + /dev\.nx\.gradle\.project-graph:dev\.nx\.gradle\.project-graph\.gradle\.plugin:([^\s\\]+)/ + ); + if (match) { + return match[1]; // returns the version part + } + } + + return null; // not found +} + +/** + * Updates the plugin version in the given Gradle file content. + */ +export function updateNxPluginVersion( + content: string, + newVersion: string +): string { + if (regex.test(content)) { + return content.replace(regex, `$1${newVersion}$3`); + } else { + logger.warn( + `Please update plugin dev.nx.gradle.project-graph to ${newVersion}` + ); + } + return content; +} + +/** + * Ensures all build.gradle(.kts) files use the expected version of dev.nx.gradle.project-graph. + */ +export async function addNxProjectGraphPlugin( + tree: Tree, + expectedVersion: string = gradleProjectGraphVersion +) { + const files = await addBuildGradleFileNextToSettingsGradle(tree); + + files.forEach(({ filePath, content }) => { + addNxProjectGraphPluginToBuildGradle( + filePath, + content, + expectedVersion, + tree + ); + }); +} + +/** + * Adds or updates the Nx Project Graph plugin in the build.gradle(.kts) file. + * Ensures the correct version and applies the plugin to all projects. + * Returns the updated build.gradle content. + */ +async function addNxProjectGraphPluginToBuildGradle( + gradleFilePath: string, + buildGradleContent: string, + expectedVersion: string = gradleProjectGraphVersion, + tree: Tree +): Promise { + const isKotlinDsl = gradleFilePath.endsWith('.kts'); + const nxProjectGraphReportPlugin = isKotlinDsl + ? `id(\"${gradleProjectGraphPluginName}\") version(\"${expectedVersion}\")` + : `id \"${gradleProjectGraphPluginName}\" version \"${expectedVersion}\"`; + + // Helper to add plugin to plugins block + function addPluginToPluginsBlock(content: string): string { + return content.replace( + /plugins\s*\{/, + `plugins {\n ${nxProjectGraphReportPlugin}` + ); + } + + // Helper to add plugins block if missing + function addPluginsBlock(content: string): string { + return `plugins {\n ${nxProjectGraphReportPlugin}\n}\n${content}`; + } + + // Helper to add plugin application to allprojects + function addPluginToAllProjects(content: string): string { + const applyPlugin = isKotlinDsl + ? `plugin(\"${gradleProjectGraphPluginName}\")` + : `plugin(\"${gradleProjectGraphPluginName}\")`; + return `${content}\nallprojects {\n apply {\n ${applyPlugin}\n }\n}`; + } + + // 1. Ensure plugins block and correct plugin version + if (buildGradleContent.includes('plugins {')) { + if (buildGradleContent.includes(gradleProjectGraphPluginName)) { + // Update version if needed + const currentVersion = await extractNxPluginVersion( + gradleFilePath, + buildGradleContent + ); + if (currentVersion && currentVersion !== expectedVersion) { + buildGradleContent = updateNxPluginVersion( + buildGradleContent, + expectedVersion + ); + } + } else { + // Add plugin to plugins block + buildGradleContent = addPluginToPluginsBlock(buildGradleContent); + } + } else { + // Add plugins block if missing + buildGradleContent = addPluginsBlock(buildGradleContent); + } + + // 2. Ensure plugin is applied to all projects + const applyPluginPattern = new RegExp( + `\\s*plugin\\(["']${gradleProjectGraphPluginName}["']\\)` + ); + if (buildGradleContent.includes('allprojects {')) { + if (!applyPluginPattern.test(buildGradleContent)) { + logger.warn( + `Please add the ${gradleProjectGraphPluginName} plugin to your ${gradleFilePath}:\nallprojects {\n apply {\n plugin(\"${gradleProjectGraphPluginName}\")\n }\n}` + ); + } + } else { + buildGradleContent = addPluginToAllProjects(buildGradleContent); + } + + // 3. Write and return updated content + tree.write(gradleFilePath, buildGradleContent); + return buildGradleContent; +} diff --git a/packages/gradle/src/generators/init/init.ts b/packages/gradle/src/generators/init/init.ts index 55950788aa..ebceda9ac1 100644 --- a/packages/gradle/src/generators/init/init.ts +++ b/packages/gradle/src/generators/init/init.ts @@ -2,21 +2,15 @@ import { addDependenciesToPackageJson, formatFiles, GeneratorCallback, - globAsync, - logger, readNxJson, runTasksInSerial, Tree, updateNxJson, } from '@nx/devkit'; -import { - gradleProjectGraphPluginName, - gradleProjectGraphVersion, - nxVersion, -} from '../../utils/versions'; +import { nxVersion } from '../../utils/versions'; import { InitGeneratorSchema } from './schema'; import { hasGradlePlugin } from '../../utils/has-gradle-plugin'; -import { dirname, join, basename } from 'path'; +import { addNxProjectGraphPlugin } from './gradle-project-graph-plugin-utils'; export async function initGenerator(tree: Tree, options: InitGeneratorSchema) { const tasks: GeneratorCallback[] = []; @@ -34,7 +28,7 @@ export async function initGenerator(tree: Tree, options: InitGeneratorSchema) { ) ); } - await addBuildGradleFileNextToSettingsGradle(tree); + await addNxProjectGraphPlugin(tree); addPlugin(tree); updateNxJsonConfiguration(tree); @@ -62,84 +56,6 @@ function addPlugin(tree: Tree) { } } -/** - * This function creates and populate build.gradle file next to the settings.gradle file. - */ -export async function addBuildGradleFileNextToSettingsGradle(tree: Tree) { - const settingsGradleFiles = await globAsync(tree, [ - '**/settings.gradle?(.kts)', - ]); - settingsGradleFiles.forEach((settingsGradleFile) => { - addNxProjectGraphPluginToBuildGradle(settingsGradleFile, tree); - }); -} - -/** - * - creates a build.gradle file next to the settings.gradle file if it does not exist. - * - adds the NxProjectGraphPlugin plugin to the build.gradle file if it does not exist. - */ -function addNxProjectGraphPluginToBuildGradle( - settingsGradleFile: string, - tree: Tree -) { - const filename = basename(settingsGradleFile); - let gradleFilePath = 'build.gradle'; - if (filename.endsWith('.kts')) { - gradleFilePath = 'build.gradle.kts'; - } - gradleFilePath = join(dirname(settingsGradleFile), gradleFilePath); - let buildGradleContent = ''; - if (!tree.exists(gradleFilePath)) { - tree.write(gradleFilePath, buildGradleContent); // create a build.gradle file near settings.gradle file if it does not exist - } else { - buildGradleContent = tree.read(gradleFilePath).toString(); - } - - const nxProjectGraphReportPlugin = filename.endsWith('.kts') - ? `id("${gradleProjectGraphPluginName}") version("${gradleProjectGraphVersion}")` - : `id "${gradleProjectGraphPluginName}" version "${gradleProjectGraphVersion}"`; - if (buildGradleContent.includes('plugins {')) { - if (!buildGradleContent.includes(gradleProjectGraphPluginName)) { - buildGradleContent = buildGradleContent.replace( - 'plugins {', - `plugins { - ${nxProjectGraphReportPlugin}` - ); - } - } else { - buildGradleContent = `plugins { - ${nxProjectGraphReportPlugin} -}\n\r${buildGradleContent}`; - } - - const applyNxProjectGraphReportPlugin = `plugin("${gradleProjectGraphPluginName}")`; - if (buildGradleContent.includes('allprojects {')) { - if ( - !buildGradleContent.includes( - `plugin("${gradleProjectGraphPluginName}")` - ) && - !buildGradleContent.includes(`plugin('${gradleProjectGraphPluginName}')`) - ) { - logger.warn( - `Please add the ${gradleProjectGraphPluginName} plugin to your ${gradleFilePath}: -allprojects { - apply { - ${applyNxProjectGraphReportPlugin} - } -}` - ); - } - } else { - buildGradleContent = `${buildGradleContent}\n\rallprojects { - apply { - ${applyNxProjectGraphReportPlugin} - } - }`; - } - - tree.write(gradleFilePath, buildGradleContent); -} - export function updateNxJsonConfiguration(tree: Tree) { const nxJson = readNxJson(tree); diff --git a/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.md b/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.md new file mode 100644 index 0000000000..2fa84fc9c3 --- /dev/null +++ b/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.md @@ -0,0 +1,26 @@ +#### Change dev.nx.gradle.project-graph to version 0.1.0 + +Change dev.nx.gradle.project-graph to version 0.1.0 in build file + +#### Sample Code Changes + +{% tabs %} +{% tab label="Before" %} + +```{% fileName="build.gradle" %} +plugins { + id "dev.nx.gradle.project-graph" version "0.0.5" +} +``` + +{% /tab %} +{% tab label="After" %} + +```{% fileName="build.gradle" %} +plugins { + id "dev.nx.gradle.project-graph" version "0.1.0" +} +``` + +{% /tab %} +{% /tabs %} diff --git a/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.spec.ts b/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.spec.ts new file mode 100644 index 0000000000..ea8ad1def8 --- /dev/null +++ b/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.spec.ts @@ -0,0 +1,128 @@ +import { TempFs } from 'nx/src/internal-testing-utils/temp-fs'; +import { Tree } from '@nx/devkit'; +import { FsTree } from 'nx/src/generators/tree'; +import update from './change-plugin-version-0-1-0'; +import { gradleProjectGraphPluginName } from '../../utils/versions'; + +describe('change-plugin-version-0-1-0 migration', () => { + let tempFs: TempFs; + let cwd: string; + let tree: Tree; + + beforeEach(async () => { + tempFs = new TempFs('test'); + cwd = process.cwd(); + process.chdir(tempFs.tempDir); + tree = new FsTree(tempFs.tempDir, false); + }); + + afterEach(() => { + jest.resetModules(); + process.chdir(cwd); + }); + + it('should update plugin version to 0.1.0 in Groovy DSL', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({ + plugins: ['@nx/gradle'], + }), + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toContain( + `id "${gradleProjectGraphPluginName}" version "0.1.0"` + ); + expect(content).not.toContain('version "0.0.1"'); + }); + + it('should update plugin version to 0.1.0 in Kotlin DSL', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({ + plugins: ['@nx/gradle'], + }), + 'proj/settings.gradle.kts': '', + 'proj/build.gradle.kts': `plugins { + id("java") + id("${gradleProjectGraphPluginName}") version("0.0.1") +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle.kts', 'utf-8'); + expect(content).toContain( + `id("${gradleProjectGraphPluginName}") version("0.1.0")` + ); + expect(content).not.toContain('version("0.0.1")'); + }); + + it('should not update if nx.json is missing', async () => { + await tempFs.createFiles({ + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).toContain('version "0.0.1"'); + expect(content).not.toContain('version "0.1.0"'); + }); + + it('should not update if Gradle plugin is not present', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({}), + 'proj/settings.gradle': '', + 'proj/build.gradle': `plugins { + id 'java' +}`, + }); + + await update(tree); + + const content = tree.read('proj/build.gradle', 'utf-8'); + expect(content).not.toContain(gradleProjectGraphPluginName); + }); + + it('should handle multiple build.gradle files', async () => { + await tempFs.createFiles({ + 'nx.json': JSON.stringify({ + plugins: ['@nx/gradle'], + }), + 'proj1/settings.gradle': '', + 'proj1/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + 'proj2/settings.gradle': '', + 'proj2/build.gradle': `plugins { + id 'java' + id "${gradleProjectGraphPluginName}" version "0.0.1" +}`, + }); + + await update(tree); + + const proj1Content = tree.read('proj1/build.gradle', 'utf-8'); + const proj2Content = tree.read('proj2/build.gradle', 'utf-8'); + + expect(proj1Content).toContain( + `id "${gradleProjectGraphPluginName}" version "0.1.0"` + ); + expect(proj2Content).toContain( + `id "${gradleProjectGraphPluginName}" version "0.1.0"` + ); + expect(proj1Content).not.toContain('version "0.0.1"'); + expect(proj2Content).not.toContain('version "0.0.1"'); + }); +}); diff --git a/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.ts b/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.ts new file mode 100644 index 0000000000..0094bfbba0 --- /dev/null +++ b/packages/gradle/src/migrations/21-1-2/change-plugin-version-0-1-0.ts @@ -0,0 +1,16 @@ +import { Tree, readNxJson } from '@nx/devkit'; +import { hasGradlePlugin } from '../../utils/has-gradle-plugin'; +import { addNxProjectGraphPlugin } from '../../generators/init/gradle-project-graph-plugin-utils'; + +/* Change the plugin version to 0.1.0 + */ +export default async function update(tree: Tree) { + const nxJson = readNxJson(tree); + if (!nxJson) { + return; + } + if (!hasGradlePlugin(tree)) { + return; + } + await addNxProjectGraphPlugin(tree, '0.1.0'); +}