@nx/gradle 23.2.0-beta.10 → 23.2.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,9 +7,9 @@
7
7
 
8
8
  <div style="text-align: center;">
9
9
 
10
- [![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)]()
10
+ [![License](https://img.shields.io/npm/l/@nx/workspace.svg?style=flat-square)](<>)
11
11
  [![NPM Version](https://badge.fury.io/js/nx.svg)](https://www.npmjs.com/package/nx)
12
- [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)]()
12
+ [![Semantic Release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](<>)
13
13
  [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
14
14
  [![Join us on the Official Nx Discord Server](https://img.shields.io/discord/1143497901675401286?label=discord)](https://go.nx.dev/community)
15
15
  [![Nx Sandboxing](https://staging.nx.app/workspaces/62d013ea0852fe0a2df74438/sandbox-badge.svg)](https://nx.dev/docs/features/ci-features/sandboxing)
@@ -0,0 +1,2 @@
1
+ import { Tree } from '@nx/devkit';
2
+ export default function update(tree: Tree): Promise<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = update;
4
+ const devkit_1 = require("@nx/devkit");
5
+ const has_gradle_plugin_1 = require("../../utils/has-gradle-plugin");
6
+ const gradle_project_graph_plugin_utils_1 = require("../../generators/init/gradle-project-graph-plugin-utils");
7
+ const version_catalog_ast_utils_1 = require("../../utils/version-catalog-ast-utils");
8
+ /* Change the plugin version to 0.1.25
9
+ */
10
+ async function update(tree) {
11
+ const nxJson = (0, devkit_1.readNxJson)(tree);
12
+ if (!nxJson) {
13
+ return;
14
+ }
15
+ if (!(0, has_gradle_plugin_1.hasGradlePlugin)(tree)) {
16
+ return;
17
+ }
18
+ const gradlePluginVersionToUpdate = '0.1.25';
19
+ // Update version in version catalogs using AST-based approach to preserve formatting
20
+ await (0, version_catalog_ast_utils_1.updateNxPluginVersionInCatalogsAst)(tree, gradlePluginVersionToUpdate);
21
+ // Then update in build.gradle(.kts) files
22
+ await (0, gradle_project_graph_plugin_utils_1.addNxProjectGraphPlugin)(tree, gradlePluginVersionToUpdate);
23
+ }
@@ -0,0 +1,21 @@
1
+ #### Change dev.nx.gradle.project-graph to version 0.1.25
2
+
3
+ Change dev.nx.gradle.project-graph to version 0.1.25 in build file
4
+
5
+ #### Sample Code Changes
6
+
7
+ ##### Before
8
+
9
+ ```text title="build.gradle"
10
+ plugins {
11
+ id "dev.nx.gradle.project-graph" version "0.1.24"
12
+ }
13
+ ```
14
+
15
+ ##### After
16
+
17
+ ```text title="build.gradle"
18
+ plugins {
19
+ id "dev.nx.gradle.project-graph" version "0.1.25"
20
+ }
21
+ ```
@@ -16,7 +16,8 @@ const createDependencies = async (options, context) => {
16
16
  dependenciesFromReport.forEach((dependencyFromPlugin) => {
17
17
  try {
18
18
  // Report paths are workspace-relative with `/` separators
19
- const sourceProjectName = Object.values(context.projects).find((project) => dependencyFromPlugin.source === project.root)?.name ?? dependencyFromPlugin.source;
19
+ const sourceProject = Object.values(context.projects).find((project) => dependencyFromPlugin.source === project.root);
20
+ const sourceProjectName = sourceProject?.name ?? dependencyFromPlugin.source;
20
21
  const targetProjectName = Object.values(context.projects).find((project) => dependencyFromPlugin.target === project.root)?.name ?? dependencyFromPlugin.target;
21
22
  const sourceFile = dependencyFromPlugin.sourceFile;
22
23
  if (!sourceProjectName ||
@@ -24,12 +25,21 @@ const createDependencies = async (options, context) => {
24
25
  !(0, node_fs_1.existsSync)((0, node_path_1.join)(devkit_1.workspaceRoot, sourceFile))) {
25
26
  return;
26
27
  }
27
- const dependency = {
28
- source: sourceProjectName,
29
- target: targetProjectName,
30
- type: devkit_1.DependencyType.static,
31
- sourceFile,
32
- };
28
+ // An ancestor-configured project's build file lies outside it, and Nx rejects a foreign
29
+ // sourceFile — record implicit rather than drop.
30
+ const ownsSourceFile = !!sourceProject && (0, node_path_1.dirname)(sourceFile) === sourceProject.root;
31
+ const dependency = ownsSourceFile
32
+ ? {
33
+ source: sourceProjectName,
34
+ target: targetProjectName,
35
+ type: devkit_1.DependencyType.static,
36
+ sourceFile,
37
+ }
38
+ : {
39
+ source: sourceProjectName,
40
+ target: targetProjectName,
41
+ type: devkit_1.DependencyType.implicit,
42
+ };
33
43
  (0, devkit_1.validateDependency)(dependency, context);
34
44
  dependencies.push(dependency);
35
45
  }
@@ -67,68 +67,54 @@ function extractNxConfigOnly(project) {
67
67
  exports.createNodes = [
68
68
  split_config_files_1.gradleConfigAndTestGlob,
69
69
  async (files, options, context) => {
70
- const { buildFiles: buildFilesFromSplitConfigFiles, gradlewFiles } = (0, split_config_files_1.splitConfigFiles)(files);
71
- const optionsHash = (0, internal_1.hashObject)(options);
72
- const cachePath = (0, node_path_1.join)(internal_1.workspaceDataDirectory, `gradle-${optionsHash}.hash`);
73
- const pluginCache = new internal_1.PluginCache(cachePath);
70
+ const { gradlewFiles } = (0, split_config_files_1.splitConfigFiles)(files);
74
71
  await (0, get_project_graph_from_gradle_plugin_1.populateProjectGraph)(context.workspaceRoot, gradlewFiles.map((f) => (0, node_path_1.join)(context.workspaceRoot, f)), options);
75
72
  const report = (0, get_project_graph_from_gradle_plugin_1.getCurrentProjectGraphReport)();
76
- const { nodes, externalNodes = {}, buildFiles = [] } = report;
77
- // Combine buildFilesFromSplitConfigFiles and buildFiles, making each value distinct
78
- const allBuildFiles = Array.from(new Set([...buildFilesFromSplitConfigFiles, ...buildFiles]));
79
- try {
80
- const results = [];
81
- const normalizedOptions = (0, gradle_plugin_options_1.normalizeOptions)(options);
82
- const buildFileProjectRoots = allBuildFiles.map((f) => (0, node_path_1.dirname)(f));
83
- const buildFileHashes = await (0, internal_1.calculateHashesForCreateNodes)(buildFileProjectRoots, normalizedOptions ?? {}, context);
84
- for (let i = 0; i < allBuildFiles.length; i++) {
85
- const gradleFilePath = allBuildFiles[i];
86
- const projectRoot = buildFileProjectRoots[i];
87
- const hash = buildFileHashes[i];
88
- // Get project from cache or nodes (report keys are workspace-relative
89
- // with `/` separators)
90
- if (!pluginCache.has(hash)) {
91
- const nodeProject = nodes[(0, devkit_1.normalizePath)(projectRoot)];
92
- if (nodeProject) {
93
- pluginCache.set(hash, nodeProject);
94
- }
95
- }
96
- const project = pluginCache.get(hash);
97
- if (!project) {
98
- continue;
99
- }
100
- const normalizedProjectRoot = (0, devkit_1.normalizePath)(projectRoot);
101
- // Result 1: Gradle-detected configuration (without nxConfig)
102
- const gradleConfig = stripNxConfig(project);
103
- gradleConfig.root = normalizedProjectRoot;
73
+ const { nodes, externalNodes = {}, buildFileByProjectRoot = {} } = report;
74
+ const results = [];
75
+ // Roots outside the workspace stay absolute in the report and cannot become Nx projects.
76
+ const projectRoots = Object.keys(nodes)
77
+ .map((root) => (0, devkit_1.normalizePath)(root))
78
+ .filter((root) => !(0, node_path_1.isAbsolute)(root) && !root.startsWith('../'));
79
+ for (const normalizedProjectRoot of projectRoots) {
80
+ const gradleFilePath = buildFileByProjectRoot[normalizedProjectRoot];
81
+ if (!gradleFilePath) {
82
+ // No build file anywhere up the ancestry, or an ancestor configures it and the plugin
83
+ // predates effectiveBuildFile.
84
+ devkit_1.logger.verbose(`[@nx/gradle] no build file reported for "${normalizedProjectRoot}"; skipping it. Add a build file (or run @nx/gradle:init), or upgrade dev.nx.gradle.project-graph if projects are missing.`);
85
+ continue;
86
+ }
87
+ const project = nodes[normalizedProjectRoot];
88
+ if (!project) {
89
+ continue;
90
+ }
91
+ // Result 1: Gradle-detected configuration (without nxConfig)
92
+ const gradleConfig = stripNxConfig(project);
93
+ gradleConfig.root = normalizedProjectRoot;
94
+ results.push([
95
+ gradleFilePath,
96
+ {
97
+ projects: {
98
+ [normalizedProjectRoot]: gradleConfig,
99
+ },
100
+ externalNodes: externalNodes,
101
+ },
102
+ ]);
103
+ // Result 2: nxConfig-only configuration (if exists)
104
+ const nxConfigOnly = extractNxConfigOnly(project);
105
+ if (nxConfigOnly) {
106
+ nxConfigOnly.root = normalizedProjectRoot;
104
107
  results.push([
105
108
  gradleFilePath,
106
109
  {
107
110
  projects: {
108
- [normalizedProjectRoot]: gradleConfig,
111
+ [normalizedProjectRoot]: nxConfigOnly,
109
112
  },
110
- externalNodes: externalNodes,
111
113
  },
112
114
  ]);
113
- // Result 2: nxConfig-only configuration (if exists)
114
- const nxConfigOnly = extractNxConfigOnly(project);
115
- if (nxConfigOnly) {
116
- nxConfigOnly.root = normalizedProjectRoot;
117
- results.push([
118
- gradleFilePath,
119
- {
120
- projects: {
121
- [normalizedProjectRoot]: nxConfigOnly,
122
- },
123
- },
124
- ]);
125
- }
126
115
  }
127
- return results;
128
- }
129
- finally {
130
- pluginCache.writeToDisk();
131
116
  }
117
+ return results;
132
118
  },
133
119
  ];
134
120
  /**
@@ -7,6 +7,11 @@ export interface ProjectGraphReport {
7
7
  dependencies: Array<StaticDependency>;
8
8
  externalNodes?: Record<string, ProjectGraphExternalNode>;
9
9
  buildFiles?: string[];
10
+ /**
11
+ * Project root -> the build file that configures it, derived here (not plugin-reported). Flat
12
+ * `buildFiles` loses the pairing; no entry means the report named no build file.
13
+ */
14
+ buildFileByProjectRoot?: Record<string, string>;
10
15
  }
11
16
  export interface ProjectGraphReportCache extends ProjectGraphReport {
12
17
  hash: string;
@@ -118,6 +118,7 @@ function processNxProjectGraph(projectGraphLines) {
118
118
  externalNodes: {},
119
119
  };
120
120
  const allBuildFiles = new Set();
121
+ const buildFileByProjectRoot = {};
121
122
  while (index < projectGraphLines.length) {
122
123
  const line = projectGraphLines[index].trim();
123
124
  if (line.startsWith('> Task ') && line.endsWith(':nxProjectGraph')) {
@@ -149,11 +150,20 @@ function processNxProjectGraph(projectGraphLines) {
149
150
  }
150
151
  if (projectGraphReportJson.buildFiles) {
151
152
  projectGraphReportJson.buildFiles.forEach((buildFile) => allBuildFiles.add(buildFile));
153
+ // One report describes one project, so its single build file is that project's.
154
+ const [buildFile] = projectGraphReportJson.buildFiles;
155
+ if (buildFile) {
156
+ for (const projectRoot of Object.keys(projectGraphReportJson.nodes ?? {})) {
157
+ buildFileByProjectRoot[projectRoot] = buildFile;
158
+ }
159
+ }
152
160
  }
153
161
  }
154
162
  index++;
155
163
  }
156
164
  // Convert Set to array for the final result
157
165
  projectGraphReportForAllProjects.buildFiles = Array.from(allBuildFiles);
166
+ projectGraphReportForAllProjects.buildFileByProjectRoot =
167
+ buildFileByProjectRoot;
158
168
  return projectGraphReportForAllProjects;
159
169
  }
@@ -7,6 +7,17 @@ const devkit_1 = require("@nx/devkit");
7
7
  const exec_gradle_1 = require("../../utils/exec-gradle");
8
8
  const internal_1 = require("@nx/devkit/internal");
9
9
  const DEFAULT_GRAPH_TIMEOUT_SECONDS = (0, internal_1.isCI)() ? 600 : 120;
10
+ // setTimeout silently clamps a delay past the 32-bit signed max to 1ms, which
11
+ // would abort immediately — the opposite of what a large value asks for.
12
+ const MAX_TIMEOUT_MS = 2 ** 31 - 1;
13
+ const GRADLE_LOCK_TIMEOUT_MESSAGE = 'Timeout waiting to lock';
14
+ // Gradle gives up on a contended lock (e.g. buildLogic.lock held by another
15
+ // build in the same project) after ~60s, so at the 120s local default one wait
16
+ // consumes the budget and the deadline guard below refuses a retry; retries
17
+ // engage on CI (600s) or a raised NX_GRADLE_PROJECT_GRAPH_TIMEOUT. Do not relax
18
+ // that `>=` to `>` — it refuses the borderline attempt rather than letting the
19
+ // abort fire mid-retry and replace the lock message with a generic timeout.
20
+ const MAX_LOCK_TIMEOUT_RETRIES = 2;
10
21
  let currentAbortController;
11
22
  /**
12
23
  * Cancel any in-flight Gradle project graph process.
@@ -23,35 +34,65 @@ function getGraphTimeoutMs() {
23
34
  if (envTimeout) {
24
35
  const parsed = Number(envTimeout);
25
36
  if (!Number.isNaN(parsed) && parsed > 0) {
26
- return parsed * 1000;
37
+ return Math.min(parsed * 1000, MAX_TIMEOUT_MS);
27
38
  }
28
39
  }
29
40
  return DEFAULT_GRAPH_TIMEOUT_SECONDS * 1000;
30
41
  }
42
+ function isGradleLockTimeout(e) {
43
+ return String(e).includes(GRADLE_LOCK_TIMEOUT_MESSAGE);
44
+ }
45
+ async function execGradleWithLockRetry(gradlewFile, args, signal, deadline) {
46
+ for (let attempt = 0;; attempt++) {
47
+ const attemptStart = Date.now();
48
+ try {
49
+ return await (0, exec_gradle_1.execGradleAsync)(gradlewFile, args, { signal });
50
+ }
51
+ catch (e) {
52
+ if (signal.aborted ||
53
+ attempt >= MAX_LOCK_TIMEOUT_RETRIES ||
54
+ !isGradleLockTimeout(e)) {
55
+ throw e;
56
+ }
57
+ // Retry only if another full lock wait still fits in the graph timeout.
58
+ // Otherwise the timeout aborts mid-attempt and its generic message
59
+ // replaces the lock guidance the user actually needs.
60
+ const lockWaitMs = Date.now() - attemptStart;
61
+ if (Date.now() + lockWaitMs >= deadline) {
62
+ throw e;
63
+ }
64
+ devkit_1.output.warn({
65
+ title: `Gradle lock is held by another build; retrying 'nxProjectGraph' (${attempt + 1}/${MAX_LOCK_TIMEOUT_RETRIES})`,
66
+ });
67
+ }
68
+ }
69
+ }
31
70
  async function getNxProjectGraphLines(gradlewFile, gradleConfigHash, gradlePluginOptions) {
32
71
  let nxProjectGraphBuffer;
33
72
  const gradlePluginOptionsArgs = Object.entries(gradlePluginOptions ?? {})?.map(([key, value]) => `-P${key}=${value}`) ?? [];
34
73
  const timeoutMs = getGraphTimeoutMs();
35
74
  const timeoutSeconds = timeoutMs / 1000;
75
+ const deadline = Date.now() + timeoutMs;
36
76
  // Cancel any in-flight Gradle process from a previous call, then create a fresh controller.
37
77
  cancelPendingProjectGraphRequest();
38
78
  const controller = new AbortController();
39
79
  currentAbortController = controller;
40
80
  const signal = controller.signal;
41
81
  const timer = setTimeout(() => controller.abort(), timeoutMs);
82
+ const args = [
83
+ 'nxProjectGraph',
84
+ `-Phash=${gradleConfigHash}`,
85
+ '--no-configuration-cache', // disable configuration cache
86
+ '--parallel', // add parallel to improve performance
87
+ '--build-cache', // enable build cache
88
+ '--warning-mode',
89
+ 'none',
90
+ ...gradlePluginOptionsArgs,
91
+ `-PworkspaceRoot=${devkit_1.workspaceRoot}`,
92
+ process.env.NX_GRADLE_VERBOSE_LOGGING ? '--info' : '',
93
+ ];
42
94
  try {
43
- nxProjectGraphBuffer = await (0, exec_gradle_1.execGradleAsync)(gradlewFile, [
44
- 'nxProjectGraph',
45
- `-Phash=${gradleConfigHash}`,
46
- '--no-configuration-cache', // disable configuration cache
47
- '--parallel', // add parallel to improve performance
48
- '--build-cache', // enable build cache
49
- '--warning-mode',
50
- 'none',
51
- ...gradlePluginOptionsArgs,
52
- `-PworkspaceRoot=${devkit_1.workspaceRoot}`,
53
- process.env.NX_GRADLE_VERBOSE_LOGGING ? '--info' : '',
54
- ], { signal });
95
+ nxProjectGraphBuffer = await execGradleWithLockRetry(gradlewFile, args, signal, deadline);
55
96
  }
56
97
  catch (e) {
57
98
  // Cancelled by a newer populateProjectGraph call — let the caller handle it
@@ -77,6 +118,15 @@ async function getNxProjectGraphLines(gradlewFile, gradleConfigHash, gradlePlugi
77
118
  ],
78
119
  ], []);
79
120
  }
121
+ else if (isGradleLockTimeout(e)) {
122
+ throw new devkit_1.AggregateCreateNodesError([
123
+ [
124
+ gradlewFile,
125
+ new Error(`Could not run 'nxProjectGraph' Gradle task because another Gradle build is holding a lock on the project.\n` +
126
+ ` Wait for the other build to finish, or run "gradlew --stop" to stop stale Gradle daemons, then try again.\n\r${e.toString()}`),
127
+ ],
128
+ ], []);
129
+ }
80
130
  else if (e.toString()?.includes(`Task 'nxProjectGraph' not found`)) {
81
131
  throw new devkit_1.AggregateCreateNodesError([
82
132
  [
@@ -42,6 +42,8 @@ function execGradleAsync(gradleBinaryPath, args, execOptions = {}) {
42
42
  env: process.env,
43
43
  ...restOptions,
44
44
  });
45
+ // A plugin worker torn down by `nx reset` would otherwise orphan the build.
46
+ (0, internal_1.killChildOnHostExit)(cp);
45
47
  let stdout = Buffer.from('');
46
48
  // On abort, kill the entire process tree (gradlew spawns java) and settle
47
49
  // immediately — a wedged JVM that outlives the kill signal would otherwise
@@ -1,3 +1,3 @@
1
1
  export declare const nxVersion: any;
2
2
  export declare const gradleProjectGraphPluginName = "dev.nx.gradle.project-graph";
3
- export declare const gradleProjectGraphVersion = "0.1.24";
3
+ export declare const gradleProjectGraphVersion = "0.1.25";
@@ -4,4 +4,4 @@ exports.gradleProjectGraphVersion = exports.gradleProjectGraphPluginName = expor
4
4
  const path_1 = require("path");
5
5
  exports.nxVersion = require((0, path_1.join)('@nx/gradle', 'package.json')).version;
6
6
  exports.gradleProjectGraphPluginName = 'dev.nx.gradle.project-graph';
7
- exports.gradleProjectGraphVersion = '0.1.24';
7
+ exports.gradleProjectGraphVersion = '0.1.25';
package/migrations.json CHANGED
@@ -180,6 +180,12 @@
180
180
  "description": "Change dev.nx.gradle.project-graph to version 0.1.24 in build file",
181
181
  "factory": "./dist/src/migrations/23-1-0/change-plugin-version-0-1-24",
182
182
  "documentation": "./dist/src/migrations/23-1-0/change-plugin-version-0-1-24.md"
183
+ },
184
+ "change-plugin-version-0-1-25": {
185
+ "version": "23.2.0-beta.11",
186
+ "description": "Change dev.nx.gradle.project-graph to version 0.1.25 in build file",
187
+ "implementation": "./dist/src/migrations/23-2-0/change-plugin-version-0-1-25",
188
+ "documentation": "./dist/src/migrations/23-2-0/change-plugin-version-0-1-25.md"
183
189
  }
184
190
  },
185
191
  "packageJsonUpdates": {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nx/gradle",
3
- "version": "23.2.0-beta.10",
3
+ "version": "23.2.0-beta.11",
4
4
  "private": false,
5
5
  "type": "commonjs",
6
6
  "files": [
@@ -77,10 +77,10 @@
77
77
  "dependencies": {
78
78
  "toml-eslint-parser": "^0.10.0",
79
79
  "tslib": "^2.3.0",
80
- "@nx/devkit": "23.2.0-beta.10"
80
+ "@nx/devkit": "23.2.0-beta.11"
81
81
  },
82
82
  "devDependencies": {
83
- "nx": "23.2.0-beta.10"
83
+ "nx": "23.2.0-beta.11"
84
84
  },
85
85
  "publishConfig": {
86
86
  "access": "public"