From 01f61d2d78ca8758614c97aea2034b9cc82728b0 Mon Sep 17 00:00:00 2001 From: "konstantin.annikov" Date: Wed, 9 Nov 2022 15:53:21 +0100 Subject: [PATCH] [Repair Utility] move to community GitOrigin-RevId: af0c92f448088c53578857b0e1b84b95c2664a4e --- native/repair-utility/.gitignore | 3 + native/repair-utility/Dockerfile | 30 ++ native/repair-utility/README.md | 43 ++ native/repair-utility/build.sh | 32 ++ native/repair-utility/cmd/caches.go | 35 ++ native/repair-utility/cmd/hashes.go | 53 +++ native/repair-utility/cmd/log.go | 49 +++ native/repair-utility/cmd/plugins.go | 75 ++++ native/repair-utility/cmd/root.go | 122 ++++++ native/repair-utility/cmd/runtime.go | 70 ++++ native/repair-utility/cmd/runtime_test.go | 63 +++ native/repair-utility/cmd/vmoptions.go | 36 ++ native/repair-utility/cmd/vmoptions_test.go | 66 ++++ native/repair-utility/go.mod | 10 + native/repair-utility/go.sum | 286 ++++++++++++++ native/repair-utility/golang-image.txt | 1 + .../repair-utility/helpers/caches_worker.go | 3 + .../helpers/errors_collector.go | 27 ++ native/repair-utility/helpers/files_helper.go | 222 +++++++++++ .../helpers/get_installation_info.go | 361 +++++++++++++++++ .../helpers/get_installation_info_test.go | 72 ++++ .../helpers/global_variables.go | 150 +++++++ .../repair-utility/helpers/hashes_worker.go | 318 +++++++++++++++ native/repair-utility/helpers/ide.go | 291 ++++++++++++++ .../helpers/installations_finder.go | 69 ++++ .../helpers/installations_finder_test.go | 58 +++ native/repair-utility/helpers/java_runner.go | 42 ++ native/repair-utility/helpers/log_parser.go | 191 +++++++++ .../repair-utility/helpers/log_parser_test.go | 369 ++++++++++++++++++ native/repair-utility/helpers/plugins.go | 70 ++++ .../repair-utility/helpers/plugins_helper.go | 203 ++++++++++ .../helpers/plugins_helper_test.go | 64 +++ native/repair-utility/helpers/plugins_test.go | 94 +++++ .../repair-utility/helpers/questions_asker.go | 83 ++++ .../repair-utility/helpers/runtime_worker.go | 209 ++++++++++ .../helpers/runtime_worker_test.go | 71 ++++ native/repair-utility/helpers/test_helpers.go | 111 ++++++ .../jbr_packages/jbr-1.8/readme.txt | 1 + .../jbr_packages/jbr-11/readme.txt | 1 + .../logs/blame_presentation_assistant.log | 90 +++++ .../logs/error_on_action.log | 166 ++++++++ .../test_helpers_files/logs/readme.txt | 1 + .../test_vmoptions_files/1.vmoptions | 21 + .../test_vmoptions_files/2.vmoptions | 18 + .../helpers/vmoptions_file_parser.go | 148 +++++++ .../helpers/vmoptions_worker.go | 181 +++++++++ native/repair-utility/logger/logger.go | 77 ++++ native/repair-utility/main.go | 9 + 48 files changed, 4765 insertions(+) create mode 100644 native/repair-utility/.gitignore create mode 100644 native/repair-utility/Dockerfile create mode 100644 native/repair-utility/README.md create mode 100755 native/repair-utility/build.sh create mode 100644 native/repair-utility/cmd/caches.go create mode 100644 native/repair-utility/cmd/hashes.go create mode 100644 native/repair-utility/cmd/log.go create mode 100644 native/repair-utility/cmd/plugins.go create mode 100644 native/repair-utility/cmd/root.go create mode 100644 native/repair-utility/cmd/runtime.go create mode 100644 native/repair-utility/cmd/runtime_test.go create mode 100644 native/repair-utility/cmd/vmoptions.go create mode 100644 native/repair-utility/cmd/vmoptions_test.go create mode 100644 native/repair-utility/go.mod create mode 100644 native/repair-utility/go.sum create mode 100644 native/repair-utility/golang-image.txt create mode 100644 native/repair-utility/helpers/caches_worker.go create mode 100644 native/repair-utility/helpers/errors_collector.go create mode 100644 native/repair-utility/helpers/files_helper.go create mode 100644 native/repair-utility/helpers/get_installation_info.go create mode 100644 native/repair-utility/helpers/get_installation_info_test.go create mode 100644 native/repair-utility/helpers/global_variables.go create mode 100644 native/repair-utility/helpers/hashes_worker.go create mode 100644 native/repair-utility/helpers/ide.go create mode 100644 native/repair-utility/helpers/installations_finder.go create mode 100644 native/repair-utility/helpers/installations_finder_test.go create mode 100644 native/repair-utility/helpers/java_runner.go create mode 100644 native/repair-utility/helpers/log_parser.go create mode 100644 native/repair-utility/helpers/log_parser_test.go create mode 100644 native/repair-utility/helpers/plugins.go create mode 100644 native/repair-utility/helpers/plugins_helper.go create mode 100644 native/repair-utility/helpers/plugins_helper_test.go create mode 100644 native/repair-utility/helpers/plugins_test.go create mode 100644 native/repair-utility/helpers/questions_asker.go create mode 100644 native/repair-utility/helpers/runtime_worker.go create mode 100644 native/repair-utility/helpers/runtime_worker_test.go create mode 100644 native/repair-utility/helpers/test_helpers.go create mode 100644 native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-1.8/readme.txt create mode 100644 native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-11/readme.txt create mode 100644 native/repair-utility/helpers/test_helpers_files/logs/blame_presentation_assistant.log create mode 100644 native/repair-utility/helpers/test_helpers_files/logs/error_on_action.log create mode 100644 native/repair-utility/helpers/test_helpers_files/logs/readme.txt create mode 100644 native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/1.vmoptions create mode 100644 native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/2.vmoptions create mode 100644 native/repair-utility/helpers/vmoptions_file_parser.go create mode 100644 native/repair-utility/helpers/vmoptions_worker.go create mode 100644 native/repair-utility/logger/logger.go create mode 100644 native/repair-utility/main.go diff --git a/native/repair-utility/.gitignore b/native/repair-utility/.gitignore new file mode 100644 index 000000000000..6969dc753b4e --- /dev/null +++ b/native/repair-utility/.gitignore @@ -0,0 +1,3 @@ +/command/jbr/test_jbr_files/jbr-1.8/Contents/ +/command/jbr/test_jbr_files/jbr-11/Contents/ +/bin \ No newline at end of file diff --git a/native/repair-utility/Dockerfile b/native/repair-utility/Dockerfile new file mode 100644 index 000000000000..a0a22e4d78fa --- /dev/null +++ b/native/repair-utility/Dockerfile @@ -0,0 +1,30 @@ +ARG GOLANG_IMAGE +FROM --platform=${BUILDPLATFORM} ${GOLANG_IMAGE} AS build +ENV CGO_ENABLED=0 +WORKDIR /src +COPY . . +ARG TARGETOS +ARG TARGETARCH +ARG DOWNLOADURL +RUN go mod download all +RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /out/repair -ldflags "-X repair/helpers.EmbeededDownloadUrl=${DOWNLOADURL}" . + +FROM scratch AS bin-linux-amd64 +COPY --from=build /out/repair /repair-linux-amd64 + +FROM scratch AS bin-linux-arm64 +COPY --from=build /out/repair /repair-linux-arm64 + +FROM scratch AS bin-darwin-amd64 +COPY --from=build /out/repair /repair-darwin-amd64 + +FROM scratch AS bin-darwin-arm64 +COPY --from=build /out/repair /repair-darwin-arm64 + +FROM scratch AS bin-windows-amd64 +COPY --from=build /out/repair /repair.exe + +FROM scratch AS bin-windows-arm64 +COPY --from=build /out/repair /repair64a.exe + +FROM bin-${TARGETOS}-${TARGETARCH} AS bin \ No newline at end of file diff --git a/native/repair-utility/README.md b/native/repair-utility/README.md new file mode 100644 index 000000000000..a04909cff302 --- /dev/null +++ b/native/repair-utility/README.md @@ -0,0 +1,43 @@ +Repair is the command line utility to troubleshoot startup problems of the IDE. + +Repair binary should be launched from the command line if the IDE cannot start. + +## Usage + +`repair [aspect] [flags]` where + +### [aspect] is one of + +| Aspect | Description | +|:----------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| hashes | Hashes aspect generates(`-g`) or checks(`-c`, default) IDE integrity by inspecting checksums of all installation files.
Hashes aspect returns Warning if some files are missing, or distribution has redundant files inside | +| log | Log aspect inspects idea.log file for exceptions and errors generated by plugins. In case of Warnings, it asks to remove caches and configs. | +| plugins | Plugins aspect scans all the installed plugins and suggest fixes if some of them are incompatible or require updates. | +| runtime | Runtime aspect finds the JDK used to start the IDE and checks if it is OK to use. Sets the bundled JDK in case of warnings. | +| vmoptions | Vmoptions aspect finds the .vmoptions file used by the IDE and analyzes its content for suspicious entries. If any of them found, one can edit the file manually, or reset VM options to default. | + +If no aspects passed, all aspects will be executed one by one. + +### [flags] are: + +| Flag | Description | +|:----------------|:-----------------------------------------------------------------| +| --debug | Enable debug | +| -h , --help | show help for repair utility | +| -n, --no | Only check the installation. All the fixes will be skipped. | +| -y, --yes | Apply all the suggested fixes automatically | +| --path [string] | path to the IDE (default is the IDE where the script is located) | + + +### Help + +To show usage overview, execute `./repair --help` + +To list all available options of an aspect, execute `./repair --help` +## Example + +`/Applications/IntelliJ IDEA.app/Contents/bin/repair` - Checks the current IDE installation through all aspects one-by-one + +`./repair hashes --path "/Applications/IntelliJ IDEA.app"` - Checks all the files of the defined IDE installation for consistency + +`./repair plugins -y` - Find all installed IDEs on the computer and check its plugins. All fixes such as updaying will be executed automatically diff --git a/native/repair-utility/build.sh b/native/repair-utility/build.sh new file mode 100755 index 000000000000..04c2b2f2baba --- /dev/null +++ b/native/repair-utility/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +function retry() { + local limit=$1 + shift + local attempt=1 + while true; do + # shellcheck disable=SC2015 + "$@" && { return 0; } || { + ec=$? + echo "Failed with exit code $ec. Attempt $attempt/$limit." + if [[ $attempt -ge limit ]]; then + return $ec + fi + ((attempt++)) + } + done +} + +GOLANG_IMAGE=$(cat golang-image.txt) +retry 3 docker pull "$GOLANG_IMAGE" +for platform in "windows/amd64" "windows/arm64" "linux/amd64" "linux/arm64" "darwin/amd64" "darwin/arm64" +do + ENVVAR=$(echo $platform | sed 's/\//_/g') + DOWNLOADURL=$(eval "echo \${${ENVVAR}_url}" ) + docker buildx build \ + --target bin --output bin/ --platform $platform \ + --build-arg "GOLANG_IMAGE=$GOLANG_IMAGE" \ + --build-arg "DOWNLOADURL=$DOWNLOADURL" . +done diff --git a/native/repair-utility/cmd/caches.go b/native/repair-utility/cmd/caches.go new file mode 100644 index 000000000000..c538640d8127 --- /dev/null +++ b/native/repair-utility/cmd/caches.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "repair/helpers" + "repair/logger" +) + +func init() { + rootCmd.AddCommand(cachesCmd) + cachesCmd.PersistentFlags().BoolVarP(&helpers.ClearCachesFlag, "clear", "c", false, "Clear caches of all projects for the IDE") +} + +var cachesCmd = &cobra.Command{ + Use: "caches", + Short: "Invalidate caches when called with `--clear` flag", + Long: `Caches aspect with '--clear' flag provides an alternative for the "Invalidate caches and restart" action, which can run outside the IDE.`, + Run: func(cmd *cobra.Command, args []string) { + RunCachesAspect(args) + }, + PreRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Caches aspect started") + }, + PostRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Caches aspect finished") + }, +} + +func RunCachesAspect(args []string) { + if helpers.ClearCachesFlag { + helpers.CurrentIde.ClearSystemDirectory() + } else { + logger.InfoLogger.Println("Nothing to do here") + } +} diff --git a/native/repair-utility/cmd/hashes.go b/native/repair-utility/cmd/hashes.go new file mode 100644 index 000000000000..e7b066971230 --- /dev/null +++ b/native/repair-utility/cmd/hashes.go @@ -0,0 +1,53 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "repair/helpers" + "repair/logger" + "strings" +) + +func init() { + rootCmd.AddCommand(hashesCmd) + hashesCmd.PersistentFlags().BoolVarP(&helpers.GenerateHashesFlag, "generate", "g", false, "Generate manifest file for IDE installation") + hashesCmd.PersistentFlags().BoolVarP(&helpers.CheckHashesFlag, "check", "c", false, "Generate manifest file for IDE installation and check it with the reference generated by JetBrains") + hashesCmd.PersistentFlags().StringVarP(&helpers.HashesOutputFile, "output-file", "o", "manifest.json", "output file for the manifest") + hashesCmd.PersistentFlags().StringVarP(&Algorithms, "algorithms", "a", "sha256,crc32", "Comma separated list of algorithms to generate hashes with. CRC32 and sha256 are supported now") + hashesCmd.PersistentFlags().BoolVarP(&helpers.CompareHashesFlag, "compare", "", false, "Compare two manifest files. Example: repair --compare --file1='/path/to/file1' --file2='/path/to/file2'") + hashesCmd.PersistentFlags().StringVarP(&helpers.CompareHashesFile1, "file1", "", "", "First manifest file to compare with") + hashesCmd.PersistentFlags().StringVarP(&helpers.CompareHashesFile2, "file2", "", "", "Second manifest file to compare with") + _ = hashesCmd.PersistentFlags().MarkHidden("file1") + _ = hashesCmd.PersistentFlags().MarkHidden("file2") +} + +var Algorithms string +var hashesCmd = &cobra.Command{ + Use: "hashes", + Short: "generates(-g) or checks(-c) IDE installation integrity", + Long: `Hashes aspect generates manifest file of an IDE installation and checks it against standard manifest file generated by JetBrains`, + Run: func(cmd *cobra.Command, args []string) { + RunHashesAspect(args) + }, + PreRun: func(cmd *cobra.Command, args []string) { + parseFlags() + logger.InfoLogger.Println("Hashes aspect started") + }, + PostRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Hashes aspect finished") + }, +} + +func parseFlags() { + Algorithms = strings.ToUpper(Algorithms) + helpers.Algorithms = strings.Split(Algorithms, ",") +} + +func RunHashesAspect(args []string) { + if helpers.GenerateHashesFlag { + helpers.GenerateMainfestFile() + } else if helpers.CompareHashesFlag { + helpers.CompareHashes() + } else { + helpers.CheckHashes() + } +} diff --git a/native/repair-utility/cmd/log.go b/native/repair-utility/cmd/log.go new file mode 100644 index 000000000000..fda1b5fde8c2 --- /dev/null +++ b/native/repair-utility/cmd/log.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "repair/helpers" + "repair/logger" +) + +func init() { + rootCmd.AddCommand(logCmd) +} + +var logCmd = &cobra.Command{ + Use: "log", + Short: "Check the idea.log file for errors", + Long: "Log aspect analyzes idea.log file for exceptions generated by plugins and other exceptions. ", + PreRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Logs aspect started") + }, + Run: func(cmd *cobra.Command, args []string) { + RunLogAspect(args) + }, + PostRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Logs aspect finished") + }, +} + +func RunLogAspect(args []string) { + ideaLogPath := helpers.GetIdeaLogPath(helpers.CurrentIde.Binary) + if ideaLogPath == "" { + logger.InfoLogger.Println("No idea.log file found") + return + } + helpers.CurrentIde.AskUserAndDisablePlugins(helpers.GetPluginsWithExceptionsInIdeaLog(ideaLogPath)) + helpers.CurrentIde.AskUserToRunIdeAndCheckTheIssue() + exceptions := helpers.GetLogEntriesWithExceptions(ideaLogPath) + if len(exceptions) > 0 { + logger.ConsoleLogger.Println("There are exceptions unrelated to plugins in idea.log") + logExceptions(exceptions) + helpers.CurrentIde.AskUserAndClearSystemDirectory() + } +} + +func logExceptions(exceptions []helpers.LogEntry) { + logger.DebugLogger.Println("The following exceptions occurred:") + for _, exception := range exceptions { + logger.DebugLogger.Println("\n" + exception.DateAndTime + " " + exception.Severity + " " + exception.Header + "\n" + exception.Body) + } +} diff --git a/native/repair-utility/cmd/plugins.go b/native/repair-utility/cmd/plugins.go new file mode 100644 index 000000000000..be56f21b2dd1 --- /dev/null +++ b/native/repair-utility/cmd/plugins.go @@ -0,0 +1,75 @@ +package cmd + +import ( + "repair/helpers" + "repair/logger" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(pluginsCmd) +} + +var pluginsCmd = &cobra.Command{ + Use: "plugins", + Short: "Check if broken, or old plugins are installed", + Long: "Plugins aspect checks every installed plugin for versions mismatch, available updates and deprecation notices. Connection to https://plugins.jetbrains.com is required.", + PreRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Plugins aspect started") + }, + Run: func(cmd *cobra.Command, args []string) { + RunPluginsAspect(args) + }, + PostRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Plugins aspect finished") + }, +} + +func RunPluginsAspect(args []string) { + incompatiblePluginsList := helpers.CurrentIde.CustomPluginsList().FilterIncompatible() + logger.DebugLogger.Printf("Detected plugins: \n%s", helpers.CustomPluginsList.ToString()) + if len(incompatiblePluginsList) > 0 { + helpers.CurrentIde.AskUserAndUpdatePlugins(incompatiblePluginsList.FilterUpdatable()) + helpers.CurrentIde.AskUserAndDisablePlugins(incompatiblePluginsList.FilterDeprecated()) + helpers.CurrentIde.RefreshPluginsList() + helpers.CurrentIde.AskUserToRunIdeAndCheckTheIssue() + } else { + logger.InfoLogger.Println("No suspicious plugins found") + if isAspectPassed() && len(helpers.PluginsList.FilterEnabled()) > 0 { + logger.InfoLogger.Println("As \"plugins\" aspect has been defined explicitly, let's disable them all.") + disablePluginsOneByOne() + } + } +} + +func disablePluginsOneByOne() { + question := "Let's try to disable all custom plugins to check if the error is caused by them?" + options := []string{"y", "n"} + if helpers.AskQuestionWithOptions(question, options) == "y" { + helpers.CurrentIde.DisableAllCustomPlugins(false) + issueIsStillHere := helpers.CurrentIde.AskUserToRunIdeAndCheckTheIssue() + if issueIsStillHere == false { + helpers.CurrentIde.DisableAllCustomPlugins(true) + question := "Repair utility will try to disable custom plugins one-by-one to find the exact one causing the issue, ok?" + options := []string{"y", "n"} + if helpers.ForceAskQuestionWithOptions(question, options) == "y" { + for _, plugin := range helpers.PluginsList { + helpers.CurrentIde.DisablePlugin(plugin) + logger.InfoLogger.Println("Plugin \"" + plugin.Name + "\" has been disabled.") + issueIsStillHere := helpers.CurrentIde.AskUserToRunIdeAndCheckTheIssue() + if issueIsStillHere == false { + logger.InfoLogger.Println("Plugin to blame: " + plugin.Name) + break + } else { + helpers.CurrentIde.EnablePlugin(plugin) + } + } + + } + } else { + logger.ConsoleLogger.Println("That means that the problem is not caused by plugins.") + helpers.CurrentIde.DisableAllCustomPlugins(true) + } + } +} diff --git a/native/repair-utility/cmd/root.go b/native/repair-utility/cmd/root.go new file mode 100644 index 000000000000..1c2c4a89d4cf --- /dev/null +++ b/native/repair-utility/cmd/root.go @@ -0,0 +1,122 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "os" + "path/filepath" + "repair/helpers" + "repair/logger" +) + +var ( + initialArgs []string + ideaBinary string + rootCmd = &cobra.Command{ + Use: "repair", + Short: "repair is the utility to check IntelliJ-based IDEs", + Long: `repair is a simple and automated way to fix the IDE when it cannot start.`, + RunE: func(cmd *cobra.Command, args []string) error { + return nil + }, + } +) + +func init() { + rootCmd.PersistentFlags().StringVar(&ideaBinary, "path", "", "path to the IDE (default is the IDE where the script is located)") + rootCmd.PersistentFlags().BoolVarP(&helpers.YesToAll, "yes", "y", false, "Apply all the suggested fixes automatically") + rootCmd.PersistentFlags().BoolVarP(&logger.DebugEnabled, "debug", "", false, "Enable debug") + rootCmd.PersistentFlags().BoolVarP(&helpers.NoToAll, "no", "n", false, "Only check the installation. All the fixes will be skipped.") + rootCmd.InitDefaultHelpCmd() + if !isHelpPassed() && !isCompareHashesPassed() { + cobra.OnInitialize(initPath) + } +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + logger.ConsoleLogger.Println(err) + os.Exit(1) + } + if !isAspectPassed() && !isHelpPassed() { + logger.InfoLogger.Println("Starting repair utility for all aspects") + runAllAspects() + logger.InfoLogger.Println("Repair utility finished execution.") + if answer := helpers.ForceAskQuestionWithOptions("Is the error still here?", []string{"y", "n"}); answer == "y" { + resetIdeSettingsAndDisableCustomPlugins() + } + } +} + +func initPath() { + var err error + if ideaBinary != "" { + passedBinary := ideaBinary + ideaBinary, err = helpers.DetectInstallationByInnerPath(ideaBinary, true) + if err != nil { + logger.ConsoleLogger.Fatal("You passed \"--path\" argument with \"" + passedBinary + "\" value, but there is no IDE binary here.") + } + } else if possibleIdeaBinary, _ := helpers.DetectInstallationByInnerPath(filepath.Dir(os.Args[0]), true); len(possibleIdeaBinary) > 0 { + ideaBinary = possibleIdeaBinary + helpers.CurrentIde.MarkRepairAsBundled() + } else { + ideaBinary, err = helpers.SelectIdeaBinary() + logger.ExitWithExceptionOnError(err) + } + helpers.CurrentIde.SetBinaryToWrokWith(ideaBinary) +} + +//returns true if user called repair command with help argument +func isHelpPassed() bool { + for _, argument := range os.Args { + if argument == "help" || argument == "--help" { + return true + } + } + return false +} +func isCompareHashesPassed() bool { + for _, argument := range os.Args { + if argument == "compare" || argument == "--compare" { + return true + } + } + return false +} + +//returns true if user called repair command with an aspect +func isAspectPassed() bool { + if len(initialArgs) == 0 { + initialArgs = os.Args + } + for _, argument := range initialArgs { + for _, aspect := range rootCmd.Commands() { + if aspect.Name() == argument { + return true + } + } + } + return false +} +func runAllAspects() { + for _, command := range rootCmd.Commands() { + if command.Name() != "help" { + os.Args = append([]string{os.Args[0], command.Name()}) + _, _ = command.ExecuteC() + } + } +} +func resetIdeSettingsAndDisableCustomPlugins() { + pluginsNotice := "" + if len(helpers.CurrentIde.CustomPluginsList().FilterEnabled()) > 0 { + pluginsNotice = " and disable custom plugins" + } + question := "Let's try to restore default settings" + pluginsNotice + " as a last troubleshooting step?" + answer := []string{"y", "n"} + if helpers.AskQuestionWithOptions(question, answer) == "y" { + if len(pluginsNotice) > 0 { + helpers.CurrentIde.DisableAllCustomPlugins(false) + } + logger.InfoLogger.Printf("Restoring default settings...") + helpers.CurrentIde.RemoveConfigurationDirectory() + } +} diff --git a/native/repair-utility/cmd/runtime.go b/native/repair-utility/cmd/runtime.go new file mode 100644 index 000000000000..eee195bb16da --- /dev/null +++ b/native/repair-utility/cmd/runtime.go @@ -0,0 +1,70 @@ +package cmd + +import ( + "errors" + "github.com/spf13/cobra" + "repair/helpers" + "repair/logger" +) + +func init() { + rootCmd.AddCommand(runtimeCmd) +} + +var runtimeCmd = &cobra.Command{ + Use: "runtime", + Short: "Check the runtime that starts IDE", + Long: "Runtime aspect finds the JDK used to start the IDE and checks if it is OK to use.", + PreRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Runtime aspect started") + }, + Run: func(cmd *cobra.Command, args []string) { + RunJbrAspect(args) + }, + PostRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("Runtime aspect finished") + }, +} + +var ( + errorsToBeFixed []error + warnings []error +) + +func RunJbrAspect(args []string) { + errorsToBeFixed, warnings = []error{}, []error{} + helpers.RuntimeInUse = helpers.RuntimeInfo{} + var err error + helpers.RuntimeInUse, err = helpers.DetectRuntimeInUse(helpers.GetIdeaBinaryToWrokWith()) + logger.ExitWithExceptionOnError(err) + logger.DebugLogger.Println( + "Runtime info: \nbinary: " + helpers.RuntimeInUse.BinaryPath + + "\nbinary defined at: " + helpers.RuntimeInUse.BinaryPathDefinedAt + + "\nbinary defined as: " + helpers.RuntimeInUse.BinaryPathDefinedAs + + "\nversion: " + helpers.RuntimeInUse.Version + " (" + helpers.RuntimeInUse.Architecture + " bit)", + ) + helpers.CollectErrorsToBeFixed(helpers.RuntimeVersionLessThan17(helpers.RuntimeInUse), &errorsToBeFixed) + helpers.CollectErrorsToBeFixed(helpers.RuntimeArchitectureCorrectness(), &errorsToBeFixed) + helpers.CollectErrorsToBeFixed(helpers.RuntimeIsBundled(), &errorsToBeFixed) + if len(warnings) > 0 { + warn := errors.New("\n" + helpers.FormatCollectedWarnings(warnings)) + logger.WarningLogger.Println(warn) + } + if len(errorsToBeFixed) > 0 { + err = errors.New("\n" + helpers.FormatCollectedErrors(errorsToBeFixed)) + logger.ErrorLogger.Println(err) + if helpers.IdeHasBundledRuntime(helpers.GetIdeaBinaryToWrokWith()) { + userAccepted := helpers.SuggestResettingRuntime() + if userAccepted { + logger.InfoLogger.Println("Reverting runtime to default") + err = helpers.ResetRuntimeInUse(helpers.RuntimeInUse) + logger.ExitWithExceptionOnError(err) + } + } else { + helpers.SuggestToChangeIdeToOneWithBundledRuntime() + } + } + if len(errorsToBeFixed) == 0 && len(warnings) == 0 { + logger.InfoLogger.Println("Default runtime is in use. No need to take actions.") + } +} diff --git a/native/repair-utility/cmd/runtime_test.go b/native/repair-utility/cmd/runtime_test.go new file mode 100644 index 000000000000..5bda5d8aa3f2 --- /dev/null +++ b/native/repair-utility/cmd/runtime_test.go @@ -0,0 +1,63 @@ +package cmd + +import ( + "bytes" + "os" + "repair/helpers" + "repair/logger" + "strings" + "testing" +) + +func TestRunJbrAspect(t *testing.T) { + helpers.CurrentIde.SetBinaryToWrokWith(helpers.GetRandomIdeInstallationBinary()) + type args struct { + args []string + ideaBinary string + } + tests := []struct { + name string + args args + testJdk helpers.RuntimeInfo + wantErr string + }{ + { + name: "runtime8", + args: args{ + args: []string{}, + }, + testJdk: helpers.RuntimeInfo{ + BinaryPath: helpers.GetAbsolutePath("../helpers/test_helpers_files/jbr_packages/jbr-1.8/Contents/Home/bin/java"), + BinaryPathDefinedAt: helpers.CurrentIde.GetDefaultConfigurationDirectory() + "idea.jdk", + BinaryPathDefinedAs: helpers.GetAbsolutePath("../helpers/test_helpers_files/jbr_packages/jbr-1.8/"), + Version: "1.8", + Architecture: "64", + IsBundled: false, + }, + wantErr: "but recomended version is 11", + }, { + name: "bundledRuntime", + args: args{ + args: []string{}, + }, + testJdk: helpers.RuntimeInfo{ + IsBundled: true, + }, + wantErr: "false", + }, + } + for _, tt := range tests { + helpers.PrepareEnvironent(tt.testJdk) + t.Run(tt.name, func(t *testing.T) { + var buf bytes.Buffer + logger.ErrorLogger.SetOutput(&buf) + RunJbrAspect(tt.args.args) + if tt.wantErr != "false" && !strings.Contains(buf.String(), tt.wantErr) { + t.Errorf("RunJbrAspect() console ouput = %v, wantErr %v", buf.String(), tt.wantErr) + } + }) + helpers.ClearEnvironemnt(tt.testJdk) + logger.RemoveLogFile() + logger.ErrorLogger.SetOutput(os.Stdout) + } +} diff --git a/native/repair-utility/cmd/vmoptions.go b/native/repair-utility/cmd/vmoptions.go new file mode 100644 index 000000000000..04bb7ccdf32a --- /dev/null +++ b/native/repair-utility/cmd/vmoptions.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "repair/helpers" + "repair/logger" +) + +var restoreVmoptionsFileBool bool + +func init() { + rootCmd.AddCommand(vmoptionsCmd) + vmoptionsCmd.PersistentFlags().BoolVarP(&helpers.CheckVmoptionsFileBool, "check", "c", false, "Check .vmoptions file for correctness") + vmoptionsCmd.PersistentFlags().BoolVarP(&restoreVmoptionsFileBool, "restore", "r", false, "Reset .vmoptions file to default") +} + +var vmoptionsCmd = &cobra.Command{ + Use: "vmoptions", + Short: "Check for problems in .vmoptions file used to start the IDE", + Long: "Vmoptions aspect finds the .vmoptions file used by the IDE and analyzes its content. ", + SilenceUsage: true, + PreRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("VmOptions aspect started") + + }, + Run: func(cmd *cobra.Command, args []string) { + if restoreVmoptionsFileBool { + logger.ExitWithExceptionOnError(helpers.RestoreVmoptionsFile()) + } + helpers.CheckVmoptionsFile() + }, + PostRun: func(cmd *cobra.Command, args []string) { + logger.InfoLogger.Println("VmOptions aspect finished") + + }, +} diff --git a/native/repair-utility/cmd/vmoptions_test.go b/native/repair-utility/cmd/vmoptions_test.go new file mode 100644 index 000000000000..76157be58139 --- /dev/null +++ b/native/repair-utility/cmd/vmoptions_test.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "io/ioutil" + "math/rand" + "os" + "repair/helpers" + "repair/logger" + "strings" + "testing" + "time" +) + +func TestRunVmoptionAspect(t *testing.T) { + helpers.CurrentIde.SetBinaryToWrokWith(helpers.GetRandomIdeInstallationBinary()) + testFiles := map[string]string{ + helpers.GetAbsolutePath("../helpers/test_helpers_files/test_vmoptions_files/1.vmoptions"): "More than one Garbage Collector in use", + helpers.GetAbsolutePath("../helpers/test_helpers_files/test_vmoptions_files/2.vmoptions"): "Xmx value (current value 1048m) should be higher than Xms (current value 1228m)", + } + tests := []struct { + name string + wantErr bool + }{ + { + name: "Check test .vmoptions files", + wantErr: true, + }, + } + for testFile, testError := range testFiles { + createdFile := createTestVmoptionsFileForBinary(helpers.CurrentIde, testFile) + rootCmd.SetArgs([]string{"vmoptions", "--path=" + helpers.GetIdeaBinaryToWrokWith()}) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := vmoptionsCmd.Execute(); err != nil { + if !strings.Contains(err.Error(), testError) { + t.Errorf("RunVmoptionAspect() error = %v, wantErr %v", err, tt.wantErr) + } + } + }) + } + err := os.Remove(createdFile) + logger.ExitWithExceptionOnError(err) + } + logger.RemoveLogFile() +} + +func getRandomVmoptionFilelocation(ide helpers.IDE) (vmOptionsFile string) { + ide.Package = helpers.GetIdeIdePackageByBinary(ide.Binary) + rand.Seed(time.Now().UnixNano()) + i := rand.Intn(2) + switch i { + case 1: + return ide.Package + ".vmoptions" + case 0: + return ide.GetConfigurationDirectory() + "idea.vmoptions" + } + return "" +} + +func createTestVmoptionsFileForBinary(ide helpers.IDE, testVmoptionsFile string) string { + testVmOptions, err := ioutil.ReadFile(testVmoptionsFile) + file := getRandomVmoptionFilelocation(ide) + err = ioutil.WriteFile(file, testVmOptions, 0644) + logger.ExitWithExceptionOnError(err) + return file +} diff --git a/native/repair-utility/go.mod b/native/repair-utility/go.mod new file mode 100644 index 000000000000..d6fd4130ddbf --- /dev/null +++ b/native/repair-utility/go.mod @@ -0,0 +1,10 @@ +module repair + +go 1.18 + +require github.com/spf13/cobra v1.1.3 + +require ( + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/native/repair-utility/go.sum b/native/repair-utility/go.sum new file mode 100644 index 000000000000..a7590291d8b1 --- /dev/null +++ b/native/repair-utility/go.sum @@ -0,0 +1,286 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/native/repair-utility/golang-image.txt b/native/repair-utility/golang-image.txt new file mode 100644 index 000000000000..ee9c0b6c8b52 --- /dev/null +++ b/native/repair-utility/golang-image.txt @@ -0,0 +1 @@ +golang:1.17.2-alpine \ No newline at end of file diff --git a/native/repair-utility/helpers/caches_worker.go b/native/repair-utility/helpers/caches_worker.go new file mode 100644 index 000000000000..bcce50b1b59c --- /dev/null +++ b/native/repair-utility/helpers/caches_worker.go @@ -0,0 +1,3 @@ +package helpers + +var ClearCachesFlag bool diff --git a/native/repair-utility/helpers/errors_collector.go b/native/repair-utility/helpers/errors_collector.go new file mode 100644 index 000000000000..1836c0781cf6 --- /dev/null +++ b/native/repair-utility/helpers/errors_collector.go @@ -0,0 +1,27 @@ +package helpers + +import "strconv" + +func FormatCollectedWarnings(warningsToBePrinted []error) string { + return "The following WARNINGS were found: \n" + ConcatErrorsFromSlice(warningsToBePrinted) +} +func FormatCollectedErrors(errorsToBeFixed []error) string { + return "The following ERRORS were found: \n" + ConcatErrorsFromSlice(errorsToBeFixed) +} + +func ConcatErrorsFromSlice(errorsToBePrinted []error) (errorText string) { + for i, err := range errorsToBePrinted { + errorText = errorText + (strconv.Itoa(i+1) + ". " + err.Error() + "\n") + } + return errorText +} +func CollectWarnings(warn error, warnings *[]error) { + if warn != nil { + *warnings = append(*warnings, warn) + } +} +func CollectErrorsToBeFixed(err error, errorsToBeFixed *[]error) { + if err != nil { + *errorsToBeFixed = append(*errorsToBeFixed, err) + } +} diff --git a/native/repair-utility/helpers/files_helper.go b/native/repair-utility/helpers/files_helper.go new file mode 100644 index 000000000000..f76ebbcd15e9 --- /dev/null +++ b/native/repair-utility/helpers/files_helper.go @@ -0,0 +1,222 @@ +package helpers + +import ( + "archive/zip" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "repair/logger" + "runtime" + "strings" +) + +// DefaultEditor is vim because we're adults ;) +const DefaultEditor = "vim" + +// GetPreferredEditorFromEnvironment returns the user's editor as defined by the +// `$EDITOR` environment variable, or the `DefaultEditor` if it is not set. +func GetPreferredEditorFromEnvironment() string { + editor := os.Getenv("EDITOR") + if editor == "" { + return DefaultEditor + } + return editor +} +func RemoveContentOfDir(dir string) error { + d, err := os.Open(dir) + if err != nil { + return err + } + defer d.Close() + names, err := d.Readdirnames(-1) + if err != nil { + return err + } + for _, name := range names { + err = os.RemoveAll(filepath.Join(dir, name)) + if err != nil { + return err + } + } + return nil +} +func RemoveDir(dir string) (err error) { + err = RemoveContentOfDir(dir) + err = os.Remove(dir) + return err +} +func resolveEditorArguments(executable string, filename string) []string { + args := []string{filename} + + if strings.Contains(executable, "Visual Studio Code.app") { + args = append([]string{"--wait"}, args...) + } + // Other common editors + return args +} + +// OpenFileInEditor opens filename in a text editor. +func OpenFileInEditor(filename string) error { + // Get the full executable path for the editor. + executable, err := exec.LookPath(GetPreferredEditorFromEnvironment()) + if err != nil { + return err + } + cmd := exec.Command(executable, resolveEditorArguments(executable, filename)...) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} +func WriteToFile(str string, filename string) { + f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + logger.ExitWithExceptionOnError(err) + _, err = f.WriteString("\n" + str + "\n") + logger.ExitWithExceptionOnError(err) + defer f.Close() +} +func RemoveFromFile(str string, filename string) error { + read, err := ioutil.ReadFile(filename) + logger.ExitWithExceptionOnError(err) + newContents := strings.Replace(string(read), str, "", -1) + err = ioutil.WriteFile(filename, []byte(newContents), 0) + logger.ExitWithExceptionOnError(err) + return nil +} +func MoveFile(src, dst string) error { + sourceFileStat, err := os.Stat(src) + if err != nil { + return err + } + if !sourceFileStat.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", src) + } + source, err := os.Open(src) + if err != nil { + return err + } + source.Close() + destination, err := os.Create(dst) + if err != nil { + return err + } + defer destination.Close() + _, err = io.Copy(destination, source) + err = os.Remove(source.Name()) + return err +} +func Unzip(src, dest string) error { + r, err := zip.OpenReader(src) + if err != nil { + return err + } + defer func() { + if err := r.Close(); err != nil { + panic(err) + } + }() + + _ = os.MkdirAll(dest, 0755) + + // Closure to address file descriptors issue with all the deferred .Close() methods + extractAndWriteFile := func(f *zip.File) error { + rc, err := f.Open() + if err != nil { + return err + } + defer func() { + if err := rc.Close(); err != nil { + panic(err) + } + }() + + path := filepath.Join(dest, f.Name) + + // Check for ZipSlip (Directory traversal) + if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) { + return fmt.Errorf("illegal file path: %s", path) + } + + if f.FileInfo().IsDir() { + err = os.MkdirAll(path, f.Mode()) + logger.ExitWithExceptionOnError(err) + } else { + err = os.MkdirAll(filepath.Dir(path), 0755) + logger.ExitWithExceptionOnError(err) + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + defer func() { + if err := f.Close(); err != nil { + panic(err) + } + }() + + _, err = io.Copy(f, rc) + if err != nil { + return err + } + } + return nil + } + + for _, f := range r.File { + err := extractAndWriteFile(f) + if err != nil { + return err + } + } + + return nil +} +func FileExists(dir string) bool { + //dir = filepath.Clean(dir) + if _, err := os.Open(dir); err == nil && len(dir) > 2 { + return true + } + return false +} +func getFrame(skipFrames int) runtime.Frame { + targetFrameIndex := skipFrames + 2 + programCounters := make([]uintptr, targetFrameIndex+2) + n := runtime.Callers(0, programCounters) + frame := runtime.Frame{Function: "unknown"} + if n > 0 { + frames := runtime.CallersFrames(programCounters[:n]) + for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ { + var frameCandidate runtime.Frame + frameCandidate, more = frames.Next() + if frameIndex == targetFrameIndex { + frame = frameCandidate + } + } + } + + return frame +} + +// GetCallerFunction returns the name of function - caller. +func GetCallerFunction() string { + // Skip GetCallerFunctionName and the function to get the caller of + return getFrame(2).Function +} +func (ide *IDE) RemoveConfigurationDirectory() { + configDir := ide.GetConfigurationDirectory() + if FileExists(configDir) { + if FileExists(configDir + ".old") { + logger.WarningLogger.Printf("Backup of configuration directory (%s) already exists. ", configDir) + if ForceAskQuestionWithOptions("Overwrite it?", []string{"y", "n"}) == "y" { + RemoveContentOfDir(strings.TrimSuffix(configDir, "/") + ".old") + os.Remove(strings.TrimSuffix(configDir, "/") + ".old") + } + } + err := os.Rename(configDir, strings.TrimSuffix(configDir, "/")+".old") + logger.ExitWithExceptionOnError(err) + } + logger.InfoLogger.Printf("Configuration directory %s been moved to %s.old", configDir, configDir[:len(configDir)]) +} diff --git a/native/repair-utility/helpers/get_installation_info.go b/native/repair-utility/helpers/get_installation_info.go new file mode 100644 index 000000000000..b760d8cfb2f9 --- /dev/null +++ b/native/repair-utility/helpers/get_installation_info.go @@ -0,0 +1,361 @@ +package helpers + +import ( + "bufio" + "encoding/json" + "errors" + "io/ioutil" + "os" + "path/filepath" + "repair/logger" + "runtime" + "runtime/debug" + "strings" +) + +type IntallationInfoLaunchInner struct { + Os string + JavaExecutablePath string +} + +func (ide *IDE) GetConfigurationDirectory() (configDir string) { + configDir = ide.GetProperty("idea.config.path") + if len(configDir) == 0 { + configDir = ide.GetDefaultConfigurationDirectory() + } + if _, err := os.Create(configDir + "/test.tmp"); err == nil { + _ = os.Remove(configDir + "/test.tmp") + return filepath.Clean(configDir) + string(os.PathSeparator) + } else if FileExists(configDir) { + logger.ExitOnError(errors.New("Could not open configuration directory for writing \"" + configDir + "\". IDE requires writable directory to work")) + } else { + logger.ExitOnError(errors.New("Could not find IDE configuration directory. Most probably, this IDE has not been started yet.")) + } + return "" +} +func (ide *IDE) GetDefaultConfigurationDirectory() (defaultConfigDir string) { + installationInfo := ide.GetInfo() + defaultConfigDir = strings.Replace(defaultConfigDirLocation[runtime.GOOS], "{dataDirectoryName}", installationInfo.DataDirectoryName, -1) + defaultConfigDir = os.ExpandEnv(defaultConfigDir) + return filepath.Clean(defaultConfigDir) + string(os.PathSeparator) +} + +func (ide *IDE) GetIdeCustomPluginsDirectory() (pluginsDir string) { + if len(ide.GetProperty("idea.plugins.path")) > 1 { + pluginsDir = ide.GetProperty("idea.plugins.path") + } else if len(GetIdeVmoptionByName("-Didea.plugins.path")) > 1 { + pluginsDir = GetIdeVmoptionByName("-Didea.plugins.path") + } else { + pluginsDir = ide.GetConfigurationDirectory() + "plugins" + } + return filepath.Clean(pluginsDir) + string(os.PathSeparator) +} +func (ide *IDE) GetSystemDirectory() (systemDir string) { + if len(ide.GetProperty("idea.system.path")) > 1 { + systemDir = ide.GetProperty("idea.system.path") + } else if len(GetIdeVmoptionByName("-Didea.system.path")) > 1 { + systemDir = GetIdeVmoptionByName("-Didea.system.path") + } else { + systemDir = GetIdeDefaultSystemDir(ide.Binary) + } + return filepath.Clean(systemDir) + string(os.PathSeparator) +} + +func GetIdeBundledPluginsDirectory(ideaBinary string) string { + return strings.Replace(defaultBundledPluginsDirLocation[runtime.GOOS], "{ideaPackage}", GetIdeIdePackageByBinary(ideaBinary), -1) +} + +func GetIdeDefaultSystemDir(ideaBinary string) (defaultSystemDir string) { + installationInfo, err := GetIdeInfoByBinary(ideaBinary) + logger.ExitWithExceptionOnError(err) + defaultSystemDir = strings.Replace(defaultSystemDirLocation[runtime.GOOS], "{dataDirectoryName}", installationInfo.DataDirectoryName, -1) + defaultSystemDir = os.ExpandEnv(defaultSystemDir) + return filepath.Clean(defaultSystemDir) + string(os.PathSeparator) +} + +func (ide *IDE) GetProperty(name string) (value string) { + if len(ide.Binary) < 1 { + logger.ExitWithExceptionOnError(errors.New("Trying to analyze undefined installation")) + } + if len(IdePropertiesMap) == 0 { + IdePropertiesMap = GetIdeProperties(ide.Binary) + } + if _, ok := IdePropertiesMap[name]; ok { + return IdePropertiesMap[name] + } + return "" +} +func GetIdePropertyByName(name string, ideaBinary string) (value string) { + if len(IdePropertiesMap) == 0 { + IdePropertiesMap = GetIdeProperties(ideaBinary) + } + if _, ok := IdePropertiesMap[name]; ok { + return IdePropertiesMap[name] + } + return "" +} +func GetIdeBasefileName(ideaBinary string) string { + for _, possibleBaseFileName := range possibleBaseFileNames { + if strings.HasSuffix(ideaBinary, possibleBaseFileName) { + return possibleBaseFileName + } + } + return "" +} + +func GetIdeVmoptionByName(optionName string) (value string) { + vmOptionsFile, err := GetVmOptionsFile() + if err != nil { + return "" + } + vmOptionsAsSlice := GetVmoptionsAsSlice(vmOptionsFile) + return vmOptionsAsSlice[optionName] +} + +//fixme: java properties (as ${user.home} could not be extended) +//possible solution is to call java binary to print list of resolved system properties. +func (ide *IDE) GetProperties() (collectedOptions map[string]string) { + if len(ide.Binary) < 1 { + logger.ExitWithExceptionOnError(errors.New("Trying to get properties of undefined IDE")) + } + var err error + var ideaPackage string + collectedOptions = make(map[string]string) + ide.Binary, err = DetectInstallationByInnerPath(ide.Binary, true) + ideaPackage, err = DetectInstallationByInnerPath(ide.Binary, false) + InstallationInfo, err = GetIdeInfoByBinary(ide.Binary) + logger.ExitWithExceptionOnError(err) + for _, possibleIdeaPropertiesFileLocation := range getOsDependentDir(possibleIdeaPropertiesFileLocations) { + possibleIdeaOptionsFile := strings.Replace(possibleIdeaPropertiesFileLocation, "{IDE_BasefileName}", strings.ToUpper(GetIdeBasefileName(ide.Binary)), -1) + possibleIdeaOptionsFile = strings.Replace(possibleIdeaOptionsFile, "{dataDirectoryName}", InstallationInfo.DataDirectoryName, -1) + possibleIdeaOptionsFile = strings.Replace(possibleIdeaOptionsFile, "{ideaPackage}", ideaPackage, -1) + possibleIdeaOptionsFile = os.ExpandEnv(possibleIdeaOptionsFile) + if FileExists(possibleIdeaOptionsFile) { + logger.DebugLogger.Println("found idea.properties file at: \"" + possibleIdeaOptionsFile + "\"") + fillIdePropertiesMap(possibleIdeaOptionsFile, collectedOptions) + } else { + logger.DebugLogger.Println("Checked " + possibleIdeaPropertiesFileLocation + ". There is no \"" + possibleIdeaOptionsFile + "\" file.") + } + } + var listOfCollectedOptions string + for option, value := range collectedOptions { + listOfCollectedOptions = listOfCollectedOptions + option + "=" + value + "\n" + } + logger.DebugLogger.Println("Collected idea properties:\n" + listOfCollectedOptions) + return collectedOptions +} +func GetIdeProperties(ideaBinary string) (collectedOptions map[string]string) { + var err error + var ideaPackage string + collectedOptions = make(map[string]string) + ideaBinary, err = DetectInstallationByInnerPath(ideaBinary, true) + ideaPackage, err = DetectInstallationByInnerPath(ideaBinary, false) + InstallationInfo, err = GetIdeInfoByBinary(ideaBinary) + logger.ExitWithExceptionOnError(err) + for _, possibleIdeaPropertiesFileLocation := range getOsDependentDir(possibleIdeaPropertiesFileLocations) { + possibleIdeaOptionsFile := strings.Replace(possibleIdeaPropertiesFileLocation, "{IDE_BasefileName}", strings.ToUpper(GetIdeBasefileName(ideaBinary)), -1) + possibleIdeaOptionsFile = strings.Replace(possibleIdeaOptionsFile, "{dataDirectoryName}", InstallationInfo.DataDirectoryName, -1) + possibleIdeaOptionsFile = strings.Replace(possibleIdeaOptionsFile, "{ideaPackage}", ideaPackage, -1) + possibleIdeaOptionsFile = os.ExpandEnv(possibleIdeaOptionsFile) + if FileExists(possibleIdeaOptionsFile) { + logger.DebugLogger.Println("found idea.properties file at: \"" + possibleIdeaOptionsFile + "\"") + fillIdePropertiesMap(possibleIdeaOptionsFile, collectedOptions) + } else { + logger.DebugLogger.Println("Checked " + possibleIdeaPropertiesFileLocation + ". There is no \"" + possibleIdeaOptionsFile + "\" file.") + } + } + var listOfCollectedOptions string + for option, value := range collectedOptions { + listOfCollectedOptions = listOfCollectedOptions + option + "=" + value + "\n" + } + logger.DebugLogger.Println("Collected idea properties:\n" + listOfCollectedOptions) + return collectedOptions +} + +func getOsDependentDir(fromVariable map[string][]string) []string { + if len(fromVariable[runtime.GOOS]) > 0 { + return fromVariable[runtime.GOOS] + } + logger.ExitWithExceptionOnError(errors.New("This OS is not yet supported")) + return nil +} + +func fillIdePropertiesMap(ideaOptionsFile string, optionsMap map[string]string) { + optionsSlice, err := ideaPropertiesFileToSliceOfStrings(ideaOptionsFile) + logger.ExitWithExceptionOnError(err) + for _, option := range optionsSlice { + if idx := strings.IndexByte(option, '='); idx >= 0 { + optionValue := option[idx+1:] + optionValue = os.ExpandEnv(optionValue) + optionName := option[:idx] + if _, exist := optionsMap[optionName]; !exist { + optionsMap[optionName] = optionValue + } + } + + } +} + +func ideaPropertiesFileToSliceOfStrings(ideaPropertiesFile string) (properties []string, err error) { + file, err := os.Open(ideaPropertiesFile) + logger.ExitWithExceptionOnError(err) + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var i int + for scanner.Scan() { + i++ + option := scanner.Text() + if len(option) != 0 { + if option[0] == '#' { + } else { + properties = append(properties, option) + } + } + } + err = file.Close() + if err != nil { + return nil, err + } + return properties, err + +} + +func GetIdeInfoByPackage(ideaPackage string) (parameterValue IdeInfo, err error) { + var a IdeInfo + var fileContent []byte + fileContent, err = ioutil.ReadFile(ideaPackage + IdeProductInfoRelatedToInstallationPath[runtime.GOOS]) + if err != nil { + for os, path := range IdeProductInfoRelatedToInstallationPath { + if content, er := ioutil.ReadFile(ideaPackage + path); er == nil { + fileContent = content + logger.WarningLogger.Printf("Could not find product-info.json for %s, but found it for %s ", runtime.GOOS, os) + } + } + } + err = json.Unmarshal(fileContent, &a) + return a, err +} +func GetIdeInfoByBinary(ideaBinary string) (parameterValue IdeInfo, err error) { + return GetIdeInfoByPackage(GetIdeIdePackageByBinary(ideaBinary)) +} + +//GetIdeBinaryByPackage return the location of idea(idea.exe) executable inside the IDE installation folder. +//if ideaPackage == /Users/konstantin.annikov/Downloads/IntelliJ IDEA.app +//then idaBinary == /Users/konstantin.annikov/Downloads/IntelliJ IDEA.app/Contents/MacOS/idea +func GetIdeBinaryByPackage(ideaPackage string) (ideaBinary string, err error) { + if len(CurrentIde.Binary) == 0 { + logger.DebugLogger.Printf("detecting IDE binary by package. %s is being checked. Stack is: %s", ideaPackage, string(debug.Stack())) + logger.DebugLogger.Printf("Binaries to check: %s", possibleBaseFileNames) + } + for _, possibleBaseFileName := range possibleBaseFileNames { + for operatingSystem, path := range IdeBinaryRelatedToInstallationPath { + currentBinaryToCheck := strings.Replace(path, "{possibleBaseFileName}", possibleBaseFileName, -1) + ideaBinary = ideaPackage + currentBinaryToCheck + if len(CurrentIde.Binary) == 0 { + logger.DebugLogger.Printf("IDE binary to check: %s", ideaBinary) + } + if FileExists(ideaBinary) { + if operatingSystem != runtime.GOOS { + logger.WarningLogger.Printf("Provided path is for %s, but repair utility is running at %s ", operatingSystem, runtime.GOOS) + } + if len(CurrentIde.Binary) == 0 { + logger.DebugLogger.Printf("Found %s binary at %s", ideaBinary, ideaPackage) + } + return filepath.Clean(ideaBinary), nil + } + _, err := os.Open(ideaBinary) + if len(CurrentIde.Binary) == 0 { + logger.DebugLogger.Printf("%s file does not exist, error: %s", ideaBinary, string(err.Error())) + } + } + } + return "", errors.New("Could not detect IDE binary in " + ideaPackage) +} + +func GetIdePackageToWorkWith(ideaBinary string) (ideaPackage string) { + if len(ideaPackageToWorkWith) > 0 { + return ideaPackageToWorkWith + } else { + return GetIdeIdePackageByBinary(ideaBinary) + } +} +func GetIdeIdePackageByBinary(ideaBinary string) (ideaPackage string) { + var err error + if ideaPackageToWorkWith, err = DetectInstallationByInnerPath(ideaBinary, false); err == nil { + return ideaPackageToWorkWith + } else { + logger.ErrorLogger.Println("Could not get detect ide installation path by binary " + ideaBinary) + return "" + } +} + +//If any part of providedPath is IDE installation path, DetectInstallationByInnerPath returns path or binary (based on returnBinary flag) +func DetectInstallationByInnerPath(providedPath string, returnBinary bool) (ideaBinary string, err error) { + providedPath = filepath.Clean(providedPath) + providedDeep := strings.Count(providedPath, string(os.PathSeparator)) + basePath := providedPath + for i := 1; i < providedDeep; i++ { + if ideaBinary, err := GetIdeBinaryByPackage(basePath); err == nil { + if returnBinary { + return ideaBinary, nil + } else { + return basePath, nil + } + } + basePath = filepath.Dir(basePath) + } + return "", errors.New("Could not detect IDE by \"" + providedPath + "\" path") +} + +func GetIdeaLogPath(ideaBinary string) (ideaLogPath string) { + if GetIdeDefaultLogsDir(ideaBinary) != "" { + return GetIdeDefaultLogsDir(ideaBinary) + "idea.log" + } else { + return "" + } +} + +func GetIdeDefaultLogsDir(ideaBinary string) (defaultLogsDir string) { + if value := GetIdePropertyByName("idea.log.path", ideaBinary); len(value) != 0 { + if FileExists(value) { + return value + } else { + logger.FatalLogger.Fatal("'idea.log.path' property is defined, but directory does not exist") + } + } + installationInfo, err := GetIdeInfoByBinary(ideaBinary) + logger.ExitWithExceptionOnError(err) + defaultLogsDir = strings.Replace(defaultLogsDirLocation[runtime.GOOS], "{dataDirectoryName}", installationInfo.DataDirectoryName, -1) + defaultLogsDir = os.ExpandEnv(defaultLogsDir) + if FileExists(defaultLogsDir) { + return defaultLogsDir + } else { + logger.InfoLogger.Println(errors.New("Could not detect logs directory location")) + return "" + } +} + +func GetVmOptionsFile() (string, error) { + if len(vmOptionsFile) > 0 { + return vmOptionsFile, nil + } else { + ideaBinary := CurrentIde.Binary + for _, possibleVmOptionsLocation := range possibleVmOptionsFileLocation[runtime.GOOS] { + possibleVmOptionsFile := os.ExpandEnv(possibleVmOptionsLocation) + possibleVmOptionsFile = strings.Replace(possibleVmOptionsFile, "{ideaPackage}", GetIdeIdePackageByBinary(ideaBinary), -1) + possibleVmOptionsFile = strings.Replace(possibleVmOptionsFile, "{IdeConfigDir}", CurrentIde.GetConfigurationDirectory(), -1) + possibleVmOptionsFile = strings.Replace(possibleVmOptionsFile, "{IDE_BaseFilename}", GetIdeBasefileName(GetIdeaBinaryToWrokWith()), -1) + + if FileExists(possibleVmOptionsFile) { + logger.DebugLogger.Println(".vmoptions file to work with: \"" + possibleVmOptionsFile + "\"") + vmOptionsFile = possibleVmOptionsFile + return possibleVmOptionsFile, nil + } else { + logger.DebugLogger.Println("Checked " + possibleVmOptionsLocation + ". There is no \"" + possibleVmOptionsFile + "\" file. Next location to check: ") + } + } + err := errors.New("Couldn't detect custom .vmoptions file for " + ideaBinary + "\nThere is nothing to do with vmoptions aspect.") + return "", err + + } +} diff --git a/native/repair-utility/helpers/get_installation_info_test.go b/native/repair-utility/helpers/get_installation_info_test.go new file mode 100644 index 000000000000..7f8642c49ac8 --- /dev/null +++ b/native/repair-utility/helpers/get_installation_info_test.go @@ -0,0 +1,72 @@ +package helpers + +import ( + "repair/logger" + "runtime" + "testing" +) + +func TestDetectInstallationByInnerPath(t *testing.T) { + ideaPackage, err := GetRandomIdeInstallationPackage() + ideaBinary, err := GetIdeBinaryByPackage(ideaPackage) + logger.ExitWithExceptionOnError(err) + type args struct { + providedPath string + returnBinary bool + } + tests := []struct { + name string + args args + wantIdeaBinary string + wantErr bool + }{ + { + name: "detect binary by package name", + args: args{ideaPackage, true}, + wantIdeaBinary: ideaBinary, + wantErr: false, + }, { + name: "detect package by binary name", + args: args{ideaBinary, false}, + wantIdeaBinary: ideaPackage, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotIdeaBinary, err := DetectInstallationByInnerPath(tt.args.providedPath, tt.args.returnBinary) + if (err != nil) != tt.wantErr { + t.Errorf("DetectInstallationByInnerPath() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotIdeaBinary != tt.wantIdeaBinary { + t.Errorf("DetectInstallationByInnerPath() gotIdeaBinary = %v, want %v", gotIdeaBinary, tt.wantIdeaBinary) + } + }) + } +} + +func TestGetIdeInstallationPathByBinary(t *testing.T) { + gotPossible_binaries, _ := FindInstalledIdePackages() + type args struct { + ideaBinary string + } + tests := []struct { + name string + args args + wantIdeaPackage string + }{ + { + name: "", + args: args{gotPossible_binaries[0] + IdeBinaryRelatedToInstallationPath[runtime.GOOS]}, + wantIdeaPackage: gotPossible_binaries[0], + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotIdeaPackage := GetIdeIdePackageByBinary(tt.args.ideaBinary); gotIdeaPackage != tt.wantIdeaPackage { + t.Errorf("GetIdeInstallationPathByBinary() = %v, want %v", gotIdeaPackage, tt.wantIdeaPackage) + } + }) + } +} diff --git a/native/repair-utility/helpers/global_variables.go b/native/repair-utility/helpers/global_variables.go new file mode 100644 index 000000000000..081468b0a0a8 --- /dev/null +++ b/native/repair-utility/helpers/global_variables.go @@ -0,0 +1,150 @@ +package helpers + +import ( + "errors" + "os" + "repair/logger" + "runtime" +) + +var ( + PluginsList PluginInfoList + CustomPluginsList PluginInfoList + LogEntries []LogEntry + InstallationInfo IdeInfo + ideaPackageToWorkWith string + ideaBinaryToWorkWith string + CurrentIde IDE + RuntimeInUse RuntimeInfo + vmOptionsFile string +) + +//Deprecated: use CurrentIde.Binary instead +func GetIdeaBinaryToWrokWith() string { + if len(CurrentIde.Binary) > 0 { + return CurrentIde.Binary + } else { + logger.ExitWithExceptionOnError(errors.New("There is no idea binary to work with")) + return "" + } +} +func UserHomeDir() string { + if runtime.GOOS == "windows" { + home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") + if home == "" { + home = os.Getenv("USERPROFILE") + } + return home + } + return os.Getenv("HOME") +} + +//OS-dependent constants +var ( + EmbeededDownloadUrl = "" + downloadsURL = "https://download.jetbrains.com" + ManifestFilesExclusions = map[string][]string{ + "darwin": {}, + "linux": {}, + "windows": {".feed", ".symlink", "bin/Uninstall.exe.nsis"}, + } + archiveExtension = map[string]string{ + "darwin": ".dmg", + "linux": ".tar.gz", + "windows": ".exe", + } + possibleBaseFileNames = []string{"appcode", "clion", "datagrip", "dataspell", "goland", "idea", "phpstorm", "pycharm", "rubymine", "webstorm", "rider", "Draft", "aqua"} + possibleBinariesPaths = map[string][]string{ + "darwin": {"/Applications/*.app/Contents/MacOS/{possibleBaseFileName}", "$HOME/Library/Application Support/JetBrains/Toolbox/apps/*/ch-*/*/*.app/Contents/MacOS/{possibleBaseFileName}"}, + "linux": {"$HOME/.local/share/JetBrains/Toolbox/apps/*/ch-*/*/bin/{possibleBaseFileName}.sh"}, + "windows": {os.Getenv("HOMEDRIVE") + "/Program Files/JetBrains/*" + IdeBinaryRelatedToInstallationPath["windows"], os.Getenv("LOCALAPPDATA") + "/JetBrains/Toolbox/apps/*/ch-*/*" + IdeBinaryRelatedToInstallationPath["windows"]}, + } + IdeBinaryRelatedToInstallationPath = map[string]string{ + "darwin": "/Contents/MacOS/{possibleBaseFileName}", + "linux": "/bin/{possibleBaseFileName}.sh", + "windows": "/bin/{possibleBaseFileName}64.exe", + } + IdeProductInfoRelatedToInstallationPath = map[string]string{ + "darwin": "/Contents/Resources/product-info.json", + "linux": "/product-info.json", + "windows": "/product-info.json", + } + possibleIdeaPropertiesFileLocations = map[string][]string{ + "darwin": {"${IDE_BasefileName}_PROPERTIES", UserHomeDir() + "/Library/Application Support/JetBrains/{dataDirectoryName}/idea.properties", UserHomeDir() + "/idea.properties", "{ideaPackage}/Contents/bin/idea.properties"}, + "linux": {"${IDE_BasefileName}_PROPERTIES", UserHomeDir() + "/.config/JetBrains/{dataDirectoryName}/idea.properties", UserHomeDir() + "/idea.properties", "{ideaPackage}/bin/idea.properties"}, + "windows": {"${IDE_BasefileName}_PROPERTIES", defaultSystemDirLocation[runtime.GOOS] + "/idea.properties", UserHomeDir() + "/idea.properties", "{ideaPackage}/bin/idea.properties"}, + } + IdeRuntimeBinaryRelatedToInstallationPath = map[string]string{ + "darwin": "/Contents/jbr/Contents/Home/bin/java", + "linux": "/jbr/bin/java", + "windows": "/jbr/bin/java.exe", + } + possibleRuntimeVariables = map[string][]string{ + "darwin": {"${IDE_BASEFILENAME}_JDK"}, + "linux": {"${IDE_BASEFILENAME}_JDK"}, + "windows": {"${IDE_BASEFILENAME}_JDK_64"}, + } + possibleRuntimeConfiguraitonFileLocations = map[string][]string{ + "darwin": {"{IdeConfigDir}/{IDE_BaseFilename}.jdk"}, + "linux": {"{IdeConfigDir}/{IDE_BaseFilename}.jdk"}, + "windows": {"{IdeConfigDir}/{IDE_BaseFilename}64.exe.jdk"}, + } + defaultLogsDirLocation = map[string]string{ + "darwin": UserHomeDir() + "/Library/Logs/JetBrains/{dataDirectoryName}/", + "linux": UserHomeDir() + "/.cache/JetBrains/{dataDirectoryName}/log/", + "windows": os.Getenv("LOCALAPPDATA") + "/JetBrains/{dataDirectoryName}/log/", + } + defaultSystemDirLocation = map[string]string{ + "darwin": "${HOME}/Library/Caches/JetBrains/{dataDirectoryName}/", + "linux": "${HOME}/.cache/JetBrains/{dataDirectoryName}/", + "windows": os.Getenv("LOCALAPPDATA") + "/JetBrains/{dataDirectoryName}/", + } + defaultConfigDirLocation = map[string]string{ + "darwin": "${HOME}/Library/Application Support/JetBrains/{dataDirectoryName}/", + "linux": "${HOME}/.config/JetBrains/{dataDirectoryName}/", + "windows": os.Getenv("APPDATA") + "/JetBrains/{dataDirectoryName}/", + } + defaultBundledPluginsDirLocation = map[string]string{ + "darwin": "{ideaPackage}/Contents/plugins", + "linux": "{ideaPackage}/plugins", + "windows": "{ideaPackage}/plugins", + } + //possibleVmOptionsFileLocation is the *ordered* list where .vmoptions files could be located + possibleVmOptionsFileLocation = map[string][]string{ + "darwin": {"$IDEA_VM_OPTIONS", "{ideaPackage}.vmoptions", "{IdeConfigDir}/idea.vmoptions"}, + "linux": {"$IDEA_VM_OPTIONS", "{ideaPackage}.vmoptions", "{IdeConfigDir}/idea.vmoptions"}, + "windows": {"$IDEA64_VM_OPTIONS", "{ideaPackage}/bin/{IDE_BaseFilename}64.exe.vmoptions"}, + } + IdePropertiesMap = map[string]string{} +) + +type LogEntry struct { + DateAndTime string + TimeSinceStart string + Severity string + Class string + Header string + Body string +} +type PluginInfo struct { + Id string `xml:"id"` + Name string `xml:"name"` + Version string `xml:"version"` + Vendor string `xml:"vendor"` + MainJarPath string + PluginXmlId string + MarketplaceId int + IsDisabled bool + IdeaVersion IdeaVersion `xml:"idea-version"` + isBundled bool + IsDeprecated bool +} +type PluginInfoList []PluginInfo +type RuntimeInfo struct { + BinaryPath string + BinaryPathDefinedAt string + BinaryPathDefinedAs string + Version string + Architecture string + IsBundled bool +} diff --git a/native/repair-utility/helpers/hashes_worker.go b/native/repair-utility/helpers/hashes_worker.go new file mode 100644 index 000000000000..42515c12ee49 --- /dev/null +++ b/native/repair-utility/helpers/hashes_worker.go @@ -0,0 +1,318 @@ +package helpers + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "hash/crc32" + "io" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "repair/logger" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +var ( + GenerateHashesFlag bool + CheckHashesFlag bool + CompareHashesFlag bool + CompareHashesFile1 string + CompareHashesFile2 string + Algorithms []string + HashesOutputFile string +) + +type fileInfo struct { + Path string `json:"Path"` + Size int64 `json:"Size,omitempty"` + SymlinkTo string `json:"SymlinkTo,omitempty"` + Mode os.FileMode `json:"Mode,omitempty"` + ModificationTime *time.Time `json:"ModificationTime,omitempty"` + Hashes map[string]string `json:"Hashes,omitempty"` +} +type hashesDiffMap map[string][]string +type manifestDiffStruct struct { + MissingLocalFiles []string + RedundantLocalFiles []string + HashesDiff hashesDiffMap +} +type manifestInstallationInfo struct { + Name string + Version string + BuildNumber string + ProductCode string +} +type ManifestStruct struct { + InstallationInfo manifestInstallationInfo + FilesInfo []fileInfo +} + +var installationFilesWitHashes []fileInfo +var manifest ManifestStruct + +func GetManifestStruct() ManifestStruct { + if len(manifest.FilesInfo) > 0 { + return manifest + } else { + return GenerateMainfest() + } +} +func GetInstallationFilesWithHashes() []fileInfo { + if len(installationFilesWitHashes) > 0 { + return installationFilesWitHashes + } else { + return GenerateFilesInfo() + } +} +func GenerateMainfest() (manifestAsStruct ManifestStruct) { + manifestAsStruct.FilesInfo = GetInstallationFilesWithHashes() + installationInfo := CurrentIde.GetInfo() + manifestAsStruct.InstallationInfo.Name = installationInfo.Name + manifestAsStruct.InstallationInfo.Version = installationInfo.Version + manifestAsStruct.InstallationInfo.BuildNumber = installationInfo.BuildNumber + manifestAsStruct.InstallationInfo.ProductCode = installationInfo.ProductCode + return manifestAsStruct +} +func GenerateMainfestFile() { + logger.ConsoleLogger.Println("hashes are being generated...") + filesInfoAsBytes := convertAnyToJson(GetManifestStruct()) + if FileExists(HashesOutputFile) { + _ = os.Remove(HashesOutputFile) + } + WriteToFile(string(filesInfoAsBytes), HashesOutputFile) +} +func CompareHashes() { + file1, _ := ioutil.ReadFile(CompareHashesFile1) + file2, _ := ioutil.ReadFile(CompareHashesFile2) + manifestStruct1 := convertJsonToSlice(file1) + manifestStruct2 := convertJsonToSlice(file2) + if manifestDiff, err := compareManifests(manifestStruct1, manifestStruct2); err == nil { + if len(manifestDiff.HashesDiff) > 0 || len(manifestDiff.MissingLocalFiles) > 0 || len(manifestDiff.RedundantLocalFiles) > 0 { + if len(manifestDiff.HashesDiff) > 0 { + logger.DebugLogger.Println(string(convertAnyToJson(manifestDiff))) + logger.ErrorLogger.Println("Hashes for the following files do not match:" + sliceAsString(manifestDiff.HashesDiff.getFilesSlice())) + } + if len(manifestDiff.MissingLocalFiles) > 0 { + logger.ErrorLogger.Println(CompareHashesFile2 + " missing the following: " + sliceAsString(manifestDiff.MissingLocalFiles)) + } + if len(manifestDiff.RedundantLocalFiles) > 0 { + logger.ErrorLogger.Println(CompareHashesFile1 + " missing the following: " + sliceAsString(manifestDiff.RedundantLocalFiles)) + } + } else { + logger.InfoLogger.Println("No illegal files found") + } + + } else { + logger.ErrorLogger.Println(err) + } + +} +func CheckHashes() { + logger.InfoLogger.Println("Downloading manifest file") + manifestJson, err := downloadManifest(CurrentIde) + if err != nil { + logger.WarningLogger.Printf("Unable to download manifest file from %s: \n\t %s", generateManifestUrl(CurrentIde), err.Error()) + return + } else { + logger.DebugLogger.Printf("Downloaded manifest from %s", generateManifestUrl(CurrentIde)) + } + manifestStruct := convertJsonToSlice(manifestJson) + logger.InfoLogger.Println("Hashes are being checked") + if manifestDiff, err := compareManifests(manifestStruct, GetManifestStruct()); err == nil { + if len(manifestDiff.HashesDiff) > 0 { + logger.DebugLogger.Println(string(convertAnyToJson(manifestDiff))) + logger.ErrorLogger.Println("Hashes for the following files does not match the hashes generated by JetBrains:" + sliceAsString(manifestDiff.HashesDiff.getFilesSlice())) + AskUserToDownloadFreshInstallation() + } else if len(manifestDiff.MissingLocalFiles) > 0 { + logger.ErrorLogger.Println("The following files were not found in the installation:" + sliceAsString(manifestDiff.MissingLocalFiles)) + } else if len(manifestDiff.RedundantLocalFiles) > 0 { + logger.WarningLogger.Println("Installation directory contains files which aren’t part of distribution:" + sliceAsString(manifestDiff.RedundantLocalFiles)) + AskUserAndRemoveFiles(manifestDiff.RedundantLocalFiles) + } else { + logger.InfoLogger.Println("No illegal files found") + } + } else { + logger.ErrorLogger.Println(err) + } +} + +func (m *hashesDiffMap) getFilesSlice() (slice []string) { + for i := range *m { + slice = append(slice, i) + } + return slice +} +func sliceAsString(slice []string) (sliceAsList string) { + for _, sliceElement := range slice { + sliceAsList = sliceAsList + "\n\t" + sliceElement + } + return sliceAsList +} + +func compareManifests(remoteManifestStruct ManifestStruct, localManifestStruct ManifestStruct) (manifestDiffStruct, error) { + if !CheckIfManifestBalongsToInstallation(remoteManifestStruct.InstallationInfo, localManifestStruct.InstallationInfo) { + return manifestDiffStruct{}, errors.New("Manifest file does not belong to the local installation.") + } + var diff manifestDiffStruct + diff.HashesDiff = make(map[string][]string) + for i := 0; i < 2; i++ { + for _, remoteFile := range remoteManifestStruct.FilesInfo { + found := false + for _, localFile := range localManifestStruct.FilesInfo { + if remoteFile.Path == localFile.Path { + found = true + for alg := range remoteFile.Hashes { + if remoteFile.Hashes[alg] != localFile.Hashes[alg] && !(len(diff.HashesDiff[remoteFile.Path]) != 0) { + diff.HashesDiff[remoteFile.Path] = []string{remoteFile.Hashes[alg], localFile.Hashes[alg]} + } + } + break + } + } + if !found && !sliceOfStringsContains(ManifestFilesExclusions[runtime.GOOS], remoteFile.Path) { + diff.RedundantLocalFiles = append(diff.RedundantLocalFiles, remoteFile.Path) + } + } + if i == 0 { + remoteManifestStruct, localManifestStruct = localManifestStruct, remoteManifestStruct + diff.MissingLocalFiles, diff.RedundantLocalFiles = diff.RedundantLocalFiles, diff.MissingLocalFiles + } + } + + return diff, nil +} + +func CheckIfManifestBalongsToInstallation(remoteInfo manifestInstallationInfo, localInfo manifestInstallationInfo) bool { + if remoteInfo.BuildNumber == localInfo.BuildNumber { + return true + } + logger.InfoLogger.Println(remoteInfo.BuildNumber + "!=" + localInfo.BuildNumber) + return false +} + +func convertJsonToSlice(manifestJson []byte) (manifest ManifestStruct) { + err := json.Unmarshal(manifestJson, &manifest) + if err == nil { + return manifest + } + logger.WarningLogger.Println("Error reading manifest file: " + err.Error()) + return ManifestStruct{} +} + +func downloadManifest(ide IDE) (json []byte, err error) { + if resp, err := http.Get(generateManifestUrl(ide)); err == nil { + defer resp.Body.Close() + if resp.StatusCode != 200 { + return nil, errors.New("Error downloading Manifest file. Response status = " + strconv.Itoa(resp.StatusCode)) + } + if json, err = ioutil.ReadAll(resp.Body); err == nil { + return json, err + } + } + return nil, err + +} + +func generateManifestUrl(ide IDE) string { + url := ide.GetDownloadUrl() + ".manifest" + return url +} + +func convertAnyToJson(toConvert interface{}) (result []byte) { + marshal, _ := json.Marshal(toConvert) + return marshal +} + +var manifestGenerationWorkGroup sync.WaitGroup + +type ProtectedFilesStruct struct { + mu sync.Mutex + distrFiles []fileInfo +} + +func GenerateFilesInfo() (distrFiles []fileInfo) { + ideaBinary := GetIdeaBinaryToWrokWith() + ideaPath := GetIdeIdePackageByBinary(ideaBinary) + timebeginning := time.Now().Unix() + var distrFilesThreaded ProtectedFilesStruct + manifestGenerationWorkGroup.Add(1) + go walkAndCollectHashes(ideaPath, &distrFilesThreaded) + manifestGenerationWorkGroup.Wait() + distrFiles = distrFilesThreaded.distrFiles + logger.InfoLogger.Printf("File struct size: %d, Time %d sec", len(distrFiles), time.Now().Unix()-timebeginning) + return distrFiles +} + +func walkAndCollectHashes(currentPath string, distrFiles *ProtectedFilesStruct) { + defer manifestGenerationWorkGroup.Done() + visit := func(path string, file os.DirEntry, err error) error { + if file.IsDir() && path != currentPath { + manifestGenerationWorkGroup.Add(1) + go walkAndCollectHashes(path, distrFiles) + return filepath.SkipDir + } + var distrFile fileInfo + distrFile.Hashes = make(map[string]string) + distrFile.Path = filepath.ToSlash(strings.TrimPrefix(path, GetIdePackageToWorkWith(GetIdeaBinaryToWrokWith())+string(os.PathSeparator))) + if file.Type().IsRegular() { + fillFileInfoStruct(path, file, &distrFile) + } else if file.Type()&os.ModeSymlink != 0 { + link, _ := os.Readlink(path) + distrFile.SymlinkTo = link + } else if file.IsDir() { + return nil + } + distrFiles.mu.Lock() + distrFiles.distrFiles = append(distrFiles.distrFiles, distrFile) + distrFiles.mu.Unlock() + return nil + } + _ = filepath.WalkDir(currentPath, visit) +} + +func fillFileInfoStruct(path string, file os.DirEntry, distrFile *fileInfo) { + info, _ := file.Info() + distrFile.Size = info.Size() + distrFile.Mode = info.Mode() + date := info.ModTime().Local() + distrFile.ModificationTime = &date + for _, algorithm := range Algorithms { + distrFile.Hashes[algorithm] = getHash(path, algorithm) + } +} + +func getHash(path string, algorithm string) string { + f, err := os.Open(path) + logger.ExitWithExceptionOnError(err) + defer func() { + _ = f.Close() + }() + + copyBuf := make([]byte, 1024*1024) + + if algorithm == "CRC32" { + tablePolynomial := crc32.MakeTable(0xedb88320) + h := crc32.New(tablePolynomial) + if _, err := io.CopyBuffer(h, f, copyBuf); err != nil { + logger.ExitWithExceptionOnError(err) + } + + return hex.EncodeToString(h.Sum(nil)) + } else if algorithm == "SHA256" { + h := sha256.New() + if _, err := io.CopyBuffer(h, f, copyBuf); err != nil { + logger.ExitWithExceptionOnError(err) + } + return hex.EncodeToString(h.Sum(nil)) + } else { + logger.ExitWithExceptionOnError(errors.New(algorithm + " is not yet supported")) + return "" + } +} diff --git a/native/repair-utility/helpers/ide.go b/native/repair-utility/helpers/ide.go new file mode 100644 index 000000000000..2de5e6b7c7db --- /dev/null +++ b/native/repair-utility/helpers/ide.go @@ -0,0 +1,291 @@ +package helpers + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "repair/logger" + "runtime" + "strconv" + "strings" +) + +type IdeInfo struct { + Name string + Version string // 2022.2.1 + BuildNumber string + ProductCode string + DataDirectoryName string + IsRepairBundled bool + Launch []struct { + Os string `json:"os"` + LauncherPath string `json:"launcherPath"` + JavaExecutablePath string `json:"javaExecutablePath"` + VmOptionsFilePath string `json:"vmOptionsFilePath"` + } `json:"launch"` +} +type IDE struct { + Binary string + Package string + Info IdeInfo +} + +func (ide *IDE) MarkRepairAsBundled() { + ide.Info.IsRepairBundled = true +} +func (ide *IDE) IsRepairBundled() bool { + return ide.Info.IsRepairBundled +} +func (ide *IDE) SetBinaryToWrokWith(ideaBinary string) { + if _, err := GetIdeInfoByBinary(ideaBinary); err == nil { + ide.Binary = ideaBinary + } +} +func (ide *IDE) GetInfo() IdeInfo { + if len(ide.Package) == 0 { + ide.Package = GetIdePackageToWorkWith(ide.Binary) + } + if len(ide.Info.Name) == 0 { + var fileContent []byte + var err error + fileContent, err = ioutil.ReadFile(ide.Package + IdeProductInfoRelatedToInstallationPath[runtime.GOOS]) + if err != nil { + for operatingSystem, path := range IdeProductInfoRelatedToInstallationPath { + if content, er := ioutil.ReadFile(ide.Package + path); er == nil { + fileContent = content + logger.WarningLogger.Printf("Could not find product-info.json for %s, but found it for %s ", runtime.GOOS, operatingSystem) + } + } + } + err = json.Unmarshal(fileContent, &ide.Info) + if err == nil { + return ide.Info + } + } + return ide.Info +} +func (ide *IDE) GetDownloadUrl() string { + if //goland:noinspection ALL + len(EmbeededDownloadUrl) > 0 && CurrentIde.IsRepairBundled() { + logger.DebugLogger.Println("Embedded download URL = " + EmbeededDownloadUrl) + return EmbeededDownloadUrl + } + ideInfo := ide.GetInfo() + ideBinary := filepath.Base(ideInfo.Launch[0].LauncherPath) + ideBinary = strings.TrimSuffix(ideBinary, ".sh") + ideBinary = strings.TrimSuffix(ideBinary, "64.exe") + architectureSuffix := "" + if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" { + architectureSuffix = "-aarch64" + } + //For example https://download.jetbrains.com/idea/ideaIU-2022.2.1.exe.manifest + url := downloadsURL + "/" + ideBinary + "/" + ideBinary + ideInfo.ProductCode + "-" + ideInfo.Version + architectureSuffix + archiveExtension[runtime.GOOS] + logger.DebugLogger.Println("No embedded download URL found. Generated download URL = " + url) + return url +} + +func (ide *IDE) GetDisabledPluginsFilename() string { + return ide.GetConfigurationDirectory() + "disabled_plugins.txt" +} +func (ide *IDE) PluginsList() *PluginInfoList { + if len(PluginsList) > 0 { + return &PluginsList + } + pluginsDirs := ide.getPluginsDirectories() + for _, pluginsDir := range pluginsDirs { + ScanDirForPlugins(pluginsDir) + } + return &PluginsList +} +func (ide *IDE) CustomPluginsList() *PluginInfoList { + if len(CustomPluginsList) > 0 { + return &CustomPluginsList + } + if len(PluginsList) < 1 { + PluginsList = *ide.PluginsList() + } + for _, plugin := range PluginsList { + if !plugin.isBundled { + CustomPluginsList = append(CustomPluginsList, plugin) + } + } + return &CustomPluginsList +} +func (ide *IDE) DisableAllCustomPlugins(revert bool) { + disabledPluginsFilename := ide.GetDisabledPluginsFilename() + if revert { + logger.InfoLogger.Println("Enabling plugins back...") + logger.DebugLogger.Printf("Moving file %s to %s", disabledPluginsFilename+".backup", disabledPluginsFilename) + err := MoveFile(disabledPluginsFilename+".backup", disabledPluginsFilename) + logger.ExitWithExceptionOnError(err) + } else { + logger.InfoLogger.Println("Disabling all custom plugins...") + logger.DebugLogger.Println("Creating the list of already disabled plugins and disabling the rest") + if _, err := os.Stat(disabledPluginsFilename); err == nil { + logger.DebugLogger.Printf("Moving file %s to %s", disabledPluginsFilename, disabledPluginsFilename+".backup") + err = MoveFile(disabledPluginsFilename, disabledPluginsFilename+".backup") + logger.ExitWithExceptionOnError(err) + } + CustomPluginsList = *CurrentIde.CustomPluginsList() + ide.disablePlugins(&CustomPluginsList) + logger.InfoLogger.Println("Custom plugins were disabled") + } + return +} +func (ide *IDE) RefreshPluginsList() { + PluginsList = *new(PluginInfoList) + CustomPluginsList = *new(PluginInfoList) + PluginsList = *ide.PluginsList() + CustomPluginsList = *ide.CustomPluginsList() +} + +func (ide *IDE) getPluginsDirectories() (pluginsDirs []string) { + pluginsDirs = append(pluginsDirs, GetIdeBundledPluginsDirectory(ide.Binary)) + pluginsDirs = append(pluginsDirs, ide.GetIdeCustomPluginsDirectory()) + //pluginsDirs = append(pluginsDirs, GetIdeCustomPluginsDirectory(ide.Binary)) + for i, pluginsDir := range pluginsDirs { + pluginsDirs[i] = filepath.Clean(pluginsDir) + string(os.PathSeparator) + } + return pluginsDirs +} +func (ide *IDE) EnablePlugin(plugin PluginInfo) { + disabledPluginsFilename := ide.GetDisabledPluginsFilename() + disabledPluginRecord := GetDisabledPluginRecord(plugin) + err := RemoveFromFile(disabledPluginRecord, disabledPluginsFilename) + logger.ExitWithExceptionOnError(err) + logger.ConsoleLogger.Printf("%s has been enabled", plugin.Name) + return +} +func (ide *IDE) disablePlugins(pluginsList *PluginInfoList) { + disabledPluginsFilename := ide.GetDisabledPluginsFilename() + var allPluginsRecords string + for _, plugin := range *pluginsList { + allPluginsRecords = allPluginsRecords + "\n" + GetDisabledPluginRecord(plugin) + plugin.IsDisabled = true + } + WriteToFile(allPluginsRecords, disabledPluginsFilename) +} +func (ide *IDE) DisablePlugin(plugin PluginInfo) { + disabledPluginsFilename := ide.GetDisabledPluginsFilename() + disabledPluginRecord := GetDisabledPluginRecord(plugin) + WriteToFile(disabledPluginRecord, disabledPluginsFilename) + logger.ConsoleLogger.Println("\"" + plugin.Name + "\" has been disabled") + return +} +func (ide *IDE) updatePlugin(plugin PluginInfo) { + ide.removePlugin(plugin) + ide.installPlugin(downloadLatestCompatibleVersionOfPlugin(ide.Binary, plugin)) +} +func (ide *IDE) updatePlugins(pluginsList []PluginInfo) { + for _, plugin := range pluginsList { + ide.updatePlugin(plugin) + logger.ConsoleLogger.Println("\"" + plugin.Name + "\" has been updated to compatible version") + } +} +func (ide *IDE) installPlugin(pluginZipPath string) { + err := Unzip(pluginZipPath, ide.GetIdeCustomPluginsDirectory()) + logger.ExitWithExceptionOnError(err) + err = os.Remove(pluginZipPath) + logger.ExitWithExceptionOnError(err) +} +func (ide *IDE) removePlugins(plugins []PluginInfo) { + for _, plugin := range plugins { + ide.removePlugin(plugin) + } +} +func (ide *IDE) removePlugin(plugin PluginInfo) { + pluginsDir := ide.GetIdeCustomPluginsDirectory() + pluginFiles := strings.TrimPrefix(plugin.MainJarPath, pluginsDir) + pluginFiles = strings.TrimPrefix(pluginFiles, string(os.PathSeparator)) + if idx := strings.Index(pluginFiles, string(os.PathSeparator)); idx != -1 { + pluginFiles = pluginFiles[:idx] + } + filesToRemove := pluginsDir + pluginFiles + matches, _ := filepath.Glob(filesToRemove) + for _, match := range matches { + fileToRemove := GetAbsolutePath(match) + err := os.RemoveAll(fileToRemove) + logger.ExitWithExceptionOnError(err) + } +} +func (ide *IDE) ClearSystemDirectory() { + logger.InfoLogger.Println("Clearing system directory...") + ideSystemDirectory := ide.GetSystemDirectory() + err := RemoveContentOfDir(ideSystemDirectory) + logger.ExitWithExceptionOnError(err) +} + +func (ide *IDE) AskUserAndUpdatePlugins(pluginsToUpdateList PluginInfoList) { + if len(pluginsToUpdateList) > 0 { + logger.ConsoleLogger.Printf("The following plugins could be updated:\n" + pluginsToUpdateList.ToString()) + question := "Update the above plugins?" + options := []string{"y", "n"} + if AskQuestionWithOptions(question, options) == "y" { + logger.ConsoleLogger.Println("Updaing plugins...") + ide.updatePlugins(pluginsToUpdateList) + } + } +} +func (ide *IDE) AskUserAndDisablePlugins(pluginsToDisableList PluginInfoList) { + if len(pluginsToDisableList) > 0 { + alertMessage := "The following plugins should be disabled as " + function := GetCallerFunction() + if strings.HasSuffix(function, "RunPluginsAspect") { + alertMessage = alertMessage + "there is no compatible version for the IDE" + } else if strings.HasSuffix(function, "RunLogAspect") { + alertMessage = alertMessage + "there are errors from these plugins in idea.log" + } + logger.ConsoleLogger.Println(alertMessage+"\n", pluginsToDisableList.ToString()) + question := "Disable the above plugins?" + options := []string{"y", "n"} + if AskQuestionWithOptions(question, options) == "y" { + logger.ConsoleLogger.Println("Disabling the above plugins...") + ide.disablePlugins(&pluginsToDisableList) + } + } +} +func (ide *IDE) AskUserAndClearSystemDirectory() { + question := "Clear system directory as a general troubleshooting step? \n\t Note: Local history will be removed!" + options := []string{"y", "n"} + if AskQuestionWithOptions(question, options) == "y" { + ide.ClearSystemDirectory() + } +} +func (ide *IDE) AskUserToRunIdeAndCheckTheIssue() bool { + question := "Let's try to start the IDE to check if the issue is still here?" + options := []string{"y", "n"} + if ForceAskQuestionWithOptions(question, options) == "y" { + process := ide.RunIde() + question = "The issue still here?" + options = []string{"y", "n"} + if AskQuestionWithOptions(question, options) == "y" { + logger.InfoLogger.Println("Shutting down the IDE and continue repairing") + err := process.Kill() + logger.WriteToLogOnError(err, logger.WarningLogger) + return true + } else { + return false + } + } else { + question = "Then restart the IDE manually. Can the issue be reproduced now?" + options = []string{"y", "n"} + if ForceAskQuestionWithOptions(question, options) == "y" { + return true + } else { + return false + } + } +} + +func (ide *IDE) RunIde() *os.Process { + cmd := exec.Command(ide.Binary) + err := cmd.Start() + if err != nil { + log.Fatal(err) + } + logger.DebugLogger.Println("Started the IDE with PID " + strconv.Itoa(cmd.Process.Pid)) + return cmd.Process +} diff --git a/native/repair-utility/helpers/installations_finder.go b/native/repair-utility/helpers/installations_finder.go new file mode 100644 index 000000000000..a78cacbc3e4e --- /dev/null +++ b/native/repair-utility/helpers/installations_finder.go @@ -0,0 +1,69 @@ +package helpers + +import ( + "os" + "path/filepath" + "repair/logger" + "strconv" + "strings" +) + +func SelectIdeaBinary() (path string, err error) { + logger.InfoLogger.Println("----------Scanning system for IDE installations----------") + var installedIdes []string + installedIdes, err = FindInstalledIdePackages() + if err != nil { + return "", err + } + + for i, idePackage := range installedIdes { + info, _ := GetIdeInfoByPackage(idePackage) + logger.ConsoleLogger.Printf("[%v] %v %v (%v-%v) - %v \n", i, info.Name, info.Version, info.ProductCode, info.BuildNumber, beautifyPackageName(idePackage)) + } + logger.ConsoleLogger.Printf("Please choose one of the above installations to perform operations with\n" + + "You can either type the digit, or define absolute path to the IDE installation") + + input, err := ReadUserInput() + input = strings.TrimSpace(input) + if number, convertible := strconv.Atoi(input); convertible == nil && number < len(installedIdes) { + path, err = GetIdeBinaryByPackage(installedIdes[number]) + logger.ExitWithExceptionOnError(err) + } else if number >= len(installedIdes) { + logger.ConsoleLogger.Fatal(input + " is out of range. Please use 1.." + strconv.Itoa(len(installedIdes)-1) + " to choose the IDE to work with") + } else { + path = strings.TrimSpace(input) + } + path = filepath.Clean(path) + _, err = os.Open(path) + if err != nil { + logger.DebugLogger.Println(err) + logger.ConsoleLogger.Fatal("Seems you tried to enter the path to the IDE binary, but there is no IDE binary in the \"" + path + "\" path") + } + logger.DebugLogger.Println("IDE binary to work with:" + path) + return path, err +} +func beautifyPackageName(idePackage string) string { + if strings.Contains(idePackage, "oolbox") { + return "From ToolBox app" + } + return idePackage +} +func FindIdeInstallationsByMask(path string) (foundIdePackages []string, err error) { + for _, possibleBaseFileName := range possibleBaseFileNames { + currentPath := strings.Replace(path, "{possibleBaseFileName}", possibleBaseFileName, -1) + matches, _ := filepath.Glob(os.ExpandEnv(currentPath)) + for _, match := range matches { + match = GetIdeIdePackageByBinary(match) + foundIdePackages = append(foundIdePackages, match) + } + } + return foundIdePackages, err +} +func FindInstalledIdePackages() (installedIdes []string, err error) { + for _, path := range getOsDependentDir(possibleBinariesPaths) { + var foundInstallations []string + foundInstallations, err = FindIdeInstallationsByMask(path) + installedIdes = append(installedIdes, foundInstallations...) + } + return installedIdes, err +} diff --git a/native/repair-utility/helpers/installations_finder_test.go b/native/repair-utility/helpers/installations_finder_test.go new file mode 100644 index 000000000000..a13ed1062fb3 --- /dev/null +++ b/native/repair-utility/helpers/installations_finder_test.go @@ -0,0 +1,58 @@ +package helpers + +import ( + "os" + "repair/logger" + "runtime" + "testing" +) + +func TestMain(m *testing.M) { + code := m.Run() + if code == 0 { + logger.RemoveLogFile() + } + os.Exit(code) +} + +var tests = []struct { + name string + wantPossible_binaries map[string][]string + wantErr bool +}{ + {name: "in /Applications folder", + wantPossible_binaries: map[string][]string{"darwin": []string{"/Applications/IntelliJ IDEA.app"}, "windows": []string{"C:\\Program Files\\JetBrains\\IntelliJ IDEA 2021.1.2"}}, + wantErr: false, + }, +} + +func TestFindPossibleIdeInstallations(t *testing.T) { + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotPossible_binaries, err := FindInstalledIdePackages() + if (err != nil) != tt.wantErr { + t.Errorf("FindInstalledIdePackages() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !sliceContainsAll(gotPossible_binaries, tt.wantPossible_binaries[runtime.GOOS]) { + t.Errorf("FindInstalledIdePackages() gotPossible_binaries = %v, want %v", gotPossible_binaries, tt.wantPossible_binaries) + } + }) + } +} + +func sliceContainsAll(allValues []string, mandatoryValues []string) (containsAll bool) { + for _, mandatoryElement := range mandatoryValues { + for _, element := range allValues { + if mandatoryElement == element { + containsAll = true + break + } + } + if containsAll == false { + return false + } + } + return true +} diff --git a/native/repair-utility/helpers/java_runner.go b/native/repair-utility/helpers/java_runner.go new file mode 100644 index 000000000000..c5c48f6f9ab4 --- /dev/null +++ b/native/repair-utility/helpers/java_runner.go @@ -0,0 +1,42 @@ +package helpers + +import ( + "io/ioutil" + "os" + "os/exec" + "repair/logger" + "runtime" + "strings" +) + +func GetJavaProperty(javaBinary string, property string) (output string, err error) { + javaText := "class Main {" + + "public static void main(String[] ideBinary) {" + + "System.out.print(System.getProperty(\"" + property + "\"));" + + "}" + + "}" + return RunJavaProgram(javaBinary, javaText) + +} +func RunJavaProgram(javaBinary string, javaText string) (output string, err error) { + if runtime.GOOS == "windows" { + javaBinary = strings.TrimRight(javaBinary, ".exe") + } + javacBinary := javaBinary + "c" + if runtime.GOOS == "windows" { + javacBinary = javacBinary + ".exe" + } + className := "Main" + err = ioutil.WriteFile(className+".java", []byte(javaText), 0644) + logger.ExitWithExceptionOnError(err) + _, err = exec.Command(javacBinary, className+".java").CombinedOutput() + logger.ExitWithExceptionOnError(err) + outputBytes, err := exec.Command(javaBinary, className).CombinedOutput() + logger.ExitWithExceptionOnError(err) + output = string(outputBytes) + err = os.Remove(className + ".class") + logger.WriteToLogOnError(err, logger.DebugLogger) + err = os.Remove(className + ".java") + logger.WriteToLogOnError(err, logger.DebugLogger) + return output, nil +} diff --git a/native/repair-utility/helpers/log_parser.go b/native/repair-utility/helpers/log_parser.go new file mode 100644 index 000000000000..4511cb1d5ed9 --- /dev/null +++ b/native/repair-utility/helpers/log_parser.go @@ -0,0 +1,191 @@ +package helpers + +import ( + "bufio" + "io" + "log" + "os" + "regexp" + "repair/logger" + "strings" +) + +var pluginsWithExceptions PluginInfoList +var logEntriesWithExceptions []LogEntry + +func GetPluginsWithExceptionsInIdeaLog(logfile string) PluginInfoList { + if len(LogEntries) == 0 { + LogEntries = ParseIdeaLogFile(logfile) + } + return pluginsWithExceptions +} +func GetLogEntriesWithExceptions(logfile string) []LogEntry { + if len(LogEntries) == 0 { + LogEntries = ParseIdeaLogFile(logfile) + } + return logEntriesWithExceptions +} +func ParseIdeaLogFile(logfile string) (logEntries []LogEntry) { + reader, err := os.Open(logfile) + logger.ExitWithExceptionOnError(err) + logger.InfoLogger.Println("Parsing idea.log file...") + bufReader := bufio.NewReader(reader) + for { + currentString, err := bufReader.ReadString('\n') + if getTimeStampFromString(currentString) != "" { + if len(logEntries) > 0 && lastLogEntryHasException(logEntries) { + logEntriesWithExceptions = append(logEntriesWithExceptions, logEntries[len(logEntries)-1]) + } + currentEntry := parseLogString(currentString) + if entryContainsPluginToBlame(currentEntry) && len(logEntries) >= 4 { + collectPluginsWithExceptions(currentEntry) + logEntriesWithExceptions = logEntriesWithExceptions[:len(logEntriesWithExceptions)-1] + } + logEntries = append(logEntries, currentEntry) + } else if len(logEntries) > 0 { + logEntries[len(logEntries)-1].Body = logEntries[len(logEntries)-1].Body + currentString + } + if err == io.EOF { + break + } + if err != nil { + log.Fatalf("ERROR: %s", err) + } + } + return logEntries +} + +func lastLogEntryHasException(logEntries []LogEntry) bool { + if len(logEntries[len(logEntries)-1].Body) > 0 && logEntries[len(logEntries)-1].Severity == "ERROR" { + return true + } + return false +} + +func collectPluginsWithExceptions(currentEntry LogEntry) { + if getEnabledPluginByBlameString(currentEntry) != nil && + !pluginsWithExceptions.Contains(*getEnabledPluginByBlameString(currentEntry)) { + pluginsWithExceptions = append(pluginsWithExceptions, *getEnabledPluginByBlameString(currentEntry)) + } +} + +func getEnabledPluginByBlameString(currentEntry LogEntry) *PluginInfo { + pluginName := getPluginNameByBlameString(currentEntry.Header) + PluginsList = *CurrentIde.PluginsList() + for _, plugin := range PluginsList { + if plugin.Name == pluginName && plugin.IsDisabled == false { + return &plugin + } + } + logger.DebugLogger.Println("There are exceptions regarding " + pluginName + " plugin, but it is uninstalled.") + return nil +} + +func getPluginNameByBlameString(logHeader string) string { + logHeader = strings.TrimPrefix(strings.TrimSpace(logHeader), "Plugin to blame: ") + versionMatcher := regexp.MustCompile("version:.*") + logHeader = strings.TrimSpace(versionMatcher.ReplaceAllString(logHeader, "${1}")) + return logHeader +} + +func entryContainsPluginToBlame(entry LogEntry) bool { + if strings.HasPrefix(strings.TrimSpace(entry.Header), "Plugin to blame") { + return true + } + return false +} + +func isTimeStamp(str string) bool { + if getTimeStampFromString(str) != "" { + return true + } + return false +} + +func parseLogString(logEntryAsString string) (currentEntry LogEntry) { + logEntryAsString = strings.TrimLeft(logEntryAsString, "\n") + classEndPosition := 0 + HeaderEndPosition := 0 + currentEntry.DateAndTime = getTimeStampFromString(logEntryAsString) + trimFoundPart(&logEntryAsString, currentEntry.DateAndTime) + rawTimeSinceStart := getRawTimeSinceStart(&logEntryAsString) + currentEntry.TimeSinceStart = getTimeSinceStart(rawTimeSinceStart) + trimFoundPart(&logEntryAsString, rawTimeSinceStart) + currentEntry.Severity = getSeverity(&logEntryAsString) + trimFoundPart(&logEntryAsString, currentEntry.Severity) + + //todo make that without for loop and unify + for i := range logEntryAsString { + if currentEntry.Class == "" && getClass(logEntryAsString[0:i]) != "" { + classEndPosition = i + currentEntry.Class = getClass(logEntryAsString[0:i]) + } + if classEndPosition != 0 { + HeaderEndPosition = strings.IndexAny(logEntryAsString, "\n") + if HeaderEndPosition == -1 { + currentEntry.Header = strings.TrimSpace(logEntryAsString[classEndPosition:]) + break + } else { + //todo check if this is needed + currentEntry.Header = logEntryAsString[classEndPosition:HeaderEndPosition] + } + + } + if HeaderEndPosition > 0 { + currentEntry.Body = logEntryAsString[HeaderEndPosition:] + break + } + } + + return currentEntry +} + +func getRawTimeSinceStart(str *string) string { + dateMatcher := regexp.MustCompile("\\[(.*?)]") + if dateMatcher.MatchString(*str) { + return dateMatcher.FindString(*str) + } + return "" +} + +func trimFoundPart(stringToCut *string, part string) { + *stringToCut = strings.TrimSpace(*stringToCut) + *stringToCut = strings.TrimPrefix(*stringToCut, part) +} + +func getClass(s string) string { + s = strings.TrimSpace(s) + if len(s) > 2 && s[0] == '-' && s[len(s)-1] == '-' { + return strings.TrimSpace(s[1 : len(s)-1]) + } + return "" +} + +func getSeverity(s *string) string { + *s = strings.TrimSpace(*s) + severities := []string{"INFO", "ERROR", "DEBUG", "WARN"} + for _, severity := range severities { + if strings.HasPrefix(*s, severity) { + return severity + } + } + return "" +} + +func getTimeSinceStart(s string) string { + s = strings.TrimSpace(s) + if len(s) > 8 && s[0] == '[' && s[len(s)-1] == ']' { + s = s[1 : len(s)-1] + return strings.TrimSpace(s) + + } + return "" +} + +func getTimeStampFromString(str string) string { + dateMatcher := regexp.MustCompile("^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}[.,]\\d{3})") + if dateMatcher.MatchString(str) { + return dateMatcher.FindString(str) + } + return "" +} diff --git a/native/repair-utility/helpers/log_parser_test.go b/native/repair-utility/helpers/log_parser_test.go new file mode 100644 index 000000000000..5137121d8e0c --- /dev/null +++ b/native/repair-utility/helpers/log_parser_test.go @@ -0,0 +1,369 @@ +package helpers + +import ( + "reflect" + "repair/logger" + "testing" +) + +func TestGetLogEntriesWithExceptions(t *testing.T) { + type args struct { + logfile string + } + tests := []struct { + name string + args args + want []LogEntry + }{ + { + name: "check first error entry in file", + args: args{logfile: GetAbsolutePath("./test_helpers_files/logs/error_on_action.log")}, + want: []LogEntry{ + { + DateAndTime: "2021-02-15 18:09:37,188", + TimeSinceStart: "61174", + Severity: "ERROR", + Class: ".wm.impl.ToolWindowManagerImpl", + Header: "Already disposed: Project (name=HelloJava, containerState=DISPOSE_IN_PROGRESS, componentStore=/Users/konstantin.annikov/IdeaProjects/HelloJava)", + Body: "java.lang.IllegalStateException: Already disposed: Project (name=HelloJava, containerState=DISPOSE_IN_PROGRESS, componentStore=/Users/konstantin.annikov/IdeaProjects/HelloJava)\n\tat com.intellij.serviceContainer.ComponentManagerImpl.getMessageBus(ComponentManagerImpl.kt:139)\n\tat com.intellij.ide.navigationToolbar.NavBarListener.subscribeTo(NavBarListener.java:71)\n\tat com.intellij.ide.navigationToolbar.NavBarPanel.addNotify(NavBarPanel.java:777)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat java.desktop/java.awt.Container.addNotify(Container.java:2800)\n\tat java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791)\n\tat com.intellij.openapi.wm.impl.InternalDecorator.addNotify(InternalDecorator.java:292)\n\tat java.desktop/java.awt.Container.addImpl(Container.java:1146)\n\tat java.desktop/java.awt.Container.add(Container.java:436)\n\tat com.intellij.openapi.ui.ThreeComponentsSplitter.doAddComponent(ThreeComponentsSplitter.java:541)\n\tat com.intellij.openapi.ui.ThreeComponentsSplitter.setLastComponent(ThreeComponentsSplitter.java:506)\n\tat com.intellij.openapi.wm.impl.ToolWindowsPane.setComponent(ToolWindowsPane.java:300)\n\tat com.intellij.openapi.wm.impl.ToolWindowsPane.removeDecorator(ToolWindowsPane.java:233)\n\tat com.intellij.openapi.wm.impl.ToolWindowManagerImpl.removeDecoratorWithoutUpdatingState(ToolWindowManagerImpl.kt:1067)\n\tat com.intellij.openapi.wm.impl.ToolWindowManagerImpl.projectClosed(ToolWindowManagerImpl.kt:510)\n\tat com.intellij.openapi.wm.impl.ToolWindowManagerImpl$ToolWindowManagerAppLevelHelper$2.projectClosed(ToolWindowManagerImpl.kt:216)\n\tat jdk.internal.reflect.GeneratedMethodAccessor141.invoke(Unknown Source)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:566)\n\tat com.intellij.util.messages.impl.MessageBusImpl.invokeListener(MessageBusImpl.java:541)\n\tat com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:143)\n\tat com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:465)\n\tat com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:426)\n\tat com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:415)\n\tat com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:397)\n\tat com.intellij.util.messages.impl.MessageBusImpl.lambda$createTopicHandler$3(MessageBusImpl.java:237)\n\tat com.sun.proxy.$Proxy78.projectClosed(Unknown Source)\n\tat com.intellij.openapi.project.impl.ProjectManagerImpl.fireProjectClosed(ProjectManagerImpl.java:807)\n\tat com.intellij.openapi.project.impl.ProjectManagerImpl.lambda$closeProject$11(ProjectManagerImpl.java:694)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:976)\n\tat com.intellij.openapi.project.impl.ProjectManagerImpl.closeProject(ProjectManagerImpl.java:687)\n\tat com.intellij.openapi.project.impl.ProjectManagerImpl.closeAndDisposeAllProjects(ProjectManagerImpl.java:611)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.disposeSelf(ApplicationImpl.java:190)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.doExit(ApplicationImpl.java:621)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:589)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:569)\n\tat com.intellij.ide.plugins.newui.MyPluginModel.runRestartButton(MyPluginModel.java:861)\n\tat com.intellij.ide.plugins.newui.RestartButton.lambda$new$0(RestartButton.java:13)\n\tat java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)\n\tat java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)\n\tat java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)\n\tat java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)\n\tat java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:270)\n\tat java.desktop/java.awt.Component.processMouseEvent(Component.java:6650)\n\tat java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3345)\n\tat java.desktop/java.awt.Component.processEvent(Component.java:6415)\n\tat java.desktop/java.awt.Container.processEvent(Container.java:2263)\n\tat java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5025)\n\tat java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321)\n\tat java.desktop/java.awt.Component.dispatchEvent(Component.java:4857)\n\tat java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)\n\tat java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)\n\tat java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)\n\tat java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307)\n\tat java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2773)\n\tat java.desktop/java.awt.Component.dispatchEvent(Component.java:4857)\n\tat java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778)\n\tat java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)\n\tat java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)\n\tat java.base/java.security.AccessController.doPrivileged(Native Method)\n\tat java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)\n\tat java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)\n\tat java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751)\n\tat java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749)\n\tat java.base/java.security.AccessController.doPrivileged(Native Method)\n\tat java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)\n\tat java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748)\n\tat com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:974)\n\tat com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:912)\n\tat com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:844)\n\tat com.intellij.ide.IdeEventQueue.lambda$null$8(IdeEventQueue.java:449)\n\tat com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:741)\n\tat com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:448)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831)\n\tat com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:502)\n\tat java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)\n\tat java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)\n\tat java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117)\n\tat java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:190)\n\tat java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:235)\n\tat java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:233)\n\tat java.base/java.security.AccessController.doPrivileged(Native Method)\n\tat java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:233)\n\tat java.desktop/java.awt.Dialog.show(Dialog.java:1063)\n\tat com.intellij.openapi.ui.impl.DialogWrapperPeerImpl$MyDialog.show(DialogWrapperPeerImpl.java:708)\n\tat com.intellij.openapi.ui.impl.DialogWrapperPeerImpl.show(DialogWrapperPeerImpl.java:437)\n\tat com.intellij.openapi.ui.DialogWrapper.doShow(DialogWrapper.java:1685)\n\tat com.intellij.openapi.ui.DialogWrapper.show(DialogWrapper.java:1644)\n\tat com.intellij.ide.actions.ShowSettingsUtilImpl.showSettingsDialog(ShowSettingsUtilImpl.java:80)\n\tat com.intellij.ide.actions.ShowSettingsAction.perform(ShowSettingsAction.java:54)\n\tat com.intellij.ui.mac.MacOSApplicationProvider$Worker.lambda$null$1(MacOSApplicationProvider.java:78)\n\tat com.intellij.ui.mac.MacOSApplicationProvider$Worker.lambda$submit$7(MacOSApplicationProvider.java:175)\n\tat com.intellij.openapi.application.TransactionGuardImpl$2.run(TransactionGuardImpl.java:201)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.lambda$invokeLater$4(ApplicationImpl.java:310)\n\tat com.intellij.openapi.application.impl.FlushQueue.doRun(FlushQueue.java:80)\n\tat com.intellij.openapi.application.impl.FlushQueue.runNextEvent(FlushQueue.java:128)\n\tat com.intellij.openapi.application.impl.FlushQueue.flushNow(FlushQueue.java:46)\n\tat com.intellij.openapi.application.impl.FlushQueue$FlushNow.run(FlushQueue.java:184)\n\tat java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)\n\tat java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:776)\n\tat java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727)\n\tat java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)\n\tat java.base/java.security.AccessController.doPrivileged(Native Method)\n\tat java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)\n\tat java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:746)\n\tat com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:974)\n\tat com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:847)\n\tat com.intellij.ide.IdeEventQueue.lambda$null$8(IdeEventQueue.java:449)\n\tat com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:741)\n\tat com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:448)\n\tat com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831)\n\tat com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:496)\n\tat java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)\n\tat java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)\n\tat java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)\n\tat java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)\n\tat java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)\n\tat java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)\n", + }, + }, + }, + } + for _, tt := range tests { + CurrentIde.SetBinaryToWrokWith(GetRandomIdeInstallationBinary()) + t.Run(tt.name, func(t *testing.T) { + if got := GetLogEntriesWithExceptions(tt.args.logfile); !(got[0].Severity == tt.want[0].Severity && got[0].Header == tt.want[0].Header) { + t.Errorf("GetLogEntriesWithExceptions() = %v, want %v", got, tt.want) + } + }) + } + logger.RemoveLogFile() +} + +func TestGetPluginsWithExceptionsInIdeaLog(t *testing.T) { + type args struct { + logfile string + } + tests := []struct { + name string + args args + want []PluginInfo + }{ + { + name: "Blame Presentation Assistant plugin", + args: args{logfile: GetAbsolutePath("./test_helpers_files/logs/blame_presentation_assistant.log")}, + want: []PluginInfo{ + { + Id: "org.nik.presentation-assistant", + Name: "Presentation Assistant", + Version: "1.0.9", + Vendor: "Nikolay Chashnikov", + MainJarPath: "", + PluginXmlId: "", + MarketplaceId: 0, + IsDisabled: false, + IdeaVersion: IdeaVersion{}, + isBundled: false, + }, + }, + }, + } + for _, tt := range tests { + CurrentIde.SetBinaryToWrokWith(GetRandomIdeInstallationBinary()) + t.Run(tt.name, func(t *testing.T) { + downloadAndInstallPlugins(GetIdeaBinaryToWrokWith(), tt.want) + if got := GetPluginsWithExceptionsInIdeaLog(tt.args.logfile); len(got) > 0 && !(got[0].Id == tt.want[0].Id) { + t.Errorf("GetPluginsWithExceptionsInIdeaLog() = %v, want %v", got, tt.want) + } + CurrentIde.removePlugins(tt.want) + }) + } + logger.RemoveLogFile() +} + +func TestParseIdeaLogFile(t *testing.T) { + type args struct { + logfile string + } + tests := []struct { + name string + args args + wantLogEntries []LogEntry + }{ + { + name: "INFO record check", + args: args{logfile: GetAbsolutePath("./test_helpers_files/logs/error_on_action.log")}, + wantLogEntries: []LogEntry{ + { + DateAndTime: "2021-02-15 18:09:40,829", + TimeSinceStart: "64816", + Severity: "INFO", + Class: "plication.impl.ApplicationImpl", + Header: "Write action Statistics:", + Body: "Event number: 8\nTotal time spent: 262ms\nAverage duration: 32ms\nMedian duration: 25ms\nMax duration: 73ms (it was 'write action (class com.intellij.openapi.module.impl.ModuleManagerImpl$$Lambda$1556/0x0000000801aec040)')\n", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotLogEntries := ParseIdeaLogFile(tt.args.logfile); !(gotLogEntries[21].Header == tt.wantLogEntries[0].Header && gotLogEntries[21].Severity == tt.wantLogEntries[0].Severity) && gotLogEntries[21].TimeSinceStart == tt.wantLogEntries[0].TimeSinceStart { + t.Errorf("ParseIdeaLogFile() = %v, want %v", gotLogEntries, tt.wantLogEntries) + } + }) + } +} + +func Test_collectPluginsWithExceptions(t *testing.T) { + type args struct { + currentEntry LogEntry + } + tests := []struct { + name string + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + }) + } +} + +func Test_entryContainsPluginToBlame(t *testing.T) { + type args struct { + entry LogEntry + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := entryContainsPluginToBlame(tt.args.entry); got != tt.want { + t.Errorf("entryContainsPluginToBlame() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getClass(t *testing.T) { + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getClass(tt.args.s); got != tt.want { + t.Errorf("getClass() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getEnabledPluginByBlameString(t *testing.T) { + type args struct { + currentEntry LogEntry + } + tests := []struct { + name string + args args + want *PluginInfo + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getEnabledPluginByBlameString(tt.args.currentEntry); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getEnabledPluginByBlameString() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getPluginNameByBlameString(t *testing.T) { + type args struct { + logHeader string + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getPluginNameByBlameString(tt.args.logHeader); got != tt.want { + t.Errorf("getPluginNameByBlameString() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getRawTimeSinceStart(t *testing.T) { + type args struct { + str *string + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getRawTimeSinceStart(tt.args.str); got != tt.want { + t.Errorf("getRawTimeSinceStart() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getSeverity(t *testing.T) { + type args struct { + s *string + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getSeverity(tt.args.s); got != tt.want { + t.Errorf("getSeverity() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getTimeSinceStart(t *testing.T) { + type args struct { + s string + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getTimeSinceStart(tt.args.s); got != tt.want { + t.Errorf("getTimeSinceStart() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_getTimeStampFromString(t *testing.T) { + type args struct { + str string + } + tests := []struct { + name string + args args + want string + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getTimeStampFromString(tt.args.str); got != tt.want { + t.Errorf("getTimeStampFromString() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_isTimeStamp(t *testing.T) { + type args struct { + str string + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isTimeStamp(tt.args.str); got != tt.want { + t.Errorf("isTimeStamp() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_parseLogString(t *testing.T) { + type args struct { + logEntryAsString string + } + tests := []struct { + name string + args args + wantCurrentEntry LogEntry + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotCurrentEntry := parseLogString(tt.args.logEntryAsString); !reflect.DeepEqual(gotCurrentEntry, tt.wantCurrentEntry) { + t.Errorf("parseLogString() = %v, want %v", gotCurrentEntry, tt.wantCurrentEntry) + } + }) + } +} + +func Test_previousLogEntryHasException(t *testing.T) { + type args struct { + logEntries []LogEntry + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := lastLogEntryHasException(tt.args.logEntries); got != tt.want { + t.Errorf("lastLogEntryHasException() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_trimFoundPart(t *testing.T) { + type args struct { + stringToCut *string + part string + } + tests := []struct { + name string + args args + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + }) + } +} diff --git a/native/repair-utility/helpers/plugins.go b/native/repair-utility/helpers/plugins.go new file mode 100644 index 000000000000..64f57d8db21f --- /dev/null +++ b/native/repair-utility/helpers/plugins.go @@ -0,0 +1,70 @@ +package helpers + +import ( + "strconv" +) + +type IdeaVersion struct { + SinceBuild string `xml:"since-build,attr"` + UntilBuild string `xml:"until-build,attr"` +} + +var disabledPlugins []string + +func (plugins *PluginInfoList) ToString() (pluginsAsString string) { + for i, plugin := range *plugins { + pluginsAsString = pluginsAsString + " " + strconv.Itoa(i+1) + ". " + plugin.Name + "\n" + } + return +} +func (plugins *PluginInfoList) Contains(pluginToCheck PluginInfo) bool { + for _, plugin := range *plugins { + if pluginToCheck == plugin { + return true + } + } + return false +} +func (plugins *PluginInfoList) FilterIncompatible() (incompatiblePlugins PluginInfoList) { + ideBuildAsFloat := ConvertBuildNumberToFloat(CurrentIde.GetInfo().BuildNumber) + for _, plugin := range *plugins { + pluginSinceBuildAsFloat := ConvertBuildNumberToFloat(plugin.IdeaVersion.SinceBuild) + pluginUntilBuildAsFloat := ConvertBuildNumberToFloat(plugin.IdeaVersion.UntilBuild) + if ((pluginSinceBuildAsFloat > ideBuildAsFloat) || (pluginUntilBuildAsFloat < ideBuildAsFloat)) && plugin.IsDisabled == false { + incompatiblePlugins = append(incompatiblePlugins, plugin) + } + } + return incompatiblePlugins +} +func (plugins *PluginInfoList) FilterDisabled() (disabledPluginsList PluginInfoList) { + for _, plugin := range *plugins { + if plugin.IsDisabled { + disabledPluginsList = append(disabledPluginsList, plugin) + } + } + return disabledPluginsList +} +func (plugins *PluginInfoList) FilterEnabled() (enabledPluginsList PluginInfoList) { + for _, plugin := range *plugins { + if !plugin.IsDisabled { + enabledPluginsList = append(enabledPluginsList, plugin) + } + } + return enabledPluginsList +} +func (plugins *PluginInfoList) FilterUpdatable() (updatablePlugins PluginInfoList) { + for _, plugin := range *plugins { + if CheckIfPluginHasCompatibleVersion(CurrentIde.Binary, &plugin) { + updatablePlugins = append(updatablePlugins, plugin) + } + } + return +} +func (plugins *PluginInfoList) FilterDeprecated() (deprecatedPlugins PluginInfoList) { + for _, plugin := range *plugins { + if !CheckIfPluginHasCompatibleVersion(CurrentIde.Binary, &plugin) { + deprecatedPlugins = append(deprecatedPlugins, plugin) + } + } + return +} diff --git a/native/repair-utility/helpers/plugins_helper.go b/native/repair-utility/helpers/plugins_helper.go new file mode 100644 index 000000000000..03bf80c508cb --- /dev/null +++ b/native/repair-utility/helpers/plugins_helper.go @@ -0,0 +1,203 @@ +package helpers + +import ( + "archive/zip" + "bufio" + "bytes" + "encoding/json" + "encoding/xml" + "io" + "io/ioutil" + "net/http" + "os" + "path" + "path/filepath" + "repair/logger" + "strconv" + "strings" +) + +func downloadLatestCompatibleVersionOfPlugin(ideaBinary string, plugin PluginInfo) (downloadedZipPath string) { + url := "https://plugins.jetbrains.com/plugin/download?" + + "pluginId=" + GetDisabledPluginRecord(plugin) + + "&version=" + RequestLatestCompatibleVersionFromMarketplace(ideaBinary, &plugin) + + downloadedZipPath = DownloadFile(url) + return downloadedZipPath +} + +func RequestLatestCompatibleVersionFromMarketplace(ideaBinary string, plugin *PluginInfo) string { + ideInfo, _ := GetIdeInfoByBinary(ideaBinary) + ideBuildNumber := ideInfo.ProductCode + "-" + ideInfo.BuildNumber + type ServerResponse []struct { + Id int `json:"id"` + PluginId int `json:"pluginId"` + PluginXmlId string `json:"pluginXmlId"` + Version string `json:"version"` + } + var m ServerResponse + url := "https://plugins.jetbrains.com/api/search/compatibleUpdates" + var jsonStr = []byte(`{"build":"` + ideBuildNumber + `","pluginXMLIds":["` + GetDisabledPluginRecord(*plugin) + `"]}`) + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) + logger.ExitWithExceptionOnError(err) + req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(req) + logger.ExitWithExceptionOnError(err) + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + err = json.Unmarshal(body, &m) + for _, newPlugin := range m { + if newPlugin.PluginXmlId == plugin.PluginXmlId { + plugin.MarketplaceId = newPlugin.PluginId + return newPlugin.Version + } + } + return "" +} + +func DownloadFile(url string) (downloadPath string) { + + downloadFolder := GetAbsolutePath("downloads/") + // Get the data + resp, err := http.Get(url) + logger.ExitWithExceptionOnError(err) + finalURL := resp.Request.URL.Path + downloadPath = downloadFolder + string(os.PathSeparator) + path.Base(finalURL) + defer resp.Body.Close() + + // Create the + if _, err := os.Stat(downloadFolder); os.IsNotExist(err) { + _ = os.Mkdir(downloadFolder, 0755) + } + out, err := os.Create(downloadPath) + logger.ExitWithExceptionOnError(err) + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + logger.ExitWithExceptionOnError(err) + return downloadPath +} + +func GetPluginsToUpdateAndPluginsToDelete(ideaBinary string, incompatiblePluginsList []PluginInfo) (pluginsToUpdateList []PluginInfo, pluginsToDeleteList []PluginInfo) { + for _, incompatiblePlugin := range incompatiblePluginsList { + if CheckIfPluginHasCompatibleVersion(ideaBinary, &incompatiblePlugin) { + pluginsToUpdateList = append(pluginsToUpdateList, incompatiblePlugin) + } else { + pluginsToDeleteList = append(pluginsToDeleteList, incompatiblePlugin) + } + } + return pluginsToUpdateList, pluginsToDeleteList +} + +func CheckIfPluginHasCompatibleVersion(ideaBinary string, plugin *PluginInfo) bool { + newPluginVersion := RequestLatestCompatibleVersionFromMarketplace(ideaBinary, plugin) + if (newPluginVersion != "") && (ConvertBuildNumberToFloat(plugin.Version) < ConvertBuildNumberToFloat(newPluginVersion)) { + return true + } + return false +} + +func ScanDirForPlugins(pluginsDir string) { + jarFilesPaths := []string{pluginsDir + "/*/lib/*.jar", pluginsDir + "/*.jar"} + for _, jarFilesPath := range jarFilesPaths { + jarFiles, _ := filepath.Glob(jarFilesPath) + for _, jarFile := range jarFiles { + pluginDescriptionXmlContent := getPluginDescriptionFileContent(jarFile) + if pluginDescriptionXmlContent != nil { + currentPlugin := parsePluginXml(pluginDescriptionXmlContent) + currentPlugin.MainJarPath = jarFile + disabledPluginRecord := GetDisabledPluginRecord(currentPlugin) + currentPlugin.IsDisabled = checkIfPluginDisabled(disabledPluginRecord) + currentPlugin.PluginXmlId = GetDisabledPluginRecord(currentPlugin) + currentPlugin.isBundled = strings.HasPrefix(currentPlugin.MainJarPath, GetIdeBundledPluginsDirectory(GetIdeaBinaryToWrokWith())) + //todo: include XMLs defined in plugin.xml + if len(currentPlugin.PluginXmlId) > 1 { + PluginsList = append(PluginsList, currentPlugin) + } + } + } + } +} +func GetDisabledPluginRecord(plugin PluginInfo) string { + if len(plugin.PluginXmlId) > 0 { + return plugin.PluginXmlId + } else if len(plugin.Id) > 0 { + return plugin.Id + } else { + return plugin.Name + } +} +func ConvertBuildNumberToFloat(buildNumber string) (build float64) { + if buildNumber == "" { + buildNumber = "*.*" + } + buildNumber = strings.Replace(buildNumber, "*", "9999", 2) + if strings.Count(buildNumber, ".") >= 2 { + buildNumberSplit := strings.Split(buildNumber, ".") + buildNumber = buildNumberSplit[0] + "." + buildNumberSplit[1] + } + build, err := strconv.ParseFloat(buildNumber, 64) + if err != nil { + logger.DebugLogger.Println(err) + } + return build +} +func parsePluginXml(pluginDescriptionXmlContent []byte) PluginInfo { + var currentPluginInfo PluginInfo + _ = xml.Unmarshal(pluginDescriptionXmlContent, ¤tPluginInfo) + return currentPluginInfo +} +func checkIfPluginDisabled(plugin string) bool { + disabledPluginsFile := CurrentIde.GetDisabledPluginsFilename() + parseDisabledPluginsFile(disabledPluginsFile) + return sliceOfStringsContains(disabledPlugins, plugin) +} +func sliceOfStringsContains(slice []string, value string) (contains bool) { + for _, element := range slice { + if value == element { + contains = true + break + } + } + return contains +} +func parseDisabledPluginsFile(disabledPluginsFile string) { + if len(disabledPlugins) == 0 { + file, err := os.Open(disabledPluginsFile) + if err == nil { + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + if len(scanner.Text()) > 0 { + disabledPlugins = append(disabledPlugins, scanner.Text()) + } + } + } + } +} +func getPluginDescriptionFileContent(jarFile string) []byte { + var err error + var file *os.File + var fi os.FileInfo + var r *zip.Reader + file, err = os.Open(jarFile) + logger.ExitWithExceptionOnError(err) + defer file.Close() + fi, err = file.Stat() + logger.ExitWithExceptionOnError(err) + r, err = zip.NewReader(file, fi.Size()) + logger.ExitWithExceptionOnError(err) + for _, f := range r.File { + var name = f.Name + if name == "META-INF/plugin.xml" { + file, err := f.Open() + logger.ExitWithExceptionOnError(err) + content, err := ioutil.ReadAll(file) + //Logger.DebugLogger.Println("Found " + jarFile + name + " file") + return content + } + } + return nil +} diff --git a/native/repair-utility/helpers/plugins_helper_test.go b/native/repair-utility/helpers/plugins_helper_test.go new file mode 100644 index 000000000000..5cc2f14f1dc7 --- /dev/null +++ b/native/repair-utility/helpers/plugins_helper_test.go @@ -0,0 +1,64 @@ +package helpers + +import ( + "testing" +) + +func TestRequestLatestCompatibleVersionFromMarketplace(t *testing.T) { + for { + CurrentIde.SetBinaryToWrokWith(GetRandomIdeInstallationBinary()) + binaryInfo, err := GetIdeInfoByBinary(GetIdeaBinaryToWrokWith()) + if err != nil { + return + } + if ConvertBuildNumberToFloat(binaryInfo.BuildNumber) > 201 && ConvertBuildNumberToFloat(binaryInfo.BuildNumber) < 212 { + break + } + } + ideInfo, _ := GetIdeInfoByBinary(GetIdeaBinaryToWrokWith()) + wantPluginVersion := "0" + if ConvertBuildNumberToFloat(ideInfo.BuildNumber) > 203 && ConvertBuildNumberToFloat(ideInfo.BuildNumber) < 204 { + wantPluginVersion = "4.0.3" + } else if ConvertBuildNumberToFloat(ideInfo.BuildNumber) > 211 { + wantPluginVersion = "4.1.0" + } else if ConvertBuildNumberToFloat(ideInfo.BuildNumber) > 202 && ConvertBuildNumberToFloat(ideInfo.BuildNumber) < 203 { + wantPluginVersion = "3.2.3.202" + } + + pluginToCheck := PluginInfo{ + Id: "mobi.hsz.idea.gitignore", + Name: ".ignore", + Version: "4.0.3", + Vendor: "JetBrains", + MainJarPath: GetAbsolutePath(CurrentIde.GetIdeCustomPluginsDirectory() + "/.ignore/lib/idea-gitignore-4.0.3.jar"), + PluginXmlId: "mobi.hsz.idea.gitignore", + MarketplaceId: 0, + IsDisabled: false, + IdeaVersion: IdeaVersion{ + SinceBuild: "203", + UntilBuild: "203.*", + }, + } + type args struct { + ideaBinary string + plugin *PluginInfo + } + tests := []struct { + name string + args args + want string + }{ + { + name: "detect binary by package name", + args: args{GetIdeaBinaryToWrokWith(), &pluginToCheck}, + want: wantPluginVersion, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := RequestLatestCompatibleVersionFromMarketplace(tt.args.ideaBinary, tt.args.plugin); got != tt.want { + t.Errorf("RequestLatestCompatibleVersionFromMarketplace() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/native/repair-utility/helpers/plugins_test.go b/native/repair-utility/helpers/plugins_test.go new file mode 100644 index 000000000000..004737289ca9 --- /dev/null +++ b/native/repair-utility/helpers/plugins_test.go @@ -0,0 +1,94 @@ +package helpers + +import ( + "reflect" + "testing" +) + +func TestGetPluginsList(t *testing.T) { + CurrentIde.SetBinaryToWrokWith(GetRandomIdeInstallationBinaryOfBuild("211")) + + tests := []struct { + name string + ideBinary string + pluginsToInstall PluginInfoList + wantPluginsList PluginInfoList + wantIncompatiblePluginsList PluginInfoList + }{ + { + name: "1 Enabled plugin", + ideBinary: GetIdeaBinaryToWrokWith(), + pluginsToInstall: []PluginInfo{ + { + PluginXmlId: "mobi.hsz.idea.gitignore", + Version: "4.1.0", + }, + { + PluginXmlId: "BashSupport", + Version: "1.7.15.192", + }}, + wantPluginsList: []PluginInfo{ + { + Id: "mobi.hsz.idea.gitignore", + Name: ".ignore", + Version: "4.1.0", + Vendor: "JetBrains", + MainJarPath: GetAbsolutePath(CurrentIde.GetIdeCustomPluginsDirectory() + "/.ignore/lib/idea-gitignore-4.1.0.jar"), + PluginXmlId: "mobi.hsz.idea.gitignore", + MarketplaceId: 0, + IsDisabled: false, + IdeaVersion: IdeaVersion{ + SinceBuild: "211", + UntilBuild: "211.*", + }, + isBundled: false, + }, + { + Id: "", + Name: "BashSupport", + Version: "1.7.15.192", + Vendor: "Joachim Ansorg", + MainJarPath: GetAbsolutePath(CurrentIde.GetIdeCustomPluginsDirectory() + "/BashSupport/lib/bashsupport-1.7.15.192.jar"), + PluginXmlId: "BashSupport", + MarketplaceId: 0, + IsDisabled: false, + IdeaVersion: IdeaVersion{ + SinceBuild: "192.0", + UntilBuild: "193.*", + }, + isBundled: false, + }}, + wantIncompatiblePluginsList: []PluginInfo{ + { + Id: "", + Name: "BashSupport", + Version: "1.7.15.192", + Vendor: "Joachim Ansorg", + MainJarPath: GetAbsolutePath(CurrentIde.GetIdeCustomPluginsDirectory() + "/BashSupport/lib/bashsupport-1.7.15.192.jar"), + PluginXmlId: "BashSupport", + MarketplaceId: 0, + IsDisabled: false, + IdeaVersion: IdeaVersion{ + SinceBuild: "192.0", + UntilBuild: "193.*", + }, + isBundled: false, + }}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + CurrentIde.TemporarilyRemovePluginsDir(false) + CurrentIde.TemporarilyRenameDisabledPluginsFile(false) + CurrentIde.downloadAndInstallPlugins(tt.pluginsToInstall) + if gotPluginsList := *CurrentIde.CustomPluginsList(); !reflect.DeepEqual(tt.wantPluginsList, gotPluginsList) { + t.Errorf("PluginsList() = %v, want %v", gotPluginsList, tt.wantPluginsList) + } + if gotPluginsList := CurrentIde.CustomPluginsList().FilterIncompatible(); !reflect.DeepEqual(tt.wantIncompatiblePluginsList, gotPluginsList) { + t.Errorf("\n IDE build %s \n FilterIncompatible() =\n %v want\n %v", CurrentIde.Info.BuildNumber, gotPluginsList.ToString(), tt.wantIncompatiblePluginsList.ToString()) + } + CurrentIde.TemporarilyRemovePluginsDir(true) + CurrentIde.TemporarilyRenameDisabledPluginsFile(true) + }) + } +} diff --git a/native/repair-utility/helpers/questions_asker.go b/native/repair-utility/helpers/questions_asker.go new file mode 100644 index 000000000000..63b4b0c5296b --- /dev/null +++ b/native/repair-utility/helpers/questions_asker.go @@ -0,0 +1,83 @@ +package helpers + +import ( + "bufio" + "errors" + "os" + "repair/logger" + "strings" +) + +var YesToAll bool +var NoToAll bool + +func AskQuestionWithOptions(question string, options []string) (answer string) { + if YesToAll { + return "y" + } + if NoToAll { + return "n" + } + return ForceAskQuestionWithOptions(question, options) +} +func ForceAskQuestionWithOptions(question string, options []string) (answer string) { + var optionsList string + for i, option := range options { + if i == len(options)-1 { + optionsList = optionsList + option + } else { + optionsList = optionsList + option + "/" + } + } + logger.ConsoleLogger.Println(question + " [" + optionsList + "]") + answer, err := ReadUserInput() + logger.ExitWithExceptionOnError(err) + answer = strings.ToLower(strings.TrimSpace(answer)) + if asnwerFitsOptions(answer, options) { + return answer + } else { + logger.ConsoleLogger.Println("There is no such option. Let me ask again:") + answer = ForceAskQuestionWithOptions(question, options) + return answer + } +} + +func asnwerFitsOptions(answer string, options []string) bool { + for _, option := range options { + if strings.ToLower(answer) == strings.ToLower(option) { + return true + } + } + return false +} + +func ReadUserInput() (input string, err error) { + stdin := bufio.NewReader(os.Stdin) + input, err = stdin.ReadString('\n') + input = strings.Replace(input, "\n", "", -1) + return input, err +} +func AskUserToDownloadFreshInstallation() { + baseFileName := GetIdeBasefileName(GetIdeaBinaryToWrokWith()) + logger.ExitOnError(errors.New("Please download fresh copy of the installation from https://www.jetbrains.com/" + baseFileName + "/download/")) +} +func AskUserAndRemoveFiles(filesToRemove []string) { + question := "Remove the mentioned files?" + answers := []string{"y", "n"} + answer := AskQuestionWithOptions(question, answers) + if answer == "n" { + return + } + for _, file := range filesToRemove { + if _, err := os.Stat(GetIdeIdePackageByBinary(GetIdeaBinaryToWrokWith()) + "/" + file); err == nil { + file = GetIdeIdePackageByBinary(GetIdeaBinaryToWrokWith()) + "/" + file + } + if _, err := os.Stat(file); err == nil { + if err = os.Remove(file); err != nil { + logger.WarningLogger.Printf("Could not remove %s file", file) + } + } else { + logger.WarningLogger.Printf("Could not find %s file", file) + } + } +} diff --git a/native/repair-utility/helpers/runtime_worker.go b/native/repair-utility/helpers/runtime_worker.go new file mode 100644 index 000000000000..2c811e8e9803 --- /dev/null +++ b/native/repair-utility/helpers/runtime_worker.go @@ -0,0 +1,209 @@ +package helpers + +import ( + "bufio" + "errors" + "io/ioutil" + "os" + "path/filepath" + "repair/logger" + "runtime" + "strings" +) + +func DetectRuntimeInUse(ideaBinary string) (runtimeInUse RuntimeInfo, err error) { + runtimeInUse.BinaryPath, runtimeInUse.BinaryPathDefinedAt, runtimeInUse.BinaryPathDefinedAs = getRuntimeBinary(ideaBinary) + logger.ExitWithExceptionOnError(err) + runtimeInUse.IsBundled = checkIfRuntimeBundled(runtimeInUse.BinaryPath) + runtimeInUse.Architecture = getRuntimeArchitecture(runtimeInUse.BinaryPath) + runtimeInUse.Version = getRuntimeVersion(runtimeInUse.BinaryPath) + return runtimeInUse, err +} + +func getRuntimeVersion(runtimeBinaryPath string) string { + output, err := GetJavaProperty(runtimeBinaryPath, "java.version") + logger.ExitWithExceptionOnError(err) + return output +} + +func getRuntimeArchitecture(runtimeBinaryPath string) string { + output, err := GetJavaProperty(runtimeBinaryPath, "sun.arch.data.model") + logger.ExitWithExceptionOnError(err) + return filepath.Clean(output) +} + +func checkIfRuntimeBundled(path string) bool { + if strings.Contains(path, IdeRuntimeBinaryRelatedToInstallationPath[runtime.GOOS]) { + return true + } else { + return false + } +} + +func getRuntimeBinary(ideaBinary string) (binaryPath string, binaryPathDefinedAt string, binaryPathDefinedAs string) { + for _, variable := range getOsDependentDir(possibleRuntimeVariables) { + runtimeLocation := os.ExpandEnv(expandRuntimeDefaultLocation(variable)) + if runtimeLocation != "" { + return findRuntimeBinaryInsidePath(runtimeLocation), "$IDEA_JDK", runtimeLocation + } + } + for _, location := range getOsDependentDir(possibleRuntimeConfiguraitonFileLocations) { + location := expandRuntimeDefaultLocation(location) + if fileWithPossibleLocation, err := os.Open(location); err == nil { + runtimeLocation := getRuntimePathFromFile(fileWithPossibleLocation) + return findRuntimeBinaryInsidePath(runtimeLocation), location, runtimeLocation + } + } + if possibleLocation, err := getBundledRuntimeBinary(ideaBinary); err == nil { + return possibleLocation, "bundled", "bundled" + } + logger.ExitWithExceptionOnError(errors.New("could not detect IDE runtime definition")) + return "", "", "" +} +func expandRuntimeDefaultLocation(dir string) (expandedDir string) { + dir = strings.Replace(dir, "{IdeConfigDir}", CurrentIde.GetConfigurationDirectory(), -1) + dir = strings.Replace(dir, "{IDE_BASEFILENAME}", strings.ToUpper(GetIdeBasefileName(GetIdeaBinaryToWrokWith())), -1) + dir = strings.Replace(dir, "{IDE_BaseFilename}", GetIdeBasefileName(GetIdeaBinaryToWrokWith()), -1) + dir = filepath.Clean(dir) + return dir +} + +func findRuntimeBinaryInsidePath(runtimePath string) (binaryPath string) { + runtimePath = filepath.Clean(runtimePath) + err := filepath.Walk(runtimePath, + func(path string, info os.FileInfo, err error) error { + if err == nil && strings.HasSuffix(path, "/bin/java") && !strings.Contains(path, "/jre") { + binaryPath = path + return nil + } + if err == nil && strings.HasSuffix(path, "\\bin\\java.exe") && !strings.Contains(path, "\\jre") && runtime.GOOS == "windows" { + binaryPath = path + return nil + } + return nil + }) + if err != nil { + logger.ExitWithExceptionOnError(err) + } + return binaryPath +} +func getRuntimePathFromFile(file *os.File) string { + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + definedLcoation := scanner.Text() + if len(definedLcoation) != 0 { + if FileExists(definedLcoation) { + return definedLcoation + } + } + } + return "" +} + +func getBundledRuntimeBinary(ideaBinary string) (runtimeBinary string, err error) { + ideaPackage := GetIdeIdePackageByBinary(ideaBinary) + runtimeBinary = ideaPackage + IdeRuntimeBinaryRelatedToInstallationPath[runtime.GOOS] + if FileExists(runtimeBinary) { + return runtimeBinary, nil + } else { + return "", err + } +} + +func PrepareEnvironent(wantRuntimeInUse RuntimeInfo) { + if wantRuntimeInUse.BinaryPathDefinedAt == "$IDEA_JDK" { + variable := strings.TrimPrefix(expandRuntimeDefaultLocation(getOsDependentDir(possibleRuntimeVariables)[0]), "$") + err := os.Setenv(variable, wantRuntimeInUse.BinaryPathDefinedAs) + logger.ExitWithExceptionOnError(err) + } + if strings.HasSuffix(wantRuntimeInUse.BinaryPathDefinedAt, "idea.jdk") { + err := ioutil.WriteFile(wantRuntimeInUse.BinaryPathDefinedAt, []byte(wantRuntimeInUse.BinaryPathDefinedAs), 0644) + logger.ExitWithExceptionOnError(err) + } +} + +func ClearEnvironemnt(wantRuntimeInUse RuntimeInfo) { + if wantRuntimeInUse.BinaryPathDefinedAt == "$IDEA_JDK" { + err := os.Unsetenv("IDEA_JDK") + logger.ExitWithExceptionOnError(err) + } + if strings.HasSuffix(wantRuntimeInUse.BinaryPathDefinedAt, "idea.jdk") { + err := os.Remove(wantRuntimeInUse.BinaryPathDefinedAt) + logger.ExitWithExceptionOnError(err) + } +} + +func RuntimeIsBundled() error { + if !RuntimeInUse.IsBundled { + return errors.New("Non-bundled runtime is in use. JetBrains Runtime (JBR) is recommended runtime to start JetBrains IDEs") + } + return nil +} + +func RuntimeArchitectureCorrectness() error { + var is64Bit = uint64(^uintptr(0)) == ^uint64(0) + if !is64Bit { + if strings.HasPrefix(RuntimeInUse.Architecture, " 64") { + return errors.New("IDE uses 64-bit runtime on 32-bit OS") + } + } + return nil +} + +func RuntimeVersionLessThan17(runtimeInfo RuntimeInfo) error { + if !strings.HasPrefix(runtimeInfo.Version, "17") && !runtimeInfo.IsBundled { + return errors.New("IDE uses java of version " + runtimeInfo.Version + ", but recomended version is 17") + } else { + return nil + } +} + +func ResetRuntimeInUse(runtime RuntimeInfo) (err error) { + if runtime.BinaryPathDefinedAt == "bundled" { + return nil + } else if runtime.BinaryPathDefinedAt == "$IDEA_JDK" { + err = os.Unsetenv("$IDEA_JDK") + } else if strings.Contains(runtime.BinaryPathDefinedAt, "idea.jdk") { + err = ioutil.WriteFile(runtime.BinaryPathDefinedAt+".old", []byte(runtime.BinaryPathDefinedAs), 0644) + if err != nil { + logger.ExitWithExceptionOnError(errors.New("Could not make a backup of " + runtime.BinaryPathDefinedAt + ": " + err.Error())) + } + err = os.Remove(runtime.BinaryPathDefinedAt) + if err != nil { + logger.ExitWithExceptionOnError(errors.New("Could not remove " + runtime.BinaryPathDefinedAt + ": " + err.Error())) + } + } else { + err = errors.New("could not reset JDK") + } + return err +} + +func IdeHasBundledRuntime(binaryToWorkWith string) bool { + info, _ := GetIdeInfoByBinary(binaryToWorkWith) + if len(info.Launch[0].JavaExecutablePath) > 0 { + return true + } + return false +} + +func SuggestToChangeIdeToOneWithBundledRuntime() { + info, _ := GetIdeInfoByBinary(GetIdeaBinaryToWrokWith()) + logger.ConsoleLogger.Println("There are errors related to runtime in use, but there is no default runtime for your IDE installation. \n" + + "Please install latest version of " + info.Name + "from https://jetbrains.com/") +} + +func SuggestResettingRuntime() bool { + question := "Reset runtime to default?" + options := []string{"y", "n"} + var answer string + if !IsInTests() { + answer = AskQuestionWithOptions(question, options) + } else { + answer = "n" + } + if answer == "y" { + return true + } + return false +} diff --git a/native/repair-utility/helpers/runtime_worker_test.go b/native/repair-utility/helpers/runtime_worker_test.go new file mode 100644 index 000000000000..5391101e3379 --- /dev/null +++ b/native/repair-utility/helpers/runtime_worker_test.go @@ -0,0 +1,71 @@ +package helpers + +import ( + "reflect" + "repair/logger" + "testing" +) + +func Test_detectRuntimeInUse(t *testing.T) { + randomIdeaBinary := GetRandomIdeInstallationBinary() + type args struct { + ideaBinary string + } + tests := []struct { + name string + args args + wantRuntimeInUse RuntimeInfo + wantErr bool + }{ + { + name: "RuntimeDefinedInVar", + args: args{ + ideaBinary: randomIdeaBinary, + }, + wantRuntimeInUse: RuntimeInfo{ + BinaryPath: GetAbsolutePath("./test_helpers_files/jbr_packages/jbr-11/Contents/Home/bin/java"), + BinaryPathDefinedAt: "$IDEA_JDK", + BinaryPathDefinedAs: GetAbsolutePath("./test_helpers_files/jbr_packages/jbr-11"), + Version: "11.0.7", + Architecture: "64", + IsBundled: false, + }, + wantErr: false, + }, { + name: "RuntimeDefinedInDefaultConfDir", + args: args{ + ideaBinary: randomIdeaBinary, + }, + wantRuntimeInUse: RuntimeInfo{ + BinaryPath: GetAbsolutePath("./test_helpers_files/jbr_packages/jbr-11/Contents/Home/bin/java"), + BinaryPathDefinedAt: GetAbsolutePath(expandRuntimeDefaultLocation(getOsDependentDir(possibleRuntimeConfiguraitonFileLocations)[0])), + BinaryPathDefinedAs: GetAbsolutePath("./test_helpers_files/jbr_packages/jbr-11"), + Version: "11.0.7", + Architecture: "64", + IsBundled: false, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + PrepareEnvironent(tt.wantRuntimeInUse) + gotRuntimeInUse, err := DetectRuntimeInUse(tt.args.ideaBinary) + if (err != nil) != tt.wantErr { + t.Errorf("detectRuntimeInUse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotRuntimeInUse, tt.wantRuntimeInUse) { + error := "\nwant BinaryPathDefinedAt: " + tt.wantRuntimeInUse.BinaryPathDefinedAt + error = error + "\ngot BinaryPathDefinedAt: " + gotRuntimeInUse.BinaryPathDefinedAt + error = error + "\ngot BinaryPathDefinedAs: " + tt.wantRuntimeInUse.BinaryPathDefinedAs + error = error + "\ngot BinaryPathDefinedAs: " + gotRuntimeInUse.BinaryPathDefinedAs + error = error + "\ngot BinaryPath: " + tt.wantRuntimeInUse.BinaryPath + error = error + "\ngot BinaryPath: " + gotRuntimeInUse.BinaryPath + t.Errorf("%s", error) + } + ClearEnvironemnt(tt.wantRuntimeInUse) + logger.RemoveLogFile() + }) + } +} diff --git a/native/repair-utility/helpers/test_helpers.go b/native/repair-utility/helpers/test_helpers.go new file mode 100644 index 000000000000..8cc631f38fc4 --- /dev/null +++ b/native/repair-utility/helpers/test_helpers.go @@ -0,0 +1,111 @@ +package helpers + +import ( + "errors" + "math/rand" + "os" + "path/filepath" + "repair/logger" + "strings" + "time" +) + +func IsInTests() bool { + for _, arg := range os.Args { + if strings.HasPrefix(arg, "-test.v") { + return true + } + } + return false +} +func GetRandomIdeInstallationBinaryOfBuild(version string) (ideaBinary string) { + for i := 0; i < 20; i++ { + possibleBinary := GetRandomIdeInstallationBinary() + if info, _ := GetIdeInfoByBinary(possibleBinary); strings.Contains(info.BuildNumber, version) { + return possibleBinary + } + } + logger.ExitWithExceptionOnError(errors.New("Could not find IDE with " + version + " version")) + return "" +} +func GetRandomIdeInstallationBinary() (ideaBinary string) { + var err error + var packagePath string + if packagePath, err = GetRandomIdeInstallationPackage(); err == nil { + if ideaBinary, err = GetIdeBinaryByPackage(packagePath); err == nil { + logger.ConsoleLogger.Println("Idea Binary to work with:" + ideaBinary) + CurrentIde.SetBinaryToWrokWith(ideaBinary) + return ideaBinary + } + } + logger.ExitWithExceptionOnError(err) + return "" +} +func GetRandomIdeInstallationPackage() (packagePath string, err error) { + var possiblePackages []string + possiblePackages, err = FindInstalledIdePackages() + if len(possiblePackages) > 0 { + rand.Seed(time.Now().UnixNano()) + for { + possibleackage := possiblePackages[rand.Intn(len(possiblePackages)-1)] + if _, err := GetIdeInfoByPackage(possibleackage); err == nil { + return possibleackage, nil + } + } + } + return "", errors.New("Could not find installed IDEs") +} +func GetAbsolutePath(relatedPath string) string { + path, err := filepath.Abs(relatedPath) + _, err = os.Stat(path) + if err != nil { + logger.WarningLogger.Println(err) + } + return path +} +func (ide *IDE) downloadAndInstallPlugins(plugins PluginInfoList) { + for _, plugin := range plugins { + ide.downloadAndInstallPlugin(plugin, plugin.Version) + } +} +func downloadAndInstallPlugins(ideaBinary string, plugins PluginInfoList) { + +} +func (ide *IDE) downloadAndInstallPlugin(plugin PluginInfo, version string) { + zipFilePath := downloadPlugin(plugin, version) + ide.installPlugin(zipFilePath) +} +func downloadPlugin(plugin PluginInfo, version string) (downloadedZipPath string) { + url := "https://plugins.jetbrains.com/plugin/download?" + + "pluginId=" + GetDisabledPluginRecord(plugin) + + "&version=" + version + downloadedZipPath = DownloadFile(url) + return downloadedZipPath +} +func (ide *IDE) TemporarilyRemovePluginsDir(revert bool) { + pluginsDir := strings.TrimSuffix(ide.GetIdeCustomPluginsDirectory(), "/") + if revert { + err := RemoveDir(pluginsDir) + if FileExists(pluginsDir + ".old") { + err = os.Rename(pluginsDir+".old", pluginsDir) + } + logger.ExitWithExceptionOnError(err) + } else { + if FileExists(pluginsDir) { + err := os.Rename(pluginsDir, pluginsDir+".old") + logger.ExitWithExceptionOnError(err) + } + } +} +func (ide *IDE) TemporarilyRenameDisabledPluginsFile(revert bool) { + disabledPluginsFilename := ide.GetDisabledPluginsFilename() + if FileExists(disabledPluginsFilename) && revert == false { + err := MoveFile(disabledPluginsFilename, disabledPluginsFilename+".backup") + logger.ExitWithExceptionOnError(err) + } + if FileExists(disabledPluginsFilename+".backup") && revert == true { + err := MoveFile(disabledPluginsFilename+".backup", disabledPluginsFilename) + logger.ExitWithExceptionOnError(err) + } + return +} diff --git a/native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-1.8/readme.txt b/native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-1.8/readme.txt new file mode 100644 index 000000000000..20d523f2fe34 --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-1.8/readme.txt @@ -0,0 +1 @@ +Add content of JBR here to run tests with this runtime \ No newline at end of file diff --git a/native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-11/readme.txt b/native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-11/readme.txt new file mode 100644 index 000000000000..20d523f2fe34 --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/jbr_packages/jbr-11/readme.txt @@ -0,0 +1 @@ +Add content of JBR here to run tests with this runtime \ No newline at end of file diff --git a/native/repair-utility/helpers/test_helpers_files/logs/blame_presentation_assistant.log b/native/repair-utility/helpers/test_helpers_files/logs/blame_presentation_assistant.log new file mode 100644 index 000000000000..2977ddd6f055 --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/logs/blame_presentation_assistant.log @@ -0,0 +1,90 @@ +2020-07-13 23:23:41,398 [182151486] ERROR - tellij.openapi.util.ObjectTree - Memory leak detected: 'org.nik.presentationAssistant.ActionInfoPanel[,0,0,508x40,invalid,layout=java.awt.BorderLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.EmptyBorder@de486dd,flags=16777224,maximumSize=,minimumSize=,preferredSize=]' of class org.nik.presentationAssistant.ActionInfoPanel +See the cause for the corresponding Disposer.register() stacktrace: +java.lang.RuntimeException: Memory leak detected: 'org.nik.presentationAssistant.ActionInfoPanel[,0,0,508x40,invalid,layout=java.awt.BorderLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.EmptyBorder@de486dd,flags=16777224,maximumSize=,minimumSize=,preferredSize=]' of class org.nik.presentationAssistant.ActionInfoPanel +See the cause for the corresponding Disposer.register() stacktrace: + + at com.intellij.openapi.util.ObjectTree.assertIsEmpty(ObjectTree.java:226) + at com.intellij.openapi.util.Disposer.assertIsEmpty(Disposer.java:130) + at com.intellij.openapi.util.Disposer.assertIsEmpty(Disposer.java:125) + at com.intellij.openapi.application.impl.ApplicationImpl.disposeContainer(ApplicationImpl.java:183) + at com.intellij.openapi.application.impl.ApplicationImpl.disposeSelf(ApplicationImpl.java:200) + at com.intellij.openapi.application.impl.ApplicationImpl.doExit(ApplicationImpl.java:621) + at com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:589) + at com.intellij.openapi.application.impl.ApplicationImpl.restart(ApplicationImpl.java:536) + at com.intellij.ide.actions.InvalidateCachesAction.actionPerformed(InvalidateCachesAction.java:96) + at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAware(ActionUtil.java:280) + at com.intellij.openapi.actionSystem.impl.ActionMenuItem$ActionTransmitter.lambda$actionPerformed$0(ActionMenuItem.java:296) + at com.intellij.openapi.wm.impl.FocusManagerImpl.runOnOwnContext(FocusManagerImpl.java:281) + at com.intellij.openapi.wm.impl.IdeFocusManagerImpl.runOnOwnContext(IdeFocusManagerImpl.java:77) + at com.intellij.openapi.actionSystem.impl.ActionMenuItem$ActionTransmitter.actionPerformed(ActionMenuItem.java:285) + at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967) + at com.intellij.openapi.actionSystem.impl.ActionMenuItem.lambda$fireActionPerformed$0(ActionMenuItem.java:112) + at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:94) + at com.intellij.openapi.actionSystem.impl.ActionMenuItem.fireActionPerformed(ActionMenuItem.java:112) + at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308) + at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405) + at java.desktop/javax.swing.JToggleButton$ToggleButtonModel.setPressed(JToggleButton.java:401) + at java.desktop/javax.swing.AbstractButton.doClick(AbstractButton.java:369) + at java.desktop/com.apple.laf.ScreenMenuItemCheckbox.itemStateChanged(ScreenMenuItemCheckbox.java:198) + at java.desktop/java.awt.CheckboxMenuItem.processItemEvent(CheckboxMenuItem.java:396) + at java.desktop/java.awt.CheckboxMenuItem.processEvent(CheckboxMenuItem.java:364) + at java.desktop/java.awt.MenuComponent.dispatchEventImpl(MenuComponent.java:375) + at java.desktop/java.awt.MenuComponent.dispatchEvent(MenuComponent.java:363) + at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:781) + at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727) + at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95) + at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751) + at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) + at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748) + at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:974) + at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:847) + at com.intellij.ide.IdeEventQueue.lambda$null$8(IdeEventQueue.java:449) + at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:741) + at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:448) + at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831) + at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:502) + at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) + at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) + at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) + at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) + at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) + at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) +Caused by: java.lang.Throwable + at com.intellij.openapi.util.ObjectNode.(ObjectNode.java:35) + at com.intellij.openapi.util.ObjectTree.createNodeFor(ObjectTree.java:106) + at com.intellij.openapi.util.ObjectTree.register(ObjectTree.java:70) + at com.intellij.openapi.util.Disposer.register(Disposer.java:65) + at com.intellij.util.Alarm.(Alarm.java:139) + at com.intellij.util.Alarm.(Alarm.java:108) + at org.nik.presentationAssistant.ActionInfoPanel.(ActionInfoPanel.kt:46) + at org.nik.presentationAssistant.ShortcutPresenter.showActionInfo(ShortcutPresenter.kt:141) + at org.nik.presentationAssistant.ShortcutPresenter$enable$1.beforeActionPerformed(ShortcutPresenter.kt:66) + at jdk.internal.reflect.GeneratedMethodAccessor147.invoke(Unknown Source) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:566) + at com.intellij.util.messages.impl.MessageBusImpl.invokeListener(MessageBusImpl.java:541) + at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:143) + at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:465) + at com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:426) + at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:415) + at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:397) + at com.intellij.util.messages.impl.MessageBusImpl.lambda$createTopicHandler$3(MessageBusImpl.java:237) + at com.sun.proxy.$Proxy114.beforeActionPerformed(Unknown Source) + at com.intellij.openapi.actionSystem.impl.ActionManagerImpl.fireBeforeActionPerformed(ActionManagerImpl.java:1438) + at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processAction(IdeKeyEventDispatcher.java:662) + at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processAction(IdeKeyEventDispatcher.java:619) + at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.processActionOrWaitSecondStroke(IdeKeyEventDispatcher.java:516) + at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.inInitState(IdeKeyEventDispatcher.java:470) + at com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher.dispatchKeyEvent(IdeKeyEventDispatcher.java:219) + at com.intellij.ide.IdeEventQueue.dispatchKeyEvent(IdeEventQueue.java:896) + at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:841) + ... 11 more +2020-07-13 23:23:41,398 [182151486] ERROR - tellij.openapi.util.ObjectTree - IntelliJ IDEA 2020.1.2 Build #IU-201.7846.76 +2020-07-13 23:23:41,398 [182151486] ERROR - tellij.openapi.util.ObjectTree - JDK: 11.0.7; VM: OpenJDK 64-Bit Server VM; Vendor: JetBrains s.r.o. +2020-07-13 23:23:41,398 [182151486] ERROR - tellij.openapi.util.ObjectTree - OS: Mac OS X +2020-07-13 23:23:41,399 [182151487] ERROR - tellij.openapi.util.ObjectTree - Plugin to blame: Presentation Assistant version: 1.0.5 \ No newline at end of file diff --git a/native/repair-utility/helpers/test_helpers_files/logs/error_on_action.log b/native/repair-utility/helpers/test_helpers_files/logs/error_on_action.log new file mode 100644 index 000000000000..dc6e68198714 --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/logs/error_on_action.log @@ -0,0 +1,166 @@ +2021-02-15 18:09:37,188 [ 61174] ERROR - .wm.impl.ToolWindowManagerImpl - Already disposed: Project (name=HelloJava, containerState=DISPOSE_IN_PROGRESS, componentStore=/Users/konstantin.annikov/IdeaProjects/HelloJava) +java.lang.IllegalStateException: Already disposed: Project (name=HelloJava, containerState=DISPOSE_IN_PROGRESS, componentStore=/Users/konstantin.annikov/IdeaProjects/HelloJava) + at com.intellij.serviceContainer.ComponentManagerImpl.getMessageBus(ComponentManagerImpl.kt:139) + at com.intellij.ide.navigationToolbar.NavBarListener.subscribeTo(NavBarListener.java:71) + at com.intellij.ide.navigationToolbar.NavBarPanel.addNotify(NavBarPanel.java:777) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at java.desktop/java.awt.Container.addNotify(Container.java:2800) + at java.desktop/javax.swing.JComponent.addNotify(JComponent.java:4791) + at com.intellij.openapi.wm.impl.InternalDecorator.addNotify(InternalDecorator.java:292) + at java.desktop/java.awt.Container.addImpl(Container.java:1146) + at java.desktop/java.awt.Container.add(Container.java:436) + at com.intellij.openapi.ui.ThreeComponentsSplitter.doAddComponent(ThreeComponentsSplitter.java:541) + at com.intellij.openapi.ui.ThreeComponentsSplitter.setLastComponent(ThreeComponentsSplitter.java:506) + at com.intellij.openapi.wm.impl.ToolWindowsPane.setComponent(ToolWindowsPane.java:300) + at com.intellij.openapi.wm.impl.ToolWindowsPane.removeDecorator(ToolWindowsPane.java:233) + at com.intellij.openapi.wm.impl.ToolWindowManagerImpl.removeDecoratorWithoutUpdatingState(ToolWindowManagerImpl.kt:1067) + at com.intellij.openapi.wm.impl.ToolWindowManagerImpl.projectClosed(ToolWindowManagerImpl.kt:510) + at com.intellij.openapi.wm.impl.ToolWindowManagerImpl$ToolWindowManagerAppLevelHelper$2.projectClosed(ToolWindowManagerImpl.kt:216) + at jdk.internal.reflect.GeneratedMethodAccessor141.invoke(Unknown Source) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:566) + at com.intellij.util.messages.impl.MessageBusImpl.invokeListener(MessageBusImpl.java:541) + at com.intellij.util.messages.impl.MessageBusConnectionImpl.deliverMessage(MessageBusConnectionImpl.java:143) + at com.intellij.util.messages.impl.MessageBusImpl.doPumpMessages(MessageBusImpl.java:465) + at com.intellij.util.messages.impl.MessageBusImpl.pumpWaitingBuses(MessageBusImpl.java:426) + at com.intellij.util.messages.impl.MessageBusImpl.pumpMessages(MessageBusImpl.java:415) + at com.intellij.util.messages.impl.MessageBusImpl.sendMessage(MessageBusImpl.java:397) + at com.intellij.util.messages.impl.MessageBusImpl.lambda$createTopicHandler$3(MessageBusImpl.java:237) + at com.sun.proxy.$Proxy78.projectClosed(Unknown Source) + at com.intellij.openapi.project.impl.ProjectManagerImpl.fireProjectClosed(ProjectManagerImpl.java:807) + at com.intellij.openapi.project.impl.ProjectManagerImpl.lambda$closeProject$11(ProjectManagerImpl.java:694) + at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:976) + at com.intellij.openapi.project.impl.ProjectManagerImpl.closeProject(ProjectManagerImpl.java:687) + at com.intellij.openapi.project.impl.ProjectManagerImpl.closeAndDisposeAllProjects(ProjectManagerImpl.java:611) + at com.intellij.openapi.application.impl.ApplicationImpl.disposeSelf(ApplicationImpl.java:190) + at com.intellij.openapi.application.impl.ApplicationImpl.doExit(ApplicationImpl.java:621) + at com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:589) + at com.intellij.openapi.application.impl.ApplicationImpl.exit(ApplicationImpl.java:569) + at com.intellij.ide.plugins.newui.MyPluginModel.runRestartButton(MyPluginModel.java:861) + at com.intellij.ide.plugins.newui.RestartButton.lambda$new$0(RestartButton.java:13) + at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967) + at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308) + at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405) + at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262) + at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:270) + at java.desktop/java.awt.Component.processMouseEvent(Component.java:6650) + at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3345) + at java.desktop/java.awt.Component.processEvent(Component.java:6415) + at java.desktop/java.awt.Container.processEvent(Container.java:2263) + at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:5025) + at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2321) + at java.desktop/java.awt.Component.dispatchEvent(Component.java:4857) + at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918) + at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547) + at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488) + at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2307) + at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2773) + at java.desktop/java.awt.Component.dispatchEvent(Component.java:4857) + at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:778) + at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727) + at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95) + at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:751) + at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:749) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) + at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:748) + at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:974) + at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.java:912) + at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:844) + at com.intellij.ide.IdeEventQueue.lambda$null$8(IdeEventQueue.java:449) + at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:741) + at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:448) + at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831) + at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:502) + at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) + at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) + at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117) + at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:190) + at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:235) + at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:233) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:233) + at java.desktop/java.awt.Dialog.show(Dialog.java:1063) + at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl$MyDialog.show(DialogWrapperPeerImpl.java:708) + at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl.show(DialogWrapperPeerImpl.java:437) + at com.intellij.openapi.ui.DialogWrapper.doShow(DialogWrapper.java:1685) + at com.intellij.openapi.ui.DialogWrapper.show(DialogWrapper.java:1644) + at com.intellij.ide.actions.ShowSettingsUtilImpl.showSettingsDialog(ShowSettingsUtilImpl.java:80) + at com.intellij.ide.actions.ShowSettingsAction.perform(ShowSettingsAction.java:54) + at com.intellij.ui.mac.MacOSApplicationProvider$Worker.lambda$null$1(MacOSApplicationProvider.java:78) + at com.intellij.ui.mac.MacOSApplicationProvider$Worker.lambda$submit$7(MacOSApplicationProvider.java:175) + at com.intellij.openapi.application.TransactionGuardImpl$2.run(TransactionGuardImpl.java:201) + at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831) + at com.intellij.openapi.application.impl.ApplicationImpl.lambda$invokeLater$4(ApplicationImpl.java:310) + at com.intellij.openapi.application.impl.FlushQueue.doRun(FlushQueue.java:80) + at com.intellij.openapi.application.impl.FlushQueue.runNextEvent(FlushQueue.java:128) + at com.intellij.openapi.application.impl.FlushQueue.flushNow(FlushQueue.java:46) + at com.intellij.openapi.application.impl.FlushQueue$FlushNow.run(FlushQueue.java:184) + at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313) + at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:776) + at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:727) + at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721) + at java.base/java.security.AccessController.doPrivileged(Native Method) + at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85) + at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:746) + at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.java:974) + at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:847) + at com.intellij.ide.IdeEventQueue.lambda$null$8(IdeEventQueue.java:449) + at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:741) + at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:448) + at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:831) + at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:496) + at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) + at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) + at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) + at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) + at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) + at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90) +2021-02-15 18:09:37,189 [ 61175] ERROR - .wm.impl.ToolWindowManagerImpl - IntelliJ IDEA 2020.1.4 Build #IU-201.8743.12 +2021-02-15 18:09:37,190 [ 61176] ERROR - .wm.impl.ToolWindowManagerImpl - JDK: 11.0.7; VM: OpenJDK 64-Bit Server VM; Vendor: JetBrains s.r.o. +2021-02-15 18:09:37,190 [ 61176] ERROR - .wm.impl.ToolWindowManagerImpl - OS: Mac OS X +2021-02-15 18:09:37,190 [ 61176] ERROR - .wm.impl.ToolWindowManagerImpl - Last Action: EditorPaste +2021-02-15 18:09:37,210 [ 61196] INFO - penapi.project.DumbServiceImpl - cancel com.intellij.util.indexing.UnindexedFilesUpdater@35e0fbd8 +2021-02-15 18:09:37,228 [ 61214] INFO - #cursive.repl.IdeReplServer - Closing IDE REPL server socket +2021-02-15 18:09:37,228 [ 61214] INFO - #cursive.repl.IdeReplServer - IDE REPL server socket closed. +2021-02-15 18:09:37,229 [ 61215] INFO - pl.local.NativeFileWatcherImpl - Watcher terminated with exit code 0 +2021-02-15 18:09:37,231 [ 61217] INFO - org.jetbrains.io.BuiltInServer - web server stopped +2021-02-15 18:09:40,655 [ 64641] INFO - .idea.vim.command.CommandState - Push new mode state: COMMAND:NONE +2021-02-15 18:09:40,655 [ 64641] INFO - .idea.vim.command.CommandState - Push new mode state: COMMAND:NONE +2021-02-15 18:09:40,655 [ 64641] INFO - .idea.vim.command.CommandState - Push new mode state: COMMAND:NONE +2021-02-15 18:09:40,667 [ 64653] INFO - Types.impl.FileTypeManagerImpl - 8 auto-detected files. Detection took 373 ms +2021-02-15 18:09:40,668 [ 64654] INFO - il.indexing.FileBasedIndexImpl - START INDEX SHUTDOWN +2021-02-15 18:09:40,753 [ 64739] INFO - tellij.psi.stubs.StubIndexImpl - StubIndexExtension-s were unloaded +2021-02-15 18:09:40,820 [ 64806] INFO - il.indexing.FileBasedIndexImpl - END INDEX SHUTDOWN +2021-02-15 18:09:40,820 [ 64806] INFO - newvfs.persistent.PersistentFS - VFS dispose started +2021-02-15 18:09:40,828 [ 64814] INFO - newvfs.persistent.PersistentFS - VFS dispose completed +2021-02-15 18:09:40,828 [ 64814] INFO - stubs.SerializationManagerImpl - START StubSerializationManager SHUTDOWN +2021-02-15 18:09:40,828 [ 64814] INFO - stubs.SerializationManagerImpl - END StubSerializationManager SHUTDOWN +2021-02-15 18:09:40,829 [ 64816] INFO - plication.impl.ApplicationImpl - Write action Statistics: +Event number: 8 +Total time spent: 262ms +Average duration: 32ms +Median duration: 25ms +Max duration: 73ms (it was 'write action (class com.intellij.openapi.module.impl.ModuleManagerImpl$$Lambda$1556/0x0000000801aec040)') +2021-02-15 18:09:40,830 [ 64816] INFO - plication.impl.ApplicationImpl - AnAction.update() Statistics: +Event number: 973 +Total time spent: 629ms +Average duration: 0ms +Median duration: 0ms +Max duration: 209ms (it was 'Find Usages action update (class com.intellij.find.actions.FindUsagesAction)') diff --git a/native/repair-utility/helpers/test_helpers_files/logs/readme.txt b/native/repair-utility/helpers/test_helpers_files/logs/readme.txt new file mode 100644 index 000000000000..a319583b787f --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/logs/readme.txt @@ -0,0 +1 @@ +Add \ No newline at end of file diff --git a/native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/1.vmoptions b/native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/1.vmoptions new file mode 100644 index 000000000000..6acd9e04b63a --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/1.vmoptions @@ -0,0 +1,21 @@ +-Xms128m +-Xmx2048m +-XX:+UseConcMarkSweepGC +-XX:SoftRefLRUPolicyMSPerMB=50 +-ea +-XX:CICompilerCount=2 +-Djdk.http.auth.tunneling.disabledSchemes="" +-XX:+HeapDumpOnOutOfMemoryError +-XX:-OmitStackTraceInFastThrow +-XX:+UseZGC +-Djdk.attach.allowAttachSelf=true +-XX:+UseSerialGC +-Dkotlinx.coroutines.debug=off +-Djdk.module.illegalAccess.silent=true +-XX:ErrorFile=$USER_HOME/java_error_in_goland_%p.log +-XX:HeapDumpPath=$USER_HOME/java_error_in_goland.hprof +-Dide.no.platform.update=true +-Dsun.io.useCanonCaches=false +-XX:+UseParallelGC +-XX:ReservedCodeCacheSize=512m +-Didea.plugins.path=/Users/konstantin.annikov/Library/Application Support/JetBrains/Toolbox/apps/Goland/ch-0/203.6682.164/GoLand.app.plugins diff --git a/native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/2.vmoptions b/native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/2.vmoptions new file mode 100644 index 000000000000..2f8a05f24643 --- /dev/null +++ b/native/repair-utility/helpers/test_helpers_files/test_vmoptions_files/2.vmoptions @@ -0,0 +1,18 @@ +-Xms1228m +-Xmx1048m +-XX:+UseConcMarkSweepGC +-XX:SoftRefLRUPolicyMSPerMB=50 +-ea +-XX:CICompilerCount=2 +-Djdk.http.auth.tunneling.disabledSchemes="" +-XX:+HeapDumpOnOutOfMemoryError +-XX:-OmitStackTraceInFastThrow +-Djdk.attach.allowAttachSelf=true +-Dkotlinx.coroutines.debug=off +-Djdk.module.illegalAccess.silent=true +-XX:ErrorFile=$USER_HOME/java_error_in_goland_%p.log +-XX:HeapDumpPath=$USER_HOME/java_error_in_goland.hprof +-Dide.no.platform.update=true +-Dsun.io.useCanonCaches=false +-XX:ReservedCodeCacheSize=512m +-Didea.plugins.path=/Users/konstantin.annikov/Library/Application Support/JetBrains/Toolbox/apps/Goland/ch-0/203.6682.164/GoLand.app.plugins diff --git a/native/repair-utility/helpers/vmoptions_file_parser.go b/native/repair-utility/helpers/vmoptions_file_parser.go new file mode 100644 index 000000000000..347f7ccdfeaf --- /dev/null +++ b/native/repair-utility/helpers/vmoptions_file_parser.go @@ -0,0 +1,148 @@ +package helpers + +import ( + "bufio" + "os" + "repair/logger" + "strconv" + "strings" +) + +var ( + vmoptionsDelimeters = []string{"=", ":", " "} + listOfPossibleGarbageCollectors = []string{"-XX:+UseSerialGC", "-XX:+UseParallelGC", "-XX:+UseG1GC", "-XX:+UseShenandoahGC", "XX:+UseEpsilonGC", "-XX:+UseConcMarkSweepGC", "-XX:+UseZGC"} + vmOptionsFileAsSlice = make(MappedVmoptions) +) + +type MappedVmoptions map[string]string + +func GetVmoptionsAsSlice(vmOptionsFile string) (vmoptions MappedVmoptions) { + var err error + if len(vmOptionsFileAsSlice) == 0 { + vmOptionsFileAsSlice, _, _, err = ParseVmOptionsFile(vmOptionsFile) + logger.ExitWithExceptionOnError(err) + } + return vmOptionsFileAsSlice +} + +func ParseVmOptionsFile(file string) (vmoptions MappedVmoptions, garbageCollectorsInUse []string, vmoptionsDuplicates map[string]string, err error) { + vmoptions = make(MappedVmoptions) + vmoptionsDuplicates = make(map[string]string) + vmOptionsAsSlice, err := vmOptionsFileToSliceOfStrings(file) + if err != nil { + return nil, nil, nil, err + } + for _, option := range vmOptionsAsSlice { + var optionName string + var optionValue string + optionName = getVmoptionName(option) + optionValue = getVmoptionValue(option, optionName) + collectGcInUse(option, &garbageCollectorsInUse) + if currentOptionIsDuplicate(vmoptions, optionName) { + vmoptionsDuplicates[optionName] = optionValue + } else { + vmoptions[optionName] = optionValue + } + } + vmOptionsFileAsSlice = vmoptions + return vmoptions, garbageCollectorsInUse, vmoptionsDuplicates, nil +} + +func collectGcInUse(option string, garbageCollectorInUse *[]string) { + for _, possibleGC := range listOfPossibleGarbageCollectors { + if possibleGC == option { + *garbageCollectorInUse = append(*garbageCollectorInUse, option) + } + } +} + +func getVmoptionValue(option string, name string) (value string) { + if isBoolean, isEnablingBoolean := isEnabingBooleanOption(option); isBoolean { + if isEnablingBoolean { + return "true" + } else { + return "false" + } + } + value = strings.TrimPrefix(option, name) + for _, delimiter := range vmoptionsDelimeters { + value = strings.TrimSpace(strings.TrimPrefix(value, delimiter)) + } + return value +} + +func getVmoptionName(option string) string { + if isBooleanOption(option) { + return option + } + delimiterPosition := findDelimiterPositionOfVmoption(option) + if delimiterPosition == -1 { + return option + } else { + return option[:delimiterPosition] + } +} + +func vmOptionsFileToSliceOfStrings(vmOptionsFile string) (options []string, err error) { + file, err := os.Open(vmOptionsFile) + if err != nil { + return nil, err + } + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + var i int + for scanner.Scan() { + i++ + option := scanner.Text() + if len(option) != 0 { + if option[0] == '-' { + options = append(options, option) + } else if option[0] == '#' { + } else { + logger.DebugLogger.Println("There is a suspicious option: \"" + option + "\" at line " + strconv.Itoa(i)) + } + } + } + err = file.Close() + if err != nil { + return nil, err + } + return options, err +} + +func findDelimiterPositionOfVmoption(option string) (delimiterPosition int) { + if strings.HasPrefix(option, "-Xmx") || strings.HasPrefix(option, "-Xms") || strings.HasPrefix(option, "-Xmn") { + return 4 + } + for _, delimeter := range vmoptionsDelimeters { + delimiterPosition = strings.Index(option, delimeter) + if delimiterPosition != -1 { + return delimiterPosition + } + } + return delimiterPosition +} + +func isEnabingBooleanOption(option string) (isBoolean bool, isEnabling bool) { + if strings.HasPrefix(option, "-XX:+") { + return true, true + } + if strings.HasPrefix(option, "-XX:-") { + return true, false + } + return false, false +} + +func isBooleanOption(option string) (result bool) { + if strings.HasPrefix(option, "-XX:-") || strings.HasPrefix(option, "-XX:+") { + return true + } + return false +} + +func currentOptionIsDuplicate(vmoptions MappedVmoptions, name string) bool { + if vmoptions[name] != "" { + return true + } + return false +} diff --git a/native/repair-utility/helpers/vmoptions_worker.go b/native/repair-utility/helpers/vmoptions_worker.go new file mode 100644 index 000000000000..b8e807f09732 --- /dev/null +++ b/native/repair-utility/helpers/vmoptions_worker.go @@ -0,0 +1,181 @@ +package helpers + +import ( + "errors" + "os" + "repair/logger" + "strconv" +) + +var CheckVmoptionsFileBool bool + +func RestoreVmoptionsFile() (err error) { + vmOptionsFile, err := GetVmOptionsFile() + if err != nil { + logger.InfoLogger.Println(err) + return nil + } + newName := vmOptionsFile + ".old" + err = os.Rename(vmOptionsFile, newName) + logger.ExitWithExceptionOnError(err) + logger.InfoLogger.Println("File has been moved to " + newName) + return err +} + +func CheckVmoptionsFile() { + vmOptionsFile, err := GetVmOptionsFile() + if err != nil { + logger.InfoLogger.Println(err) + } + var errorsToBeFixed []error + var warnings []error + mappedOptions, garbageCollectorsInUse, vmoptionsDuplicates, err := ParseVmOptionsFile(vmOptionsFile) + if err != nil { + return + } + collectWarnings(getDuplicatedVmOptions(vmoptionsDuplicates), &warnings) + collectWarnings(checkIfThereIsJavaAgent(mappedOptions), &warnings) + collectErrorsToBeFixed(checkIfMoreThanOneGcInUse(garbageCollectorsInUse), &errorsToBeFixed) + collectErrorsToBeFixed(checkXmxOn32Bit(mappedOptions["-Xmx"]), &errorsToBeFixed) + collectErrorsToBeFixed(checkXmxVsXms(mappedOptions), &errorsToBeFixed) + + if len(warnings) > 0 { + warn := errors.New("\n" + formatCollectedWarnings(warnings)) + logger.WarningLogger.Println(warn) + } + if len(errorsToBeFixed) > 0 { + err = errors.New("\n" + formatCollectedErrors(errorsToBeFixed)) + logger.ErrorLogger.Println(err) + if !IsInTests() || !CheckVmoptionsFileBool { + suggestToFixVmoptionsFile(vmOptionsFile) + checkVmoptionsFileAgain() + } + } + if len(errorsToBeFixed) == 0 && len(warnings) == 0 { + logger.InfoLogger.Println("VM options seems OK. No need to take actions.") + } +} + +func checkVmoptionsFileAgain() { + logger.InfoLogger.Println("Checking .vmoptions file once again") + CheckVmoptionsFileBoolVal := CheckVmoptionsFileBool + CheckVmoptionsFileBool = true + CheckVmoptionsFile() + CheckVmoptionsFileBool = CheckVmoptionsFileBoolVal +} + +func checkIfThereIsJavaAgent(options MappedVmoptions) error { + if options["-javaagent"] != "" { + return errors.New("Unknown java agent detected: " + options["-javaagent"]) + } + return nil +} + +func formatCollectedWarnings(warningsToBePrinted []error) string { + return "The following WARNINGS were found during .vmoptions file checking: \n" + ConcatErrorsFromSlice(warningsToBePrinted) +} +func formatCollectedErrors(errorsToBeFixed []error) string { + return "The following ERRORS were found during .vmoptions file checking: \n" + ConcatErrorsFromSlice(errorsToBeFixed) +} + +func collectWarnings(warn error, warnings *[]error) { + if warn != nil { + *warnings = append(*warnings, warn) + } +} + +func collectErrorsToBeFixed(err error, errorsToBeFixed *[]error) { + if err != nil { + *errorsToBeFixed = append(*errorsToBeFixed, err) + } +} + +func checkIfMoreThanOneGcInUse(garbageCollectorsInUse []string) error { + if len(garbageCollectorsInUse) > 1 { + var gclist string + for _, gc := range garbageCollectorsInUse { + gclist += "\n\t" + gc + } + return errors.New("More than one Garbage Collector in use: " + gclist) + } + return nil +} + +func checkXmxOn32Bit(xmxValue string) (err error) { + var is64Bit = uint64(^uintptr(0)) == ^uint64(0) + var xmxInBytes int + if !is64Bit { + xmxInBytes = toBytes(xmxValue) + logger.ExitWithExceptionOnError(err) + if xmxInBytes > 2576980377 { + return errors.New("Xmx value is too large for 32-bit OS " + strconv.Itoa(xmxInBytes)) + } + } + return err +} +func checkXmxVsXms(options MappedVmoptions) (err error) { + xmxInBytes := toBytes(options["-Xmx"]) + if xmxInBytes == -1 { + return errors.New("Couldn't convert -Xmx=" + options["-Xmx"] + " to bytes. ") + } + xmsInBytes := toBytes(options["-Xms"]) + if xmsInBytes == -1 { + return errors.New("Couldn't convert -Xms=" + options["-Xms"] + " to bytes. ") + } + if xmxInBytes >= xmsInBytes { + return nil + } else { + return errors.New("Xmx value (current value " + options["-Xmx"] + ") should be higher than Xms (current value " + options["-Xms"] + ")") + } +} +func toBytes(initial string) (initialInBytes int) { + if len(initial) < 1 { + return -1 + } + units := initial[len(initial)-1] + initial = initial[:len(initial)-1] + initialInt, _ := strconv.Atoi(initial) + switch units { + case 'k', 'K': + initialInBytes = initialInt << 10 + return initialInBytes + case 'm', 'M': + initialInBytes = initialInt << 20 + return initialInBytes + case 'g', 'G': + initialInBytes = initialInt << 30 + return initialInBytes + } + return -1 +} + +func getDuplicatedVmOptions(vmoptionsDuplicates map[string]string) (err error) { + if len(vmoptionsDuplicates) == 0 { + return nil + } + var duplicatedOptions string + for option := range vmoptionsDuplicates { + duplicatedOptions += ("\n\t" + option) + } + err = errors.New("Duplicated VM options: " + duplicatedOptions) + return err +} + +func suggestToFixVmoptionsFile(file string) { + question := "There are two options to fix the above error:\n" + + "\t 1. Reset .vmoptions file to default (current .vmoptions file will be backed up) \n" + + "\t 2. Open the .vmoptions file for editing and correct it manually \n" + + "Please choose" + options := []string{"1", "2"} + answer := AskQuestionWithOptions(question, options) + if answer == "1" || answer == "y" { + logger.InfoLogger.Println("Restoring default .vmoptions file") + logger.ExitWithExceptionOnError(RestoreVmoptionsFile()) + } else if answer == "2" { + openVmoptionsEditor(file) + } +} +func openVmoptionsEditor(vmOptionsFile string) { + err := OpenFileInEditor(vmOptionsFile) + logger.WriteToLogOnError(err, logger.ErrorLogger) +} diff --git a/native/repair-utility/logger/logger.go b/native/repair-utility/logger/logger.go new file mode 100644 index 000000000000..f25fa9956c1b --- /dev/null +++ b/native/repair-utility/logger/logger.go @@ -0,0 +1,77 @@ +package logger + +import ( + "errors" + "io" + "io/ioutil" + "log" + "os" + "runtime/debug" +) + +type RepairLogger struct { + log *log.Logger + logtype string +} + +var ( + WarningLogger RepairLogger + InfoLogger RepairLogger + ErrorLogger RepairLogger + ConsoleLogger RepairLogger + FatalLogger RepairLogger + DebugLogger RepairLogger + DebugEnabled bool + LogFile *os.File +) + +func init() { + dest, err := ioutil.TempDir("", "IntelliJRepairBinary") + LogFile, err = os.OpenFile(dest+string(os.PathSeparator)+"repair.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + mw := io.MultiWriter(os.Stdout, LogFile) + ConsoleLogger = RepairLogger{log.New(mw, "", 0), "console"} + InfoLogger = RepairLogger{log.New(mw, "INFO: ", log.Ldate|log.Ltime), "info"} + DebugLogger = RepairLogger{log.New(LogFile, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile), "debug"} + WarningLogger = RepairLogger{log.New(mw, "WARNING: ", 0), "warning"} + ErrorLogger = RepairLogger{log.New(mw, "ERROR: ", 0), "error"} + FatalLogger = RepairLogger{log.New(mw, "FATAL ERROR: ", 0), "fatal"} + InfoLogger.Printf("Log file %s cretaed", LogFile.Name()) +} +func RemoveLogFile() { + if _, err := os.Open(LogFile.Name()); err == nil { + _ = os.Remove(LogFile.Name()) + } +} +func WriteToLogOnError(err error, log RepairLogger) { + if err != nil { + log.log.Println(err) + } +} +func ExitOnError(err error) { + if err != nil { + FatalLogger.log.Fatal(err) + } +} +func ExitWithExceptionOnError(err error) { + if err != nil { + err = errors.New(string(debug.Stack()) + err.Error()) + FatalLogger.log.Fatal(err) + } +} + +func (l *RepairLogger) Fatal(v ...interface{}) { + l.log.Fatal(v...) +} +func (l *RepairLogger) Println(v ...interface{}) { + l.log.Println(v...) +} +func (l *RepairLogger) Printf(format string, v ...interface{}) { + l.log.Printf(format, v...) +} + +func (l *RepairLogger) SetOutput(w io.Writer) { + l.log.SetOutput(w) +} diff --git a/native/repair-utility/main.go b/native/repair-utility/main.go new file mode 100644 index 000000000000..2327a3d181ef --- /dev/null +++ b/native/repair-utility/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "repair/cmd" +) + +func main() { + cmd.Execute() +}