pear cli installation functionality

This commit is contained in:
dmc
2024-01-15 17:23:38 +01:00
parent 8be7b869ce
commit ef7832de1f
2 changed files with 103 additions and 23 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +1,41 @@
/* eslint-env browser */
import os from 'os'
import fs from 'fs'
import path from 'path'
import { spawn } from 'child_process'
import { config } from 'pear'
const pearBin = path.join(config.pearDir, 'bin')
const BIN = path.join(config.pearDir, 'bin')
const isWin = process.platform === 'win32'
const paths = process.env.PATH.split(path.delimiter)
customElements.define('pear-welcome', class extends HTMLElement {
constructor () {
super()
this.template = document.createElement('template')
this.template.innerHTML = `
<blockquote>
<p>Pear is installed.</p>
</blockquote>
<p>✔ Pear is in the system PATH and ready to go.</p>
`
this.root = this.attachShadow({ mode: 'open' })
this.root.appendChild(this.template.content.cloneNode(true))
}
})
customElements.define('system-status', class extends HTMLElement {
constructor () {
super()
this.vianode = false
this.installed = paths.some((p) => {
if (p.includes('node') && p.includes('bin')) this.vianode = fs.existsSync(path.join(p, 'pear'))
return p === pearBin
this.zsh = false
this.bash = false
this.paths = process.env.PATH.split(path.delimiter)
this.installed = this.paths.some((bin) => {
if (isWin === false) {
if (this.zsh === false && fs.existsSync(path.join(bin, 'zsh'))) this.zsh = true
if (this.bash === false && fs.existsSync(path.join(bin, 'bash'))) this.bash = true
}
return bin === BIN && fs.existsSync(path.join(BIN, isWin ? 'pear.cmd' : 'pear'))
})
this.template = document.createElement('template')
this.template.innerHTML = `
@@ -38,17 +61,14 @@ customElements.define('system-status', class extends HTMLElement {
</style>
<h1>System Status</h1>
${
this.installed ? `
<blockquote>
<p>Pear is installed.</p>
</blockquote>
<p>✔ Pear is in the system PATH and ready to go.</p>
` : `
this.installed
? '<pear-welcome></pear-welcome>'
: `
<blockquote>
<p>Pear setup is nearly complete.</p>
</blockquote>
<p><button> Complete Pear Setup </button><p>
<p id=tip><small>Click the button to add </small><code>${pearBin}</code><small> to the system PATH<small></p>
<p id=tip><small>Click the button to add </small><code>${BIN}</code><small> to the system PATH<small></p>
`
}
</div>
@@ -56,17 +76,77 @@ customElements.define('system-status', class extends HTMLElement {
this.root = this.attachShadow({ mode: 'open' })
this.root.appendChild(this.template.content.cloneNode(true))
this.button = this.root.querySelector('button')
if (this.button) {
const listener = () => {
this.button.removeEventListener('click', listener)
this.#installPear()
.then(() => {
this.replaceWith(new this.constructor())
console.log('now show version info, and a gif showing pear command line help output run through')
})
.catch((err) => this.#error(err))
}
this.button.addEventListener('click', listener)
}
}
#error (err) {
console.error(err)
}
#installPear () {
const runtime = path.join(config.pearDir, 'current', 'by-arch', process.platform + '-' + process.arch, 'bin', 'bare', 'pear')
fs.mkdirSync(BIN, { recursive: true })
if (isWin) {
const ps1tmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear')
fs.writeFileSync(ps1tmp, `function pear { & "${runtime}" }; Export-ModuleMember -Function pear`)
fs.renameSync(ps1tmp, path.join(BIN, 'pear.ps1'))
const cmdtmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear')
fs.writeFileSync(cmdtmp, `@echo off\n"${runtime}" %*`)
fs.renameSync(cmdtmp, path.join(BIN, 'pear.cmd'))
return new Promise((resolve, reject) => {
spawn('cmd', ['/c', `setx PATH "${BIN};%PATH%"`]).on('exit', (code) => {
const codes = [code]
spawn('powershell', ['-Command', `$env:PATH = "${BIN};$env:PATH"`]).on('exit', (code) => {
codes.push(code)
if (codes[0] + codes[1] !== 0) {
reject(new Error(
'Failed to set PATH in' + [
codes[0] ? ' cmd - exit code: ' + codes[0] : '',
codes[1] ? ' powershell - exit code:' + codes[1] : ''
].filter(Boolean)
))
return
}
process.env.PATH = `${BIN};${process.env.PATH}`
resolve()
})
})
})
}
const pear = path.join(BIN, 'pear')
const tmp = path.join(BIN, Math.floor(Math.random() * 1000) + '.pear')
fs.symlinkSync(runtime, tmp)
fs.renameSync(tmp, pear)
fs.chmodSync(pear, 0o755)
let shellProfileExists = false
const statement = `\nexport PATH="${BIN}":$PATH\n`
for (const file of ['.zshrc', '.zshenv', '.zshprofile', '.zlogin', '.profile', '.bashrc']) {
const filepath = path.join(os.homedir(), file)
if (fs.existsSync(filepath)) {
fs.writeFileSync(filepath, statement, { flag: 'a' })
shellProfileExists = true
}
}
if (shellProfileExists === false) {
fs.writeFileSync(path.join(os.homedir(), this.bash ? '.bashrc' : '.profile'), statement, { flag: 'a' })
if (this.zsh) fs.writeFileSync(path.join(os.homedir(), '.zshrc'), statement, { flag: 'a' })
}
process.env.PATH = `${BIN}:${process.env.PATH}`
return Promise.resolve()
}
})