Bug fixes to installx

This commit is contained in:
Brian Zalewski 2024-05-04 05:11:59 +00:00
parent 3113d5aa5a
commit 2340501574
2 changed files with 71 additions and 27 deletions

View file

@ -41,7 +41,7 @@ async function runScript(key, script) {
fs.writeFileSync(`${cacheDir}/${key}-glow`, (file.stdout ? `# ${file.stdout}\n\n` : '') + (brief.stdout ? `> ${brief.stdout}\n\n` : '') + '```sh\n' + templatedScript.stdout + "\n```") fs.writeFileSync(`${cacheDir}/${key}-glow`, (file.stdout ? `# ${file.stdout}\n\n` : '') + (brief.stdout ? `> ${brief.stdout}\n\n` : '') + '```sh\n' + templatedScript.stdout + "\n```")
fs.writeFileSync(`${cacheDir}/${key}`, templatedScript.stdout) fs.writeFileSync(`${cacheDir}/${key}`, templatedScript.stdout)
try { try {
runSilentCommand(`glow --width 140 "${cacheDir}/${key}-glow"`) runSilentCommand(`glow --width 80 "${cacheDir}/${key}-glow"`)
// TODO: Set process.env.DEBUG || true here because the asynchronous method is not logging properly / running slow // TODO: Set process.env.DEBUG || true here because the asynchronous method is not logging properly / running slow
if (process.env.DEBUG || true) { if (process.env.DEBUG || true) {
return runSilentCommand(`bash "${cacheDir}/${key}" || logg error 'Error occurred while processing script for ${key}'`) return runSilentCommand(`bash "${cacheDir}/${key}" || logg error 'Error occurred while processing script for ${key}'`)
@ -143,8 +143,8 @@ function expandDeps(keys) {
return [...keys] return [...keys]
} }
async function createCaskLinks() { async function createCaskLinks(caskMap) {
const caskApps = pkgMap(pkgs) const caskApps = caskMap
.filter(x => { .filter(x => {
// Filter out macOS apps that already have a _app installed // Filter out macOS apps that already have a _app installed
if (x.installType === 'cask' || (osId === 'darwin' && x._app)) { if (x.installType === 'cask' || (osId === 'darwin' && x._app)) {
@ -153,14 +153,27 @@ async function createCaskLinks() {
const sysDir = fs.existsSync(`/Applications/${x[appField]}`) const sysDir = fs.existsSync(`/Applications/${x[appField]}`)
const homeDir = fs.existsSync(`${os.homedir()}/Applications/${x[appField]}`) const homeDir = fs.existsSync(`${os.homedir()}/Applications/${x[appField]}`)
const binFile = fs.existsSync(`${os.homedir()}/.local/bin/cask/${x[binField]}`) const binFile = fs.existsSync(`${os.homedir()}/.local/bin/cask/${x[binField]}`)
return (sysDir || homeDir) && !binFile if (sysDir || homeDir) {
} return !binFile
} else {
return false return false
}
} else {
return false
}
}) })
caskApps.length && await $`mkdir -p "$HOME/.local/bin/cask"` caskApps.length && await $`mkdir -p "$HOME/.local/bin/cask"`
for (const app of caskApps) { for (const app of caskApps) {
const appField = getPkgData('_app', app, app.installType) const appField = getPkgData('_app', app, app.installType)
if (!appField) {
log(`${app.listKey} is missing an _app definition`)
return
}
const binField = getPkgData('_bin', app, app.installType) const binField = getPkgData('_bin', app, app.installType)
if (!binField) {
log(`${app.listKey} is missing a _bin definition`)
return
}
if (fs.existsSync(`${os.homedir()}/Applications/${app[appField]}`)) { if (fs.existsSync(`${os.homedir()}/Applications/${app[appField]}`)) {
fs.writeFileSync(`${os.homedir()}/.local/bin/cask/${app[binField]}`, `#!/usr/bin/env bash\nopen "$HOME/Applications/${app[appField]}" $*`) fs.writeFileSync(`${os.homedir()}/.local/bin/cask/${app[binField]}`, `#!/usr/bin/env bash\nopen "$HOME/Applications/${app[appField]}" $*`)
await $`chmod +x '${os.homedir()}/.local/bin/cask/${app[binField]}'` await $`chmod +x '${os.homedir()}/.local/bin/cask/${app[binField]}'`
@ -174,10 +187,10 @@ async function createCaskLinks() {
caskApps.length && log(`Finished creating Homebrew cask links in ~/.local/bin/cask`) caskApps.length && log(`Finished creating Homebrew cask links in ~/.local/bin/cask`)
} }
async function createFlatpakLinks() { async function createFlatpakLinks(flatpakMap) {
const flatpakInstallations = await $`flatpak --installations` const flatpakInstallations = await $`flatpak --installations`
const flatpakDir = flatpakInstallations.stdout.replace('\n', '') const flatpakDir = flatpakInstallations.stdout.replace('\n', '')
const flatpakApps = pkgMap(pkgs) const flatpakApps = flatpakMap
.filter(x => { .filter(x => {
if (x.installType === 'flatpak') { if (x.installType === 'flatpak') {
const binField = getPkgData('_bin', x, x.installType) const binField = getPkgData('_bin', x, x.installType)
@ -189,6 +202,10 @@ async function createFlatpakLinks() {
flatpakApps.length && await $`mkdir -p "$HOME/.local/bin/flatpak"` flatpakApps.length && await $`mkdir -p "$HOME/.local/bin/flatpak"`
for (const app of flatpakApps) { for (const app of flatpakApps) {
const binField = getPkgData('_bin', app, app.installType) const binField = getPkgData('_bin', app, app.installType)
if (!binField) {
log(`${app.listKey} is missing a _bin definition`)
return
}
if (fs.existsSync(`${flatpakDir}/app/${app.installList[0]}`)) { if (fs.existsSync(`${flatpakDir}/app/${app.installList[0]}`)) {
fs.writeFileSync(`${os.homedir()}/.local/bin/flatpak/${app[binField]}`, `#!/usr/bin/env bash\nflatpak run ${app.installList[0]} $*`) fs.writeFileSync(`${os.homedir()}/.local/bin/flatpak/${app[binField]}`, `#!/usr/bin/env bash\nflatpak run ${app.installList[0]} $*`)
await $`chmod +x '${os.homedir()}/.local/bin/flatpak/${app[binField]}'` await $`chmod +x '${os.homedir()}/.local/bin/flatpak/${app[binField]}'`
@ -199,15 +216,21 @@ async function createFlatpakLinks() {
flatpakApps.length && log(`Finished creating Flatpak links in ~/.local/bin/flatpak`) flatpakApps.length && log(`Finished creating Flatpak links in ~/.local/bin/flatpak`)
} }
async function bundleInstall(brews, casks) { async function bundleInstall(brews, casks, caskMap) {
try { try {
const lines = [] const lines = []
log(`Adding following casks to Brewfile for installation: ${casks.join(' ')}`) casks.length && log(`Adding following casks to Brewfile for installation: ${casks.join(' ')}`)
for (const cask of casks) { for (const cask of casks) {
if (cask.indexOf('/') !== -1) {
lines.push(`tap "${cask.substring(0, cask.lastIndexOf('/'))}"`)
}
lines.push(`cask "${cask}"`) lines.push(`cask "${cask}"`)
} }
log(`Adding following brews to Brewfile for installation: ${casks.join(' ')}`) brews.length && log(`Adding following brews to Brewfile for installation: ${brews.join(' ')}`)
for (const brew of brews) { for (const brew of brews) {
if (brew.indexOf('/') !== -1) {
lines.push(`tap "${brew.substring(0, brew.lastIndexOf('/'))}"`)
}
lines.push(`brew "${brew}"`) lines.push(`brew "${brew}"`)
} }
log(`Creating Brewfile to install from`) log(`Creating Brewfile to install from`)
@ -215,8 +238,9 @@ async function bundleInstall(brews, casks) {
log(`Installing packages via brew bundle`) log(`Installing packages via brew bundle`)
await $`brew bundle --file Brewfile` await $`brew bundle --file Brewfile`
log(`Finished installing via Brewfile`) log(`Finished installing via Brewfile`)
await createCaskLinks() await createCaskLinks(caskMap)
} catch (e) { } catch (e) {
console.log('Error:', e)
log(`Error occurred while installing via Brewfile`) log(`Error occurred while installing via Brewfile`)
} }
} }
@ -239,7 +263,7 @@ async function installPackages(pkgInstructions) {
} }
log(`Running Homebrew installation via Brewfile`) log(`Running Homebrew installation via Brewfile`)
if ((combined.brew && combined.brew.length) || (combined.cask && combined.cask.length)) { if ((combined.brew && combined.brew.length) || (combined.cask && combined.cask.length)) {
promises.push(bundleInstall(combined.brew ? combined.brew.flatMap(x => x.installList.flatMap(i => i)) : [], combined.cask ? combined.cask.flatMap(x => x.installList.flatMap(i => i)) : [])) promises.push(bundleInstall(combined.brew ? combined.brew.flatMap(x => x.installList.flatMap(i => i)) : [], combined.cask ? combined.cask.flatMap(x => x.installList.flatMap(i => i)) : [], combined.cask))
} }
for (const key of Object.keys(combined)) { for (const key of Object.keys(combined)) {
if (key !== 'script') { if (key !== 'script') {
@ -272,13 +296,15 @@ async function installPackages(pkgInstructions) {
case 'crew': case 'crew':
case 'gem': case 'gem':
case 'go': case 'go':
case 'npm':
case 'pip': case 'pip':
case 'pipx': case 'pipx':
case 'scoop': // Maybe needs forEachSeries case 'scoop': // Maybe needs forEachSeries
case 'winget': // Maybe needs forEachSeries case 'winget': // Maybe needs forEachSeries
promises.push(...combined[key].flatMap(x => x.installList.flatMap(i => $`${key} install ${i}`))) promises.push(...combined[key].flatMap(x => x.installList.flatMap(i => $`${key} install ${i}`)))
break break
case 'npm':
promises.push(...combined[key].flatMap(x => x.installList.flatMap(i => $`${key} install -g ${i}`)))
break
case 'binary': case 'binary':
// TODO // TODO
promises.push(...combined[key].flatMap(x => x.installList.flatMap(i => $`TMP="$(mktemp)" && curl -sSL ${i} > "$TMP" && sudo mv "$TMP" /usr/local/src/${x._bin} && chmod +x /usr/local/src/${x._bin}`))) promises.push(...combined[key].flatMap(x => x.installList.flatMap(i => $`TMP="$(mktemp)" && curl -sSL ${i} > "$TMP" && sudo mv "$TMP" /usr/local/src/${x._bin} && chmod +x /usr/local/src/${x._bin}`)))
@ -337,16 +363,18 @@ async function installPackages(pkgInstructions) {
log(`Unable to find install key instructions for ${key}`) log(`Unable to find install key instructions for ${key}`)
} }
} }
log(`Performing ${promises.length} installations`)
process.env.DEBUG && console.log('Queued installs:', promises)
const installs = await Promise.allSettled(promises) const installs = await Promise.allSettled(promises)
log(`All of the installations have finished`) log(`All of the installations have finished`)
process.env.DEBUG && console.log('Installs:', installs) process.env.DEBUG && console.log('Completed installs:', installs)
await postInstall(combined) await postInstall(combined)
} }
async function postInstall(combined) { async function postInstall(combined) {
log(`Running post-install routine`) log(`Running post-install routine`)
const promises = [] const promises = []
Object.keys(combined).includes('flatpak') && promises.push(createFlatpakLinks()) Object.keys(combined).includes('flatpak') && promises.push(createFlatpakLinks(combined.flatpak))
const postInstalls = await Promise.allSettled(promises) const postInstalls = await Promise.allSettled(promises)
process.env.DEBUG && console.log('Post installs:', postInstalls) process.env.DEBUG && console.log('Post installs:', postInstalls)
} }
@ -408,7 +436,7 @@ async function main() {
sysType = initData[2] sysType = initData[2]
installOrder = initData[1].installerPreference installOrder = initData[1].installerPreference
log(`Populating lists of pre-installed packages`) log(`Populating lists of pre-installed packages`)
const lists = [ const listPromises = [
acquireManagerList('apt', `if command -v dpkg; then dpkg -l; fi`), acquireManagerList('apt', `if command -v dpkg; then dpkg -l; fi`),
acquireManagerList('brew', `brew list -1`), acquireManagerList('brew', `brew list -1`),
acquireManagerList('cargo', `cargo install --list | awk '/^[[:alnum:]]/ {print $1}'`), acquireManagerList('cargo', `cargo install --list | awk '/^[[:alnum:]]/ {print $1}'`),
@ -422,6 +450,7 @@ async function main() {
acquireManagerList('snap', `if command -v snapd; then snap list; fi`), acquireManagerList('snap', `if command -v snapd; then snap list; fi`),
acquireManagerList('zap', `zap list`) acquireManagerList('zap', `zap list`)
] ]
const lists = await Promise.all(listPromises)
const managerLists = { const managerLists = {
appimage: lists[6], appimage: lists[6],
apt: lists[0], apt: lists[0],
@ -445,30 +474,46 @@ async function main() {
const installData = pkgMap(installKeys) const installData = pkgMap(installKeys)
log(`Filtering install instructions`) log(`Filtering install instructions`)
const installInstructions = installData const installInstructions = installData
.map(x => {
return {
...x,
installList: x.installList.filter(y => {
if ((x.installType === 'brew' || x.installType === 'cask') && y.includes('/')) {
return managerLists[x.installType] ? !managerLists[x.installType].includes(y.substring(y.lastIndexOf('/') + 1, y.length)) : true
} else {
return managerLists[x.installType] ? !managerLists[x.installType].includes(y) : true
}
})
}
})
.filter(x => { .filter(x => {
// Filter out packages already installed by by package managers // Filter out packages already installed by by package managers
return !Object.keys(managerLists).includes(x.installType) return x.installList.length
}) })
.filter(x => { .filter(x => {
// Filter out macOS apps that already have a _app installed // Filter out macOS apps that already have a _app installed
if (x.installType === 'cask' || (osId === 'darwin' && x._app)) { if (x.installType === 'cask' || (osId === 'darwin' && x._app)) {
const appField = getPkgData('_app', x, x.installType) const appField = getPkgData('_app', x, x.installType)
return !(fs.existsSync(`/Applications/${x[appField]}`) || fs.existsSync(`${os.homedir()}/Applications/${x[appField]}`)) const appCheck = fs.existsSync(`/Applications/${x[appField]}`) || fs.existsSync(`${os.homedir()}/Applications/${x[appField]}`)
} appCheck && log(`Skipping installation of ${x.listKey} because the application is in an Applications folder`)
return !appCheck
} else {
return true return true
}
}) })
.filter(x => { .filter(x => {
// Filter out packages that already have a bin in the PATH // Filter out packages that already have a bin in the PATH
const binField = getPkgData('_bin', x, x.installType) const binField = getPkgData('_bin', x, x.installType)
const isArray = Array.isArray(x[binField]) const isArray = Array.isArray(x[binField])
if (typeof x[binField] === 'string' || isArray) { if (typeof x[binField] === 'string' || isArray) {
if (isArray) { isArray && log(`_bin field for ${x.listKey} is an array so the first entry will be used to check`)
log(`_bin field for ${x.listKey} is an array so the first entry will be used to check`) const whichCheck = which.sync(typeof x[binField] === 'string' ? x[binField] : x[binField][0], { nothrow: true })
} whichCheck && log(`Skipping installation of ${x.listKey} because its binary is available in the PATH`)
return !(which.sync(typeof x[binField] === 'string' ? x[binField] : x[binField][0], { nothrow: true })) return !whichCheck
} } else {
log(`Ignoring _bin check because the _bin field for ${x.listKey} is not a string or array`) log(`Ignoring _bin check because the _bin field for ${x.listKey} is not a string or array`)
return true return true
}
}) })
.filter(x => { .filter(x => {
// Filter out packages that do not pass _when check // Filter out packages that do not pass _when check

View file

@ -1748,7 +1748,7 @@ softwarePackages:
scoop: cloc scoop: cloc
clocker: clocker:
_app: Clocker.app _app: Clocker.app
_bin: null _bin: clocker
_desc: Clocker is designed to help you keep track of your friends and colleagues in different time zones. _desc: Clocker is designed to help you keep track of your friends and colleagues in different time zones.
_github: https://github.com/n0shake/clocker _github: https://github.com/n0shake/clocker
_name: "Clocker " _name: "Clocker "
@ -11998,7 +11998,6 @@ softwarePackages:
_name: upt _name: upt
_short: "upt is a lightweight uptime monitor written in Go. " _short: "upt is a lightweight uptime monitor written in Go. "
cargo: upt cargo: upt
"cargo:": upt
upx: upx:
_bin: upx _bin: upx
_desc: "[UPX](https://upx.github.io/) is an advanced executable file compressor. UPX will typically reduce the file size of programs and DLLs by around 50%-70%, thus reducing disk space, network load times, download times and other distribution and storage costs. It supports compressing a wide variety of binary-like files. Surprisingly, it even compresses executables better than WinZip. Best of all, it is free and open source." _desc: "[UPX](https://upx.github.io/) is an advanced executable file compressor. UPX will typically reduce the file size of programs and DLLs by around 50%-70%, thus reducing disk space, network load times, download times and other distribution and storage costs. It supports compressing a wide variety of binary-like files. Surprisingly, it even compresses executables better than WinZip. Best of all, it is free and open source."