From 0c10f93cc38461e5efb4cbe509175fccb2b876bb Mon Sep 17 00:00:00 2001 From: Joaquim Alvino de Mesquita Neto Date: Tue, 24 Aug 2021 14:57:18 -0300 Subject: [PATCH] Add mono support (#2) Added mono sonar scanner execution and options to pass custom mono installations. Keep in mind that mono is only considered when running in non-windows platforms. There also also some minor refactors and the .github folder. Co-authored-by: Joaquim Neto --- .github/CODEOWNERS | 1 + .github/CODE_OF_CONDUCT.md | 51 +++++++ .github/CONTRIBUTING.md | 134 ++++++++++++++++++ .github/ISSUE_TEMPLATE.md | 15 ++ .github/PULL_REQUEST_TEMPLATE.md | 35 +++++ .github/SUPPORT.md | 5 + .github/dependabot.yml | 36 +++++ ...onarScannerExtensionIntegrationSpec.groovy | 2 +- .../BuildSolutionTaskIntegrationSpec.groovy | 2 +- ...onarScannerBeginTaskIntegrationSpec.groovy | 5 +- .../SonarScannerEndTaskIntegrationSpec.groovy | 5 +- ...arScannerInstallTaskIntegrationSpec.groovy | 30 +++- .../tasks/utils/PluginIntegrationSpec.groovy | 24 +++- .../dotnetsonar/SonarScannerExtension.groovy | 50 +++++-- .../tasks/SonarScannerBegin.groovy | 3 +- .../dotnetsonar/tasks/SonarScannerEnd.groovy | 3 +- .../tasks/SonarScannerInstall.groovy | 9 +- .../tasks/internal/GradleMonoShell.groovy | 30 ++++ .../tasks/internal/GradleShell.groovy | 2 + .../tasks/internal/SonarScanner.groovy | 21 ++- .../tasks/internal/SonarScannerFactory.groovy | 40 ++++++ ....wooga.gradle.dotnet-sonarqube.properties} | 2 +- .../tasks/internal/GradleMonoShellSpec.groovy | 32 +++++ .../tasks/internal/OSOpsSpec.groovy | 11 +- .../tasks/internal/SonarScannerSpec.groovy | 6 +- .../dotnetsonar/utils/FakeExecutable.groovy | 59 ++++++-- .../gradle/dotnetsonar/utils/FakeShell.groovy | 2 +- .../gradle/dotnetsonar/utils/SpecFakes.groovy | 11 +- 28 files changed, 555 insertions(+), 71 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/SUPPORT.md create mode 100644 .github/dependabot.yml create mode 100644 src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShell.groovy create mode 100644 src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerFactory.groovy rename src/main/resources/META-INF/gradle-plugins/{net.wooga.plugins.properties => net.wooga.gradle.dotnet-sonarqube.properties} (89%) create mode 100644 src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShellSpec.groovy diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3634bf7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* larusso diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f5c2a79 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,51 @@ +# Code of Conduct + +## 1. Purpose + +At Wooga we are committed to providing a friendly, safe and welcoming environment for everyone who works here or with us, regardless to gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, age, religion (or lack thereof) and game preferences. +This code of conduct outlines our expectations for all those who work at or with Wooga, as well as the consequences for unacceptable behavior. +We invite all those who participate in atlas-dotnet-sonarqube to help us create safe and positive experiences for everyone. + +## 2. Open Source Citizenship + +A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. +Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. +If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. + +## 3. Expected Behavior + +The following behaviors are expected and requested of all community members: +Exercise consideration and respect in your speech and actions. +Attempt collaboration before conflict. +Refrain from demeaning, discriminatory, or harassing behavior and speech. +Be mindful of your surroundings and of your fellow Woogas. We encourage everyone to call it out if a dangerous situation, someone in distress, or violations of this Code of Conduct is noticed, even if they seem inconsequential. + +## 4. Unacceptable Behavior + +Unacceptable behaviors include: intimidating, harassing, abusive, discriminatory, derogatory or demeaning speech or actions by any employee, partner or customer here at Wooga, online and in one-on-one communications carried out in the context of teamwork. + +## 5. Consequences of Unacceptable Behavior + +Unacceptable behavior from any community member, will not be tolerated. +Anyone asked to stop unacceptable behavior is expected to comply immediately. +If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning. + +## 6. Reporting Guidelines + +If you are subject to or witness unacceptable behavior, or have any other concerns, please notify Wooga’s Equality and Diversity ambassador as soon as possible aleksandra.zdeb@wooga.com. + +## 8. Scope + +We expect all community participants () to abide by this Code of Conduct, in-person–as well as in all one-on-one communications pertaining to community business. + +## 9. Contact info + +aleksandra.zdeb@wooga.com + +## 10. License and attribution + +This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). + +Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy) and the [Wooga Code of Conduct](https://www.wooga.com/about/code-of-conduct/). + +Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..ed094d1 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,134 @@ +# How to contribute efficiently + +Thank you for taking the time and energy to read this file and contribute to one of the wooga libraries. All contributions are welcome. Be it bug reports, documentation updates, bug fixes or improvements. + +Sections covered in this file: + +* [Reporting bugs or proposing features](#reporting-bugs-or-proposing-features) +* [Contributing pull requests](#contributing-pull-requests) +* [Communicating with other developers](#communicating-with--other-developers) + +**Please read the first section before reporting a bug!** + +## Reporting bugs or proposing features + +The golden rule is to **always open *one* issue for *one* bug**. If you notice several bugs and want to report them, make sure to create one new issue for each of them. + +Everything referred to hereafter as "bug" also applies for feature requests. + +If you are reporting a new issue, you will make our life much simpler (and the fix come much sooner) by following those guidelines: + +#### Specify the platform + +If you believe your issue is device/platform dependent, please specify: + +* Operating system +* Platform (including architecture, e.g. x86, x86_64, arm, etc.) +* Java version +* gradle version + +#### Specify steps to reproduce + +Many bugs can't be reproduced unless specific steps are taken. Please **specify the exact steps** that must be taken to reproduce the condition, and try to keep them as minimal as possible. + +#### Use Labels + +Githubs label system makes it easier to search for specific issues or group them into clusters. There is a wide varity of labels already defined. + +## Contributing pull requests + +If you want to add new engine functionalities, please make sure that: + +* This functionality is desired. +* You talked to other developers on how to implement it best (on either communication channel, and maybe in a GitHub issue first before making your PR). +* Even if it does not get merged, your PR is useful for future work by another developer. + +Similar rules can be applied when contributing bug fixes - it's always best to discuss the implementation in the bug report first if you are not 100% about what would be the best fix. + +#### Be nice to the git history + +Try to make simple PRs with that handle one specific topic. Just like for reporting issues, it's better to open 3 different PRs that each address a different issue than one big PR with three commits. + +When updating your topic branch with changes from master, please use `git pull --rebase` to avoid creating "merge commits". Those commits unnecessarily pollute the git history when coming from PRs. + +```bash +git pull origin master --rebase +``` + +This will `fetch` the upstream changes, rewind your local checkout to the last integration point, apply the changes from remote and after that apply all your local changes. + +You can also set `branch.autosetuprebase` in `git-config` + +_from the git man page_ +```man +-r, --rebase[=false|true|preserve] + When true, rebase the current branch on top of the upstream branch + after fetching. If there is a remote-tracking branch corresponding + to the upstream branch and the upstream branch was rebased since + last fetched, the rebase uses that information to avoid rebasing + non-local changes. + + When preserve, also rebase the current branch on top of the + upstream branch, but pass --preserve-merges along to git rebase so + that locally created merge commits will not be flattened. + + When false, merge the current branch into the upstream branch. + + See pull.rebase, branch..rebase and branch.autosetuprebase in + git-config(1) if you want to make git pull always use --rebase + instead of merging. + + Note + This is a potentially dangerous mode of operation. It rewrites + history, which does not bode well when you published that + history already. Do not use this option unless you have read + git-rebase(1) carefully. +``` + +**Pro:** +- The history looks cleaner +- no pull merge commits `Merge origin/master into master` + +**Cons:** +- you could end up fxing merge issues multiple times (in most cases it's the fault of the first commit not correctly merged) +- by default the commit dates are no longer in order (there is the flag `--committer-date-is-author-date`) +- you need to stash/unstash local changes (there is a config option `rebase.autoStash` or flag `--autostash` fot that) + +This git style guide has some good practices to have in mind: + +#### Format your commit logs with readability in mind + +The way you format your commit logs is quite important to ensure that the commit history and changelog will be easy to read and understand. A git commit log is formatted as a short title (first line) and an extended description (everything after the first line and an empty separation line). + +The short title is the most important part, as it is what will appear in the `shortlog` changelog (one line per commit, so no description shown) or in the GitHub interface unless you click the "expand" button. As the name tells it, try to keep that first line relatively short (ideally <= 50 chars, though it's rare to be able to tell enough in so few characters, so you can go a bit higher) - it should describe what the commit does globally, while details would go in the description. Typically, if you can't keep the title short because you have too much stuff to mention, it means that you should probably split your changes in several commits :) + +I could go on and on about the format and length of a good commit message. But check out this post instead. + +In short: + +```plain +Capitalized, short (50 chars or less) summary + +More detailed explanatory text, if necessary. Wrap it to about 72 characters +or so. In some contexts, the first line is treated as the subject of an email +and the rest of the text as the body. The blank line separating the summary +from the body is critical (unless you omit the body entirely); tools like +rebase can get confused if you run the two together. + +Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or +"Fixes bug." This convention matches up with commit messages generated by +commands like git merge and git revert. + +Further paragraphs come after blank lines. + +- Bullet points are okay, too +- Typically a hyphen or asterisk is used for the bullet, followed by a + single space, with blank lines in between, but conventions vary here +- Use a hanging indent + +Use markdown when possible. Exceptions are `# Headlines` +``` + +Thanks! + +The Atlas development team diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..b4b962e --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,15 @@ +Before open a new issue check the following list. + +_You don't need to add this checklist to the pull request body._ + +- [ ] check issue list if issue is already reported +- [ ] write good issue description +- [ ] add appropriate labels + +## Description +Add short description + +## Steps to reproduce + +1. open ... +2. ... \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..beddb3a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ +Before open a new pull request please check the following list. + +_You don't need to add this checklist to the pull request body._ +- [ ] add new tests to test your changes +- [ ] run the tests and check that they pass +- [ ] cleanup your commits +- [ ] write good pull request description +- [ ] add appropiate labels + +## Description +Add short description + +## Changes +* ![ADD] add new file x.cs +* ![FIX] broken `String` in ... +... + + + + +[NEW]:https://resources.atlas.wooga.com/icons/icon_new.svg "New" +[ADD]:https://resources.atlas.wooga.com/icons/icon_add.svg "Add" +[IMPROVE]:https://resources.atlas.wooga.com/icons/icon_improve.svg "IMPROVE" +[CHANGE]:https://resources.atlas.wooga.com/icons/icon_change.svg "Change" +[FIX]:https://resources.atlas.wooga.com/icons/icon_fix.svg "Fix" +[UPDATE]:https://resources.atlas.wooga.com/icons/icon_update.svg "Update" + +[BREAK]:https://resources.atlas.wooga.com/icons/icon_break.svg "Remove" +[REMOVE]:https://resources.atlas.wooga.com/icons/icon_remove.svg "Remove" +[IOS]:https://resources.atlas.wooga.com/icons/icon_iOS.svg "iOS" +[ANDROID]:https://resources.atlas.wooga.com/icons/icon_android.svg "Android" +[WEBGL]:https://resources.atlas.wooga.com/icons/icon_webGL.svg "WebGL" +[UNITY]:https://resources.atlas.wooga.com/icons/icon_unity.svg "Unity" +[GRADLE]:https://resources.atlas.wooga.com/icons/icon_gradle.svg "Gradle" +[LINUX]:https://resources.atlas.wooga.com/icons/icon_linux.svg "Linux" diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..1f7fc7f --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,5 @@ +## Communicating with other developers + +pullrequests for all packages. +- [GitHub issues](https://github.com/wooga/atlas-dotnet-sonarqube/issues): All discussions around bugs, feature requests. +- [GitHub pull requests](https://github.com/wooga/atlas-dotnet-sonarqube/pulls): All discussions bugfixes, important and new additions. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7c7e0dc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,36 @@ +version: 2 +registries: + maven-repository-wooga-jfrog-io-wooga-atlas-maven: + type: maven-repository + url: https://wooga.jfrog.io/wooga/atlas-maven + username: atlas + password: "${{secrets.MAVEN_REPOSITORY_WOOGA_JFROG_IO_WOOGA_ATLAS_MAVEN_PASSWORD}}" + +updates: +- package-ecosystem: gradle + directory: "/" + schedule: + interval: daily + time: "04:00" + open-pull-requests-limit: 10 + ignore: + - dependency-name: org.spockframework:spock-core + versions: + - ">= 0" + - dependency-name: net.wooga.plugins + versions: + - 1.5.0 + - 2.0.0 + - dependency-name: com.gradle.plugin-publish + versions: + - 0.13.0 + - 0.14.0 + - dependency-name: com.netflix.nebula:nebula-test + versions: + - 7.10.2 + - 8.1.0 + - dependency-name: org.kt3k.gradle.plugin:coveralls-gradle-plugin + versions: + - 2.8.3 + registries: + - maven-repository-wooga-jfrog-io-wooga-atlas-maven diff --git a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/SonarScannerExtensionIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/SonarScannerExtensionIntegrationSpec.groovy index 116e309..99f677f 100644 --- a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/SonarScannerExtensionIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/SonarScannerExtensionIntegrationSpec.groovy @@ -13,7 +13,7 @@ class SonarScannerExtensionIntegrationSpec extends PluginIntegrationSpec { File fakeScannerExec = SpecFakes.argReflectingFakeExecutable("sonarscanner") and: "custom task registering itself on sonar scanner extension" buildFile << """ - ${forceAddSonarScannerObjectToExtension(fakeScannerExec)} + ${forceAddObjectsToExtension(fakeScannerExec)} ${setupSonarScannerExtension("sonarScannerExt")} tasks.register("custom") { sonarScannerExt.registerBuildTask(it) diff --git a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolutionTaskIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolutionTaskIntegrationSpec.groovy index 9d7ad0a..5412386 100644 --- a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolutionTaskIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/BuildSolutionTaskIntegrationSpec.groovy @@ -15,7 +15,7 @@ class BuildSolutionTaskIntegrationSpec extends PluginIntegrationSpec { def setup() { def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 0) - buildFile << forceAddSonarScannerObjectToExtension(fakeSonarScannerExec) + buildFile << forceAddObjectsToExtension(fakeSonarScannerExec) } @Unroll diff --git a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBeginTaskIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBeginTaskIntegrationSpec.groovy index d155770..54088d7 100644 --- a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBeginTaskIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBeginTaskIntegrationSpec.groovy @@ -14,7 +14,7 @@ class SonarScannerBeginTaskIntegrationSpec extends PluginIntegrationSpec { given: "a sonar scanner executable" def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 0) and: "a set up sonar scanner extension" - buildFile << forceAddSonarScannerObjectToExtension(fakeSonarScannerExec) + buildFile << forceAddObjectsToExtension(fakeSonarScannerExec) and: "a configured sonarqube extension" def projectVersion = "0.0.1" buildFile << """ @@ -42,7 +42,7 @@ class SonarScannerBeginTaskIntegrationSpec extends PluginIntegrationSpec { given: "a failing sonar scanner executable" def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 1) and: "a set up sonar scanner extension" - buildFile << forceAddSonarScannerObjectToExtension(fakeSonarScannerExec) + buildFile << forceAddObjectsToExtension(fakeSonarScannerExec) when: "running the sonarScannerBegin task" def result = runTasksWithFailure("sonarScannerBegin") @@ -50,7 +50,6 @@ class SonarScannerBeginTaskIntegrationSpec extends PluginIntegrationSpec { then: "should fail on execution with non-zero exit value" def e = rootCause(result.failure) e.getClass().name == ExecException.name - e.message.contains(fakeSonarScannerExec.absolutePath) e.message.contains("exit value 1") } } diff --git a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEndTaskIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEndTaskIntegrationSpec.groovy index 42b5690..462780d 100644 --- a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEndTaskIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEndTaskIntegrationSpec.groovy @@ -14,7 +14,7 @@ class SonarScannerEndTaskIntegrationSpec extends PluginIntegrationSpec { and: "a sonar scanner executable" def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 0) and: "a set up sonar scanner extension" - buildFile << forceAddSonarScannerObjectToExtension(fakeSonarScannerExec) + buildFile << forceAddObjectsToExtension(fakeSonarScannerExec) and: "a configurated sonarqube extension" def loginToken = "loginToken" buildFile << """ @@ -39,7 +39,7 @@ class SonarScannerEndTaskIntegrationSpec extends PluginIntegrationSpec { given: "a failing sonar scanner executable" def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 1) and: "a set up sonar scanner extension" - buildFile << forceAddSonarScannerObjectToExtension(fakeSonarScannerExec) + buildFile << forceAddObjectsToExtension(fakeSonarScannerExec) when: "running the sonarScannerEnd task" def result = runTasksWithFailure("sonarScannerEnd") @@ -47,7 +47,6 @@ class SonarScannerEndTaskIntegrationSpec extends PluginIntegrationSpec { then: "should fail on execution with non-zero exit value" def e = rootCause(result.failure) e.getClass().name == ExecException.name - e.message.contains(fakeSonarScannerExec.absolutePath) e.message.contains("exit value 1") } diff --git a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstallTaskIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstallTaskIntegrationSpec.groovy index c258858..f29a95c 100644 --- a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstallTaskIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstallTaskIntegrationSpec.groovy @@ -1,12 +1,17 @@ package wooga.gradle.dotnetsonar.tasks +import spock.lang.IgnoreIf import spock.lang.Unroll import wooga.gradle.dotnetsonar.tasks.utils.PluginIntegrationSpec import wooga.gradle.dotnetsonar.utils.SpecFakes +import static wooga.gradle.dotnetsonar.utils.SpecUtils.isWindows +import static wooga.gradle.dotnetsonar.utils.SpecUtils.wrapValueBasedOnType + class SonarScannerInstallTaskIntegrationSpec extends PluginIntegrationSpec { //again take care with too big file path length on windows, MS does not likes these. + @IgnoreIf({ !isWindows() }) def "installs sonar scanner with default values"() { given: "installed dotnet sonarscanner plugin" and: "no pre-existent sonarscanner executable on PATH" @@ -23,6 +28,29 @@ class SonarScannerInstallTaskIntegrationSpec extends PluginIntegrationSpec { "${defaultInstallDir}/sonarscanner-${defaultVersion}/${SonarScannerInstall.EXECUTABLE_NAME}").exists() } + @IgnoreIf({ isWindows() }) + def "installs in mono-dependent environment"() { + given: "installed dotnet sonarscanner plugin" + and: "a setup mono executable" + File monoExec = SpecFakes.runFirstParameterFakeExecutable("mono") + buildFile << """ + ${setupSonarScannerExtension("sonarScannerExt")} + sonarScannerExt.monoExecutable = ${wrapValueBasedOnType(monoExec.absolutePath, File)} + """ + and: "no pre-existent sonarscanner executable on PATH nor in extension" + + when: "running sonar scanner installation task" + def result = runTasksSuccessfully("sonarScannerInstall") + + then: "sonar scanner should be executed" + result.wasExecuted(":sonarScannerInstall") + and: "dotnet sonar scanner package should be installed at default directory" + def defaultVersion = "5.2.2.33595" + def defaultInstallDir = "build/bin/net-sonarscanner/" + new File(projectDir, + "${defaultInstallDir}/sonarscanner-${defaultVersion}/${SonarScannerInstall.EXECUTABLE_NAME}").exists() + } + @Unroll def "installs sonar scanner version #version-net46 in #installDir"() { given: "installed dotnet sonarscanner plugin" @@ -54,7 +82,7 @@ class SonarScannerInstallTaskIntegrationSpec extends PluginIntegrationSpec { given: "installed dotnet sonarscanner plugin" and: "pre-existent sonarscanner executable" def scannerExec = SpecFakes.argReflectingFakeExecutable("sonarscanner.bat") - buildFile << forceAddSonarScannerObjectToExtension(scannerExec) + buildFile << forceAddObjectsToExtension(scannerExec) when: "running sonar scanner installation task" def result = runTasksSuccessfully("sonarScannerInstall") diff --git a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/utils/PluginIntegrationSpec.groovy b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/utils/PluginIntegrationSpec.groovy index 87f3d6b..4872078 100644 --- a/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/utils/PluginIntegrationSpec.groovy +++ b/src/integrationTest/groovy/wooga/gradle/dotnetsonar/tasks/utils/PluginIntegrationSpec.groovy @@ -4,7 +4,9 @@ import nebula.test.IntegrationSpec import wooga.gradle.dotnetsonar.DotNetSonarqubePlugin import wooga.gradle.dotnetsonar.SonarScannerExtension import wooga.gradle.dotnetsonar.tasks.internal.SonarScanner +import wooga.gradle.dotnetsonar.utils.SpecFakes +import static wooga.gradle.dotnetsonar.utils.SpecUtils.isWindows import static wooga.gradle.dotnetsonar.utils.SpecUtils.wrapValueBasedOnType class PluginIntegrationSpec extends IntegrationSpec { @@ -24,15 +26,29 @@ class PluginIntegrationSpec extends IntegrationSpec { """ } - String forceAddSonarScannerObjectToExtension(File sonarScannerExec) { + String forceAddObjectsToExtension(File sonarScannerExec) { return """ + ${forceAddMonoOnNonWindows()} def setupSonarScannerFile() { ${setupSonarScannerExtension("sonarScannerExt")} - sonarScannerExt.sonarScanner = SonarScanner.gradleBased(project, - ${wrapValueBasedOnType(sonarScannerExec.absolutePath, File)}, - project.projectDir) + sonarScannerExt.sonarScanner = sonarScannerExt.createSonarScanner(${wrapValueBasedOnType(sonarScannerExec.absolutePath, File)}) } setupSonarScannerFile() """ } + + String forceAddMonoOnNonWindows() { + if(!isWindows()) { + File monoExec = SpecFakes.runFirstParameterFakeExecutable("mono") + return """ + def setupMono() { + ${setupSonarScannerExtension("sonarScannerExt")} + sonarScannerExt.monoExecutable = ${wrapValueBasedOnType(monoExec.absolutePath, File)} + } + setupMono() + """ + } else { + return "" + } + } } diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/SonarScannerExtension.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/SonarScannerExtension.groovy index 4fa04a4..3b94f7e 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/SonarScannerExtension.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/SonarScannerExtension.groovy @@ -2,12 +2,16 @@ package wooga.gradle.dotnetsonar import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.sonarqube.gradle.ActionBroadcast import org.sonarqube.gradle.SonarPropertyComputer import org.sonarqube.gradle.SonarQubeProperties import wooga.gradle.dotnetsonar.tasks.SonarScannerBegin import wooga.gradle.dotnetsonar.tasks.SonarScannerEnd import wooga.gradle.dotnetsonar.tasks.internal.SonarScanner +import wooga.gradle.dotnetsonar.tasks.internal.SonarScannerFactory class SonarScannerExtension { @@ -18,15 +22,19 @@ class SonarScannerExtension { public static final String BEGIN_TASK_NAME = "sonarScannerBegin" public static final String END_TASK_NAME = "sonarScannerEnd" - private Project project; - private SonarScanner sonarScanner; - private Map sonarQubeProperties; + private final Project project; + private final Property sonarScanner + private final RegularFileProperty monoExecutable + private Map sonarQubeProperties private ActionBroadcast actionBroadcast; - + private SonarScannerFactory scannerFactory; SonarScannerExtension(Project project, ActionBroadcast actionBroadcast) { + this.sonarScanner = project.objects.property(SonarScanner) + this.monoExecutable = project.objects.fileProperty() this.project = project this.actionBroadcast = actionBroadcast + this.scannerFactory = SonarScannerFactory.withPATHFallback(project, monoExecutable.map{it.asFile}) } void registerBuildTask(Task... task) { @@ -37,15 +45,33 @@ class SonarScannerExtension { } } - Optional getSonarScanner() { - if(sonarScanner == null) { - this.sonarScanner = findOnPath() - } - return Optional.ofNullable(sonarScanner) + SonarScanner createSonarScanner(File sonarScannerExec) { + return scannerFactory.fromExecutable(sonarScannerExec, project.projectDir) + } + + Provider getSonarScanner() { + + def factory = this.scannerFactory //groovy "this" can be as dumb as JS one sometimes. + def proj = this.project + return sonarScanner.orElse(project.provider { + factory.fromPath(proj.projectDir).orElse(null) + }) } void setSonarScanner(SonarScanner sonarScanner) { - this.sonarScanner = sonarScanner + this.sonarScanner.set(sonarScanner) + } + + void setSonarScanner(Provider sonarScanner) { + this.sonarScanner.set(sonarScanner) + } + + RegularFileProperty getMonoExecutable() { + return monoExecutable + } + + void setMonoExecutable(File monoExecutable) { + this.monoExecutable.set(monoExecutable) } Map getSonarQubeProperties() { @@ -55,10 +81,6 @@ class SonarScannerExtension { return sonarQubeProperties; } - SonarScanner findOnPath() { - return SonarScanner.fromPath(project, project.projectDir).orElse(null) - } - Map computeSonarProperties(Project project) { def actionBroadcastMap = new HashMap>() actionBroadcastMap[project.path] = actionBroadcast diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBegin.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBegin.groovy index 4ac295a..7620491 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBegin.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerBegin.groovy @@ -41,7 +41,8 @@ class SonarScannerBegin extends DefaultTask { def run() { def sonarScannerExt = project.extensions.getByType(SonarScannerExtension) def sonarQubeProperties = sonarScannerExt.sonarQubeProperties - def sonarScanner = sonarScannerExt.sonarScanner.orElseThrow { + def sonarScanner = sonarScannerExt.sonarScanner.getOrNull() + if(sonarScanner == null) { throw new IllegalStateException("couldn't find SonarScanner on Gradle nor its executable on PATH") } assertKeyOnMap(sonarQubeProperties, "sonar.projectKey") diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEnd.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEnd.groovy index 13aae5f..d208592 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEnd.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerEnd.groovy @@ -20,7 +20,8 @@ class SonarScannerEnd extends DefaultTask { @TaskAction def run() { def sonarScannerExt = project.extensions.getByType(SonarScannerExtension) - def sonarScanner = sonarScannerExt.sonarScanner.orElseThrow { + def sonarScanner = sonarScannerExt.sonarScanner.getOrNull() + if(sonarScanner == null) { throw new IllegalStateException("couldn't find SonarScanner on Gradle nor its executable on PATH") } diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstall.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstall.groovy index ca8a228..0a7b503 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstall.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/SonarScannerInstall.groovy @@ -7,7 +7,6 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import wooga.gradle.dotnetsonar.SonarScannerExtension -import wooga.gradle.dotnetsonar.tasks.internal.SonarScanner import wooga.gradle.dotnetsonar.tasks.internal.SonarScannerInstaller class SonarScannerInstall extends DefaultTask { @@ -39,21 +38,21 @@ class SonarScannerInstall extends DefaultTask { @TaskAction def run() { SonarScannerExtension sonarScannerExt = project.extensions.getByType(SonarScannerExtension) - sonarScannerExt.sonarScanner = downloadSonarScanner() + def scannerExec = downloadSonarScannerFiles() + sonarScannerExt.sonarScanner = sonarScannerExt.createSonarScanner(scannerExec) } - private SonarScanner downloadSonarScanner() { + private File downloadSonarScannerFiles() { def version = version.get() def dotnetVersion = SonarScannerInstaller.DOTNET_VERSION def installationDir = installationDir.map{it.dir("sonarscanner-${version}")}.get() def scannerInstaller = SonarScannerInstaller.gradleBased(project) - def scannerExec = sourceURL.map{urlStr -> + return sourceURL.map{urlStr -> scannerInstaller.install(new URL(urlStr), installationDir.asFile) }.orElse( project.provider { scannerInstaller.install(version, dotnetVersion, installationDir.asFile) } ).get() - return SonarScanner.gradleBased(project, scannerExec, project.projectDir) } @Input @Optional diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShell.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShell.groovy new file mode 100644 index 0000000..e9cbfce --- /dev/null +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShell.groovy @@ -0,0 +1,30 @@ +package wooga.gradle.dotnetsonar.tasks.internal + +import org.gradle.api.Project +import org.gradle.process.ExecSpec + +class GradleMonoShell implements Shell { + + private Shell baseShell; + private File monoExecutable + + + static GradleMonoShell forProject(Project project, File monoExecutable) { + Shell baseShell = new GradleShell(project) + return new GradleMonoShell(baseShell, monoExecutable) + } + + GradleMonoShell(Shell baseShell, File monoExecutable) { + this.baseShell = baseShell + this.monoExecutable = monoExecutable + } + + @Override + public ShellResult execute(boolean logging=true, Closure execSpecClosure) { + return baseShell.execute { ExecSpec exec -> + execSpecClosure(exec) + exec.args = [exec.executable] + exec.args + exec.executable = monoExecutable.absolutePath + } + } +} diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleShell.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleShell.groovy index 4c74083..97adc0f 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleShell.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleShell.groovy @@ -34,4 +34,6 @@ class GradleShell implements Shell { stdErr.close() } } + + } diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScanner.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScanner.groovy index 1db3b62..582a01e 100644 --- a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScanner.groovy +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScanner.groovy @@ -5,24 +5,19 @@ import org.gradle.process.ExecSpec class SonarScanner { + static SonarScanner nativeBased(Project project, File executable, File workingDir) { + return new SonarScanner(new GradleShell(project), executable, workingDir) + } + + static SonarScanner monoBased(Project project, File executable, File monoExecutable, File workingDir) { + return new SonarScanner(GradleMonoShell.forProject(project, monoExecutable), executable, workingDir) + } private Shell shell; private File executable; private File workingDir; - static Optional fromPath(Project project, File workingDir) { - Shell shell = new GradleShell(project) - def maybeExecutable = OSOps.findInOSPath(shell, SonarScannerInstaller.EXECUTABLE_NAME) - return maybeExecutable.map{ - executable -> new SonarScanner(shell, executable, workingDir) - } - } - - static SonarScanner gradleBased(Project project, File executable, File workingDir) { - return new SonarScanner(new GradleShell(project), executable, workingDir) - } - - SonarScanner(Shell shell, File executable, File workingDir) { + private SonarScanner(Shell shell, File executable, File workingDir) { this.shell = shell this.executable = executable this.workingDir = workingDir diff --git a/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerFactory.groovy b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerFactory.groovy new file mode 100644 index 0000000..ee375cc --- /dev/null +++ b/src/main/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerFactory.groovy @@ -0,0 +1,40 @@ +package wooga.gradle.dotnetsonar.tasks.internal + +import org.gradle.api.Project +import org.gradle.api.provider.Provider + +class SonarScannerFactory { + + private Project project; + private Provider monoExecutable; + + public static SonarScannerFactory withPATHFallback(Project project, Provider monoExecutable) { + def monoProvider = monoExecutable.orElse(project.provider { + OSOps.findInOSPath(project, "mono").orElseThrow { + return new FileNotFoundException + ("Could not find 'mono' executable in OS PATH nor in SonarScannerExtension properties") + } + }) + return new SonarScannerFactory(project, monoProvider) + } + + SonarScannerFactory(Project project, Provider monoExecutable) { + this.project = project; + this.monoExecutable = monoExecutable + } + + public Optional fromPath(File workingDir) { + def maybeExecutable = OSOps.findInOSPath(project, SonarScannerInstaller.EXECUTABLE_NAME) + return maybeExecutable.map {executable -> fromExecutable(executable, workingDir) } + } + + public SonarScanner fromExecutable(File scannerExec, File workingDir) { + if(OSOps.isWindows()) { + return SonarScanner.nativeBased(project, scannerExec, workingDir) + } else { + def monoExec = monoExecutable.get() + return SonarScanner.monoBased(project, scannerExec, monoExec, workingDir) + } + } + +} diff --git a/src/main/resources/META-INF/gradle-plugins/net.wooga.plugins.properties b/src/main/resources/META-INF/gradle-plugins/net.wooga.gradle.dotnet-sonarqube.properties similarity index 89% rename from src/main/resources/META-INF/gradle-plugins/net.wooga.plugins.properties rename to src/main/resources/META-INF/gradle-plugins/net.wooga.gradle.dotnet-sonarqube.properties index 0cb63e2..c0886cc 100644 --- a/src/main/resources/META-INF/gradle-plugins/net.wooga.plugins.properties +++ b/src/main/resources/META-INF/gradle-plugins/net.wooga.gradle.dotnet-sonarqube.properties @@ -14,4 +14,4 @@ # limitations under the License. # -implementation-class=wooga.gradle.plugins.PluginsPlugin +implementation-class=wooga.gradle.dotnetsonar.DotNetSonarqubePlugin diff --git a/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShellSpec.groovy b/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShellSpec.groovy new file mode 100644 index 0000000..fa485a8 --- /dev/null +++ b/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/GradleMonoShellSpec.groovy @@ -0,0 +1,32 @@ +package wooga.gradle.dotnetsonar.tasks.internal + +import org.gradle.process.ExecSpec +import spock.lang.Specification + +import static wooga.gradle.dotnetsonar.utils.SpecFakes.argReflectingFakeExecutable +import static wooga.gradle.dotnetsonar.utils.SpecFakes.fakeShell +import static wooga.gradle.dotnetsonar.utils.SpecUtils.emptyTmpFile + +class GradleMonoShellSpec extends Specification { + + def "sets up execution specification to be ran using given mono executable"() { + given: "a mono executable" + def monoExec = emptyTmpFile("mono") + and: "a executable file to be executed using mono" + def runExec = argReflectingFakeExecutable("executable", 0) + and: "a base shell" + def shell = fakeShell() + + when: "executing given file using gradle mono shell" + def monoShell = new GradleMonoShell(shell, monoExec) + monoShell.execute { ExecSpec exec -> + exec.executable = runExec.absolutePath + exec.args = ["arg1", "arg2"] + } + + then: "spec should run using mono" + shell.lastExecSpec.executable == monoExec.absolutePath + and: "executable file and its arguments must be present in order in the argument list" + shell.lastExecSpec.args == [runExec.absolutePath, "arg1", "arg2"] + } +} diff --git a/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/OSOpsSpec.groovy b/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/OSOpsSpec.groovy index 2e61af0..618914c 100644 --- a/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/OSOpsSpec.groovy +++ b/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/OSOpsSpec.groovy @@ -7,8 +7,7 @@ import spock.lang.Unroll import java.nio.file.Paths -import static wooga.gradle.dotnetsonar.utils.SpecUtils.blocksWindowsFile -import static wooga.gradle.dotnetsonar.utils.SpecUtils.emptyTmpFile +import static wooga.gradle.dotnetsonar.utils.SpecUtils.* class OSOpsSpec extends ProjectSpec { @@ -20,7 +19,7 @@ class OSOpsSpec extends ProjectSpec { } @Unroll - @IgnoreIf({ !System.getProperty("os.name").toLowerCase().contains("windows") }) + @IgnoreIf({ !isWindows() }) def "finds external executable #filename on windows path"() { given: "a windows OS" and: "a executable file name" @@ -42,7 +41,7 @@ class OSOpsSpec extends ProjectSpec { } @Unroll - @IgnoreIf({ System.getProperty("os.name").toLowerCase().contains("windows") }) + @IgnoreIf({ isWindows() }) def "finds external executable #filename on unix path"() { given: "a unix OS" and: "a executable file name" @@ -64,7 +63,7 @@ class OSOpsSpec extends ProjectSpec { //https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/unblock-file?view=powershell-7.1 @Unroll - @IgnoreIf({ !System.getProperty("os.name").toLowerCase().contains("windows") }) + @IgnoreIf({ !isWindows() }) def "unblocks #file windows file"() { given: "a blocked NTFS windows file" if(blocked) { @@ -88,7 +87,7 @@ class OSOpsSpec extends ProjectSpec { emptyTmpFile("normalFile.bat") | false } - @IgnoreIf({ !System.getProperty("os.name").toLowerCase().contains("windows") }) + @IgnoreIf({ !isWindows() }) def "fails when unblocking non-existing windows file"() { given: "inexistent windows file" def falseFile = new File("imnothere") diff --git a/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerSpec.groovy b/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerSpec.groovy index 2a3c93b..6d3d141 100644 --- a/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerSpec.groovy +++ b/src/test/groovy/wooga/gradle/dotnetsonar/tasks/internal/SonarScannerSpec.groovy @@ -63,7 +63,8 @@ class SonarScannerSpec extends Specification { given: "a Sonar Scanner executable with a working dir" File scannerExec = new File("sonarscanner.exe") File workingDir = new File("a_dir") - and: "some sonarqube properties" + and: "a sonarqube login token" + def sonarToken = "token" and: "a shell executor" def shell = fakeShell() @@ -75,8 +76,5 @@ class SonarScannerSpec extends Specification { shell.lastExecSpec.executable == scannerExec.absolutePath shell.lastExecSpec.workingDir == workingDir shell.lastExecSpec.args == ["end", "-d:sonar.login=${sonarToken}"] - - where: - sonarToken << ["token"] } } diff --git a/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeExecutable.groovy b/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeExecutable.groovy index 539eab9..03f80d8 100644 --- a/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeExecutable.groovy +++ b/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeExecutable.groovy @@ -9,17 +9,34 @@ import static SpecUtils.isWindows class FakeExecutable { - static File argsReflector(Path fakeFilePath, int exitCode, boolean overwrites=true) { - File fakeExec = fakeFilePath.toFile() - if(fakeExec.exists()) { - if(overwrites) { - fakeExec.delete() - } else { - throw new IllegalArgumentException("File ${fakeFilePath} already exists") - } + static File runFirstParam(Path fakeFilePath, boolean overwrites=true) { + File fakeExec = setupExecutableFile(fakeFilePath, overwrites) + if(isWindows()) { + //https://stackoverflow.com/questions/935609/batch-parameters-everything-after-1 + fakeExec << """ + @echo off + echo [[${fakeExec.name}]] + for /f "tokens=1,* delims= " %%a in ("%*") do set ALL_BUT_FIRST=%%b + call %1 %ALL_BUT_FIRST% + echo [[end ${fakeExec.name}]] + exit %errorlevel% + """.stripIndent() + } else { + //running subscript with '.' in practice includes it on the mother script, including its parameters + fakeExec << + """#!/usr/bin/env bash + echo [[mono]] + fst_param=\$1 + shift + . "\${fst_param}" + echo [[end mono]] + exit \$? + """.stripIndent() } - fakeExec.createNewFile() - fakeExec.executable = true + } + + static File argsReflector(Path fakeFilePath, int exitCode, boolean overwrites=true) { + File fakeExec = setupExecutableFile(fakeFilePath, overwrites) if (isWindows()) { fakeExec << """ @echo off @@ -47,10 +64,29 @@ class FakeExecutable { } } + private static File setupExecutableFile(Path fakeFilePath, boolean overwrites) { + File fakeExec = fakeFilePath.toFile() + if (fakeExec.exists()) { + if (overwrites) { + fakeExec.delete() + } else { + throw new IllegalArgumentException("File ${fakeFilePath} already exists") + } + } + fakeExec.createNewFile() + fakeExec.executable = true + return fakeExec + } + static Result lastExecutionResults(ExecutionResult executionResult) { - return new Result(executionResult.standardOutput) + return lastExecutionResults(executionResult.standardOutput) } + static Result lastExecutionResults(String stdout) { + return new Result(stdout) + } + + static Result executionResults(File file, ExecutionResult executionResult) { return new Result(file, executionResult.standardOutput) } @@ -97,6 +133,7 @@ class FakeExecutable { private static ArrayList loadArgs(String stdOutput) { def lastExecutionOffset = stdOutput.lastIndexOf("[[arguments]]") if(lastExecutionOffset < 0) { + System.out.println(stdOutput) throw new IllegalArgumentException("stdout does not contains a execution of a file generated by " + "FakeExecutable.argsReflector") } diff --git a/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeShell.groovy b/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeShell.groovy index 69b948a..b9980bb 100644 --- a/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeShell.groovy +++ b/src/test/groovy/wooga/gradle/dotnetsonar/utils/FakeShell.groovy @@ -18,7 +18,7 @@ class FakeShell implements Shell { } @Override - ShellResult execute(boolean logging=false, Closure execSpecClosure) { + ShellResult execute(boolean logging=true, Closure execSpecClosure) { ExecSpec spec = new DefaultExecSpec(SpecFakes.fakeResolver()) execSpecClosure(spec) this.lastExecSpec = spec diff --git a/src/test/groovy/wooga/gradle/dotnetsonar/utils/SpecFakes.groovy b/src/test/groovy/wooga/gradle/dotnetsonar/utils/SpecFakes.groovy index 1070a2e..c4272bb 100644 --- a/src/test/groovy/wooga/gradle/dotnetsonar/utils/SpecFakes.groovy +++ b/src/test/groovy/wooga/gradle/dotnetsonar/utils/SpecFakes.groovy @@ -9,7 +9,6 @@ import static SpecUtils.isWindows class SpecFakes { - static File argReflectingFakeExecutable(String fakeFilePath, exitCode = 0) { String osAwareFakePath = isWindows() && !fakeFilePath.endsWith(".bat")? "${fakeFilePath}.bat" : @@ -19,6 +18,16 @@ class SpecFakes { return fakeExecFile } + static File runFirstParameterFakeExecutable(String fakeFilePath) { + String osAwareFakePath = isWindows() && !fakeFilePath.endsWith(".bat")? + "${fakeFilePath}.bat" : + fakeFilePath + def fakeExecFile = FakeExecutable.runFirstParam(Paths.get(osAwareFakePath)) + fakeExecFile.deleteOnExit() + return fakeExecFile + } + + static PathToFileResolver fakeResolver() { return new PathToFileResolver() { @Override