void (@Nullable Project project, @NotNull Runnable onSuccess, @NotNull Runnable onFailure, boolean withReplace) { File directory = new File(PathManager.getPluginsPath(), ShLanguage.INSTANCE.getID()); if (!directory.exists()) { //noinspection ResultOfMethodCallIgnored directory.mkdirs(); } File shellcheck = new File(directory, SHELLCHECK_BIN); File oldShellcheck = new File(directory, "old_" + SHELLCHECK_BIN); if (shellcheck.exists()) { if (withReplace) { boolean successful = renameOldShellcheck(shellcheck, oldShellcheck, onFailure); if (!successful) return; } else { setupShellcheckPath(shellcheck, onSuccess, onFailure); return; } } String url = getShellcheckDistributionLink(); if (StringUtil.isEmpty(url)) { LOG.debug("Unsupported OS for shellcheck"); return; } String downloadName = SHELLCHECK + SHELLCHECK_ARCHIVE_EXTENSION; DownloadableFileService service = DownloadableFileService.getInstance(); DownloadableFileDescription description = service.createFileDescription(url, downloadName); FileDownloader downloader = service.createDownloader(Collections.singletonList(description), downloadName); Task.Backgroundable task = new Task.Backgroundable(project, message("sh.shellcheck.download.label.text")) { @Override public void run(@NotNull ProgressIndicator indicator) { try { List<Pair<File, DownloadableFileDescription>> pairs = downloader.download(directory); Pair<File, DownloadableFileDescription> first = ContainerUtil.getFirstItem(pairs); File file = first != null ? first.first : null; if (file != null) { String path = decompressShellcheck(file, directory); if (StringUtil.isNotEmpty(path)) { FileUtil.setExecutable(new File(path)); ShSettings.setShellcheckPath(path); if (withReplace) { LOG.debug("Remove old shellcheck"); FileUtil.delete(oldShellcheck); } ApplicationManager.getApplication().invokeLater(onSuccess); EXTERNAL_ANNOTATOR_DOWNLOADED_EVENT_ID.log(); } } } catch (IOException e) { LOG.warn("Can't download shellcheck", e); if (withReplace) rollbackToOldShellcheck(shellcheck, oldShellcheck); ApplicationManager.getApplication().invokeLater(onFailure); } } }; BackgroundableProcessIndicator processIndicator = new BackgroundableProcessIndicator(task); processIndicator.setIndeterminate(false); ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, processIndicator); }