UNPKG

@nx/gradle

Version:

The Nx Plugin for Gradle allows Gradle tasks to be run through Nx

267 lines (266 loc) • 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createNodes = exports.makeCreateNodesForGradleConfigFile = exports.createNodesV2 = void 0; exports.writeTargetsToCache = writeTargetsToCache; const devkit_1 = require("@nx/devkit"); const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes"); const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const cache_directory_1 = require("nx/src/utils/cache-directory"); const devkit_internals_1 = require("nx/src/devkit-internals"); const get_gradle_report_1 = require("../utils/get-gradle-report"); const file_hasher_1 = require("nx/src/hasher/file-hasher"); const split_config_files_1 = require("../utils/split-config-files"); const exec_gradle_1 = require("../utils/exec-gradle"); const cacheableTaskType = new Set(['Build', 'Verification']); const dependsOnMap = { build: ['^build', 'classes', 'test'], testClasses: ['classes'], test: ['testClasses'], classes: ['^classes'], }; function normalizeOptions(options) { options ??= {}; options.testTargetName ??= 'test'; options.classesTargetName ??= 'classes'; options.buildTargetName ??= 'build'; return options; } function readTargetsCache(cachePath) { return (0, node_fs_1.existsSync)(cachePath) ? (0, devkit_1.readJsonFile)(cachePath) : {}; } function writeTargetsToCache(cachePath, results) { (0, devkit_1.writeJsonFile)(cachePath, results); } exports.createNodesV2 = [ split_config_files_1.gradleConfigAndTestGlob, async (files, options, context) => { const { buildFiles, projectRoots, gradlewFiles, testFiles } = (0, split_config_files_1.splitConfigFiles)(files); const optionsHash = (0, file_hasher_1.hashObject)(options); const cachePath = (0, node_path_1.join)(cache_directory_1.workspaceDataDirectory, `gradle-${optionsHash}.hash`); const targetsCache = readTargetsCache(cachePath); await (0, get_gradle_report_1.populateGradleReport)(context.workspaceRoot, gradlewFiles.map((f) => (0, node_path_1.join)(context.workspaceRoot, f))); const gradleReport = (0, get_gradle_report_1.getCurrentGradleReport)(); const gradleProjectRootToTestFilesMap = getGradleProjectRootToTestFilesMap(testFiles, projectRoots); try { return (0, devkit_1.createNodesFromFiles)((0, exports.makeCreateNodesForGradleConfigFile)(gradleReport, targetsCache, gradleProjectRootToTestFilesMap), buildFiles, options, context); } finally { writeTargetsToCache(cachePath, targetsCache); } }, ]; const makeCreateNodesForGradleConfigFile = (gradleReport, targetsCache = {}, gradleProjectRootToTestFilesMap = {}) => async (gradleFilePath, options, context) => { const projectRoot = (0, node_path_1.dirname)(gradleFilePath); options = normalizeOptions(options); const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options ?? {}, context); targetsCache[hash] ??= await createGradleProject(gradleReport, gradleFilePath, options, context, gradleProjectRootToTestFilesMap[projectRoot]); const project = targetsCache[hash]; if (!project) { return {}; } return { projects: { [projectRoot]: project, }, }; }; exports.makeCreateNodesForGradleConfigFile = makeCreateNodesForGradleConfigFile; /** @deprecated This is replaced with {@link createNodesV2}. Update your plugin to export its own `createNodesV2` function that wraps this one instead. This function will change to the v2 function in Nx 20. */ exports.createNodes = [ split_config_files_1.gradleConfigGlob, async (buildFile, options, context) => { devkit_1.logger.warn('`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.'); const { gradlewFiles } = (0, split_config_files_1.splitConfigFiles)(context.configFiles); await (0, get_gradle_report_1.populateGradleReport)(context.workspaceRoot, gradlewFiles); const gradleReport = (0, get_gradle_report_1.getCurrentGradleReport)(); const internalCreateNodes = (0, exports.makeCreateNodesForGradleConfigFile)(gradleReport); return await internalCreateNodes(buildFile, options, context); }, ]; async function createGradleProject(gradleReport, gradleFilePath, options, context, testFiles = []) { try { const { gradleProjectToTasksTypeMap, gradleProjectToTasksMap, gradleFileToOutputDirsMap, gradleFileToGradleProjectMap, gradleProjectToProjectName, } = gradleReport; const gradleProject = gradleFileToGradleProjectMap.get(gradleFilePath); const projectName = gradleProjectToProjectName.get(gradleProject); if (!projectName) { return; } const tasksTypeMap = gradleProjectToTasksTypeMap.get(gradleProject); const tasksSet = gradleProjectToTasksMap.get(gradleProject); let tasks = []; tasksSet.forEach((taskName) => { tasks.push({ type: tasksTypeMap?.get(taskName), name: taskName, }); }); if (options.includeSubprojectsTasks) { tasksTypeMap.forEach((taskType, taskName) => { if (!tasksSet.has(taskName)) { tasks.push({ type: taskType, name: taskName, }); } }); } const outputDirs = gradleFileToOutputDirsMap.get(gradleFilePath); const { targets, targetGroups } = await createGradleTargets(tasks, options, context, outputDirs, gradleProject, gradleFilePath, testFiles); const project = { name: projectName, projectType: 'application', targets, metadata: { targetGroups, technologies: ['gradle'], }, }; return project; } catch (e) { console.error(e); return undefined; } } async function createGradleTargets(tasks, options, context, outputDirs, gradleProject, gradleBuildFilePath, testFiles = []) { const inputsMap = createInputsMap(context); const gradlewFileDirectory = (0, node_path_1.dirname)((0, exec_gradle_1.findGraldewFile)(gradleBuildFilePath, context.workspaceRoot)); const targets = {}; const targetGroups = {}; for (const task of tasks) { const targetName = options?.[`${task.name}TargetName`] ?? 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, gradlewFileDirectory); } const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}${task.name}`; targets[targetName] = { command: `${(0, exec_gradle_1.getGradleExecFile)()} ${taskCommandToRun}`, options: { cwd: gradlewFileDirectory, }, cache: cacheableTaskType.has(task.type), inputs: inputsMap[task.name], dependsOn: dependsOnMap[task.name], metadata: { technologies: ['gradle'], help: { command: `${(0, exec_gradle_1.getGradleExecFile)()} help --task ${taskCommandToRun}`, example: { options: { args: ['--rerun'], }, }, }, }, ...(outputs && outputs.length ? { outputs } : {}), }; if (task.type) { if (!targetGroups[task.type]) { targetGroups[task.type] = []; } targetGroups[task.type].push(targetName); } } return { targetGroups, targets }; } function createInputsMap(context) { const namedInputs = context.nxJsonConfiguration.namedInputs; return { build: namedInputs?.production ? ['production', '^production'] : ['default', '^default'], test: ['default', namedInputs?.production ? '^production' : '^default'], classes: namedInputs?.production ? ['production', '^production'] : ['default', '^default'], }; } function getTestCiTargets(testFiles, gradleProject, testTargetName, ciTargetName, inputs, outputs, targetGroupName, targets, targetGroups, gradlewFileDirectory) { if (!testFiles || testFiles.length === 0 || !ciTargetName) { return; } const taskCommandToRun = `${gradleProject ? gradleProject + ':' : ''}test`; if (!targetGroups[targetGroupName]) { targetGroups[targetGroupName] = []; } const dependsOn = []; testFiles.forEach((testFile) => { const testName = (0, node_path_1.basename)(testFile).split('.')[0]; const targetName = ciTargetName + '--' + testName; targets[targetName] = { command: `${(0, exec_gradle_1.getGradleExecFile)()} ${taskCommandToRun} --tests ${testName}`, options: { cwd: gradlewFileDirectory, }, cache: true, inputs, dependsOn: dependsOnMap['test'], metadata: { technologies: ['gradle'], description: `Runs Gradle test ${testFile} in CI`, help: { command: `${(0, exec_gradle_1.getGradleExecFile)()} help --task ${taskCommandToRun}`, example: { options: { args: ['--rerun'], }, }, }, }, ...(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, help: { command: `${(0, exec_gradle_1.getGradleExecFile)()} help --task ${taskCommandToRun}`, example: { options: { args: ['--rerun'], }, }, }, }, }; targetGroups[targetGroupName].push(ciTargetName); } function getGradleProjectRootToTestFilesMap(testFiles, projectRoots) { if (testFiles.length === 0 || projectRoots.length === 0) { return; } const roots = new Map(projectRoots.map((root) => [root, root])); const testFilesToGradleProjectMap = {}; testFiles.forEach((testFile) => { const projectRoot = (0, devkit_internals_1.findProjectForPath)(testFile, roots); if (projectRoot) { if (!testFilesToGradleProjectMap[projectRoot]) { testFilesToGradleProjectMap[projectRoot] = []; } testFilesToGradleProjectMap[projectRoot].push(testFile); } }); return testFilesToGradleProjectMap; }