@@ -28374,20 +28374,25 @@ async function run() {
2837428374 }
2837528375
2837628376 // ── build.gradle / build.gradle.kts ────────────────────────────────────
28377- // Only the project version declaration is updated (version = '...' / version = "...").
28378- // Dependency versions are managed exclusively via gradle.properties in Spring Cloud .
28377+ // The project version declaration, plus any `{prefix}Version` properties declared in an
28378+ // ext block rather than in gradle.properties.
2837928379 const buildGradleFiles = [
2838028380 ...findFiles(directory, 'build.gradle'),
2838128381 ...findFiles(directory, 'build.gradle.kts'),
2838228382 ];
2838328383 if (buildGradleFiles.length > 0) {
2838428384 core.info(`Found ${buildGradleFiles.length} build.gradle file(s)`);
2838528385 for (const file of buildGradleFiles) {
28386- const { changed } = updateBuildGradleVersion(file, projectVersion);
28386+ const { changed, updatedProperties } = updateBuildGradleVersion(
28387+ file,
28388+ projectVersion,
28389+ versions
28390+ );
2838728391 if (changed) {
28388- core.info(`Updated ${path.relative(directory, file)}: version`);
28392+ core.info(`Updated ${path.relative(directory, file)}: ` +
28393+ ['version', ...updatedProperties].join(', '));
2838928394 } else {
28390- core.info(`No changes to ${path.relative(directory, file)}: version `);
28395+ core.info(`No changes to ${path.relative(directory, file)}`);
2839128396 }
2839228397 }
2839328398 }
@@ -28774,27 +28779,29 @@ function updateGradlePropertiesContent(content, projectVersion, versions) {
2877428779// ── build.gradle / build.gradle.kts ───────────────────────────────────────
2877528780
2877628781/**
28777- * Updates the project version declaration in a build.gradle or build.gradle.kts file.
28778- * Handles both single-quoted and double-quoted versions:
28782+ * Updates the project version declaration and any version properties in a build.gradle
28783+ * or build.gradle.kts file. Handles both single-quoted and double-quoted versions:
2877928784 * version = '4.1.0'
28780- * version = "4.1.0"
28781- *
28782- * Only the project-level version line is updated; dependency version properties
28783- * are managed via gradle.properties in Spring Cloud projects.
28785+ * springCloudFunctionVersion = "4.1.0"
2878428786 *
2878528787 * Exported for unit testing.
2878628788 *
2878728789 * @param {string} filePath
2878828790 * @param {string} projectVersion
28791+ * @param {Record<string, string>} versions
2878928792 */
28790- function updateBuildGradleVersion(filePath, projectVersion) {
28793+ function updateBuildGradleVersion(filePath, projectVersion, versions ) {
2879128794 const content = fs.readFileSync(filePath, 'utf-8');
28792- const { updated } = updateBuildGradleContent(content, projectVersion);
28795+ const { updated, updatedProperties } = updateBuildGradleContent(
28796+ content,
28797+ projectVersion,
28798+ versions
28799+ );
2879328800 const changed = updated !== content;
2879428801 if (changed) {
2879528802 fs.writeFileSync(filePath, updated, 'utf-8');
2879628803 }
28797- return { changed };
28804+ return { changed, updatedProperties };
2879828805}
2879928806
2880028807/**
@@ -28803,14 +28810,64 @@ function updateBuildGradleVersion(filePath, projectVersion) {
2880328810 *
2880428811 * Exported for unit testing.
2880528812 */
28806- function updateBuildGradleContent(content, projectVersion) {
28813+ function updateBuildGradleContent(content, projectVersion, versions = {} ) {
2880728814 // Match: version = '...' or version = "..." at the start of a line (with optional spaces)
28808- const updated = content.replace(
28815+ const withProjectVersion = content.replace(
2880928816 /^(version\s*=\s*)(['"])([^'"]+)(['"])/m,
2881028817 (_, prefix, openQuote, _oldVersion, closeQuote) =>
2881128818 `${prefix}${openQuote}${projectVersion}${closeQuote}`
2881228819 );
28813- return { updated };
28820+
28821+ // `{prefix}Version` assignments, resolved exactly as they are in gradle.properties:
28822+ // camelCase prefix -> kebab-case project name -> the train's version for it. These live
28823+ // in an `ext { }` or `buildscript { ext { } }` block rather than at the start of a line,
28824+ // so leading whitespace is part of the match and is preserved.
28825+ //
28826+ // Spring Cloud projects mostly declare these in gradle.properties, which is why this
28827+ // file only ever rewrote the project version. spring-cloud-function's Gradle samples
28828+ // declare them here instead, so a release left them at whatever they had been pinned at.
28829+ //
28830+ // A key that resolves to no project is left alone, which is what keeps `javaVersion` and
28831+ // similar build settings out of it.
28832+ const updatedProperties = [];
28833+ const updated = withProjectVersion.split('\n').map((line) => {
28834+ const match = line.match(
28835+ /^(\s*)([a-zA-Z][a-zA-Z0-9]*Version)(\s*=\s*)(['"])([^'"]+)(['"])(.*)$/
28836+ );
28837+ if (!match) return line;
28838+
28839+ const [, indent, key, separator, openQuote, currentValue, closeQuote, trailing] = match;
28840+ const projectName = camelToKebab(key.slice(0, -'Version'.length));
28841+ const targetVersion = versions[projectName];
28842+ if (!targetVersion || currentValue === targetVersion) return line;
28843+
28844+ updatedProperties.push(`${key}: ${targetVersion}`);
28845+ return `${indent}${key}${separator}${openQuote}${targetVersion}${closeQuote}${trailing}`;
28846+ }).join('\n');
28847+
28848+ // Inline dependency coordinates - "group:artifact:version" - for artifacts this train
28849+ // releases. Most Spring Cloud builds express these through a property or let the BOM
28850+ // manage them, which is why this file never needed it; spring-cloud-function's Azure
28851+ // sample pins one directly.
28852+ //
28853+ // Only org.springframework.* groups, so a third-party artifact that happens to share a
28854+ // prefix is never touched, and never when the version is an interpolation - rewriting
28855+ // "...:${springCloudFunctionVersion}" would replace the reference with a literal and
28856+ // break the very indirection the build is using.
28857+ const COORDINATE = /(['"])(org\.springframework\.[a-z0-9.]+):([A-Za-z0-9_.-]+):([^'"]+)\1/g;
28858+ const withCoordinates = updated.replace(
28859+ COORDINATE,
28860+ (whole, quote, groupId, artifactId, version) => {
28861+ if (version.includes('$')) return whole;
28862+ const projectName = projectForArtifact(artifactId, versions);
28863+ const targetVersion = projectName && versions[projectName];
28864+ if (!targetVersion || version === targetVersion) return whole;
28865+ updatedProperties.push(`${artifactId}: ${targetVersion}`);
28866+ return `${quote}${groupId}:${artifactId}:${targetVersion}${quote}`;
28867+ }
28868+ );
28869+
28870+ return { updated: withCoordinates, updatedProperties };
2881428871}
2881528872
2881628873// ── Utilities ──────────────────────────────────────────────────────────────
@@ -28861,6 +28918,31 @@ function artifactIdToProjectName(artifactId) {
2886128918 .replace(/-parent$/, '');
2886228919}
2886328920
28921+ /**
28922+ * Resolves a Maven artifactId to the project in `versions` that releases it: an exact
28923+ * match, or the longest project name the artifactId extends on a `-` boundary.
28924+ *
28925+ * Spring Cloud publishes a project's modules at the project's own version, so
28926+ * spring-cloud-function-adapter-azure ships with spring-cloud-function and
28927+ * spring-cloud-config-server with spring-cloud-config. The boundary matters:
28928+ * spring-cloud-configuration would not resolve to spring-cloud-config.
28929+ *
28930+ * Longest wins so a project whose name extends another still resolves to itself.
28931+ * Returns null when nothing matches, which leaves the coordinate alone.
28932+ *
28933+ * Exported for unit testing.
28934+ */
28935+ function projectForArtifact(artifactId, versions) {
28936+ if (Object.prototype.hasOwnProperty.call(versions, artifactId)) return artifactId;
28937+ let best = null;
28938+ for (const name of Object.keys(versions)) {
28939+ if (artifactId.startsWith(`${name}-`) && (best === null || name.length > best.length)) {
28940+ best = name;
28941+ }
28942+ }
28943+ return best;
28944+ }
28945+
2886428946/**
2886528947 * Returns true when a child pom's parent is part of this project
2886628948 * (i.e. not an external parent like spring-boot-starter-parent or
@@ -28932,6 +29014,7 @@ module.exports = {
2893229014 findFiles,
2893329015 camelToKebab,
2893429016 artifactIdToProjectName,
29017+ projectForArtifact,
2893529018 isChildOfRoot,
2893629019};
2893729020
0 commit comments