fix(gradle): add migration script to check gradle plugin version (#31304)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> currently, when migrate @nx/gradle to latest, we don't check the dev.nx.gradle.project-graph version in build file ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> we should check the project-praph in the build file and make sure it is correct version ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
5537df6411
commit
4a94841916
@ -2225,6 +2225,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"migrations": {
|
"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": {
|
"/nx-api/gradle/migrations/change-ciTargetName-to-ciTestTargetName": {
|
||||||
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json",
|
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json",
|
||||||
|
|||||||
@ -2209,6 +2209,16 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"migrations": [
|
"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",
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json",
|
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.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"
|
||||||
|
}
|
||||||
@ -29,6 +29,12 @@
|
|||||||
"cli": "nx",
|
"cli": "nx",
|
||||||
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
"factory": "./src/migrations/21-0-0/change-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": {}
|
"packageJsonUpdates": {}
|
||||||
|
|||||||
@ -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/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -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<string | null> {
|
||||||
|
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<string> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
@ -2,21 +2,15 @@ import {
|
|||||||
addDependenciesToPackageJson,
|
addDependenciesToPackageJson,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
GeneratorCallback,
|
GeneratorCallback,
|
||||||
globAsync,
|
|
||||||
logger,
|
|
||||||
readNxJson,
|
readNxJson,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
Tree,
|
Tree,
|
||||||
updateNxJson,
|
updateNxJson,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import {
|
import { nxVersion } from '../../utils/versions';
|
||||||
gradleProjectGraphPluginName,
|
|
||||||
gradleProjectGraphVersion,
|
|
||||||
nxVersion,
|
|
||||||
} from '../../utils/versions';
|
|
||||||
import { InitGeneratorSchema } from './schema';
|
import { InitGeneratorSchema } from './schema';
|
||||||
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
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) {
|
export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
@ -34,7 +28,7 @@ export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await addBuildGradleFileNextToSettingsGradle(tree);
|
await addNxProjectGraphPlugin(tree);
|
||||||
addPlugin(tree);
|
addPlugin(tree);
|
||||||
updateNxJsonConfiguration(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) {
|
export function updateNxJsonConfiguration(tree: Tree) {
|
||||||
const nxJson = readNxJson(tree);
|
const nxJson = readNxJson(tree);
|
||||||
|
|
||||||
|
|||||||
@ -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 %}
|
||||||
@ -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"');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -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');
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user