/* * Initscript for injection of Develocity into Gradle builds. * Version: v1.1 */ import org.gradle.util.GradleVersion initscript { // NOTE: there is no mechanism to share code between the initscript{} block and the main script, so some logic is duplicated def isTopLevelBuild = !gradle.parent if (!isTopLevelBuild) { return } def getInputParam = { Gradle gradle, String name -> def ENV_VAR_PREFIX = '' def envVarName = ENV_VAR_PREFIX + name.toUpperCase().replace('.', '_').replace('-', '_') return gradle.startParameter.systemPropertiesArgs[name] ?: System.getProperty(name) ?: System.getenv(envVarName) } def requestedInitScriptName = getInputParam(gradle, 'develocity.injection.init-script-name') def initScriptName = buildscript.sourceFile.name if (requestedInitScriptName != initScriptName) { return } // Plugin loading is only required for Develocity injection. Abort early if not enabled. def develocityInjectionEnabled = Boolean.parseBoolean(getInputParam(gradle, "develocity.injection-enabled")) if (!develocityInjectionEnabled) { return } def pluginRepositoryUrl = getInputParam(gradle, 'gradle.plugin-repository.url') def pluginRepositoryUsername = getInputParam(gradle, 'gradle.plugin-repository.username') def pluginRepositoryPassword = getInputParam(gradle, 'gradle.plugin-repository.password') def develocityPluginVersion = getInputParam(gradle, 'develocity.plugin.version') def ccudPluginVersion = getInputParam(gradle, 'develocity.ccud-plugin.version') def atLeastGradle5 = GradleVersion.current() >= GradleVersion.version('5.0') def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0') if (develocityPluginVersion || ccudPluginVersion && atLeastGradle4) { pluginRepositoryUrl = pluginRepositoryUrl ?: 'https://plugins.gradle.org/m2' logger.lifecycle("Develocity plugins resolution: $pluginRepositoryUrl") repositories { maven { url pluginRepositoryUrl if (pluginRepositoryUsername && pluginRepositoryPassword) { logger.lifecycle("Using credentials for plugin repository") credentials { username(pluginRepositoryUsername) password(pluginRepositoryPassword) } authentication { basic(BasicAuthentication) } } } } } dependencies { if (develocityPluginVersion) { if (atLeastGradle5) { if (GradleVersion.version(develocityPluginVersion) >= GradleVersion.version("3.17")) { classpath "com.gradle:develocity-gradle-plugin:$develocityPluginVersion" } else { classpath "com.gradle:gradle-enterprise-gradle-plugin:$develocityPluginVersion" } } else { classpath "com.gradle:build-scan-plugin:1.16" } } if (ccudPluginVersion && atLeastGradle4) { classpath "com.gradle:common-custom-user-data-gradle-plugin:$ccudPluginVersion" } } } static getInputParam(Gradle gradle, String name) { def ENV_VAR_PREFIX = '' def envVarName = ENV_VAR_PREFIX + name.toUpperCase().replace('.', '_').replace('-', '_') return gradle.startParameter.systemPropertiesArgs[name] ?: System.getProperty(name) ?: System.getenv(envVarName) } def isTopLevelBuild = !gradle.parent if (!isTopLevelBuild) { return } def requestedInitScriptName = getInputParam(gradle, 'develocity.injection.init-script-name') def initScriptName = buildscript.sourceFile.name if (requestedInitScriptName != initScriptName) { logger.quiet("Ignoring init script '${initScriptName}' as requested name '${requestedInitScriptName}' does not match") return } def develocityInjectionEnabled = Boolean.parseBoolean(getInputParam(gradle, "develocity.injection-enabled")) if (develocityInjectionEnabled) { enableDevelocityInjection() } // To enable build-scan capture, a `captureBuildScanLink(String)` method must be added to `BuildScanCollector`. def buildScanCollector = new BuildScanCollector() def buildScanCaptureEnabled = buildScanCollector.metaClass.respondsTo(buildScanCollector, 'captureBuildScanLink', String) if (buildScanCaptureEnabled) { enableBuildScanLinkCapture(buildScanCollector) } void enableDevelocityInjection() { def BUILD_SCAN_PLUGIN_ID = 'com.gradle.build-scan' def BUILD_SCAN_PLUGIN_CLASS = 'com.gradle.scan.plugin.BuildScanPlugin' def GRADLE_ENTERPRISE_PLUGIN_ID = 'com.gradle.enterprise' def GRADLE_ENTERPRISE_PLUGIN_CLASS = 'com.gradle.enterprise.gradleplugin.GradleEnterprisePlugin' def DEVELOCITY_PLUGIN_ID = 'com.gradle.develocity' def DEVELOCITY_PLUGIN_CLASS = 'com.gradle.develocity.agent.gradle.DevelocityPlugin' def CI_AUTO_INJECTION_CUSTOM_VALUE_NAME = 'CI auto injection' def CCUD_PLUGIN_ID = 'com.gradle.common-custom-user-data-gradle-plugin' def CCUD_PLUGIN_CLASS = 'com.gradle.CommonCustomUserDataGradlePlugin' def develocityUrl = getInputParam(gradle, 'develocity.url') def develocityAllowUntrustedServer = Boolean.parseBoolean(getInputParam(gradle, 'develocity.allow-untrusted-server')) def develocityEnforceUrl = Boolean.parseBoolean(getInputParam(gradle, 'develocity.enforce-url')) def buildScanUploadInBackground = Boolean.parseBoolean(getInputParam(gradle, 'develocity.build-scan.upload-in-background')) def develocityCaptureFileFingerprints = getInputParam(gradle, 'develocity.capture-file-fingerprints') ? Boolean.parseBoolean(getInputParam(gradle, 'develocity.capture-file-fingerprints')) : true def develocityPluginVersion = getInputParam(gradle, 'develocity.plugin.version') def ccudPluginVersion = getInputParam(gradle, 'develocity.ccud-plugin.version') def buildScanTermsOfUseUrl = getInputParam(gradle, 'develocity.terms-of-use.url') def buildScanTermsOfUseAgree = getInputParam(gradle, 'develocity.terms-of-use.agree') def ciAutoInjectionCustomValueValue = getInputParam(gradle, 'develocity.auto-injection.custom-value') def atLeastGradle5 = GradleVersion.current() >= GradleVersion.version('5.0') def atLeastGradle4 = GradleVersion.current() >= GradleVersion.version('4.0') def shouldApplyDevelocityPlugin = atLeastGradle5 && develocityPluginVersion && isAtLeast(develocityPluginVersion, '3.17') def dvOrGe = { def dvValue, def geValue -> if (shouldApplyDevelocityPlugin) { return dvValue instanceof Closure ? dvValue() : dvValue } return geValue instanceof Closure ? geValue() : geValue } def printEnforcingDevelocityUrl = { logger.lifecycle("Enforcing Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer") } def printAcceptingGradleTermsOfUse = { logger.lifecycle("Accepting Gradle Terms of Use: $buildScanTermsOfUseUrl") } // finish early if configuration parameters passed in via system properties are not valid/supported if (ccudPluginVersion && isNotAtLeast(ccudPluginVersion, '1.7')) { logger.warn("Common Custom User Data Gradle plugin must be at least 1.7. Configured version is $ccudPluginVersion.") return } // Conditionally apply and configure the Develocity plugin if (GradleVersion.current() < GradleVersion.version('6.0')) { rootProject { buildscript.configurations.getByName("classpath").incoming.afterResolve { ResolvableDependencies incoming -> def resolutionResult = incoming.resolutionResult if (develocityPluginVersion) { def scanPluginComponent = resolutionResult.allComponents.find { it.moduleVersion.with { group == "com.gradle" && ['build-scan-plugin', 'gradle-enterprise-gradle-plugin', 'develocity-gradle-plugin'].contains(name) } } if (!scanPluginComponent) { def pluginClass = dvOrGe(DEVELOCITY_PLUGIN_CLASS, BUILD_SCAN_PLUGIN_CLASS) def pluginVersion = atLeastGradle5 ? develocityPluginVersion : "1.16" applyPluginExternally(pluginManager, pluginClass, pluginVersion) def rootExtension = dvOrGe( { develocity }, { buildScan } ) def buildScanExtension = dvOrGe( { rootExtension.buildScan }, { rootExtension } ) if (develocityUrl) { logger.lifecycle("Connection to Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints") rootExtension.server = develocityUrl rootExtension.allowUntrustedServer = develocityAllowUntrustedServer } if (!shouldApplyDevelocityPlugin) { // Develocity plugin publishes scans by default buildScanExtension.publishAlways() } // uploadInBackground not available for build-scan-plugin 1.16 if (buildScanExtension.metaClass.respondsTo(buildScanExtension, 'setUploadInBackground', Boolean)) buildScanExtension.uploadInBackground = buildScanUploadInBackground buildScanExtension.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, ciAutoInjectionCustomValueValue if (isAtLeast(develocityPluginVersion, '2.1') && atLeastGradle5) { logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints") if (isAtLeast(develocityPluginVersion, '3.17')) { buildScanExtension.capture.fileFingerprints.set(develocityCaptureFileFingerprints) } else if (isAtLeast(develocityPluginVersion, '3.7')) { buildScanExtension.capture.taskInputFiles = develocityCaptureFileFingerprints } else { buildScanExtension.captureTaskInputFiles = develocityCaptureFileFingerprints } } } } eachDevelocityProjectExtension(project, { develocity -> afterEvaluate { if (develocityUrl && develocityEnforceUrl) { printEnforcingDevelocityUrl() develocity.server = develocityUrl develocity.allowUntrustedServer = develocityAllowUntrustedServer } } if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) { printAcceptingGradleTermsOfUse() develocity.buildScan.termsOfUseUrl = buildScanTermsOfUseUrl develocity.buildScan.termsOfUseAgree = buildScanTermsOfUseAgree } }, { buildScan -> afterEvaluate { if (develocityUrl && develocityEnforceUrl) { printEnforcingDevelocityUrl() buildScan.server = develocityUrl buildScan.allowUntrustedServer = develocityAllowUntrustedServer } } if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) { printAcceptingGradleTermsOfUse() if (buildScan.metaClass.respondsTo(buildScan, 'setTermsOfServiceUrl', String)) { buildScan.termsOfServiceUrl = buildScanTermsOfUseUrl buildScan.termsOfServiceAgree = buildScanTermsOfUseAgree } else { buildScan.licenseAgreementUrl = buildScanTermsOfUseUrl buildScan.licenseAgree = buildScanTermsOfUseAgree } } } ) if (ccudPluginVersion && atLeastGradle4) { def ccudPluginComponent = resolutionResult.allComponents.find { it.moduleVersion.with { group == "com.gradle" && name == "common-custom-user-data-gradle-plugin" } } if (!ccudPluginComponent) { logger.lifecycle("Applying $CCUD_PLUGIN_CLASS with version $ccudPluginVersion via init script") pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS)) } } } } } else { gradle.settingsEvaluated { settings -> if (develocityPluginVersion) { if (!settings.pluginManager.hasPlugin(GRADLE_ENTERPRISE_PLUGIN_ID) && !settings.pluginManager.hasPlugin(DEVELOCITY_PLUGIN_ID)) { def pluginClass = dvOrGe(DEVELOCITY_PLUGIN_CLASS, GRADLE_ENTERPRISE_PLUGIN_CLASS) applyPluginExternally(settings.pluginManager, pluginClass, develocityPluginVersion) if (develocityUrl) { logger.lifecycle("Connection to Develocity: $develocityUrl, allowUntrustedServer: $develocityAllowUntrustedServer, captureFileFingerprints: $develocityCaptureFileFingerprints") eachDevelocitySettingsExtension(settings) { ext -> // server and allowUntrustedServer must be configured via buildScan extension for gradle-enterprise-plugin 3.1.1 and earlier if (ext.metaClass.respondsTo(ext, 'getServer')) { ext.server = develocityUrl ext.allowUntrustedServer = develocityAllowUntrustedServer } else { ext.buildScan.server = develocityUrl ext.buildScan.allowUntrustedServer = develocityAllowUntrustedServer } } } eachDevelocitySettingsExtension(settings) { ext -> ext.buildScan.uploadInBackground = buildScanUploadInBackground ext.buildScan.value CI_AUTO_INJECTION_CUSTOM_VALUE_NAME, ciAutoInjectionCustomValueValue } eachDevelocitySettingsExtension(settings, { develocity -> logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints") develocity.buildScan.capture.fileFingerprints = develocityCaptureFileFingerprints }, { gradleEnterprise -> gradleEnterprise.buildScan.publishAlways() if (isAtLeast(develocityPluginVersion, '2.1')) { logger.lifecycle("Setting captureFileFingerprints: $develocityCaptureFileFingerprints") if (isAtLeast(develocityPluginVersion, '3.7')) { gradleEnterprise.buildScan.capture.taskInputFiles = develocityCaptureFileFingerprints } else { gradleEnterprise.buildScan.captureTaskInputFiles = develocityCaptureFileFingerprints } } } ) } } eachDevelocitySettingsExtension(settings, { develocity -> if (develocityUrl && develocityEnforceUrl) { printEnforcingDevelocityUrl() develocity.server = develocityUrl develocity.allowUntrustedServer = develocityAllowUntrustedServer } if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) { printAcceptingGradleTermsOfUse() develocity.buildScan.termsOfUseUrl = buildScanTermsOfUseUrl develocity.buildScan.termsOfUseAgree = buildScanTermsOfUseAgree } }, { gradleEnterprise -> if (develocityUrl && develocityEnforceUrl) { printEnforcingDevelocityUrl() // server and allowUntrustedServer must be configured via buildScan extension for gradle-enterprise-plugin 3.1.1 and earlier if (gradleEnterprise.metaClass.respondsTo(gradleEnterprise, 'getServer')) { gradleEnterprise.server = develocityUrl gradleEnterprise.allowUntrustedServer = develocityAllowUntrustedServer } else { gradleEnterprise.buildScan.server = develocityUrl gradleEnterprise.buildScan.allowUntrustedServer = develocityAllowUntrustedServer } } if (buildScanTermsOfUseUrl && buildScanTermsOfUseAgree) { printAcceptingGradleTermsOfUse() gradleEnterprise.buildScan.termsOfServiceUrl = buildScanTermsOfUseUrl gradleEnterprise.buildScan.termsOfServiceAgree = buildScanTermsOfUseAgree } } ) if (ccudPluginVersion) { if (!settings.pluginManager.hasPlugin(CCUD_PLUGIN_ID)) { logger.lifecycle("Applying $CCUD_PLUGIN_CLASS with version $ccudPluginVersion via init script") settings.pluginManager.apply(initscript.classLoader.loadClass(CCUD_PLUGIN_CLASS)) } } } } } void applyPluginExternally(def pluginManager, String pluginClassName, String pluginVersion) { logger.lifecycle("Applying $pluginClassName with version $pluginVersion via init script") def externallyApplied = 'develocity.externally-applied' def externallyAppliedDeprecated = 'gradle.enterprise.externally-applied' def oldValue = System.getProperty(externallyApplied) def oldValueDeprecated = System.getProperty(externallyAppliedDeprecated) System.setProperty(externallyApplied, 'true') System.setProperty(externallyAppliedDeprecated, 'true') try { pluginManager.apply(initscript.classLoader.loadClass(pluginClassName)) } finally { if (oldValue == null) { System.clearProperty(externallyApplied) } else { System.setProperty(externallyApplied, oldValue) } if (oldValueDeprecated == null) { System.clearProperty(externallyAppliedDeprecated) } else { System.setProperty(externallyAppliedDeprecated, oldValueDeprecated) } } } /** * Apply the `dvAction` to all 'develocity' extensions. * If no 'develocity' extensions are found, apply the `geAction` to all 'gradleEnterprise' extensions. * (The develocity plugin creates both extensions, and we want to prefer configuring 'develocity'). */ static def eachDevelocitySettingsExtension(def settings, def dvAction, def geAction = dvAction) { def GRADLE_ENTERPRISE_EXTENSION_CLASS = 'com.gradle.enterprise.gradleplugin.GradleEnterpriseExtension' def DEVELOCITY_CONFIGURATION_CLASS = 'com.gradle.develocity.agent.gradle.DevelocityConfiguration' def dvExtensions = settings.extensions.extensionsSchema.elements .findAll { it.publicType.concreteClass.name == DEVELOCITY_CONFIGURATION_CLASS } .collect { settings[it.name] } if (!dvExtensions.empty) { dvExtensions.each(dvAction) } else { def geExtensions = settings.extensions.extensionsSchema.elements .findAll { it.publicType.concreteClass.name == GRADLE_ENTERPRISE_EXTENSION_CLASS } .collect { settings[it.name] } geExtensions.each(geAction) } } /** * Apply the `dvAction` to the 'develocity' extension. * If no 'develocity' extension is found, apply the `bsAction` to the 'buildScan' extension. * (The develocity plugin creates both extensions, and we want to prefer configuring 'develocity'). */ static def eachDevelocityProjectExtension(def project, def dvAction, def bsAction = dvAction) { def BUILD_SCAN_PLUGIN_ID = 'com.gradle.build-scan' def DEVELOCITY_PLUGIN_ID = 'com.gradle.develocity' def configureDvOrBsExtension = { if (project.extensions.findByName("develocity")) { dvAction(project.develocity) } else { bsAction(project.buildScan) } } project.pluginManager.withPlugin(BUILD_SCAN_PLUGIN_ID, configureDvOrBsExtension) project.pluginManager.withPlugin(DEVELOCITY_PLUGIN_ID) { // Proper extension will be configured by the build-scan callback. if (project.pluginManager.hasPlugin(BUILD_SCAN_PLUGIN_ID)) return configureDvOrBsExtension() } } static boolean isAtLeast(String versionUnderTest, String referenceVersion) { GradleVersion.version(versionUnderTest) >= GradleVersion.version(referenceVersion) } static boolean isNotAtLeast(String versionUnderTest, String referenceVersion) { !isAtLeast(versionUnderTest, referenceVersion) } void enableBuildScanLinkCapture(BuildScanCollector collector) { // Conditionally apply and configure the Develocity plugin if (GradleVersion.current() < GradleVersion.version('6.0')) { rootProject { eachDevelocityProjectExtension(project, { develocity -> buildScanPublishedAction(develocity.buildScan, collector) }, { buildScan -> buildScanPublishedAction(buildScan, collector) } ) } } else { gradle.settingsEvaluated { settings -> eachDevelocitySettingsExtension(settings) { ext -> buildScanPublishedAction(ext.buildScan, collector) } } } } // Action will only be called if a `BuildScanCollector.captureBuildScanLink` method is present. // Add `void captureBuildScanLink(String) {}` to the `BuildScanCollector` class to respond to buildScanPublished events static buildScanPublishedAction(def buildScanExtension, BuildScanCollector collector) { if (buildScanExtension.metaClass.respondsTo(buildScanExtension, 'buildScanPublished', Action)) { buildScanExtension.buildScanPublished { scan -> collector.captureBuildScanLink(scan.buildScanUri.toString()) } } } class BuildScanCollector {}