mibexsoftware / sonar-bitbucket-plugin

SonarQube plug-in to create pull request comments for found issues in Bitbucket Cloud
Other
122 stars 72 forks source link

NullPointerException when fetching pull request with deleted comments #67

Closed mikkopiu closed 6 years ago

mikkopiu commented 6 years ago

Expected Behavior

SonarQube Scanner in Jenkins (using this plugin) should update Bitbucket pull request comments with new scan statuses.

Actual Behavior

Received NullPointerException with stack trace:

ERROR: Error during SonarQube Scanner execution
java.lang.NullPointerException
    at ch.mibex.bitbucket.sonar.client.BitbucketClient.ch$mibex$bitbucket$sonar$client$BitbucketClient$$isFromUs$1(BitbucketClient.scala:145)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3$$anonfun$apply$2.apply(BitbucketClient.scala:150)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3$$anonfun$apply$2.apply(BitbucketClient.scala:150)
    at scala.collection.TraversableLike$WithFilter$$anonfun$map$2.apply(TraversableLike.scala:728)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at scala.collection.TraversableLike$WithFilter.map(TraversableLike.scala:727)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3.apply(BitbucketClient.scala:150)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3.apply(BitbucketClient.scala:149)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient.fetchPage(BitbucketClient.scala:312)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient.ch$mibex$bitbucket$sonar$client$BitbucketClient$$fetchPullRequestCommentsPage$1(BitbucketClient.scala:148)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$findOwnPullRequestComments$1.apply(BitbucketClient.scala:172)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$findOwnPullRequestComments$1.apply(BitbucketClient.scala:171)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient.forEachResultPage(BitbucketClient.scala:319)
    at ch.mibex.bitbucket.sonar.client.BitbucketClient.findOwnPullRequestComments(BitbucketClient.scala:171)
    at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob.ch$mibex$bitbucket$sonar$review$SonarReviewPostJob$$handlePullRequest(SonarReviewPostJob.scala:41)
    at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob$$anonfun$execute$1.apply(SonarReviewPostJob.scala:27)
    at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob$$anonfun$execute$1.apply(SonarReviewPostJob.scala:25)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob.execute(SonarReviewPostJob.scala:25)
    at org.sonar.scanner.postjob.PostJobWrapper.executeOn(PostJobWrapper.java:58)
    at org.sonar.scanner.phases.PostJobsExecutor.execute(PostJobsExecutor.java:66)
    at org.sonar.scanner.phases.PostJobsExecutor.execute(PostJobsExecutor.java:55)
    at org.sonar.scanner.phases.AbstractPhaseExecutor.execute(AbstractPhaseExecutor.java:84)
    at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:179)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:144)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:129)
    at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:261)
    at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:256)
    at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:245)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:144)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:129)
    at org.sonar.scanner.task.ScanTask.execute(ScanTask.java:47)
    at org.sonar.scanner.task.TaskContainer.doAfterStart(TaskContainer.java:84)
    at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:144)
    at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:129)
    at org.sonar.scanner.bootstrap.GlobalContainer.executeTask(GlobalContainer.java:119)
    at org.sonar.batch.bootstrapper.Batch.executeTask(Batch.java:116)
    at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
    at com.sun.proxy.$Proxy0.execute(Unknown Source)
    at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:233)
    at org.sonarsource.scanner.api.EmbeddedScanner.runAnalysis(EmbeddedScanner.java:151)
    at org.sonarsource.scanner.cli.Main.runAnalysis(Main.java:123)
    at org.sonarsource.scanner.cli.Main.execute(Main.java:77)
    at org.sonarsource.scanner.cli.Main.main(Main.java:61)

Plug-in version, SonarQube version, CI system, build type

The NPE is coming from this line: https://github.com/mibexsoftware/sonar-bitbucket-plugin/blob/v1.2.1/src/main/scala/ch/mibex/bitbucket/sonar/client/BitbucketClient.scala#L145

and when I tested the Bitbucket API endpoint it (fetchPage) is using (https://api.bitbucket.org/2.0/repositories/{account}/{repoSlug}/pullrequests/{prId}/comments), I noticed that deleted comments are returned with a user value of null, e.g.:

{
    "pagelen": 10,
    "size": 51,
    "values": [
        {
            "links": <snip>,
            "deleted": true,
            "pullrequest": <snip>,
            "content": {
                "raw": "",
                "markup": "markdown",
                "html": ""
            },
            "created_on": "2017-11-24T11:38:30.264053+00:00",
            "user": null,
            "inline": {
                "to": 376,
                "from": null,
                "path": "fake.js"
            },
            "updated_on": "2017-11-29T14:56:42.214876+00:00",
            "type": "pullrequest_comment",
            "id": 50383913
        },
    ]
}

I assume this bit then fails because user is null, and therefore cannot have a property uuid:

comment("user").asInstanceOf[Map[String, Any]]("uuid").asInstanceOf[String] equals uuid

It sounds like something has changed in the Bitbucket REST API but I wasn't able to find any changelogs etc. describing such a change. This has been working fine so far, started happening today.

mikkopiu commented 6 years ago

I'm not too familiar with Scala, would this be an idiomatic fix:

def isFromUs(comment: Map[String, Any]): Boolean = {
  val user = comment("user").asInstanceOf[Map[String, Any]]
  if (user == null) false else user("uuid").asInstanceOf[String] equals uuid
}

or possibly:

    def isFromUs(comment: Map[String, Any]): Boolean =
      Option(comment("user").asInstanceOf[Map[String, Any]])
        .getOrElse(Map("uuid" -> null))("uuid").asInstanceOf[String] equals uuid

?

rhaex commented 6 years ago

We are having the exact same issue.

Plugin version: 1.2.1 SonarQube version: 6.7 (build 33306) CI system: Bamboo, version: 6.2.1 (build 60208) Build type: Maven3 using 'Sonar for Bamboo plugin', Java build

nfalco79 commented 6 years ago

same issue here

Jtag84 commented 6 years ago

Same issue

Plugin version: 1.2.1 SonarQube version: 6.2 Bamboo version: 5.14.4.1 build 51417

andrew-makarenko commented 6 years ago

I experience the same issue

kmalmur commented 6 years ago

After updating the plugin to 1.2.2 I still get the same issue:

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:sonarqube'.
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:100)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:97)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:87)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:625)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:580)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: java.lang.NullPointerException
        at ch.mibex.bitbucket.sonar.client.BitbucketClient.ch$mibex$bitbucket$sonar$client$BitbucketClient$$isFromUs$1(BitbucketClient.scala:147)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3$$anonfun$apply$2.apply(BitbucketClient.scala:152)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3$$anonfun$apply$2.apply(BitbucketClient.scala:152)
        at scala.collection.TraversableLike$WithFilter$$anonfun$map$2.apply(TraversableLike.scala:728)
        at scala.collection.immutable.List.foreach(List.scala:381)
        at scala.collection.TraversableLike$WithFilter.map(TraversableLike.scala:727)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3.apply(BitbucketClient.scala:152)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$3.apply(BitbucketClient.scala:151)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient.fetchPage(BitbucketClient.scala:314)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient.ch$mibex$bitbucket$sonar$client$BitbucketClient$$fetchPullRequestCommentsPage$1(BitbucketClient.scala:150)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$findOwnPullRequestComments$1.apply(BitbucketClient.scala:174)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient$$anonfun$findOwnPullRequestComments$1.apply(BitbucketClient.scala:173)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient.forEachResultPage(BitbucketClient.scala:321)
        at ch.mibex.bitbucket.sonar.client.BitbucketClient.findOwnPullRequestComments(BitbucketClient.scala:173)
        at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob.ch$mibex$bitbucket$sonar$review$SonarReviewPostJob$$handlePullRequest(SonarReviewPostJob.scala:43)
        at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob$$anonfun$execute$1.apply(SonarReviewPostJob.scala:29)
        at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob$$anonfun$execute$1.apply(SonarReviewPostJob.scala:27)
        at scala.collection.immutable.List.foreach(List.scala:381)
        at ch.mibex.bitbucket.sonar.review.SonarReviewPostJob.execute(SonarReviewPostJob.scala:27)
        at org.sonar.scanner.postjob.PostJobWrapper.executeOn(PostJobWrapper.java:58)
        at org.sonar.scanner.phases.PostJobsExecutor.execute(PostJobsExecutor.java:66)
        at org.sonar.scanner.phases.PostJobsExecutor.execute(PostJobsExecutor.java:55)
        at org.sonar.scanner.phases.AbstractPhaseExecutor.execute(AbstractPhaseExecutor.java:94)
        at org.sonar.scanner.scan.ModuleScanContainer.doAfterStart(ModuleScanContainer.java:180)
        at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
        at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:121)
        at org.sonar.scanner.scan.ProjectScanContainer.scan(ProjectScanContainer.java:288)
        at org.sonar.scanner.scan.ProjectScanContainer.scanRecursively(ProjectScanContainer.java:283)
        at org.sonar.scanner.scan.ProjectScanContainer.doAfterStart(ProjectScanContainer.java:261)
        at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
        at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:121)
        at org.sonar.scanner.task.ScanTask.execute(ScanTask.java:48)
        at org.sonar.scanner.task.TaskContainer.doAfterStart(TaskContainer.java:84)
        at org.sonar.core.platform.ComponentContainer.startComponents(ComponentContainer.java:135)
        at org.sonar.core.platform.ComponentContainer.execute(ComponentContainer.java:121)
        at org.sonar.scanner.bootstrap.GlobalContainer.executeTask(GlobalContainer.java:121)
        at org.sonar.batch.bootstrapper.Batch.doExecuteTask(Batch.java:116)
        at org.sonar.batch.bootstrapper.Batch.executeTask(Batch.java:111)
        at org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:63)
        at org.sonarsource.scanner.api.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:60)
        at com.sun.proxy.$Proxy100.execute(Unknown Source)
        at org.sonarsource.scanner.api.EmbeddedScanner.doExecute(EmbeddedScanner.java:233)
        at org.sonarsource.scanner.api.EmbeddedScanner.runAnalysis(EmbeddedScanner.java:151)
        at org.sonarqube.gradle.SonarQubeTask.run(SonarQubeTask.java:99)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
        at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.doExecute(DefaultTaskClassInfoStore.java:142)
        at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:135)
        at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:122)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:762)
        at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:729)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:121)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:110)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
mikkopiu commented 6 years ago

@kmalmur : I think 1.2.2 is the previously released version, and the fix in #68 hasn't made it into a release yet.

kmalmur commented 6 years ago

@mikkopiu from what I see in the change log, the fix should be already there: https://github.com/mibexsoftware/sonar-bitbucket-plugin/compare/v1.2.2...master

mikkopiu commented 6 years ago

Oh, so did you build your own version from master?

kmalmur commented 6 years ago

@mikkopiu sorry, I've used a wrong diff. You are right, this is not released yet. Do you have any estimations of the next release?

mikkopiu commented 6 years ago

@kmalmur : Unfortunately not, I'm just a contributor -- maybe @mrueegg might know?

rhaex commented 6 years ago

You could clone (or download the zip of) the master branch, and on the root run a mvn package, and upload the generated jar to your Sonar server in the meantime. That's how I did it until it is actually shipped in a new release.

Of course this requires you to have a JDK and Maven installed.

mrueegg commented 6 years ago

I'll try to release a new version today.

mrueegg commented 6 years ago

@kmalmur I've just created a new release 1.2.3. Please build a JAR from this release with mvn clean package.

jonbakerarco commented 6 years ago

Will there be a release JAR for this like there was for 1.2.1 and before or do we need to build it ourselves?

NielsClay commented 6 years ago

When will this be released? Is there a .jar available?