diff --git a/src/integrationTest/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginIntegrationSpec.groovy index dfe3491..3fc20ae 100644 --- a/src/integrationTest/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginIntegrationSpec.groovy @@ -5,6 +5,8 @@ import com.wooga.gradle.test.run.result.GradleRunResult import com.wooga.gradle.test.writers.PropertyGetterTaskWriter import com.wooga.gradle.test.writers.PropertySetterWriter +import static com.wooga.gradle.PlatformUtils.windows + class UnitySonarqubePluginIntegrationSpec extends IntegrationSpec { def setup() { @@ -33,10 +35,13 @@ class UnitySonarqubePluginIntegrationSpec extends IntegrationSpec { then: def run = new GradleRunResult(result) - run["asdfBinstubsDotnet"].wasExecutedBefore("sonarBuildUnity") + run[installDotnet].wasExecutedBefore("sonarBuildUnity") run["generateSolution"].wasExecutedBefore("sonarBuildUnity") run["sonarScannerBegin"].wasExecutedBefore("sonarBuildUnity") run["sonarScannerEnd"].wasExecutedAfter("sonarBuildUnity") + + where: + installDotnet = isWindows()? "dotnetWindowsInstall" : "asdfBinstubsDotnet" } def "sonarqube build task runs after unity tests"() { @@ -54,15 +59,22 @@ class UnitySonarqubePluginIntegrationSpec extends IntegrationSpec { given: "applied unity-sonarqube plugin" when: getter.write(buildFile) - def tasksResult = runTasks("asdfBinstubsDotnet", getter.taskName) + def tasksResult = runTasks(installDotnet, getter.taskName) def query = getter.generateQuery(this, tasksResult) + File dotnetExec = dotnetFile(projectDir) then: - query.matches(new File(projectDir, "bin/dotnet").absolutePath) + query.matches(dotnetExec.absolutePath) where: + installDotnet = isWindows()? "dotnetWindowsInstall" : "asdfBinstubsDotnet" task << ["sonarBuildUnity", "sonarScannerBegin", "sonarScannerEnd"] getter = new PropertyGetterTaskWriter("${task}.executable") + + gradleHome = System.getenv("GRADLE_USER_HOME")? new File(System.getenv("GRADLE_USER_HOME")) : new File(System.getProperty("user.home"), ".gradle") + dotnetFile = isWindows()? + { _ -> new File(gradleHome, "net.wooga.unity-sonarqube/dotnet/7.0.100/dotnet.exe") }: + {File projDir -> new File(projDir, "bin/dotnet") } } def "#task uses extension-set dotnet as executable"() { diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolution.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolution.groovy index 8ef736b..5e546b4 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolution.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolution.groovy @@ -47,6 +47,6 @@ class BuildSolution extends Build { } BuildSolution() { - this.additionalArguments.addAll(solution.map {[it.asFile.absolutePath] }.orElse([])) + this.target.convention(solution.map {it.asFile.absolutePath}) } } diff --git a/src/main/groovy/wooga/gradle/unitysonar/UnitySonarqubePlugin.groovy b/src/main/groovy/wooga/gradle/unitysonar/UnitySonarqubePlugin.groovy index 1e15499..fc199d3 100644 --- a/src/main/groovy/wooga/gradle/unitysonar/UnitySonarqubePlugin.groovy +++ b/src/main/groovy/wooga/gradle/unitysonar/UnitySonarqubePlugin.groovy @@ -1,5 +1,6 @@ package wooga.gradle.unitysonar +import com.wooga.gradle.PlatformUtils import org.gradle.api.Action import org.gradle.api.DefaultTask import org.gradle.api.Plugin @@ -18,6 +19,7 @@ import wooga.gradle.dotnetsonar.SonarScannerExtension import wooga.gradle.dotnetsonar.tasks.BuildSolution import wooga.gradle.unity.UnityPlugin import wooga.gradle.unity.UnityPluginExtension +import wooga.gradle.unitysonar.tasks.DotnetWindowsInstall class UnitySonarqubePlugin implements Plugin { @@ -33,9 +35,10 @@ class UnitySonarqubePlugin implements Plugin { def unitySonarqube = createExtension("unitySonarqube") //uses same dotnet version for both sonarScanner and buildTask - def asdf = configureAsdfWithTool("dotnet", unitySonarqube.buildDotnetVersion) + def asdf = project.extensions.getByType(AsdfPluginExtension) def unity = configureUnityPlugin() - configureSonarScanner(asdf) + def dotnetExecutable = getDotnet(asdf, unitySonarqube) + configureSonarScanner(dotnetExecutable) configureSonarqubePlugin(unity) @@ -46,7 +49,7 @@ class UnitySonarqubePlugin implements Plugin { it.dependsOn(createSolutionTask) it.mustRunAfter(unityTestTask) - def buildDotnetDefault = unitySonarqube.buildDotnetExecutable.orElse(asdf.getTool("dotnet").getExecutable("dotnet")) + def buildDotnetDefault = unitySonarqube.buildDotnetExecutable.orElse(dotnetExecutable) it.executableName.convention(buildDotnetDefault) it.solution.convention(unity.projectDirectory.file("${project.name}.sln")) it.environment.put("FrameworkPathOverride", unity.monoFrameworkDir.map { it.asFile.absolutePath }) @@ -66,23 +69,15 @@ class UnitySonarqubePlugin implements Plugin { return unitySonarqube } - AsdfPluginExtension configureAsdfWithTool(String name, Provider version) { - def asdf = project.extensions.getByType(AsdfPluginExtension) - asdf.tool(new ToolVersionInfo(name, version)) - return asdf - } - UnityPluginExtension configureUnityPlugin() { def unityExt = project.extensions.findByType(UnityPluginExtension) unityExt.enableTestCodeCoverage = true return unityExt } - SonarScannerExtension configureSonarScanner(AsdfPluginExtension asdf) { + SonarScannerExtension configureSonarScanner(Provider dotnetExecutable) { def sonarScanner = project.extensions.findByType(SonarScannerExtension) - - def asdfDotnet = asdf.getTool("dotnet") - sonarScanner.dotnetExecutable.convention(asdfDotnet.getExecutable("dotnet")) + sonarScanner.dotnetExecutable.convention(dotnetExecutable) return sonarScanner } @@ -114,6 +109,27 @@ class UnitySonarqubePlugin implements Plugin { return propsFixTmpFile } + //https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script + Provider getDotnet(AsdfPluginExtension asdf, UnitySonarqubeExtension unitySonarqube) { + if(PlatformUtils.windows) { + def winInstall = project.tasks.register("dotnetWindowsInstall", DotnetWindowsInstall) { + it.version.convention(unitySonarqube.buildDotnetVersion) + + def installDir = it.version.map { + def dir = new File(project.gradle.gradleUserHomeDir, + "net.wooga.unity-sonarqube/dotnet/$it") + dir.mkdirs() + return dir + } + it.installDir.convention(project.layout.dir(installDir)) + } + return winInstall.flatMap({ it.dotnetExecutable() }) + } else { + asdf.tool(new ToolVersionInfo("dotnet", unitySonarqube.buildDotnetVersion)) + return asdf.getTool("dotnet").getExecutable("dotnet") + } + } + protected TaskProvider namedOrRegister(String taskName, Class type = DefaultTask.class, Action configuration = { it -> }) { try { return project.tasks.named(taskName, type, configuration) diff --git a/src/main/groovy/wooga/gradle/unitysonar/tasks/DotnetWindowsInstall.groovy b/src/main/groovy/wooga/gradle/unitysonar/tasks/DotnetWindowsInstall.groovy new file mode 100644 index 0000000..974f398 --- /dev/null +++ b/src/main/groovy/wooga/gradle/unitysonar/tasks/DotnetWindowsInstall.groovy @@ -0,0 +1,66 @@ +package wooga.gradle.unitysonar.tasks + +import com.wooga.gradle.ArgumentsSpec +import com.wooga.gradle.io.ExecSpec +import com.wooga.gradle.io.ProcessExecutor +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import org.gradle.process.ExecResult +import wooga.gradle.dotnetsonar.tasks.internal.Downloader + +class DotnetWindowsInstall extends DefaultTask implements ArgumentsSpec, ExecSpec { + + @InputFile + final RegularFileProperty dotnetInstallScript = objects.fileProperty() + @Input + final Property version = objects.property(String) + @InputDirectory + final DirectoryProperty installDir = objects.directoryProperty() + @OutputFile + final RegularFileProperty dotnetExecutable = objects.fileProperty() + + Provider dotnetExecutable() { + return dotnetExecutable.map { + it.asFile.absolutePath + } + } + + DotnetWindowsInstall() { + dotnetInstallScript.convention(layout.file(project.provider { + def installer = File.createTempFile("dotnet-install", ".ps1") + new Downloader().download("https://dot.net/v1/dotnet-install.ps1".toURL(), installer) + return installer + })) + executableName.convention(dotnetInstallScript.asFile.map {it.absolutePath}) + internalArguments = version.zip(installDir) { version, installDir -> + ["-Version", version, "-InstallDir", installDir.asFile.absolutePath, "-NoPath"] + } + dotnetExecutable.set(installDir.file("dotnet.exe")) + onlyIf { + def noDotnetExecutable = !installDir.file("dotnet").map {it.asFile.file }.getOrElse(false) + if(!noDotnetExecutable) { + logger.info("Dotnet executable already present in " + + "${dotnetExecutable.get().asFile.absolutePath}, skipping...") + } + } + } + + @TaskAction + def install() { + ExecResult execResult = ProcessExecutor.from(this) + .withArguments(this) + .withWorkingDirectory(workingDirectory.getOrNull()) + .ignoreExitValue() + .execute() + execResult.assertNormalExitValue() + } +} + diff --git a/src/test/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginSpec.groovy b/src/test/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginSpec.groovy index ac935d1..1198ec5 100644 --- a/src/test/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginSpec.groovy +++ b/src/test/groovy/wooga/gradle/unitysonar/UnitySonarqubePluginSpec.groovy @@ -1,5 +1,6 @@ package wooga.gradle.unitysonar +import com.wooga.gradle.PlatformUtils import nebula.test.ProjectSpec import org.gradle.api.DefaultTask import org.gradle.api.Task @@ -9,6 +10,8 @@ import wooga.gradle.dotnetsonar.tasks.BuildSolution import wooga.gradle.unity.UnityPlugin import wooga.gradle.unity.UnityPluginExtension +import static com.wooga.gradle.PlatformUtils.windows + class UnitySonarqubePluginSpec extends ProjectSpec { public static final String PLUGIN_NAME = 'net.wooga.unity-sonarqube' @@ -47,17 +50,18 @@ class UnitySonarqubePluginSpec extends ProjectSpec { then: Task task = project.tasks.findByName(taskName) - task.getTaskDependencies().getDependencies(task).findAll { t -> - dependencies.find { - depName -> t.name == depName - } + dependencies.each {expectedDepName -> + def taskDeps = task.getTaskDependencies().getDependencies(task) + def actualDepName = taskDeps.find {it.name == expectedDepName }?.name + assert expectedDepName == actualDepName } task.getFinalizedBy().getDependencies(task).collect{it.name } == finalizedBy where: taskName | dependencies | finalizedBy "sonarqube" | [UnityPlugin.Tasks.test.toString(), "sonarBuildUnity"] | [] - "sonarBuildUnity" | ["asdfBinstubsDotnet", "generateSolution", "sonarScannerBegin"] | ["sonarScannerEnd"] + "sonarBuildUnity" | [isWindows()? "dotnetWindowsInstall" : "asdfBinstubsDotnet", "generateSolution", "sonarScannerBegin"] | ["sonarScannerEnd"] + } def "configures sonarqube extension"() {