WIP: add logic to locate user active installed chromium browser, make it can bu used for our product rather than download new one. TODO: test

This commit is contained in:
geekgeekrun
2024-02-24 11:47:03 +08:00
parent 2c549b4d1c
commit 32ad7c54c5
8 changed files with 135 additions and 17 deletions
+1
View File
@@ -30,6 +30,7 @@
"JSONStream": "^1.3.5", "JSONStream": "^1.3.5",
"electron-updater": "^6.1.7", "electron-updater": "^6.1.7",
"element-plus": "^2.5.5", "element-plus": "^2.5.5",
"find-chrome-bin": "^2.0.1",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"vue-router": "^4.2.5" "vue-router": "^4.2.5"
}, },
@@ -4,6 +4,7 @@ import * as fs from 'node:fs'
import type { InstalledBrowser } from '@puppeteer/browsers' import type { InstalledBrowser } from '@puppeteer/browsers'
import { is } from '@electron-toolkit/utils' import { is } from '@electron-toolkit/utils'
import electron from 'electron' import electron from 'electron'
import { saveLastUsedAndAvailableBrowserPath } from './history-utils'
const expectBuildId = process.env.EXPECT_CHROME_FOR_PUPPETEER_BUILD_ID || '121.0.6167.85' const expectBuildId = process.env.EXPECT_CHROME_FOR_PUPPETEER_BUILD_ID || '121.0.6167.85'
const cacheDir = path.join( const cacheDir = path.join(
@@ -31,7 +32,7 @@ const getPuppeteerManagerModule = async () => {
return puppeteerManager return puppeteerManager
} }
export const getExpectPuppeteerExecutablePath = async () => { export const getExpectCachedPuppeteerExecutablePath = async () => {
const puppeteerManager = await getPuppeteerManagerModule() const puppeteerManager = await getPuppeteerManagerModule()
return puppeteerManager.computeExecutablePath({ return puppeteerManager.computeExecutablePath({
@@ -41,9 +42,9 @@ export const getExpectPuppeteerExecutablePath = async () => {
}) })
} }
export const checkPuppeteerExecutable = async () => { export const checkCachedPuppeteerExecutable = async () => {
try { try {
const executablePath = await getExpectPuppeteerExecutablePath() const executablePath = await getExpectCachedPuppeteerExecutablePath()
return fs.existsSync(executablePath) return fs.existsSync(executablePath)
} catch { } catch {
// should limit [ERR_MODULE_NOT_FOUND] // should limit [ERR_MODULE_NOT_FOUND]
@@ -59,7 +60,7 @@ const checkAndDownloadPuppeteerExecutable = async (
) => { ) => {
const puppeteerManager = await getPuppeteerManagerModule() const puppeteerManager = await getPuppeteerManagerModule()
let installedBrowser: InstalledBrowser let installedBrowser: InstalledBrowser
if (!(await checkPuppeteerExecutable())) { if (!(await checkCachedPuppeteerExecutable())) {
try { try {
await options.confirmContinuePromise await options.confirmContinuePromise
} catch { } catch {
@@ -84,6 +85,7 @@ const checkAndDownloadPuppeteerExecutable = async (
}) })
).find((it) => it.buildId === expectBuildId)! ).find((it) => it.buildId === expectBuildId)!
} }
await saveLastUsedAndAvailableBrowserPath(await getExpectCachedPuppeteerExecutablePath())
return installedBrowser return installedBrowser
} }
@@ -0,0 +1,17 @@
import { findChrome } from "find-chrome-bin";
import * as os from 'node:os'
export default async function findAndLocateExistedChromiumExecutable() {
// For windows, try to find Edge(chromium)
if (os.platform() === 'win32') {
// TODO: handle windows
}
// For other, use findChrome
const targetBrowser = await findChrome({})
if (!targetBrowser?.executablePath) {
throw new Error('NO_EXPECT_CHROMIUM_FOUND')
}
return {
path: targetBrowser.executablePath
}
}
@@ -0,0 +1,52 @@
import * as path from 'path'
import * as os from 'os'
import * as fs from 'fs'
import * as fsPromise from 'fs/promises'
const runtimeFolderPath = path.join(os.homedir(), '.geekgeekrun')
export const lastUsedBrowserRecordFilePath = path.join(
runtimeFolderPath,
'last-used-browser-record'
)
/**
* check if last used browser is still available.
*
* look if last used one is exist, maybe it's downloaded by puppeteer
* immediately return its path
* else remove its history
* @returns
*/
export const getLastUsedAndAvailableBrowserPath = async (): Promise<string | null> => {
if (!fs.existsSync(lastUsedBrowserRecordFilePath)) {
return null
}
try {
const fileContent = (await fsPromise.readFile(lastUsedBrowserRecordFilePath)).toString()
if (!fileContent || !fs.existsSync(fileContent)) {
await removeLastUsedAndAvailableBrowserPath()
return null
}
return fileContent
} catch {
await removeLastUsedAndAvailableBrowserPath()
return null
}
}
export const saveLastUsedAndAvailableBrowserPath = async (pathToBrowser: string) => {
try {
if (!fs.existsSync(runtimeFolderPath)) {
await fsPromise.mkdir(runtimeFolderPath)
}
await fsPromise.writeFile(lastUsedBrowserRecordFilePath, pathToBrowser)
} catch {
console.warn('lastUsedBrowserRecordFile write error')
}
}
export const removeLastUsedAndAvailableBrowserPath = async () => {
if (!fs.existsSync(lastUsedBrowserRecordFilePath)) {
return
}
await fsPromise.unlink(lastUsedBrowserRecordFilePath)
}
@@ -1,12 +1,45 @@
import { app } from 'electron' import { app } from 'electron'
import checkAndDownloadPuppeteerExecutable from './check-and-download-puppeteer-executable' import checkAndDownloadPuppeteerExecutable, {
checkCachedPuppeteerExecutable,
getExpectCachedPuppeteerExecutablePath
} from './check-and-download-puppeteer-executable'
import * as net from 'net' import * as net from 'net'
import { pipeWriteRegardlessError } from '../utils/pipe' import { pipeWriteRegardlessError } from '../utils/pipe'
import {
removeLastUsedAndAvailableBrowserPath,
getLastUsedAndAvailableBrowserPath,
saveLastUsedAndAvailableBrowserPath
} from './history-utils'
import findAndLocateExistedChromiumExecutable from './check-and-locate-existed-chromium-executable'
export enum DOWNLOAD_ERROR_EXIT_CODE { export enum DOWNLOAD_ERROR_EXIT_CODE {
NO_ERROR = 0, NO_ERROR = 0,
DOWNLOAD_ERROR = 1 DOWNLOAD_ERROR = 1
} }
export const getAnyAvailablePuppeteerExecutablePath = async (): Promise<string | null> => {
const lastUsedOnePath = await getLastUsedAndAvailableBrowserPath()
if (lastUsedOnePath) {
return lastUsedOnePath
}
// find existed browser - the one maybe actively installed by user or ship with os like Edge on windows
try {
const existedOnePath = (await findAndLocateExistedChromiumExecutable()).path
await saveLastUsedAndAvailableBrowserPath(existedOnePath)
// save its path
return existedOnePath
} catch {
console.log('no existed browser path found')
}
// find existed browser - the fallback one
if (await checkCachedPuppeteerExecutable()) {
return await getExpectCachedPuppeteerExecutablePath()
}
// if no one available, then return null and remove last used browser
await removeLastUsedAndAvailableBrowserPath()
return null
}
export const checkAndDownloadDependenciesForInit = async () => { export const checkAndDownloadDependenciesForInit = async () => {
process.on('disconnect', () => app.exit()) process.on('disconnect', () => app.exit())
app.dock.hide() app.dock.hide()
@@ -4,7 +4,7 @@ import { SyncHook, AsyncSeriesHook } from 'tapable'
import { readConfigFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' import { readConfigFile } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
import * as net from 'net' import * as net from 'net'
import { import {
checkPuppeteerExecutable, checkCachedPuppeteerExecutable,
} from './CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer-executable' } from './CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer-executable'
import { pipeWriteRegardlessError } from './utils/pipe' import { pipeWriteRegardlessError } from './utils/pipe'
@@ -51,7 +51,7 @@ export const runAutoChat = async () => {
return return
} }
const isPuppeteerExecutable = await checkPuppeteerExecutable() const isPuppeteerExecutable = await checkCachedPuppeteerExecutable()
if (!isPuppeteerExecutable) { if (!isPuppeteerExecutable) {
app.exit(1) app.exit(1)
return return
+11 -10
View File
@@ -9,13 +9,12 @@ import {
writeConfigFile writeConfigFile
} from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs' } from '@geekgeekrun/geek-auto-start-chat-with-boss/runtime-file-utils.mjs'
import { ChildProcess } from 'child_process' import { ChildProcess } from 'child_process'
import {
checkPuppeteerExecutable,
getExpectPuppeteerExecutablePath
} from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES/check-and-download-puppeteer-executable'
import * as JSONStream from 'JSONStream' import * as JSONStream from 'JSONStream'
import { DOWNLOAD_ERROR_EXIT_CODE } from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES' import {
let mainWindow: BrowserWindow = null DOWNLOAD_ERROR_EXIT_CODE,
getAnyAvailablePuppeteerExecutablePath
} from '../flow/CHECK_AND_DOWNLOAD_DEPENDENCIES'
let mainWindow: BrowserWindow | null = null
export function createMainWindow(): void { export function createMainWindow(): void {
// Create the browser window. // Create the browser window.
@@ -95,7 +94,7 @@ export function createMainWindow(): void {
const subProcessEnv = { const subProcessEnv = {
...process.env, ...process.env,
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'geekAutoStartWithBoss', MAIN_BOSSGEEKGO_UI_RUN_MODE: 'geekAutoStartWithBoss',
PUPPETEER_EXECUTABLE_PATH: await getExpectPuppeteerExecutablePath() PUPPETEER_EXECUTABLE_PATH: (await getAnyAvailablePuppeteerExecutablePath())!
} }
subProcessOfPuppeteer = childProcess.spawn(process.argv[0], process.argv.slice(1), { subProcessOfPuppeteer = childProcess.spawn(process.argv[0], process.argv.slice(1), {
env: subProcessEnv, env: subProcessEnv,
@@ -130,9 +129,11 @@ export function createMainWindow(): void {
}) })
ipcMain.handle('check-dependencies', async () => { ipcMain.handle('check-dependencies', async () => {
const [puppeteerExecutableAvailable] = await Promise.all([checkPuppeteerExecutable()]) const [anyAvailablePuppeteerExecutablePath] = await Promise.all([
getAnyAvailablePuppeteerExecutablePath()
])
return { return {
puppeteerExecutableAvailable puppeteerExecutableAvailable: !!anyAvailablePuppeteerExecutablePath
} }
}) })
@@ -144,7 +145,7 @@ export function createMainWindow(): void {
const subProcessEnv = { const subProcessEnv = {
...process.env, ...process.env,
MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit', MAIN_BOSSGEEKGO_UI_RUN_MODE: 'checkAndDownloadDependenciesForInit',
PUPPETEER_EXECUTABLE_PATH: await getExpectPuppeteerExecutablePath() PUPPETEER_EXECUTABLE_PATH: (await getAnyAvailablePuppeteerExecutablePath())!
} }
subProcessOfCheckAndDownloadDependencies = childProcess.spawn( subProcessOfCheckAndDownloadDependencies = childProcess.spawn(
process.argv[0], process.argv[0],
+12
View File
@@ -69,6 +69,9 @@ importers:
element-plus: element-plus:
specifier: ^2.5.5 specifier: ^2.5.5
version: 2.5.5(vue@3.4.15) version: 2.5.5(vue@3.4.15)
find-chrome-bin:
specifier: ^2.0.1
version: 2.0.1
normalize.css: normalize.css:
specifier: ^8.0.1 specifier: ^8.0.1
version: 8.0.1 version: 8.0.1
@@ -3056,6 +3059,15 @@ packages:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
dev: true dev: true
/find-chrome-bin@2.0.1:
resolution: {integrity: sha512-aDwC2y0dLxt0GFmQ+q8bqBCZ10VW9zYT/lNV806tRDqDAh5XpkTWulB96RKDHDuKu36m/dEvhmhD5IU237oOTg==}
engines: {node: '>=18.0.0'}
dependencies:
'@puppeteer/browsers': 1.9.0
transitivePeerDependencies:
- supports-color
dev: false
/find-up@5.0.0: /find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'} engines: {node: '>=10'}