Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(desktop): support setup cli to windows path #1715

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 63 additions & 16 deletions src/main/installCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { BrowserWindow, dialog } from 'electron'
import { getAppDataPath } from 'appdata-path'
import sudo from 'sudo-prompt'
import version from '@/version'
import { exec } from 'child_process'
import { exec, spawnSync } from 'child_process'
import { compareVersions } from 'compare-versions'
import { getCurrentLang } from './updateChecker'

Expand All @@ -19,11 +19,14 @@ const MQTTX_VERSION = `v${version}`
* @param win - The BrowserWindow instance.
* @returns A promise that resolves to a boolean indicating whether the MQTTX CLI is installed and up to date.
*/
async function checkInstalledMqttxCLI(win: BrowserWindow): Promise<boolean> {
async function checkInstalledMqttxCLI(win: BrowserWindow, isWindows: boolean): Promise<boolean> {
if (isWindows) {
return Promise.resolve(false)
}
return new Promise((resolve) => {
exec('mqttx --version', (error, stdout, stderr) => {
if (error) {
// MQTTX CLI is not installed
if (error || stderr) {
dialog.showErrorBox('Error', `Failed to check MQTTX CLI version: ${stderr || error?.message}`)
resolve(false)
} else {
// Extract the version from the stdout
Expand Down Expand Up @@ -137,15 +140,59 @@ async function sudoInstall(outputPath: string, win: BrowserWindow): Promise<void
})
}

function showDownloadedWindowsCLI(outputPath: string, fileName: string, win: BrowserWindow) {
/**
* Sets up the MQTTX CLI on Windows.
*
* @param outputPath - The output path where the MQTTX CLI will be installed.
* @param fileName - The name of the file to be renamed.
* @param win - The BrowserWindow object.
*/
function setupWindows(outputPath: string, fileName: string, win: BrowserWindow) {
const newFileName = 'mqttx.exe'
const newFilePath = path.join(outputPath, newFileName)

try {
fs.renameSync(path.join(outputPath, fileName), newFilePath)
} catch (err) {
const message = err instanceof Error ? err.message : 'An unknown error occurred'
dialog.showErrorBox('Rename Error', `Failed to rename ${fileName} to ${newFileName}: ${message}`)
win.webContents.send('installedCLI')
return
}

const currentPath = process.env.PATH || ''
// Always remove the existing outputPath if it exists to update or correct it.
const cleanedPath = currentPath
.split(';')
.filter((p) => !p.trim().startsWith(outputPath))
.join(';')

// Now add the new outputPath
const newPath = `${cleanedPath};${outputPath}`
const result = spawnSync('setx', ['PATH', newPath])

if (result.error || (result.stderr && result.stderr.toString().trim())) {
const errorMessage = result.error ? result.error.message : result.stderr.toString()
dialog.showErrorBox('PATH Update Error', `Failed to update the PATH environment variable: ${errorMessage}`)
win.webContents.send('installedCLI')
return
}

dialog.showMessageBox({
type: 'info',
title: 'Download Completed',
message: `MQTTX CLI has been successfully downloaded.\n\nPlease manually run '${fileName}' located at: ${outputPath} to use it.`,
title: 'Installation Completed',
message: 'MQTTX CLI has been successfully installed.\n\nYou can run "mqttx.exe" commands in the terminal now.',
})

win.webContents.send('installedCLI')
}

/**
* Returns the architecture suffix based on the provided architecture and operating system.
* @param arch - The architecture string.
* @param isWindows - Indicates whether the operating system is Windows.
* @returns The architecture suffix.
*/
function getArchSuffix(arch: string, isWindows: boolean): string {
let suffix: string
switch (arch) {
Expand Down Expand Up @@ -176,20 +223,20 @@ function getArchSuffix(arch: string, isWindows: boolean): string {
* @returns A Promise that resolves when the installation is complete.
*/
export default async function installCLI(win: BrowserWindow) {
const isInstalled = await checkInstalledMqttxCLI(win)
if (isInstalled) {
win.webContents.send('installedCLI')
return
}

const lang = await getCurrentLang()

const { platform, arch } = {
platform: os.platform(),
arch: os.arch(),
}
const isWindows = platform === 'win32'
const isMacOS = platform === 'darwin'

const isInstalled = await checkInstalledMqttxCLI(win, isWindows)
if (isInstalled) {
win.webContents.send('installedCLI')
return
}

const lang = await getCurrentLang()
const suffix = isWindows ? 'win' : isMacOS ? 'macos' : 'linux'
let archSuffix = getArchSuffix(arch, isWindows)
const fileName = `mqttx-cli-${suffix}-${archSuffix}`
Expand All @@ -201,7 +248,7 @@ export default async function installCLI(win: BrowserWindow) {
if (!isWindows) {
await sudoInstall(outputPath, win)
} else {
showDownloadedWindowsCLI(outputPath, fileName, win)
setupWindows(outputPath, fileName, win)
}
} catch (error) {
dialog.showErrorBox('Error', `Failed to install MQTTX CLI: ${error}`)
Expand Down
Loading