feat(gradle): gradle atomizer (#26663)
<!-- 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 --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
e15479b691
commit
62baf4f307
10
graph/ui-project-details/jest.config.ts
Normal file
10
graph/ui-project-details/jest.config.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
export default {
|
||||||
|
displayName: 'graph-ui-project-details',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
|
},
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
|
coverageDirectory: '../../coverage/graph/graph-ui-project-details',
|
||||||
|
};
|
||||||
@ -13,6 +13,9 @@
|
|||||||
{
|
{
|
||||||
"path": "./tsconfig.lib.json"
|
"path": "./tsconfig.lib.json"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.storybook.json"
|
"path": "./tsconfig.storybook.json"
|
||||||
}
|
}
|
||||||
|
|||||||
20
graph/ui-project-details/tsconfig.spec.json
Normal file
20
graph/ui-project-details/tsconfig.spec.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.test.tsx",
|
||||||
|
"src/**/*.spec.tsx",
|
||||||
|
"src/**/*.test.js",
|
||||||
|
"src/**/*.spec.js",
|
||||||
|
"src/**/*.test.jsx",
|
||||||
|
"src/**/*.spec.jsx",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -6,5 +6,5 @@ export default {
|
|||||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||||
},
|
},
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||||
coverageDirectory: '../../coverage/graph/ui-graph',
|
coverageDirectory: '../../coverage/graph/graph-ui-tooltips',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,17 +19,17 @@ describe('@nx/gradle:init', () => {
|
|||||||
});
|
});
|
||||||
const nxJson = readNxJson(tree);
|
const nxJson = readNxJson(tree);
|
||||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"options": {
|
"options": {
|
||||||
"buildTargetName": "build",
|
"buildTargetName": "build",
|
||||||
"classesTargetName": "classes",
|
"classesTargetName": "classes",
|
||||||
"testTargetName": "test",
|
"testTargetName": "test",
|
||||||
},
|
},
|
||||||
"plugin": "@nx/gradle",
|
"plugin": "@nx/gradle",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not overwrite existing plugins', async () => {
|
it('should not overwrite existing plugins', async () => {
|
||||||
@ -42,18 +42,18 @@ describe('@nx/gradle:init', () => {
|
|||||||
});
|
});
|
||||||
const nxJson = readNxJson(tree);
|
const nxJson = readNxJson(tree);
|
||||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
"options": {
|
"options": {
|
||||||
"buildTargetName": "build",
|
"buildTargetName": "build",
|
||||||
"classesTargetName": "classes",
|
"classesTargetName": "classes",
|
||||||
"testTargetName": "test",
|
"testTargetName": "test",
|
||||||
},
|
},
|
||||||
"plugin": "@nx/gradle",
|
"plugin": "@nx/gradle",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not add plugin if already in array', async () => {
|
it('should not add plugin if already in array', async () => {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { readFileSync } from 'node:fs';
|
|||||||
import { basename } from 'node:path';
|
import { basename } from 'node:path';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
GRADLE_BUILD_FILES,
|
||||||
getCurrentGradleReport,
|
getCurrentGradleReport,
|
||||||
newLineSeparator,
|
newLineSeparator,
|
||||||
} from '../utils/get-gradle-report';
|
} from '../utils/get-gradle-report';
|
||||||
@ -58,14 +59,12 @@ export const createDependencies: CreateDependencies = async (
|
|||||||
return Array.from(dependencies);
|
return Array.from(dependencies);
|
||||||
};
|
};
|
||||||
|
|
||||||
const gradleConfigFileNames = new Set(['build.gradle', 'build.gradle.kts']);
|
|
||||||
|
|
||||||
function findGradleFiles(fileMap: FileMap): string[] {
|
function findGradleFiles(fileMap: FileMap): string[] {
|
||||||
const gradleFiles: string[] = [];
|
const gradleFiles: string[] = [];
|
||||||
|
|
||||||
for (const [_, files] of Object.entries(fileMap.projectFileMap)) {
|
for (const [_, files] of Object.entries(fileMap.projectFileMap)) {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
if (gradleConfigFileNames.has(basename(file.file))) {
|
if (GRADLE_BUILD_FILES.has(basename(file.file))) {
|
||||||
gradleFiles.push(file.file);
|
gradleFiles.push(file.file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
|
|||||||
import { type GradleReport } from '../utils/get-gradle-report';
|
import { type GradleReport } from '../utils/get-gradle-report';
|
||||||
|
|
||||||
let gradleReport: GradleReport;
|
let gradleReport: GradleReport;
|
||||||
jest.mock('../utils/get-gradle-report.ts', () => {
|
jest.mock('../utils/get-gradle-report', () => {
|
||||||
return {
|
return {
|
||||||
|
GRADLE_BUILD_FILES: new Set(['build.gradle', 'build.gradle.kts']),
|
||||||
populateGradleReport: jest.fn().mockImplementation(() => void 0),
|
populateGradleReport: jest.fn().mockImplementation(() => void 0),
|
||||||
getCurrentGradleReport: jest.fn().mockImplementation(() => gradleReport),
|
getCurrentGradleReport: jest.fn().mockImplementation(() => gradleReport),
|
||||||
};
|
};
|
||||||
@ -23,14 +24,14 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
tempFs = new TempFs('test');
|
tempFs = new TempFs('test');
|
||||||
gradleReport = {
|
gradleReport = {
|
||||||
gradleFileToGradleProjectMap: new Map<string, string>([
|
gradleFileToGradleProjectMap: new Map<string, string>([
|
||||||
['proj/gradle.build', 'proj'],
|
['proj/build.gradle', 'proj'],
|
||||||
]),
|
]),
|
||||||
buildFileToDepsMap: new Map<string, string>(),
|
buildFileToDepsMap: new Map<string, string>(),
|
||||||
gradleFileToOutputDirsMap: new Map<string, Map<string, string>>([
|
gradleFileToOutputDirsMap: new Map<string, Map<string, string>>([
|
||||||
['proj/gradle.build', new Map([['build', 'build']])],
|
['proj/build.gradle', new Map([['build', 'build']])],
|
||||||
]),
|
]),
|
||||||
gradleProjectToTasksTypeMap: new Map<string, Map<string, string>>([
|
gradleProjectToTasksTypeMap: new Map<string, Map<string, string>>([
|
||||||
['proj', new Map([['test', 'Test']])],
|
['proj', new Map([['test', 'Verification']])],
|
||||||
]),
|
]),
|
||||||
gradleProjectToProjectName: new Map<string, string>([['proj', 'proj']]),
|
gradleProjectToProjectName: new Map<string, string>([['proj', 'proj']]),
|
||||||
};
|
};
|
||||||
@ -48,7 +49,7 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await tempFs.createFiles({
|
await tempFs.createFiles({
|
||||||
'proj/gradle.build': ``,
|
'proj/build.gradle': ``,
|
||||||
gradlew: '',
|
gradlew: '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -60,7 +61,7 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
|
|
||||||
it('should create nodes based on gradle', async () => {
|
it('should create nodes based on gradle', async () => {
|
||||||
const results = await createNodesFunction(
|
const results = await createNodesFunction(
|
||||||
['proj/gradle.build'],
|
['proj/build.gradle'],
|
||||||
{
|
{
|
||||||
buildTargetName: 'build',
|
buildTargetName: 'build',
|
||||||
},
|
},
|
||||||
@ -70,13 +71,13 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
expect(results).toMatchInlineSnapshot(`
|
expect(results).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
"proj/gradle.build",
|
"proj/build.gradle",
|
||||||
{
|
{
|
||||||
"projects": {
|
"projects": {
|
||||||
"proj": {
|
"proj": {
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"targetGroups": {
|
"targetGroups": {
|
||||||
"Test": [
|
"Verification": [
|
||||||
"test",
|
"test",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -87,7 +88,7 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
"name": "proj",
|
"name": "proj",
|
||||||
"targets": {
|
"targets": {
|
||||||
"test": {
|
"test": {
|
||||||
"cache": false,
|
"cache": true,
|
||||||
"command": "./gradlew proj:test",
|
"command": "./gradlew proj:test",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"classes",
|
"classes",
|
||||||
@ -114,23 +115,23 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
it('should create nodes based on gradle for nested project root', async () => {
|
it('should create nodes based on gradle for nested project root', async () => {
|
||||||
gradleReport = {
|
gradleReport = {
|
||||||
gradleFileToGradleProjectMap: new Map<string, string>([
|
gradleFileToGradleProjectMap: new Map<string, string>([
|
||||||
['nested/nested/proj/gradle.build', 'proj'],
|
['nested/nested/proj/build.gradle', 'proj'],
|
||||||
]),
|
]),
|
||||||
buildFileToDepsMap: new Map<string, string>(),
|
buildFileToDepsMap: new Map<string, string>(),
|
||||||
gradleFileToOutputDirsMap: new Map<string, Map<string, string>>([
|
gradleFileToOutputDirsMap: new Map<string, Map<string, string>>([
|
||||||
['nested/nested/proj/gradle.build', new Map([['build', 'build']])],
|
['nested/nested/proj/build.gradle', new Map([['build', 'build']])],
|
||||||
]),
|
]),
|
||||||
gradleProjectToTasksTypeMap: new Map<string, Map<string, string>>([
|
gradleProjectToTasksTypeMap: new Map<string, Map<string, string>>([
|
||||||
['proj', new Map([['test', 'Test']])],
|
['proj', new Map([['test', 'Verification']])],
|
||||||
]),
|
]),
|
||||||
gradleProjectToProjectName: new Map<string, string>([['proj', 'proj']]),
|
gradleProjectToProjectName: new Map<string, string>([['proj', 'proj']]),
|
||||||
};
|
};
|
||||||
await tempFs.createFiles({
|
await tempFs.createFiles({
|
||||||
'nested/nested/proj/gradle.build': ``,
|
'nested/nested/proj/build.gradle': ``,
|
||||||
});
|
});
|
||||||
|
|
||||||
const results = await createNodesFunction(
|
const results = await createNodesFunction(
|
||||||
['nested/nested/proj/gradle.build'],
|
['nested/nested/proj/build.gradle'],
|
||||||
{
|
{
|
||||||
buildTargetName: 'build',
|
buildTargetName: 'build',
|
||||||
},
|
},
|
||||||
@ -140,13 +141,101 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
expect(results).toMatchInlineSnapshot(`
|
expect(results).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
"nested/nested/proj/gradle.build",
|
"nested/nested/proj/build.gradle",
|
||||||
|
{
|
||||||
|
"projects": {
|
||||||
|
"nested/nested/proj": {
|
||||||
|
"metadata": {
|
||||||
|
"targetGroups": {
|
||||||
|
"Verification": [
|
||||||
|
"test",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"technologies": [
|
||||||
|
"gradle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"name": "proj",
|
||||||
|
"targets": {
|
||||||
|
"test": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "./gradlew proj:test",
|
||||||
|
"dependsOn": [
|
||||||
|
"classes",
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"technologies": [
|
||||||
|
"gradle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create nodes with atomized tests targets based on gradle for nested project root', async () => {
|
||||||
|
gradleReport = {
|
||||||
|
gradleFileToGradleProjectMap: new Map<string, string>([
|
||||||
|
['nested/nested/proj/build.gradle', 'proj'],
|
||||||
|
]),
|
||||||
|
buildFileToDepsMap: new Map<string, string>(),
|
||||||
|
gradleFileToOutputDirsMap: new Map<string, Map<string, string>>([
|
||||||
|
['nested/nested/proj/build.gradle', new Map([['build', 'build']])],
|
||||||
|
]),
|
||||||
|
gradleProjectToTasksTypeMap: new Map<string, Map<string, string>>([
|
||||||
|
['proj', new Map([['test', 'Test']])],
|
||||||
|
]),
|
||||||
|
gradleProjectToProjectName: new Map<string, string>([['proj', 'proj']]),
|
||||||
|
};
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'nested/nested/proj/build.gradle': ``,
|
||||||
|
});
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'proj/src/test/java/test/rootTest.java': ``,
|
||||||
|
});
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'nested/nested/proj/src/test/java/test/test.java': ``,
|
||||||
|
});
|
||||||
|
await tempFs.createFiles({
|
||||||
|
'nested/nested/proj/src/test/java/test/test1.java': ``,
|
||||||
|
});
|
||||||
|
|
||||||
|
const results = await createNodesFunction(
|
||||||
|
[
|
||||||
|
'nested/nested/proj/build.gradle',
|
||||||
|
'proj/src/test/java/test/rootTest.java',
|
||||||
|
'nested/nested/proj/src/test/java/test/test.java',
|
||||||
|
'nested/nested/proj/src/test/java/test/test1.java',
|
||||||
|
],
|
||||||
|
{
|
||||||
|
buildTargetName: 'build',
|
||||||
|
ciTargetName: 'test-ci',
|
||||||
|
},
|
||||||
|
context
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(results).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"nested/nested/proj/build.gradle",
|
||||||
{
|
{
|
||||||
"projects": {
|
"projects": {
|
||||||
"nested/nested/proj": {
|
"nested/nested/proj": {
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"targetGroups": {
|
"targetGroups": {
|
||||||
"Test": [
|
"Test": [
|
||||||
|
"test-ci--test",
|
||||||
|
"test-ci--test1",
|
||||||
|
"test-ci",
|
||||||
"test",
|
"test",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -172,6 +261,67 @@ describe('@nx/gradle/plugin', () => {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"test-ci": {
|
||||||
|
"cache": true,
|
||||||
|
"dependsOn": [
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "test-ci--test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": "forward",
|
||||||
|
"projects": "self",
|
||||||
|
"target": "test-ci--test1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"executor": "nx:noop",
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Gradle Tests in CI",
|
||||||
|
"nonAtomizedTarget": "test",
|
||||||
|
"technologies": [
|
||||||
|
"gradle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test-ci--test": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "./gradlew proj:test --tests test",
|
||||||
|
"dependsOn": [
|
||||||
|
"classes",
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Gradle test nested/nested/proj/src/test/java/test/test.java in CI",
|
||||||
|
"technologies": [
|
||||||
|
"gradle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"test-ci--test1": {
|
||||||
|
"cache": true,
|
||||||
|
"command": "./gradlew proj:test --tests test1",
|
||||||
|
"dependsOn": [
|
||||||
|
"classes",
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
"default",
|
||||||
|
"^production",
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"description": "Runs Gradle test nested/nested/proj/src/test/java/test/test1.java in CI",
|
||||||
|
"technologies": [
|
||||||
|
"gradle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import {
|
|||||||
CreateNodes,
|
CreateNodes,
|
||||||
CreateNodesV2,
|
CreateNodesV2,
|
||||||
CreateNodesContext,
|
CreateNodesContext,
|
||||||
CreateNodesContextV2,
|
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
TargetConfiguration,
|
TargetConfiguration,
|
||||||
createNodesFromFiles,
|
createNodesFromFiles,
|
||||||
@ -13,8 +12,9 @@ import {
|
|||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||||
import { existsSync } from 'node:fs';
|
import { existsSync } from 'node:fs';
|
||||||
import { dirname, join } from 'node:path';
|
import { basename, dirname, join } from 'node:path';
|
||||||
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
|
||||||
|
import { findProjectForPath } from 'nx/src/devkit-internals';
|
||||||
|
|
||||||
import { getGradleExecFile } from '../utils/exec-gradle';
|
import { getGradleExecFile } from '../utils/exec-gradle';
|
||||||
import {
|
import {
|
||||||
@ -22,6 +22,8 @@ import {
|
|||||||
getCurrentGradleReport,
|
getCurrentGradleReport,
|
||||||
GradleReport,
|
GradleReport,
|
||||||
gradleConfigGlob,
|
gradleConfigGlob,
|
||||||
|
GRADLE_BUILD_FILES,
|
||||||
|
gradleConfigAndTestGlob,
|
||||||
} from '../utils/get-gradle-report';
|
} from '../utils/get-gradle-report';
|
||||||
import { hashObject } from 'nx/src/hasher/file-hasher';
|
import { hashObject } from 'nx/src/hasher/file-hasher';
|
||||||
|
|
||||||
@ -38,12 +40,21 @@ interface GradleTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface GradlePluginOptions {
|
export interface GradlePluginOptions {
|
||||||
|
ciTargetName?: string;
|
||||||
testTargetName?: string;
|
testTargetName?: string;
|
||||||
classesTargetName?: string;
|
classesTargetName?: string;
|
||||||
buildTargetName?: string;
|
buildTargetName?: string;
|
||||||
[taskTargetName: string]: string | undefined;
|
[taskTargetName: string]: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeOptions(options: GradlePluginOptions): GradlePluginOptions {
|
||||||
|
options ??= {};
|
||||||
|
options.testTargetName ??= 'test';
|
||||||
|
options.classesTargetName ??= 'classes';
|
||||||
|
options.buildTargetName ??= 'build';
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
type GradleTargets = Record<
|
type GradleTargets = Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
@ -62,8 +73,9 @@ export function writeTargetsToCache(cachePath: string, results: GradleTargets) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
||||||
gradleConfigGlob,
|
gradleConfigAndTestGlob,
|
||||||
async (configFiles, options, context) => {
|
async (files, options, context) => {
|
||||||
|
const { configFiles, projectRoots, testFiles } = splitConfigFiles(files);
|
||||||
const optionsHash = hashObject(options);
|
const optionsHash = hashObject(options);
|
||||||
const cachePath = join(
|
const cachePath = join(
|
||||||
workspaceDataDirectory,
|
workspaceDataDirectory,
|
||||||
@ -73,10 +85,18 @@ export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
|||||||
|
|
||||||
await populateGradleReport(context.workspaceRoot);
|
await populateGradleReport(context.workspaceRoot);
|
||||||
const gradleReport = getCurrentGradleReport();
|
const gradleReport = getCurrentGradleReport();
|
||||||
|
const gradleProjectRootToTestFilesMap = getGradleProjectRootToTestFilesMap(
|
||||||
|
testFiles,
|
||||||
|
projectRoots
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await createNodesFromFiles(
|
return createNodesFromFiles(
|
||||||
makeCreateNodes(gradleReport, targetsCache),
|
makeCreateNodesForGradleConfigFile(
|
||||||
|
gradleReport,
|
||||||
|
targetsCache,
|
||||||
|
gradleProjectRootToTestFilesMap
|
||||||
|
),
|
||||||
configFiles,
|
configFiles,
|
||||||
options,
|
options,
|
||||||
context
|
context
|
||||||
@ -87,10 +107,11 @@ export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const makeCreateNodes =
|
export const makeCreateNodesForGradleConfigFile =
|
||||||
(
|
(
|
||||||
gradleReport: GradleReport,
|
gradleReport: GradleReport,
|
||||||
targetsCache: GradleTargets
|
targetsCache: GradleTargets = {},
|
||||||
|
gradleProjectRootToTestFilesMap: Record<string, string[]> = {}
|
||||||
): CreateNodesFunction =>
|
): CreateNodesFunction =>
|
||||||
async (
|
async (
|
||||||
gradleFilePath,
|
gradleFilePath,
|
||||||
@ -98,17 +119,19 @@ export const makeCreateNodes =
|
|||||||
context: CreateNodesContext
|
context: CreateNodesContext
|
||||||
) => {
|
) => {
|
||||||
const projectRoot = dirname(gradleFilePath);
|
const projectRoot = dirname(gradleFilePath);
|
||||||
|
options = normalizeOptions(options);
|
||||||
|
|
||||||
const hash = await calculateHashForCreateNodes(
|
const hash = await calculateHashForCreateNodes(
|
||||||
projectRoot,
|
projectRoot,
|
||||||
options ?? {},
|
options ?? {},
|
||||||
context
|
context
|
||||||
);
|
);
|
||||||
targetsCache[hash] ??= createGradleProject(
|
targetsCache[hash] ??= await createGradleProject(
|
||||||
gradleReport,
|
gradleReport,
|
||||||
gradleFilePath,
|
gradleFilePath,
|
||||||
options,
|
options,
|
||||||
context
|
context,
|
||||||
|
gradleProjectRootToTestFilesMap[projectRoot]
|
||||||
);
|
);
|
||||||
const project = targetsCache[hash];
|
const project = targetsCache[hash];
|
||||||
if (!project) {
|
if (!project) {
|
||||||
@ -133,16 +156,18 @@ export const createNodes: CreateNodes<GradlePluginOptions> = [
|
|||||||
);
|
);
|
||||||
await populateGradleReport(context.workspaceRoot);
|
await populateGradleReport(context.workspaceRoot);
|
||||||
const gradleReport = getCurrentGradleReport();
|
const gradleReport = getCurrentGradleReport();
|
||||||
const internalCreateNodes = makeCreateNodes(gradleReport, {});
|
const internalCreateNodes =
|
||||||
|
makeCreateNodesForGradleConfigFile(gradleReport);
|
||||||
return await internalCreateNodes(configFile, options, context);
|
return await internalCreateNodes(configFile, options, context);
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function createGradleProject(
|
async function createGradleProject(
|
||||||
gradleReport: GradleReport,
|
gradleReport: GradleReport,
|
||||||
gradleFilePath: string,
|
gradleFilePath: string,
|
||||||
options: GradlePluginOptions | undefined,
|
options: GradlePluginOptions | undefined,
|
||||||
context: CreateNodesContext
|
context: CreateNodesContext,
|
||||||
|
testFiles = []
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
@ -177,12 +202,14 @@ function createGradleProject(
|
|||||||
string
|
string
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const { targets, targetGroups } = createGradleTargets(
|
const { targets, targetGroups } = await createGradleTargets(
|
||||||
tasks,
|
tasks,
|
||||||
options,
|
options,
|
||||||
context,
|
context,
|
||||||
outputDirs,
|
outputDirs,
|
||||||
gradleProject
|
gradleProject,
|
||||||
|
gradleFilePath,
|
||||||
|
testFiles
|
||||||
);
|
);
|
||||||
const project = {
|
const project = {
|
||||||
name: projectName,
|
name: projectName,
|
||||||
@ -200,16 +227,18 @@ function createGradleProject(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGradleTargets(
|
async function createGradleTargets(
|
||||||
tasks: GradleTask[],
|
tasks: GradleTask[],
|
||||||
options: GradlePluginOptions | undefined,
|
options: GradlePluginOptions | undefined,
|
||||||
context: CreateNodesContext,
|
context: CreateNodesContext,
|
||||||
outputDirs: Map<string, string>,
|
outputDirs: Map<string, string>,
|
||||||
gradleProject: string
|
gradleProject: string,
|
||||||
): {
|
gradleFilePath: string,
|
||||||
|
testFiles: string[] = []
|
||||||
|
): Promise<{
|
||||||
targetGroups: Record<string, string[]>;
|
targetGroups: Record<string, string[]>;
|
||||||
targets: Record<string, TargetConfiguration>;
|
targets: Record<string, TargetConfiguration>;
|
||||||
} {
|
}> {
|
||||||
const inputsMap = createInputsMap(context);
|
const inputsMap = createInputsMap(context);
|
||||||
|
|
||||||
const targets: Record<string, TargetConfiguration> = {};
|
const targets: Record<string, TargetConfiguration> = {};
|
||||||
@ -217,28 +246,43 @@ function createGradleTargets(
|
|||||||
for (const task of tasks) {
|
for (const task of tasks) {
|
||||||
const targetName = options?.[`${task.name}TargetName`] ?? task.name;
|
const targetName = options?.[`${task.name}TargetName`] ?? task.name;
|
||||||
|
|
||||||
const outputs = outputDirs.get(task.name);
|
let outputs = [outputDirs.get(task.name)].filter(Boolean);
|
||||||
|
if (task.name === 'test') {
|
||||||
|
outputs = [
|
||||||
|
outputDirs.get('testReport'),
|
||||||
|
outputDirs.get('testResults'),
|
||||||
|
].filter(Boolean);
|
||||||
|
getTestCiTargets(
|
||||||
|
testFiles,
|
||||||
|
gradleProject,
|
||||||
|
targetName,
|
||||||
|
options.ciTargetName,
|
||||||
|
inputsMap['test'],
|
||||||
|
outputs,
|
||||||
|
task.type,
|
||||||
|
targets,
|
||||||
|
targetGroups
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}${
|
||||||
|
task.name
|
||||||
|
}`;
|
||||||
targets[targetName] = {
|
targets[targetName] = {
|
||||||
command: `${getGradleExecFile()} ${
|
command: `${getGradleExecFile()} ${taskCommandToRun}`,
|
||||||
gradleProject ? gradleProject + ':' : ''
|
|
||||||
}${task.name}`,
|
|
||||||
cache: cacheableTaskType.has(task.type),
|
cache: cacheableTaskType.has(task.type),
|
||||||
inputs: inputsMap[task.name],
|
inputs: inputsMap[task.name],
|
||||||
dependsOn: dependsOnMap[task.name],
|
dependsOn: dependsOnMap[task.name],
|
||||||
metadata: {
|
metadata: {
|
||||||
technologies: ['gradle'],
|
technologies: ['gradle'],
|
||||||
},
|
},
|
||||||
|
...(outputs && outputs.length ? { outputs } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (outputs) {
|
|
||||||
targets[targetName].outputs = [outputs];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetGroups[task.type]) {
|
if (!targetGroups[task.type]) {
|
||||||
targetGroups[task.type] = [];
|
targetGroups[task.type] = [];
|
||||||
}
|
}
|
||||||
targetGroups[task.type].push(task.name);
|
targetGroups[task.type].push(targetName);
|
||||||
}
|
}
|
||||||
return { targetGroups, targets };
|
return { targetGroups, targets };
|
||||||
}
|
}
|
||||||
@ -257,3 +301,103 @@ function createInputsMap(
|
|||||||
: ['default', '^default'],
|
: ['default', '^default'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTestCiTargets(
|
||||||
|
testFiles: string[],
|
||||||
|
gradleProject: string,
|
||||||
|
testTargetName: string,
|
||||||
|
ciTargetName: string,
|
||||||
|
inputs: TargetConfiguration['inputs'],
|
||||||
|
outputs: string[],
|
||||||
|
targetGroupName: string,
|
||||||
|
targets: Record<string, TargetConfiguration>,
|
||||||
|
targetGroups: Record<string, string[]>
|
||||||
|
): void {
|
||||||
|
if (!testFiles || testFiles.length === 0 || !ciTargetName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}test`;
|
||||||
|
|
||||||
|
if (!targetGroups[targetGroupName]) {
|
||||||
|
targetGroups[targetGroupName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const dependsOn: TargetConfiguration['dependsOn'] = [];
|
||||||
|
testFiles.forEach((testFile) => {
|
||||||
|
const testName = basename(testFile).split('.')[0];
|
||||||
|
const targetName = ciTargetName + '--' + testName;
|
||||||
|
|
||||||
|
targets[targetName] = {
|
||||||
|
command: `${getGradleExecFile()} ${taskCommandToRun} --tests ${testName}`,
|
||||||
|
cache: true,
|
||||||
|
inputs,
|
||||||
|
dependsOn: dependsOnMap['test'],
|
||||||
|
metadata: {
|
||||||
|
technologies: ['gradle'],
|
||||||
|
description: `Runs Gradle test ${testFile} in CI`,
|
||||||
|
},
|
||||||
|
...(outputs && outputs.length > 0 ? { outputs } : {}),
|
||||||
|
};
|
||||||
|
targetGroups[targetGroupName].push(targetName);
|
||||||
|
dependsOn.push({
|
||||||
|
target: targetName,
|
||||||
|
projects: 'self',
|
||||||
|
params: 'forward',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
targets[ciTargetName] = {
|
||||||
|
executor: 'nx:noop',
|
||||||
|
cache: true,
|
||||||
|
inputs,
|
||||||
|
dependsOn: dependsOn,
|
||||||
|
...(outputs && outputs.length > 0 ? { outputs } : {}),
|
||||||
|
metadata: {
|
||||||
|
technologies: ['gradle'],
|
||||||
|
description: 'Runs Gradle Tests in CI',
|
||||||
|
nonAtomizedTarget: testTargetName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
targetGroups[targetGroupName].push(ciTargetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitConfigFiles(files: readonly string[]): {
|
||||||
|
configFiles: string[];
|
||||||
|
testFiles: string[];
|
||||||
|
projectRoots: string[];
|
||||||
|
} {
|
||||||
|
const configFiles = [];
|
||||||
|
const testFiles = [];
|
||||||
|
const projectRoots = new Set<string>();
|
||||||
|
files.forEach((file) => {
|
||||||
|
if (GRADLE_BUILD_FILES.has(basename(file))) {
|
||||||
|
configFiles.push(file);
|
||||||
|
projectRoots.add(dirname(file));
|
||||||
|
} else {
|
||||||
|
testFiles.push(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { configFiles, testFiles, projectRoots: Array.from(projectRoots) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGradleProjectRootToTestFilesMap(
|
||||||
|
testFiles: string[],
|
||||||
|
projectRoots: string[]
|
||||||
|
): Record<string, string[]> | undefined {
|
||||||
|
if (testFiles.length === 0 || projectRoots.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const roots = new Map(projectRoots.map((root) => [root, root]));
|
||||||
|
const testFilesToGradleProjectMap: Record<string, string[]> = {};
|
||||||
|
testFiles.forEach((testFile) => {
|
||||||
|
const projectRoot = findProjectForPath(testFile, roots);
|
||||||
|
if (projectRoot) {
|
||||||
|
if (!testFilesToGradleProjectMap[projectRoot]) {
|
||||||
|
testFilesToGradleProjectMap[projectRoot] = [];
|
||||||
|
}
|
||||||
|
testFilesToGradleProjectMap[projectRoot].push(testFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return testFilesToGradleProjectMap;
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
normalizePath,
|
normalizePath,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
|
import { combineGlobPatterns } from 'nx/src/utils/globs';
|
||||||
|
|
||||||
import { execGradleAsync } from './exec-gradle';
|
import { execGradleAsync } from './exec-gradle';
|
||||||
import { hashWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
import { hashWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||||
@ -30,7 +31,20 @@ export interface GradleReport {
|
|||||||
let gradleReportCache: GradleReport;
|
let gradleReportCache: GradleReport;
|
||||||
let gradleCurrentConfigHash: string;
|
let gradleCurrentConfigHash: string;
|
||||||
|
|
||||||
export const gradleConfigGlob = '**/build.{gradle.kts,gradle}';
|
export const GRADLE_BUILD_FILES = new Set(['build.gradle', 'build.gradle.kts']);
|
||||||
|
export const GRADLE_TEST_FILES = [
|
||||||
|
'**/src/test/java/**/*.java',
|
||||||
|
'**/src/test/kotlin/**/*.kt',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const gradleConfigGlob = combineGlobPatterns(
|
||||||
|
...Array.from(GRADLE_BUILD_FILES).map((file) => `**/${file}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const gradleConfigAndTestGlob = combineGlobPatterns(
|
||||||
|
...Array.from(GRADLE_BUILD_FILES).map((file) => `**/${file}`),
|
||||||
|
...GRADLE_TEST_FILES
|
||||||
|
);
|
||||||
|
|
||||||
export function getCurrentGradleReport() {
|
export function getCurrentGradleReport() {
|
||||||
if (!gradleReportCache) {
|
if (!gradleReportCache) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user