pear
Version:
203 lines (182 loc) • 5.54 kB
JavaScript
const process = require('process')
const HypercoreID = require('hypercore-id-encoding')
const os = require('os')
const path = require('path')
const fs = require('fs')
const { isWindows, isLinux, isMac, platform, arch } = require('which-runtime')
const goodbye = require('graceful-goodbye')
const speedometer = require('speedometer')
const byteSize = require('tiny-byte-size')
const { discoveryKey } = require('hypercore-crypto')
const isTTY = process.stdout.isTTY
const PROD_KEY = 'pear://pzcjqmpoo6szkoc4bpkw65ib9ctnrq7b6mneeinbhbheihaq6p6o'
const PEAR_KEY = require('./package.json').pear.platform.key
const DKEY = discoveryKey(HypercoreID.decode(PEAR_KEY)).toString('hex')
const HOST = platform + '-' + arch
const PEAR_DIR = isMac
? path.join(os.homedir(), 'Library', 'Application Support', 'pear')
: isLinux
? path.join(os.homedir(), '.config', 'pear')
: path.join(os.homedir(), 'AppData', 'Roaming', 'pear')
const LINK = path.join(PEAR_DIR, 'current')
const BIN = path.join(PEAR_DIR, 'bin')
const CURRENT_BIN = path.join(
LINK,
'by-arch',
HOST,
'bin/pear-runtime' + (isWindows ? '.exe' : '')
)
const forceUpdate = process.argv[2] === 'update'
if (isInstalled() && !forceUpdate) {
const warning = `[ WARNING ] To complete Pear installation, prepend the following to the system ${isWindows ? 'Path environment variable' : '$PATH'}:
${BIN}
Until then, this executable spawns the ${'`pear`'} binary.
Fix automatically with: pear run pear://runtime`
console.error(warning)
let child = null
const childProcessExit = new Promise((resolve) => {
child = require('child_process')
.spawn(CURRENT_BIN, process.argv.slice(2), {
stdio: 'inherit'
})
.on('exit', function (code) {
resolve(code)
})
})
goodbye(async () => {
child.kill()
const code = await childProcessExit
process.exit(code)
})
} else {
if (isLinux && !libatomicCheck()) {
console.log(
'Installation failed. The required library libatomic.so was not found on the system.'
)
console.log(`
Please install it first using the appropriate package manager for your system.
- Debian/Ubuntu: sudo apt install libatomic1
- Fedora: sudo dnf install libatomic
- Arch Linux: sudo pacman -S libatomic_ops
- Alpine Linux: sudo apk add libatomic
- RHEL/CentOS: sudo yum install libatomic
`)
process.exit(1)
}
const bootstrap = require('pear-updater-bootstrap')
console.log(
'Installing Pear Runtime (Please stand by, this might take a bit...)\n'
)
if (PEAR_KEY !== PROD_KEY) console.log('Bootstrapping:', PEAR_KEY)
bootstrap(PEAR_KEY, PEAR_DIR, {
onupdater: startDriveMonitor,
force: forceUpdate
}).then(
function () {
stopDriveMonitor()
console.log('Pear Runtime installed!')
console.log()
console.log('Finish the installation by opening the runtime app')
console.log()
console.log('npx pear run pear://runtime')
if (makeBin()) {
console.log()
console.log('Or by adding the following to your path')
console.log()
if (isWindows) {
console.log(`cmd: set PATH="${BIN};%PATH%"`)
console.log(`PowerShell: $env:PATH="${BIN};$env:PATH"`)
} else {
console.log(`export PATH="${BIN}:$PATH"`)
}
}
},
function (err) {
if (forceUpdate && err.code === 'ENOENT') {
console.log(
`Update failed: Pear Runtime is not installed at ${err.path}`
)
} else {
throw err
}
}
)
}
function makeBin() {
try {
fs.mkdirSync(BIN, { recursive: true })
if (isWindows) {
fs.writeFileSync(
path.join(BIN, 'pear.cmd'),
`@echo off\r\n"${CURRENT_BIN}" %*`
)
fs.writeFileSync(path.join(BIN, 'pear.ps1'), `& "${CURRENT_BIN}" @args`)
} else {
fs.symlinkSync(CURRENT_BIN, path.join(BIN, 'pear'))
}
} catch {
return false
}
return true
}
function isInstalled() {
try {
const p = fs.realpathSync(LINK)
return path.basename(path.dirname(p)) === DKEY
} catch {
return false
}
}
let monitorInterval = null
function clear() {
process.stdout.write('\x1b[2K') // clear line
process.stdout.write('\r') // cursor to 0
}
function stopDriveMonitor() {
clearInterval(monitorInterval)
if (isTTY) clear()
}
function startDriveMonitor(updater) {
if (!isTTY) return
const downloadSpeedometer = speedometer()
const uploadSpeedometer = speedometer()
let peers = 0
let downloadedBytes = 0
let uploadedBytes = 0
updater.drive
.getBlobs()
.then((blobs) => {
blobs.core.on('download', (_index, bytes) => {
downloadedBytes += bytes
downloadSpeedometer(bytes)
})
blobs.core.on('upload', (_index, bytes) => {
uploadedBytes += bytes
uploadSpeedometer(bytes)
})
blobs.core.on('peer-add', () => {
peers = blobs.core.peers.length
})
blobs.core.on('peer-remove', () => {
peers = blobs.core.peers.length
})
})
.catch(() => {
// ignore
})
monitorInterval = setInterval(() => {
clear()
process.stdout.write(
`[⬇ ${byteSize(downloadedBytes)} - ${byteSize(downloadSpeedometer())}/s - ${peers} peers] [⬆ ${byteSize(uploadedBytes)} - ${byteSize(uploadSpeedometer())}/s - ${peers} peers]`
)
}, 500)
}
function libatomicCheck() {
try {
require('rocksdb-native')
return true
} catch {
return false
}
}