From 3e7620290b844bdb03cae9d1fde48ebc1a0f2169 Mon Sep 17 00:00:00 2001 From: Alex Freska Date: Wed, 31 Jan 2024 12:01:38 -0400 Subject: [PATCH] binary download --- hostd/main/binary.ts | 5 +- hostd/main/daemon.ts | 87 +++++++------------ hostd/main/preload.ts | 2 - hostd/package.json | 13 +-- .../components/useIsDaemonRunning.tsx | 17 ---- hostd/{main => scripts}/download.ts | 32 +++++-- hostd/scripts/tsconfig.json | 24 +++++ renterd/main/binary.ts | 5 +- renterd/main/daemon.ts | 87 ++++++++----------- renterd/main/preload.ts | 2 - renterd/package.json | 13 +-- renterd/renderer/components/Header.tsx | 2 +- .../components/useIsDaemonRunning.tsx | 17 ---- renterd/{main => scripts}/download.ts | 22 ++++- renterd/scripts/tsconfig.json | 24 +++++ 15 files changed, 182 insertions(+), 170 deletions(-) delete mode 100644 hostd/renderer/components/useIsDaemonRunning.tsx rename hostd/{main => scripts}/download.ts (83%) create mode 100644 hostd/scripts/tsconfig.json delete mode 100644 renterd/renderer/components/useIsDaemonRunning.tsx rename renterd/{main => scripts}/download.ts (88%) create mode 100644 renterd/scripts/tsconfig.json diff --git a/hostd/main/binary.ts b/hostd/main/binary.ts index 34bcb5b..08c3f35 100644 --- a/hostd/main/binary.ts +++ b/hostd/main/binary.ts @@ -1,7 +1,10 @@ import * as path from 'path' +import { env } from './env' export function getBinaryDirectoryPath(): string { - return path.join(__dirname, '../bin') + return env.isDev + ? path.join(process.cwd(), 'bin') + : path.join(__dirname, '../bin') } export function getBinaryFilePath(): string { diff --git a/hostd/main/daemon.ts b/hostd/main/daemon.ts index 21c3d77..512cb25 100644 --- a/hostd/main/daemon.ts +++ b/hostd/main/daemon.ts @@ -1,42 +1,39 @@ import { spawn } from 'child_process' import { state } from './state' import { getConfig, getConfigFilePath } from './config' -import axios from 'axios' -import { getBinaryFilePath } from './binary' +import path from 'path' +import fs from 'fs' +import { getBinaryDirectoryPath, getBinaryFilePath } from './binary' -export function startDaemon(): Promise { - return new Promise(async (resolve, reject) => { +export async function startDaemon(): Promise { + try { await stopDaemon() + const config = getConfig() + const binaryFilePath = getBinaryFilePath() + state.daemon = spawn(binaryFilePath, ['-env'], { + env: { ...process.env, HOSTD_CONFIG_FILE: getConfigFilePath() }, + cwd: config.directory, + }) - try { - const config = getConfig() - const binaryFilePath = getBinaryFilePath() - state.daemon = spawn(binaryFilePath, ['-env'], { - env: { ...process.env, HOSTD_CONFIG_FILE: getConfigFilePath() }, - cwd: config.directory, - }) - - state.daemon.stdout?.on('data', (data) => { - console.log(`stdout: ${data}`) - // Emit events or log data as needed - }) - - state.daemon.stderr?.on('data', (data) => { - console.error(`stderr: ${data}`) - // Emit events or log data as needed - }) + state.daemon.stdout?.on('data', (data) => { + console.log(`stdout: ${data}`) + // Emit events or log data as needed + }) - state.daemon.on('close', (code) => { - console.log(`child process exited with code ${code}`) - state.daemon = null - }) + state.daemon.stderr?.on('data', (data) => { + console.error(`stderr: ${data}`) + // Emit events or log data as needed + }) - resolve() - } catch (err) { + state.daemon.on('close', (code) => { + console.log(`child process exited with code ${code}`) state.daemon = null - reject(err) - } - }) + }) + } catch (err) { + console.log('Failed to start daemon', err) + state.daemon = null + throw err + } } export function stopDaemon(): Promise { @@ -60,31 +57,11 @@ export function getIsDaemonRunning(): boolean { } export async function getInstalledVersion(): Promise { - if (!getIsDaemonRunning()) { - return '' - } - const config = getConfig() - let address = config.http.address - ? 'http://' + config.http.address - : 'http://127.0.0.1:9980' - - // localhost tries to uses ipv6, which the daemon does not support - address = address.replace('http://localhost:', 'http://127.0.0.1:') - + const versionFilePath = path.join(getBinaryDirectoryPath(), 'version') try { - const auth = Buffer.from(`:${config.http.password}`).toString('base64') - const response = await axios.get<{ version: string }>( - address + '/api/state/host', - { - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${auth}`, - }, - } - ) - return response.data.version - } catch (err) { - console.error(err) - return '' + const version = await fs.promises.readFile(versionFilePath, 'utf8') + return version + } catch (e) { + return 'error: no daemon' } } diff --git a/hostd/main/preload.ts b/hostd/main/preload.ts index 84b1a81..6deecbb 100644 --- a/hostd/main/preload.ts +++ b/hostd/main/preload.ts @@ -11,7 +11,6 @@ contextBridge.exposeInMainWorld('electron', { checkIsDaemonRunning: () => ipcRenderer.invoke('daemon-is-running'), daemonStart: () => ipcRenderer.invoke('daemon-start'), daemonStop: () => ipcRenderer.invoke('daemon-stop'), - daemonUpdate: () => ipcRenderer.invoke('daemon-update'), openBrowser: (url: string) => ipcRenderer.invoke('open-browser', url), getConfig: () => ipcRenderer.invoke('config-get'), saveConfig: (config: Config) => ipcRenderer.invoke('config-save', config), @@ -20,5 +19,4 @@ contextBridge.exposeInMainWorld('electron', { getDefaultDataDirectory: () => ipcRenderer.invoke('get-default-data-directory'), getInstalledVersion: () => ipcRenderer.invoke('get-installed-version'), - getLatestVersion: () => ipcRenderer.invoke('get-latest-version'), }) diff --git a/hostd/package.json b/hostd/package.json index 5a95b09..54bd8f0 100644 --- a/hostd/package.json +++ b/hostd/package.json @@ -14,12 +14,13 @@ }, "main": "dist/main/index.js", "scripts": { - "clean": "rimraf out dist renderer/.next", - "dev": "npm run build-electron && electron .", - "build-renderer": "next build renderer", - "build-electron": "tsc -p main", - "download-daemon": "npm run build-electron && node main/download.js", - "build": "npm run build-renderer && npm run build-electron", + "clean": "rimraf bin out dist renderer/.next", + "dev": "npm run build:main && electron .", + "build": "npm run build:renderer && npm run build:main", + "build:renderer": "next build renderer", + "build:main": "tsc -p main", + "build:scripts": "tsc -p scripts", + "download:binary": "npm run build:scripts && node dist/scripts/download.js", "type-check": "tsc -p ./renderer/tsconfig.json && tsc -p ./main/tsconfig.json", "start": "electron-forge start", "package": "electron-forge package", diff --git a/hostd/renderer/components/useIsDaemonRunning.tsx b/hostd/renderer/components/useIsDaemonRunning.tsx deleted file mode 100644 index 81ec5d2..0000000 --- a/hostd/renderer/components/useIsDaemonRunning.tsx +++ /dev/null @@ -1,17 +0,0 @@ -'use client' - -import useSWR from 'swr' - -export function useIsDaemonRunning() { - return useSWR( - 'isDaemonRunning', - async () => { - const isRunning = await window.electron.checkIsDaemonRunning() - console.log('here', isRunning) - return isRunning - }, - { - refreshInterval: 10_000, - } - ) -} diff --git a/hostd/main/download.ts b/hostd/scripts/download.ts similarity index 83% rename from hostd/main/download.ts rename to hostd/scripts/download.ts index 1d6b60a..935f100 100644 --- a/hostd/main/download.ts +++ b/hostd/scripts/download.ts @@ -5,12 +5,10 @@ import admZip from 'adm-zip' import { promisify } from 'util' import stream from 'stream' import axios from 'axios' -import { system } from './system' -import { getBinaryDirectoryPath, getBinaryFilePath } from './binary' downloadRelease() -// export async function getLatestVersion(): Promise { +// async function getLatestVersion(): Promise { // try { // const octokit = new Octokit() // const response = await octokit.repos.getLatestRelease({ @@ -24,7 +22,7 @@ downloadRelease() // } // } -export async function downloadRelease(): Promise { +async function downloadRelease(): Promise { try { const octokit = new Octokit() const releaseData = await octokit.repos.getLatestRelease({ @@ -76,7 +74,7 @@ async function extractBinary(): Promise { const zip = new admZip(zipFilePath) zip.extractAllTo(extractDir, true) - const binaryName = process.platform === 'win32' ? 'hostd.exe' : 'hostd' + const binaryName = system.isWindows ? 'hostd.exe' : 'hostd' const extractedBinaryPath = path.join(extractDir, binaryName) const finalBinaryPath = getBinaryFilePath() @@ -102,7 +100,7 @@ function getTempDownloadsPath(): string { } function getBinaryZipStagingPath(): string { - const binaryName = system.isWindows ? `hostd.exe` : `hostd` + const binaryName = process.platform === 'win32' ? `hostd.exe` : `hostd` return path.join(getTempDownloadsPath(), binaryName + '.zip') } @@ -140,5 +138,25 @@ function releaseAsset(): string { } } - return `hostd_${goos}_${goarch}.zip` + return `renterd_${goos}_${goarch}.zip` +} + +function getBinaryDirectoryPath(): string { + // running from dist/main/download.ts + return path.join(__dirname, '../../bin') +} + +function getBinaryFilePath(): string { + const binaryName = process.platform === 'win32' ? 'hostd.exe' : 'hostd' + return path.join(getBinaryDirectoryPath(), binaryName) +} + +const system: { + isDarwin: boolean + isLinux: boolean + isWindows: boolean +} = { + isDarwin: process.platform === 'darwin', + isLinux: process.platform === 'linux', + isWindows: process.platform === 'win32', } diff --git a/hostd/scripts/tsconfig.json b/hostd/scripts/tsconfig.json new file mode 100644 index 0000000..c205b3b --- /dev/null +++ b/hostd/scripts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "allowJs": true, + "alwaysStrict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "jsx": "preserve", + "lib": ["dom", "es2017"], + "module": "commonjs", + "moduleResolution": "node", + "noEmit": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext", + "outDir": "../dist/scripts" + }, + "exclude": ["node_modules"], + "include": ["**/*.ts", "**/*.tsx", "**/*.js"] +} diff --git a/renterd/main/binary.ts b/renterd/main/binary.ts index db96b80..581d9e1 100644 --- a/renterd/main/binary.ts +++ b/renterd/main/binary.ts @@ -1,7 +1,10 @@ import * as path from 'path' +import { env } from './env' export function getBinaryDirectoryPath(): string { - return path.join(__dirname, '../bin') + return env.isDev + ? path.join(process.cwd(), 'bin') + : path.join(__dirname, '../bin') } export function getBinaryFilePath(): string { diff --git a/renterd/main/daemon.ts b/renterd/main/daemon.ts index bd29788..f44c7bd 100644 --- a/renterd/main/daemon.ts +++ b/renterd/main/daemon.ts @@ -1,42 +1,40 @@ import { spawn } from 'child_process' import { state } from './state' import { getConfig, getConfigFilePath } from './config' -import { getBinaryFilePath } from './binary' -import axios from 'axios' +import { getBinaryDirectoryPath, getBinaryFilePath } from './binary' +import path from 'path' +import fs from 'fs' -export function startDaemon(): Promise { - return new Promise(async (resolve, reject) => { +export async function startDaemon(): Promise { + try { await stopDaemon() + const config = getConfig() + const binaryFilePath = getBinaryFilePath() + state.daemon = spawn(binaryFilePath, [], { + env: { ...process.env, RENTERD_CONFIG_FILE: getConfigFilePath() }, + cwd: config.directory, + }) - try { - const config = getConfig() - const binaryFilePath = getBinaryFilePath() - state.daemon = spawn(binaryFilePath, [], { - env: { ...process.env, RENTERD_CONFIG_FILE: getConfigFilePath() }, - cwd: config.directory, - }) - - state.daemon.stdout?.on('data', (data) => { - console.log(`stdout: ${data}`) - // Emit events or log data as needed - }) - - state.daemon.stderr?.on('data', (data) => { - console.error(`stderr: ${data}`) - // Emit events or log data as needed - }) + state.daemon.stdout?.on('data', (data) => { + console.log(`stdout: ${data}`) + // Emit events or log data as needed + }) - state.daemon.on('close', (code) => { - console.log(`child process exited with code ${code}`) - state.daemon = null - }) + state.daemon.stderr?.on('data', (data) => { + console.error(`stderr: ${data}`) + // Emit events or log data as needed + }) - resolve() - } catch (err) { + state.daemon.on('close', (code) => { + console.log(`child process exited with code ${code}`) state.daemon = null - reject(err) - } - }) + }) + } catch (err) { + console.log('Failed to start daemon', err) + state.daemon = null + console.log('state is', state) + throw err + } } export function stopDaemon(): Promise { @@ -56,33 +54,16 @@ export function stopDaemon(): Promise { } export function getIsDaemonRunning(): boolean { + console.log('daemon', state) return !!state.daemon && !state.daemon.killed } export async function getInstalledVersion(): Promise { - if (!getIsDaemonRunning()) { - return '' - } - const config = getConfig() - let address = config.http.address - ? 'http://' + config.http.address - : 'http://127.0.0.1:9980' - - // localhost tries to uses ipv6, which the daemon does not support - address = address.replace('http://localhost:', 'http://127.0.0.1:') - + const versionFilePath = path.join(getBinaryDirectoryPath(), 'version') try { - const response = await axios.get<{ version: string }>( - address + '/api/bus/state', - { - headers: { - Authorization: 'Basic ' + btoa(':' + config.http.password), - }, - } - ) - return response.data.version - } catch (err) { - console.error(err) - return '' + const version = await fs.promises.readFile(versionFilePath, 'utf8') + return version + } catch (e) { + return 'error: no daemon' } } diff --git a/renterd/main/preload.ts b/renterd/main/preload.ts index 84b1a81..6deecbb 100644 --- a/renterd/main/preload.ts +++ b/renterd/main/preload.ts @@ -11,7 +11,6 @@ contextBridge.exposeInMainWorld('electron', { checkIsDaemonRunning: () => ipcRenderer.invoke('daemon-is-running'), daemonStart: () => ipcRenderer.invoke('daemon-start'), daemonStop: () => ipcRenderer.invoke('daemon-stop'), - daemonUpdate: () => ipcRenderer.invoke('daemon-update'), openBrowser: (url: string) => ipcRenderer.invoke('open-browser', url), getConfig: () => ipcRenderer.invoke('config-get'), saveConfig: (config: Config) => ipcRenderer.invoke('config-save', config), @@ -20,5 +19,4 @@ contextBridge.exposeInMainWorld('electron', { getDefaultDataDirectory: () => ipcRenderer.invoke('get-default-data-directory'), getInstalledVersion: () => ipcRenderer.invoke('get-installed-version'), - getLatestVersion: () => ipcRenderer.invoke('get-latest-version'), }) diff --git a/renterd/package.json b/renterd/package.json index 539ba76..10a70b6 100644 --- a/renterd/package.json +++ b/renterd/package.json @@ -14,12 +14,13 @@ }, "main": "dist/main/index.js", "scripts": { - "clean": "rimraf out dist renderer/.next", - "dev": "npm run build-electron && electron .", - "build-renderer": "next build renderer", - "build-electron": "tsc -p main", - "download-daemon": "npm run build-electron && node main/download.js", - "build": "npm run build-renderer && npm run build-electron", + "clean": "rimraf bin out dist renderer/.next", + "dev": "npm run build:main && electron .", + "build": "npm run build:renderer && npm run build:main", + "build:renderer": "next build renderer", + "build:main": "tsc -p main", + "build:scripts": "tsc -p scripts", + "download:binary": "npm run build:scripts && node dist/scripts/download.js", "type-check": "tsc -p ./renderer/tsconfig.json && tsc -p ./main/tsconfig.json", "start": "electron-forge start", "package": "electron-forge package", diff --git a/renterd/renderer/components/Header.tsx b/renterd/renderer/components/Header.tsx index 87f0c4a..0294e05 100644 --- a/renterd/renderer/components/Header.tsx +++ b/renterd/renderer/components/Header.tsx @@ -18,7 +18,7 @@ export function Header() { useConfig() const config = useConfigData() const installedVersion = useInstalledVersion() - console.log(config.data) + console.log(isRunning.data) return ( { - const isRunning = await window.electron.checkIsDaemonRunning() - console.log('here', isRunning) - return isRunning - }, - { - refreshInterval: 10_000, - } - ) -} diff --git a/renterd/main/download.ts b/renterd/scripts/download.ts similarity index 88% rename from renterd/main/download.ts rename to renterd/scripts/download.ts index 8ed18b8..11bf53c 100644 --- a/renterd/main/download.ts +++ b/renterd/scripts/download.ts @@ -5,8 +5,6 @@ import admZip from 'adm-zip' import { promisify } from 'util' import stream from 'stream' import axios from 'axios' -import { system } from './system' -import { getBinaryDirectoryPath, getBinaryFilePath } from './binary' downloadRelease() @@ -142,3 +140,23 @@ function releaseAsset(): string { return `renterd_${goos}_${goarch}.zip` } + +function getBinaryDirectoryPath(): string { + // running from dist/main/download.ts + return path.join(__dirname, '../../bin') +} + +function getBinaryFilePath(): string { + const binaryName = process.platform === 'win32' ? 'renterd.exe' : 'renterd' + return path.join(getBinaryDirectoryPath(), binaryName) +} + +const system: { + isDarwin: boolean + isLinux: boolean + isWindows: boolean +} = { + isDarwin: process.platform === 'darwin', + isLinux: process.platform === 'linux', + isWindows: process.platform === 'win32', +} diff --git a/renterd/scripts/tsconfig.json b/renterd/scripts/tsconfig.json new file mode 100644 index 0000000..c205b3b --- /dev/null +++ b/renterd/scripts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "allowJs": true, + "alwaysStrict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "jsx": "preserve", + "lib": ["dom", "es2017"], + "module": "commonjs", + "moduleResolution": "node", + "noEmit": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "esnext", + "outDir": "../dist/scripts" + }, + "exclude": ["node_modules"], + "include": ["**/*.ts", "**/*.tsx", "**/*.js"] +}