Skip to content
This repository has been archived by the owner on Dec 5, 2024. It is now read-only.

Commit

Permalink
Add git repository info as sonarqube property defaults (#8)
Browse files Browse the repository at this point in the history
Changed default values of sonarqube properties sonar.projectKey and sonar.projectName to be derived from git. If no git repository exists, the property defaults to null, using the default values provided by sonarqube gradle extension itself.

Sonarqube property sonar.branch.name was added to provide support to multibranch analysis. It uses the git branch provided by atlas-github, and if it detects the branch name as a PR branch (with name starting with "PR-"), then it tries to find this branch "real" name using atlas-github exposed github api. Finally if it does not finds anything, it unsets the property.

Co-authored-by: Joaquim Neto <[email protected]>
  • Loading branch information
Joaquimmnetto and Joaquimmnetto authored Sep 28, 2021
1 parent d893ff8 commit 7417bab
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 86 deletions.
25 changes: 23 additions & 2 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
#!groovy
@Library('github.com/wooga/[email protected]') _

withCredentials([string(credentialsId: 'atlas_plugins_sonar_token', variable: 'sonar_token')]) {
buildGradlePlugin platforms: ['macos','windows', 'linux'], sonarToken: sonar_token
withCredentials([usernamePassword(credentialsId: 'github_integration', passwordVariable: 'githubPassword', usernameVariable: 'githubUser'),
usernamePassword(credentialsId: 'github_integration_2', passwordVariable: 'githubPassword2', usernameVariable: 'githubUser2'),
usernamePassword(credentialsId: 'github_integration_3', passwordVariable: 'githubPassword3', usernameVariable: 'githubUser3'),
string(credentialsId: 'atlas_plugins_sonar_token', variable: 'sonar_token')]) {
def testEnvironment = [
'macos':
[
"ATLAS_GITHUB_INTEGRATION_USER=${githubUser}",
"ATLAS_GITHUB_INTEGRATION_PASSWORD=${githubPassword}"
],
'windows':
[
"ATLAS_GITHUB_INTEGRATION_USER=${githubUser2}",
"ATLAS_GITHUB_INTEGRATION_PASSWORD=${githubPassword2}"
],
'linux':
[
"ATLAS_GITHUB_INTEGRATION_USER=${githubUser3}",
"ATLAS_GITHUB_INTEGRATION_PASSWORD=${githubPassword3}"
]
]

buildGradlePlugin platforms: ['macos','windows', 'linux'], sonarToken: sonar_token, testEnvironment: testEnvironment
}
13 changes: 13 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,17 @@ github {

dependencies {
api 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.2.0'
implementation 'gradle.plugin.net.wooga.gradle:atlas-github:2.+'
testImplementation 'com.wooga.spock.extensions:spock-github-extension:0.2.0'
testImplementation 'org.ajoberstar.grgit:grgit-core:4.1.0'
}

configurations.all {
resolutionStrategy {
force 'org.codehaus.groovy:groovy-all:2.5.12'
force 'org.codehaus.groovy:groovy-macro:2.5.12'
force 'org.codehaus.groovy:groovy-nio:2.5.12'
force 'org.codehaus.groovy:groovy-sql:2.5.12'
force 'org.codehaus.groovy:groovy-xml:2.5.12'
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package wooga.gradle.dotnetsonar.tasks

import com.wooga.spock.extensions.github.GithubRepository
import com.wooga.spock.extensions.github.Repository
import com.wooga.spock.extensions.github.api.RateLimitHandlerWait
import com.wooga.spock.extensions.github.api.TravisBuildNumberPostFix
import org.ajoberstar.grgit.Grgit
import org.gradle.process.internal.ExecException
import spock.lang.Unroll
import wooga.gradle.dotnetsonar.SonarScannerExtension
import wooga.gradle.dotnetsonar.tasks.utils.PluginIntegrationSpec
import wooga.gradle.dotnetsonar.utils.FakeExecutable

Expand All @@ -11,12 +15,68 @@ import static wooga.gradle.dotnetsonar.utils.SpecUtils.rootCause

class SonarScannerBeginTaskIntegrationSpec extends PluginIntegrationSpec {

@GithubRepository(
usernameEnv = "ATLAS_GITHUB_INTEGRATION_USER",
tokenEnv = "ATLAS_GITHUB_INTEGRATION_PASSWORD",
repositoryPostFixProvider = [TravisBuildNumberPostFix.class],
rateLimitHandler = RateLimitHandlerWait.class,
resetAfterTestCase = true
)
def "task executes sonar scanner tool begin command with git-based properties"(Repository testRepo) {
given: "a sonar scanner executable"
def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 0)
and: "a set up sonar scanner extension"
buildFile << forceAddObjectsToExtension(fakeSonarScannerExec)
and: "a set up github extension"
buildFile << """
github {
repositoryName = "${testRepo.fullName}"
username = "${testRepo.userName}"
token = "${testRepo.token}"
}
"""
and: "a pull request open for this repository"
def prBranchName = "realbranch"
testRepo.createBranch(prBranchName)
testRepo.commit("commitmsg", prBranchName)
def pr = testRepo.createPullRequest("Test", prBranchName, testRepo.defaultBranch.name, "description")

and: "a setup PR git repository"
def git = Grgit.init(dir: projectDir)
git.commit(message: "any message")
git.checkout(branch: "PR-${pr.number}", createBranch: true)

when:
def result = runTasksSuccessfully("sonarScannerBegin")

then:
def execResults = FakeExecutable.lastExecutionResults(result)
def companyName = testRepo.fullName.split("/")[0]
execResults.args.contains("-n:${testRepo.name}".toString())
execResults.args.contains("-k:${companyName}_${testRepo.name}".toString())
execResults.args.contains("-d:sonar.branch.name=${prBranchName}".toString())
}

def "task executes sonar scanner tool begin command with extension properties"() {
given: "a sonar scanner executable"
def fakeSonarScannerExec = argReflectingFakeExecutable("sonarscanner", 0)
and: "a set up sonar scanner extension"
buildFile << forceAddObjectsToExtension(fakeSonarScannerExec)

and: "a configured github extension"
def companyName = "company"
def repoName = "repo"
buildFile << """
github {
repositoryName = "${companyName}/${repoName}"
}
"""
and: "a git repository on branch"
def branchName = "branch"
def git = Grgit.init(dir: projectDir)
git.commit(message: "any message")
git.checkout(branch: branchName, createBranch: true)

and: "a configured sonarqube extension"
def projectVersion = "0.0.1"
buildFile << """
Expand All @@ -28,12 +88,16 @@ class SonarScannerBeginTaskIntegrationSpec extends PluginIntegrationSpec {
}
}
"""

when: "running the sonarScannerBegin task"
def result = runTasksSuccessfully("sonarScannerBegin")

then:
def execResults = FakeExecutable.lastExecutionResults(result)
execResults.args.contains("-k:${moduleName}".toString())
execResults.args.contains("-n:${repoName}".toString())
execResults.args.contains("-k:${companyName}_${repoName}".toString())
execResults.args.contains("-v:${projectVersion}".toString())
execResults.args.contains("-d:sonar.branch.name=${branchName}".toString())
execResults.args.contains("-d:sonar.exclusions=src")
!execResults.args.contains("-d:sonar.sources=")
!execResults.args.contains("-d:sonar.prop=")
Expand Down
132 changes: 51 additions & 81 deletions src/main/groovy/wooga/gradle/dotnetsonar/DotNetSonarqubePlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ package wooga.gradle.dotnetsonar

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.UncheckedIOException
import org.gradle.api.provider.Provider
import org.sonarqube.gradle.ActionBroadcast
import org.sonarqube.gradle.SonarQubeExtension
import org.sonarqube.gradle.SonarQubeProperties
import wooga.gradle.dotnetsonar.tasks.BuildSolution
import wooga.gradle.dotnetsonar.tasks.SonarScannerBegin
import wooga.gradle.dotnetsonar.tasks.SonarScannerEnd
import wooga.gradle.github.base.GithubBasePlugin
import wooga.gradle.github.base.GithubPluginExtension
import wooga.gradle.github.base.internal.DefaultGithubPluginExtension

import static SonarScannerExtension.*

class DotNetSonarqubePlugin implements Plugin<Project> {

@Override
void apply(Project project) {
project.plugins.apply(GithubBasePlugin)
def githubExt = project.extensions.findByType(DefaultGithubPluginExtension)

def actionBroadcast = new ActionBroadcast<SonarQubeProperties>()
def sonarScannerExt = project.extensions.create(SONARSCANNER_EXTENSION_NAME, SonarScannerExtension, project, actionBroadcast)
def sonarQubeExt = project.extensions.create(SonarQubeExtension.SONARQUBE_EXTENSION_NAME, SonarQubeExtension, actionBroadcast)
sonarQubeExt.properties { it ->
defaultSonarProperties(project, it)
defaultSonarProperties(project, githubExt, it)
}

project.tasks.register(MS_BUILD_TASK_NAME, BuildSolution).configure { buildTask ->
Expand All @@ -46,94 +54,56 @@ class DotNetSonarqubePlugin implements Plugin<Project> {
}
}

static final void defaultSonarProperties(Project project, SonarQubeProperties properties) {
static final void defaultSonarProperties(Project project, GithubPluginExtension githubExt,
SonarQubeProperties properties) {
def companyNameProvider = githubExt.repositoryName.map{String fullRepoName -> fullRepoName.split("/")[0]}
def repoNameProvider = githubExt.repositoryName.map{String fullRepoName -> fullRepoName.split("/")[1]}
def keyProvider = companyNameProvider.map { comp ->
return repoNameProvider.map {repoName -> "${comp}_${repoName}"}.getOrNull()
}
def branchProvider = localBranchProviderWithPR(project, githubExt).map { it.trim().isEmpty() ? null : it }
properties.with {
property("sonar.login", System.getenv('SONAR_TOKEN'))
property("sonar.host.url", System.getenv('SONAR_HOST'))
//would be better if this was associated to github repository, see atlas-plugins
property("sonar.projectKey", project.rootProject.name)
property("sonar.projectName", project.rootProject.name)
//property("sonar.sources", ".")
property("sonar.projectKey", keyProvider.getOrNull())
property("sonar.projectName", repoNameProvider.getOrNull())
property("sonar.branch.name", branchProvider.getOrNull())
if(project.version != null) {
property("sonar.version", project.version.toString())
}
}
}
}

/*
steps:
1. Create solution if not exists already //unity task
2. Setup unity code coverage //unity task
3. download dotnet-sonarscanner if not exists //done(ish)
3. SonarBegin
4. Build C# solution //done(ish)
5. SonarEnd
* */
static Provider<String> localBranchProviderWithPR(Project project, GithubPluginExtension githubExt) {
def clientProvider = emptyProviderForException(project, githubExt.clientProvider, UncheckedIOException)

return githubExt.branchName.map {currentBranch ->
return clientProvider.map {client ->
def repository = client.getRepository(githubExt.repositoryName.get())
if (currentBranch.toUpperCase().startsWith("PR-")) {
def maybePrNumber = currentBranch.replace("PR-", "").trim()
if (maybePrNumber.isNumber()) {
def prNumber = Integer.valueOf(maybePrNumber)
return repository.getPullRequest(prNumber).head.ref
}
return null
}
}.getOrElse(currentBranch)
}
}

// task(setupUnityProject, type:wooga.gradle.unity.tasks.Unity) {
// args "-executeMethod", "UnityEditor.SyncVS.SyncSolution"
// quit = true
// }
// tasks.withType(wooga.gradle.unity.tasks.Test) {
// it.dependsOn setupCodeCoverage
// it.args "-enableCodeCoverage"
// it.args "-debugCodeOptimization"
// it.args "-coverageResultsPath", "build/codeCoverage"
// it.args "-coverageOptions", "generateAdditionalMetrics"
// }
// task setupCodeCoverage {
// dependsOn setupUnityProject
// doLast {
// def manifest = new File(project.projectDir, 'Packages/manifest.json')
// def jsonSlurper = new JsonSlurper()
// def data = jsonSlurper.parse(manifest)
// data.dependencies["com.unity.testtools.codecoverage"] = "1.1.0"
// def json_str = JsonOutput.toJson(data)
// def json_beauty = JsonOutput.prettyPrint(json_str)
// manifest.write(json_beauty)
// }
// }

// configuration inspired by https://github.com/MirageNet/Mirage/blob/master/.github/workflows/main.yml
// task(sonarBegin, type:Exec) {
// dependsOn test
// executable "packages/dotnet-framework-sonarscanner/tools/SonarScanner.MSBuild.exe"
// args "begin"
// args "/k:${rootProject.name}"
// args "/v:${version}"
// args "/d:sonar.login=${System.getenv('SONAR_TOKEN')}"
// args "/d:sonar.host.url=${System.getenv('SONAR_HOST')}"
// args "/d:sonar.exclusions=Assets/Paket.Unity3D/**"
// args "/d:sonar.cpd.exclusions=Assets/Tests/**"
// args "/d:sonar.coverage.exclusions=Assets/Tests/**"
// args "/d:sonar.cs.nunit.reportsPaths=build/reports/unity/*/*.xml"
// args "/d:sonar.cs.opencover.reportsPaths=build/codeCoverage/**/*.xml"
// }
//
// task(sonarMsbuild) {
// dependsOn sonarBegin
// doLast {
// def contentPath = "${unity.unityPath.getParent()}\\..\\Editor\\Data"
// exec {
// executable "${contentPath}\\NetCore\\Sdk-2.2.107\\dotnet"
// environment "FrameworkPathOverride", "${contentPath}\\MonoBleedingEdge"
// args "build"
// args "${project.name}.sln"
// }
// }
// }
//
// task(sonarEnd, type:Exec) {
// dependsOn sonarMsbuild
// executable "packages/dotnet-framework-sonarscanner/tools/SonarScanner.MSBuild.exe"
// args "end"
// args "/d:sonar.login=${System.getenv('SONAR_TOKEN')}"
// }
//
// task(sonarqube) {
// mustRunAfter test
// dependsOn sonarBegin, sonarEnd, sonarMsbuild
// }
//
protected static <T> Provider<T> emptyProviderForException(Project project,
Provider<T> provider,
Class<? extends Throwable> exceptionClass) {
return project.provider {
try {
return provider.get()
}catch(Throwable e) {
if(exceptionClass.isInstance(e)) {
return null
}
throw e
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class SonarScannerBegin extends DefaultTask {
assertKeyOnMap(properties, "sonar.version")
sonarScanner.begin(
properties["sonar.projectKey"].toString(),
properties["sonar.projectName"].toString(),
properties["sonar.projectName"]?.toString(),
properties["sonar.version"]?.toString(),
properties)
}
Expand Down

0 comments on commit 7417bab

Please sign in to comment.