fix(gradle): add gradle migration to change ciTargetName (#30965)

<!-- 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 -->
this pr address comments in https://github.com/nrwl/nx/pull/30457

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
- update executor description
- use pseudo terminal when run batch command
- add an e2e test to run gradle command in batch
- add migration script to change ciTargetName to ciTestTargetName

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Emily Xiong 2025-05-01 13:21:39 -04:00 committed by GitHub
parent 11ca6fc685
commit 3877a43a47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 261 additions and 19 deletions

View File

@ -2105,7 +2105,7 @@
"source": "/packages/gradle/src",
"executors": {
"/nx-api/gradle/executors/gradle": {
"description": "The Gradlew executor is used to run Gradle tasks.",
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew.",
"file": "generated/packages/gradle/executors/gradle.json",
"hidden": false,
"name": "gradle",
@ -2135,6 +2135,16 @@
}
},
"migrations": {
"/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",
"hidden": false,
"name": "change-ciTargetName-to-ciTestTargetName",
"version": "21.0.0-beta.13",
"originalFilePath": "/packages/gradle",
"path": "/nx-api/gradle/migrations/change-ciTargetName-to-ciTestTargetName",
"type": "migration"
},
"/nx-api/gradle/migrations/change-plugin-to-v1": {
"description": "Change @nx/gradle plugin to version 1",
"file": "generated/packages/gradle/migrations/change-plugin-to-v1.json",

View File

@ -2089,7 +2089,7 @@
],
"executors": [
{
"description": "The Gradlew executor is used to run Gradle tasks.",
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew.",
"file": "generated/packages/gradle/executors/gradle.json",
"hidden": false,
"name": "gradle",
@ -2119,6 +2119,16 @@
}
],
"migrations": [
{
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json",
"hidden": false,
"name": "change-ciTargetName-to-ciTestTargetName",
"version": "21.0.0-beta.13",
"originalFilePath": "/packages/gradle",
"path": "gradle/migrations/change-ciTargetName-to-ciTestTargetName",
"type": "migration"
},
{
"description": "Change @nx/gradle plugin to version 1",
"file": "generated/packages/gradle/migrations/change-plugin-to-v1.json",

View File

@ -29,7 +29,7 @@
"required": ["taskName"],
"presets": []
},
"description": "The Gradlew executor is used to run Gradle tasks.",
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew.",
"aliases": [],
"hidden": false,
"path": "/packages/gradle/src/executors/gradle/schema.json",

View File

@ -0,0 +1,14 @@
{
"name": "change-ciTargetName-to-ciTestTargetName",
"version": "21.0.0-beta.13",
"cli": "nx",
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
"factory": "./src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName",
"implementation": "/packages/gradle/src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName.ts",
"aliases": [],
"hidden": false,
"path": "/packages/gradle",
"schema": null,
"type": "migration",
"examplesFile": "#### Change @nx/gradle plugin option ciTargetName to ciTestTargetName\n\nChange @nx/gradle plugin option ciTargetName to ciTestTargetName in nx.json\n\n#### Sample Code Changes\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"nx.json\" %}\n{\n \"plugins\": [\n \"plugin\": \"@nx/gradle\",\n \"options\": {\n \"ciTargetName\": \"ci\"\n }\n ]\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```json {% highlightLines=[5] fileName=\"nx.json\" %}\n{\n \"plugins\": [\n \"plugin\": \"@nx/gradle\",\n \"options\": {\n \"ciTestTargetName\": \"ci\"\n }\n ]\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
}

View File

@ -30,7 +30,7 @@ describe('Gradle', () => {
expect(projects).toContain('utilities');
expect(projects).toContain(gradleProjectName);
const buildOutput = runCLI('build app', { verbose: true });
let buildOutput = runCLI('build app', { verbose: true });
expect(buildOutput).toContain(':list:classes');
expect(buildOutput).toContain(':utilities:classes');
@ -39,6 +39,10 @@ describe('Gradle', () => {
`list/build/libs/list.jar`,
`utilities/build/libs/utilities.jar`
);
buildOutput = runCLI('build app --batch', { verbose: true });
expect(buildOutput).toContain(':list:classes');
expect(buildOutput).toContain(':utilities:classes');
});
xit('should track dependencies for new app', () => {
@ -86,6 +90,11 @@ dependencies {
expect(buildOutput).toContain(':utilities:classes');
checkFilesExist(`app2/build/libs/app2.jar`);
buildOutput = runCLI('build app2 --batch', { verbose: true });
expect(buildOutput).toContain(':app:classes');
expect(buildOutput).toContain(':list:classes');
expect(buildOutput).toContain(':utilities:classes');
});
it('should run atomized test target', () => {

View File

@ -4,7 +4,7 @@
"batchImplementation": "./src/executors/gradle/gradle-batch.impl",
"implementation": "./src/executors/gradle/gradle.impl",
"schema": "./src/executors/gradle/schema.json",
"description": "The Gradlew executor is used to run Gradle tasks."
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew."
}
}
}

View File

@ -23,6 +23,12 @@
"cli": "nx",
"description": "Change @nx/gradle plugin to version 1",
"factory": "./src/migrations/21-0-0/change-plugin-to-v1"
},
"change-ciTargetName-to-ciTestTargetName": {
"version": "21.0.0-beta.13",
"cli": "nx",
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
"factory": "./src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName"
}
},
"packageJsonUpdates": {}

View File

@ -1,4 +1,4 @@
import { ExecutorContext, output, TaskGraph } from '@nx/devkit';
import { ExecutorContext, output, TaskGraph, workspaceRoot } from '@nx/devkit';
import {
LARGE_BUFFER,
RunCommandsOptions,
@ -8,6 +8,10 @@ import { gradleExecutorSchema } from './schema';
import { findGradlewFile } from '../../utils/exec-gradle';
import { dirname, join } from 'path';
import { execSync } from 'child_process';
import {
createPseudoTerminal,
PseudoTerminal,
} from 'nx/src/tasks-runner/pseudo-terminal';
export const batchRunnerPath = join(
__dirname,
@ -56,20 +60,42 @@ export default async function gradleBatch(
return gradlewTasksToRun;
}, {});
const gradlewBatchStart = performance.mark(`gradlew-batch:start`);
const batchResults = execSync(
`java -jar ${batchRunnerPath} --tasks='${JSON.stringify(
gradlewTasksToRun
)}' --workspaceRoot=${root} --args='${args
.join(' ')
.replaceAll("'", '"')}' ${
process.env.NX_VERBOSE_LOGGING === 'true' ? '' : '--quiet'
}`,
{
const usePseudoTerminal =
process.env.NX_NATIVE_COMMAND_RUNNER !== 'false' &&
PseudoTerminal.isSupported();
const command = `java -jar ${batchRunnerPath} --tasks='${JSON.stringify(
gradlewTasksToRun
)}' --workspaceRoot=${root} --args='${args
.join(' ')
.replaceAll("'", '"')}' ${
process.env.NX_VERBOSE_LOGGING === 'true' ? '' : '--quiet'
}`;
let batchResults;
if (usePseudoTerminal) {
const terminal = createPseudoTerminal();
await terminal.init();
const cp = terminal.runCommand(command, {
cwd: workspaceRoot,
jsEnv: process.env,
quiet: process.env.NX_VERBOSE_LOGGING !== 'true',
});
const results = await cp.getResults();
batchResults = results.terminalOutput;
batchResults = batchResults.replace(command, '');
const startIndex = batchResults.indexOf('{');
const endIndex = batchResults.lastIndexOf('}');
batchResults = batchResults.substring(startIndex, endIndex + 1);
} else {
batchResults = execSync(command, {
cwd: workspaceRoot,
windowsHide: true,
env: process.env,
maxBuffer: LARGE_BUFFER,
}
);
}).toString();
}
const gradlewBatchEnd = performance.mark(`gradlew-batch:end`);
performance.measure(
`gradlew-batch`,

View File

@ -0,0 +1,36 @@
#### Change @nx/gradle plugin option ciTargetName to ciTestTargetName
Change @nx/gradle plugin option ciTargetName to ciTestTargetName in nx.json
#### Sample Code Changes
{% tabs %}
{% tab label="Before" %}
```json {% fileName="nx.json" %}
{
"plugins": [
"plugin": "@nx/gradle",
"options": {
"ciTargetName": "ci"
}
]
}
```
{% /tab %}
{% tab label="After" %}
```json {% highlightLines=[5] fileName="nx.json" %}
{
"plugins": [
"plugin": "@nx/gradle",
"options": {
"ciTestTargetName": "ci"
}
]
}
```
{% /tab %}
{% /tabs %}

View File

@ -0,0 +1,94 @@
import { Tree, readNxJson } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import update from './change-ciTargetName-to-ciTestTargetName';
describe('change ciTargetName to ciTestTargetName', () => {
let tree: Tree;
beforeAll(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should not add @nx/gradle plugin if it does not exist', async () => {
tree.write('nx.json', JSON.stringify({ namedInputs: {} }));
update(tree);
expect(readNxJson(tree)).toMatchInlineSnapshot(`
{
"namedInputs": {},
}
`);
});
it('should not add options to @nx/gradle if it have any options', async () => {
tree.write('nx.json', JSON.stringify({ plugins: ['@nx/gradle'] }));
update(tree);
expect(readNxJson(tree)).toMatchInlineSnapshot(`
{
"plugins": [
"@nx/gradle",
],
}
`);
});
it('should not add options to @nx/gradle/plugin-v1', async () => {
tree.write(
'nx.json',
JSON.stringify({ plugins: ['@nx/gradle/plugin-v1'] })
);
update(tree);
expect(readNxJson(tree)).toMatchInlineSnapshot(`
{
"plugins": [
"@nx/gradle/plugin-v1",
],
}
`);
});
it('should not change to @nx/gradle plugin if ciTargetName does not exist', async () => {
tree.write(
'nx.json',
JSON.stringify({
plugins: [
{ plugin: '@nx/gradle', options: { testTargetName: 'test' } },
],
})
);
update(tree);
expect(readNxJson(tree)).toMatchInlineSnapshot(`
{
"plugins": [
{
"options": {
"testTargetName": "test",
},
"plugin": "@nx/gradle",
},
],
}
`);
});
it('should change to @nx/gradle plugin ciTargetName', async () => {
tree.write(
'nx.json',
JSON.stringify({
plugins: [{ plugin: '@nx/gradle', options: { ciTargetName: 'test' } }],
})
);
update(tree);
expect(readNxJson(tree)).toMatchInlineSnapshot(`
{
"plugins": [
{
"options": {
"ciTestTargetName": "test",
},
"plugin": "@nx/gradle",
},
],
}
`);
});
});

View File

@ -0,0 +1,32 @@
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
/* This function changes the @nx/gradle plugin option from ciTargetName to ciTestTargetName
*/
export default function update(tree: Tree) {
const nxJson = readNxJson(tree);
if (!nxJson) {
return;
}
if (!hasGradlePlugin(tree)) {
return;
}
let gradlePluginIndex = nxJson.plugins.findIndex((p) =>
typeof p === 'string' ? p === '@nx/gradle' : p.plugin === '@nx/gradle'
);
let gradlePlugin = nxJson.plugins[gradlePluginIndex];
if (
typeof gradlePlugin === 'object' &&
gradlePlugin.plugin === '@nx/gradle'
) {
const ciTargetName = (gradlePlugin.options as Record<string, string>)?.[
'ciTargetName'
];
if (ciTargetName) {
delete (gradlePlugin.options as Record<string, string>)?.['ciTargetName'];
(gradlePlugin.options as Record<string, string>)['ciTestTargetName'] =
ciTargetName;
}
}
updateNxJson(tree, nxJson);
}

View File

@ -31,7 +31,7 @@ describe('ChangePluginToV1', () => {
`);
});
it('should add change to @nx/gradle plugin with options', async () => {
it('should change @nx/gradle plugin name with options', async () => {
tree.write(
'nx.json',
JSON.stringify({

View File

@ -17,6 +17,7 @@ import {
import { GradlePluginOptions } from './utils/gradle-plugin-options';
import { GRALDEW_FILES, splitConfigFiles } from '../utils/split-config-files';
import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
import { existsSync } from 'node:fs';
export const createDependencies: CreateDependencies<
GradlePluginOptions
@ -52,7 +53,11 @@ export const createDependencies: CreateDependencies<
Object.values(context.projects).find(
(project) => target === project.root
)?.name ?? dependencyFromPlugin.target;
if (!sourceProjectName || !targetProjectName) {
if (
!sourceProjectName ||
!targetProjectName ||
!existsSync(dependencyFromPlugin.sourceFile)
) {
return;
}
const dependency: StaticDependency = {