Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import com.codacy.intellij.plugin.services.common.Config.Companion.CODACY_TOOLS_
import com.codacy.intellij.plugin.services.common.Config.Companion.CODACY_YAML_NAME
import com.codacy.intellij.plugin.services.common.GitRemoteParser
import com.codacy.intellij.plugin.services.common.IconUtils
import com.codacy.intellij.plugin.services.common.OsType
import com.codacy.intellij.plugin.services.common.SystemDetectionService
import com.codacy.intellij.plugin.services.git.GitProvider
import com.codacy.intellij.plugin.services.paths.PathsBehaviour
import com.codacy.intellij.plugin.services.paths.behaviour.PathsUnix
Expand Down Expand Up @@ -172,19 +174,19 @@ class CodacyCliService() {
repository: String,
project: Project,
): CodacyCliService {
val systemOs = System.getProperty("os.name").lowercase()
val systemOs = SystemDetectionService.detectOs()
val cli = project.getService(CodacyCliService::class.java)

val (cliBehaviour, pathsBehaviour) = when {
systemOs == "mac os x" || systemOs.contains("darwin") -> {
systemOs == OsType.MacOS -> {
CliUnix() to PathsUnix()
}

systemOs == "linux" -> {
systemOs == OsType.Linux -> {
CliUnix() to PathsUnix()
}

systemOs.contains("windows") -> {
systemOs == OsType.Windows -> {
val process = ProcessBuilder("wsl", "--list", "--quiet")
.redirectErrorStream(true)
.start()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.codacy.intellij.plugin.services.common

import com.intellij.openapi.application.ApplicationNamesInfo

enum class OsType {
Windows,
MacOS,
Linux,
Unknown;

override fun toString(): String {
return when (this) {
Windows -> "Windows"
MacOS -> "MacOS"
Linux -> "Linux"
Unknown -> "Unknown"
}
}


companion object {
fun fromString(osName: String): OsType {
return when (osName.lowercase()) {
"windows" -> Windows
"macos", "mac os", "mac" -> MacOS
"linux" -> Linux
else -> Unknown // Default to Unknown if OS is not recognized
}
}
}
}


enum class IdeType {
Intellij,
Webstorm,
Pycharm,
Phpstorm,
Rubymine,
Goland,
Rider,
Clion,
Datagrip,
Dataspell,
Unknown;

override fun toString(): String {
return when (this) {
Intellij -> "IntelliJ"
Webstorm -> "WebStorm"
Pycharm -> "PyCharm"
Phpstorm -> "PhpStorm"
Rubymine -> "RubyMine"
Goland -> "GoLand"
Rider -> "Rider"
Clion -> "CLion"
Datagrip -> "DataGrip"
Dataspell -> "DataSpell"
Unknown -> "Unknown"
}
}

companion object {

fun fromString(ideName: String): IdeType {
return when (ideName.lowercase()) {
"intellij" -> Intellij
"webstorm" -> Webstorm
"pycharm" -> Pycharm
"phpstorm" -> Phpstorm
"rubymine" -> Rubymine
"goland" -> Goland
"rider" -> Rider
"clion" -> Clion
"datagrip" -> Datagrip
"dataspell" -> Dataspell
else -> Unknown
}
}
}
}


object SystemDetectionService {


fun detectOs(): OsType {
val systemOs = System.getProperty("os.name").lowercase()
return when {
systemOs.contains("win") || systemOs.contains("windows") -> OsType.Windows
systemOs.contains("mac os x") || systemOs.contains("darwin") -> OsType.MacOS
listOf("nux", "nix", "aix", "linux").any { systemOs.contains(it) } -> OsType.Linux
else -> OsType.Unknown
}
}

fun detectIde(): IdeType = IdeType
.fromString(
ApplicationNamesInfo.getInstance().productName.lowercase()
)


}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.codacy.intellij.plugin.services.git

import com.codacy.intellij.plugin.services.api.Api
import com.codacy.intellij.plugin.services.api.models.*
import com.codacy.intellij.plugin.services.common.*
import com.codacy.intellij.plugin.telemetry.BranchStateChangeEvent
import com.codacy.intellij.plugin.services.api.models.RepositoryData
import com.codacy.intellij.plugin.services.common.Config
import com.codacy.intellij.plugin.services.common.GitRemoteParser
import com.codacy.intellij.plugin.services.common.Logger
import com.codacy.intellij.plugin.services.common.TimeoutManager
import com.codacy.intellij.plugin.telemetry.PullRequestStateChangeEvent
import com.codacy.intellij.plugin.telemetry.RepositoryStateChangeEvent
import com.codacy.intellij.plugin.telemetry.Telemetry
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
Expand All @@ -21,7 +25,11 @@ const val MAX_LOAD_ATTEMPTS: Int = 5
class RepositoryManager(private val project: Project) {

enum class RepositoryManagerState {
NoRepository, NoRemote, Initializing, NeedsAuthentication, Loaded
Initializing,
NeedsAuthentication,
NoGitRepository,
Loaded,
NoRepository
}

enum class PullRequestState {
Expand Down Expand Up @@ -63,7 +71,7 @@ class RepositoryManager(private val project: Project) {
}

if (currentRepository != gitRepository) {
ProgressManager.getInstance().run(object: Task.Backgroundable(project, "Opening repository", false) {
ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Opening repository", false) {
override fun run(indicator: com.intellij.openapi.progress.ProgressIndicator) {
GlobalScope.launch {
currentRepository = gitRepository
Expand All @@ -74,7 +82,7 @@ class RepositoryManager(private val project: Project) {
setNewState(RepositoryManagerState.Initializing)
} else {
if (remoteUrl.isNullOrEmpty()) {
setNewState(RepositoryManagerState.NoRemote)
setNewState(RepositoryManagerState.NoGitRepository)
Logger.error("No remote found")
return@launch
}
Expand Down Expand Up @@ -117,13 +125,16 @@ class RepositoryManager(private val project: Project) {
branch = currentHead
pullRequest = null
notifyDidUpdatePullRequest()
prState = PullRequestState.NoPullRequest
setNewPullRequestState(PullRequestState.NoPullRequest)

loadPullRequest()
} else {
val currentHeadCommitSHA = GitProvider.getHeadCommitSHA(project)
val currentHeadAhead: Boolean = GitProvider.isHeadAhead(project)
if (currentHeadCommitSHA != null && pullRequest != null && prState === PullRequestState.Loaded && currentHeadCommitSHA !== pullRequest?.meta?.headCommitSHA && !currentHeadAhead) {
if (refreshTimeout.isTimeoutRunning()) refreshTimeout.clearTimeout()
if (isPrLoadedAndNotCheckedOut(pullRequest, prState, currentHeadCommitSHA, currentHeadAhead)) {
if (refreshTimeout.isTimeoutRunning()) {
refreshTimeout.clearTimeout()
}
refreshTimeout.startTimeout(10000) {
Logger.info("Pushed all local commits, refreshing pull request...")
pullRequestInstance!!.refresh()
Expand All @@ -132,6 +143,18 @@ class RepositoryManager(private val project: Project) {
}
}

private fun isPrLoadedAndNotCheckedOut(
pullRequest: PullRequest?,
prState: PullRequestState,
currentHeadCommitSHA: String,
currentHeadAhead: Boolean
): Boolean =
pullRequest != null &&
prState === PullRequestState.Loaded &&
currentHeadCommitSHA != pullRequest.meta?.headCommitSHA &&
!currentHeadAhead


@OptIn(DelicateCoroutinesApi::class)
private suspend fun loadPullRequest() {
if (loadTimeout.isTimeoutRunning()) loadTimeout.clearTimeout()
Expand All @@ -141,17 +164,17 @@ class RepositoryManager(private val project: Project) {
branch = currentRepository?.currentBranch?.name
if (branch.isNullOrBlank()) {
Logger.warn("No HEAD information found: ${currentRepository?.currentBranch}")
prState = PullRequestState.NoPullRequest
setNewPullRequestState(PullRequestState.NoPullRequest)
return
}

if (branch == repo.defaultBranch.name) {
Logger.info("Current branch is the default branch: $branch")
prState = PullRequestState.NoPullRequest
setNewPullRequestState(PullRequestState.NoPullRequest)
return
}

ProgressManager.getInstance().run(object: Task.Backgroundable(project, "Loading pull request", false) {
ProgressManager.getInstance().run(object : Task.Backgroundable(project, "Loading pull request", false) {
override fun run(indicator: com.intellij.openapi.progress.ProgressIndicator) {
GlobalScope.launch {
try {
Expand All @@ -162,9 +185,7 @@ class RepositoryManager(private val project: Project) {

if (pr == null) {
Logger.info("No PR found in Codacy for: $branch")
prState = PullRequestState.NoPullRequest
// Evaluate branch state after PR discovery fails
evaluateBranchState()
setNewPullRequestState(PullRequestState.NoPullRequest)

if (loadAttempts < MAX_LOAD_ATTEMPTS) {
loadTimeout.startTimeout(LOAD_RETRY_TIME) {
Expand All @@ -185,9 +206,7 @@ class RepositoryManager(private val project: Project) {
notifyDidUpdatePullRequest()
}

prState = PullRequestState.Loaded
// Evaluate branch state after PR is loaded
evaluateBranchState()
setNewPullRequestState(PullRequestState.Loaded)
} catch (e: Exception) {
Logger.error("Error loading pull request: ${e.message}")
}
Expand All @@ -208,11 +227,21 @@ class RepositoryManager(private val project: Project) {
val stateChange: Boolean = newState !== state
state = newState
if (stateChange) {
Telemetry.track(RepositoryStateChangeEvent(newState.name))
// TODO("execute command setContext")
notifyDidChangeState()
}
}

fun setNewPullRequestState(newState: PullRequestState) {
val stateChange: Boolean = newState !== prState
prState = newState
if (stateChange) {
// Track pull request state change telemetry
Telemetry.track(PullRequestStateChangeEvent(newState.name))
}
}

fun onDidUpdatePullRequest(listener: () -> Unit): Disposable {
onDidUpdatePullRequestListeners.add(listener)
return Disposable { onDidUpdatePullRequestListeners.remove(listener) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.codacy.intellij.plugin.services.paths

import com.codacy.intellij.plugin.services.common.OsType
import com.codacy.intellij.plugin.services.common.SystemDetectionService
import com.codacy.intellij.plugin.services.paths.behaviour.PathsUnix
import com.codacy.intellij.plugin.services.paths.behaviour.PathsWindows
import com.intellij.openapi.project.Project
Expand All @@ -16,21 +18,16 @@ interface PathsBehaviour {

object Factory {
fun build(): PathsBehaviour {
val systemOs = System.getProperty("os.name").lowercase()
val osType = SystemDetectionService.detectOs()

val pathsInstance = when {
systemOs == "mac os x" || systemOs.contains("darwin") -> PathsUnix()

systemOs == "linux" -> PathsUnix()

systemOs.contains("windows") -> PathsWindows()

else -> {
throw IllegalStateException("Unsupported OS: $systemOs")
}
osType == OsType.MacOS -> PathsUnix()
osType == OsType.Linux -> PathsUnix()
osType == OsType.Windows -> PathsWindows()
else -> throw IllegalStateException("Unsupported OS: $osType")
}

return pathsInstance
}
}
}
}
22 changes: 11 additions & 11 deletions src/main/kotlin/com/codacy/intellij/plugin/telemetry/Telemetry.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.codacy.intellij.plugin.telemetry

import com.codacy.intellij.plugin.services.common.Config
import com.codacy.intellij.plugin.services.common.SystemDetectionService
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.components.Service
import com.segment.analytics.kotlin.core.Analytics

Expand All @@ -21,18 +23,15 @@ data object ExtensionUnloadedEvent : TelemetryEvent("extension_unloaded") {
}

data class RepositoryStateChangeEvent(val state: String) : TelemetryEvent("Repository State Change") {
override fun toPayload(): Map<String, Any?> =
mapOf("state" to state)
override fun toPayload(): Map<String, Any?> = mapOf("state" to state)
}

data class BranchStateChangeEvent(val state: String) : TelemetryEvent("Branch State Change") {
override fun toPayload(): Map<String, Any?> =
mapOf("state" to state)
override fun toPayload(): Map<String, Any?> = mapOf("state" to state)
}

data class PullRequestStateChangeEvent(val state: String) : TelemetryEvent("Pull Request State Change") {
override fun toPayload(): Map<String, Any?> =
mapOf("state" to state)
override fun toPayload(): Map<String, Any?> = mapOf("state" to state)
}

data class UnexpectedErrorEvent(val message: String) : TelemetryEvent("Unexpected Error") {
Expand All @@ -43,9 +42,10 @@ data class UnexpectedErrorEvent(val message: String) : TelemetryEvent("Unexpecte
@Service
object Telemetry {

private const val IDE: String = "jetbrains"

private val os: String = (System.getProperty("os.name") ?: "unknown").lowercase()
private val ide: String by lazy {
"JetBrains ${SystemDetectionService.detectIde()}"
}
private val os: String by lazy { SystemDetectionService.detectOs().toString() }

private val analytics: Analytics by lazy {
Analytics("ckhmOOSC1drlNKLYmzbK6BAJo8drHqNQ") {
Expand All @@ -64,7 +64,7 @@ object Telemetry {
userId = userId.toString(),
traits = mapOf(
"anonymousId" to anonymousId,
"ide" to IDE,
"ide" to ide,
"os" to os
)
)
Expand All @@ -80,7 +80,7 @@ object Telemetry {
properties = mapOf(
"anonymousId" to anonymousId,
"userId" to userId,
"ide" to IDE,
"ide" to ide,
"os" to os
) + event.toPayload()
)
Expand Down