mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[fus] IJPL-251495 New CLI install usage collector
(cherry picked from commit c13e3c73397748f1449a3e7a0774f7d2c470ab5b) IJ-CR-218650 (cherry picked from commit e1fda984eebc3e73f9e09187fd59316b47e356d5) GitOrigin-RevId: 48c19ea6cd123134b4744b35a8097650ef75b42d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d06e28fa8f
commit
c92ee9ab0e
+1
-1
@@ -7,7 +7,7 @@ import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.EventId1
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
|
||||
import com.intellij.openapi.progress.checkCanceled
|
||||
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.EnvironmentScanner
|
||||
import com.intellij.openapi.util.EnvironmentScanner
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
+2
-49
@@ -5,15 +5,7 @@ import com.intellij.ide.IdeBundle
|
||||
import com.intellij.ide.plugins.DependencyCollector
|
||||
import com.intellij.ide.plugins.DependencyInformation
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import com.intellij.util.system.OS
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isExecutable
|
||||
import kotlin.io.path.isRegularFile
|
||||
import com.intellij.openapi.util.EnvironmentScanner
|
||||
|
||||
internal class EnvironmentDependencyCollector : DependencyCollector {
|
||||
private val ALLOWED_EXECUTABLES: List<String> = listOf(
|
||||
@@ -35,43 +27,4 @@ internal class EnvironmentDependencyCollector : DependencyCollector {
|
||||
.filter { EnvironmentScanner.hasToolInLocalPath(pathNames, it) }
|
||||
.map { DependencyInformation(it, IdeBundle.message("plugins.configurable.suggested.features.executable", it)) }
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
object EnvironmentScanner {
|
||||
fun getPathNames(): List<Path> {
|
||||
val fs = FileSystems.getDefault()
|
||||
|
||||
@Suppress("IO_FILE_USAGE")
|
||||
val pathDelimiter = java.io.File.pathSeparatorChar
|
||||
|
||||
val pathNames = EnvironmentUtil.getEnvironmentMap()["PATH"]?.split(pathDelimiter)
|
||||
?.mapNotNull {
|
||||
try {
|
||||
fs.getPath(it)
|
||||
}
|
||||
catch (_: InvalidPathException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
?.filter(Files::exists)
|
||||
return pathNames ?: emptyList()
|
||||
}
|
||||
|
||||
fun hasToolInLocalPath(pathNames: List<Path>, executableWithoutExt: String): Boolean {
|
||||
val baseNames = if (OS.CURRENT == OS.Windows) {
|
||||
sequenceOf(".bat", ".cmd", ".com", ".exe")
|
||||
.map { exeSuffix -> executableWithoutExt + exeSuffix }
|
||||
}
|
||||
else {
|
||||
sequenceOf(executableWithoutExt)
|
||||
}
|
||||
|
||||
return pathNames.asSequence()
|
||||
.flatMap { pathEntry ->
|
||||
baseNames.map { basename -> pathEntry.resolve(basename) }
|
||||
}
|
||||
.filter(Path::isRegularFile)
|
||||
.any(Path::isExecutable)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.util
|
||||
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import com.intellij.util.system.LowLevelLocalMachineAccess
|
||||
import com.intellij.util.system.OS
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isExecutable
|
||||
import kotlin.io.path.isRegularFile
|
||||
|
||||
@ApiStatus.Internal
|
||||
object EnvironmentScanner {
|
||||
fun getPathNames(): List<Path> {
|
||||
val fs = FileSystems.getDefault()
|
||||
|
||||
@Suppress("IO_FILE_USAGE")
|
||||
val pathDelimiter = java.io.File.pathSeparatorChar
|
||||
|
||||
val pathNames = EnvironmentUtil.getEnvironmentMap()["PATH"]?.split(pathDelimiter)
|
||||
?.mapNotNull {
|
||||
try {
|
||||
fs.getPath(it)
|
||||
}
|
||||
catch (_: InvalidPathException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
?.filter(Files::exists)
|
||||
return pathNames ?: emptyList()
|
||||
}
|
||||
|
||||
@OptIn(LowLevelLocalMachineAccess::class)
|
||||
fun hasToolInLocalPath(pathNames: List<Path>, executableWithoutExt: String): Boolean {
|
||||
val baseNames = if (OS.CURRENT == OS.Windows) {
|
||||
sequenceOf(".bat", ".cmd", ".com", ".exe")
|
||||
.map { exeSuffix -> executableWithoutExt + exeSuffix }
|
||||
}
|
||||
else {
|
||||
sequenceOf(executableWithoutExt)
|
||||
}
|
||||
|
||||
return pathNames.asSequence()
|
||||
.flatMap { pathEntry ->
|
||||
baseNames.map { basename -> pathEntry.resolve(basename) }
|
||||
}
|
||||
.filter(Path::isRegularFile)
|
||||
.any(Path::isExecutable)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.ide.plugins.advertiser
|
||||
|
||||
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.EnvironmentScanner
|
||||
import com.intellij.openapi.util.EnvironmentScanner
|
||||
import com.intellij.util.EnvironmentUtil
|
||||
import com.intellij.util.system.LowLevelLocalMachineAccess
|
||||
import com.intellij.util.system.OS
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<applicationService
|
||||
serviceInterface="org.jetbrains.plugins.terminal.hyperlinks.menu.BackendHyperlinkInfoService"
|
||||
serviceImplementation="com.intellij.terminal.backend.hyperlinks.BackendHyperlinkInfoServiceImpl"/>
|
||||
<statistics.applicationUsagesCollector implementation="com.intellij.terminal.backend.CliUtilInstallCollector"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.plugins.terminal">
|
||||
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
package com.intellij.terminal.backend
|
||||
|
||||
import com.intellij.internal.statistic.beans.MetricEvent
|
||||
import com.intellij.internal.statistic.eventLog.EventLogGroup
|
||||
import com.intellij.internal.statistic.eventLog.events.EventFields
|
||||
import com.intellij.internal.statistic.eventLog.events.EventId1
|
||||
import com.intellij.internal.statistic.service.fus.collectors.ApplicationUsagesCollector
|
||||
import com.intellij.openapi.util.EnvironmentScanner
|
||||
import com.intellij.util.SystemProperties
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.Path
|
||||
|
||||
internal class CliUtilInstallCollector : ApplicationUsagesCollector() {
|
||||
private val GROUP: EventLogGroup = EventLogGroup("cli.tools", 1)
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
private val CLI_EXECUTABLES: List<String> = listOf(
|
||||
"aliyun",
|
||||
"amplify",
|
||||
"ansible",
|
||||
"argocd",
|
||||
"aws",
|
||||
"az",
|
||||
"bpftrace",
|
||||
"buildah",
|
||||
"bun",
|
||||
"cargo",
|
||||
"cdk",
|
||||
"cfn",
|
||||
"chaos-mesh",
|
||||
"chef",
|
||||
"cilium",
|
||||
"circleci",
|
||||
"code",
|
||||
"codium",
|
||||
"colima",
|
||||
"conftest",
|
||||
"consul",
|
||||
"crane",
|
||||
"crictl",
|
||||
"ctr",
|
||||
"datadog-agent",
|
||||
"devcontainer-cli",
|
||||
"devspace",
|
||||
"docker",
|
||||
"doctl",
|
||||
"doppler",
|
||||
"drone",
|
||||
"eb",
|
||||
"eksctl",
|
||||
"envoy",
|
||||
"etcdctl",
|
||||
"fleet",
|
||||
"flux",
|
||||
"fly",
|
||||
"gcloud",
|
||||
"gem",
|
||||
"git",
|
||||
"gitpod",
|
||||
"glab",
|
||||
"gradle",
|
||||
"grafana",
|
||||
"hadolint",
|
||||
"harbor",
|
||||
"hcloud",
|
||||
"helm",
|
||||
"helmfile",
|
||||
"hubble",
|
||||
"ibmcloud",
|
||||
"istioctl",
|
||||
"jenkins-cli",
|
||||
"jf",
|
||||
"jq",
|
||||
"k3d",
|
||||
"k3s",
|
||||
"k9s",
|
||||
"kibana",
|
||||
"kind",
|
||||
"kubeconform",
|
||||
"kubectl",
|
||||
"kubectx",
|
||||
"kubefwd",
|
||||
"kubens",
|
||||
"kubeseal",
|
||||
"kubetail",
|
||||
"kustomize",
|
||||
"lima",
|
||||
"localstack",
|
||||
"lxc",
|
||||
"lxd",
|
||||
"maven",
|
||||
"meshery",
|
||||
"microk8s",
|
||||
"minikube",
|
||||
"mirrord",
|
||||
"ncp",
|
||||
"nerdctl",
|
||||
"nerdctl", //Docker-compatible CLI for containerd.
|
||||
"newrelic",
|
||||
"ngrok",
|
||||
"nix",
|
||||
"nomad",
|
||||
"npm",
|
||||
"openstack",
|
||||
"otel",
|
||||
"pack", //CLI tool maintained by the CNB project to support the use of buildpacks.
|
||||
"packer",
|
||||
"pip",
|
||||
"pnpm",
|
||||
"podman",
|
||||
"poetry",
|
||||
"pre-commit",
|
||||
"promtool",
|
||||
"pulumi",
|
||||
"puppet",
|
||||
"quarkus",
|
||||
"rancher",
|
||||
"rke",
|
||||
"sam",
|
||||
"sdkman",
|
||||
"semgrep",
|
||||
"serverless",
|
||||
"sf",
|
||||
"shellcheck",
|
||||
"skaffold",
|
||||
"sops",
|
||||
"splunk",
|
||||
"st2",
|
||||
"stern",
|
||||
"tanzu",
|
||||
"telepresence",
|
||||
"terraform",
|
||||
"terraform-docs",
|
||||
"terragrunt",
|
||||
"terramate",
|
||||
"tflint",
|
||||
"tfsec",
|
||||
"tfswitch",
|
||||
"tilt",
|
||||
"tofu",
|
||||
"travis",
|
||||
"trivy",
|
||||
"uv",
|
||||
"vagrant",
|
||||
"vault",
|
||||
"velero",
|
||||
"werf",
|
||||
"yamllint",
|
||||
"yarn",
|
||||
"yc",
|
||||
"yq",
|
||||
|
||||
"claude",
|
||||
"codex",
|
||||
"cursor",
|
||||
"junie",
|
||||
"opencode",
|
||||
"copilot",
|
||||
)
|
||||
|
||||
private val TOOL_DISCOVERED: EventId1<String> = GROUP.registerEvent(
|
||||
"tool.discovered",
|
||||
EventFields.String("tool", CLI_EXECUTABLES)
|
||||
)
|
||||
|
||||
private val KUBECONFIG_ID: String = "kubeconfig"
|
||||
|
||||
private val CLAUDE_ID: String = ".claude"
|
||||
private val CODEX_ID: String = ".codex"
|
||||
private val COPILOT_ID: String = ".copilot"
|
||||
private val CURSOR_ID: String = ".cursor"
|
||||
|
||||
private val CONFIG_EXISTS: EventId1<String> = GROUP.registerEvent(
|
||||
"config.exists",
|
||||
EventFields.String("config", listOf(KUBECONFIG_ID, CLAUDE_ID))
|
||||
)
|
||||
|
||||
override fun getGroup(): EventLogGroup = GROUP
|
||||
|
||||
override fun getMetrics(): Set<MetricEvent> {
|
||||
val pathNames = EnvironmentScanner.getPathNames()
|
||||
val metrics = CLI_EXECUTABLES
|
||||
.filter { EnvironmentScanner.hasToolInLocalPath(pathNames, it) }
|
||||
.map { TOOL_DISCOVERED.metric(it) }
|
||||
.toMutableList()
|
||||
|
||||
if (userPathExists(Path.of(".kube", "config"))) {
|
||||
metrics.add(CONFIG_EXISTS.metric(KUBECONFIG_ID))
|
||||
}
|
||||
|
||||
if (userPathExists(Path.of(".claude"))) {
|
||||
metrics.add(CONFIG_EXISTS.metric(CLAUDE_ID))
|
||||
}
|
||||
if (userPathExists(Path.of(".codex"))) {
|
||||
metrics.add(CONFIG_EXISTS.metric(CODEX_ID))
|
||||
}
|
||||
if (userPathExists(Path.of(".copilot"))) {
|
||||
metrics.add(CONFIG_EXISTS.metric(COPILOT_ID))
|
||||
}
|
||||
if (userPathExists(Path.of(".cursor"))) {
|
||||
metrics.add(CONFIG_EXISTS.metric(CURSOR_ID))
|
||||
}
|
||||
|
||||
return metrics.toSet()
|
||||
}
|
||||
|
||||
private fun userPathExists(subPath: Path): Boolean {
|
||||
val fs = FileSystems.getDefault()
|
||||
val configPath = try {
|
||||
fs.getPath(SystemProperties.getUserHome()).resolve(subPath)
|
||||
}
|
||||
catch (_: InvalidPathException) {
|
||||
null
|
||||
}
|
||||
|
||||
return configPath != null && try {
|
||||
Files.exists(configPath)
|
||||
}
|
||||
catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user