An interactive command-line interface (CLI) tool to help you install, use, and administer an AO instance.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

198 lines
6.4 KiB

// Functions related to OS and installing software
import chalk from 'chalk'
import { execSync } from 'child_process'
// Exits ao-cli if has been run with sudo or while logged in as root/superuser using su
export function exitIfRoot() {
try {
let loggedInUserUnixID = parseInt(execSync('echo $EUID').toString())
if(loggedInUserUnixID === 0) {
console.log(`${chalk.red.bold(exclaim())} Seems you're running this script as a superuser.`)
console.log('That might cause some issues with permissions and whatnot. Run this script as your default user (without sudo) and I\'ll ask you when I need superuser permissions.')
process.exit(1)
}
} catch(err) {
console.log('Error checking whether or not you are root. Continuing but please do not run ao-cli as root or sudo! It will install things with admin privileges and then you won\'t be able to access them.')
}
}
// Detects the operating system we are running on
let distro
export function detectOS() {
if(distro) {
return distro
}
try {
execSync('[ -f "/etc/debian_version" ]')
distro = 'debian'
console.log(`${greenChalk('Debian')}, Ubuntu, or Raspbian OS detected.`)
} catch(err) {}
try {
execSync('[ -f "/etc/arch-release" ]')
distro = 'arch'
console.log(`${greenChalk('Arch or Manjaro-based')} OS detected.`)
} catch(err) {}
try {
execSync('[ -f "/etc/fedora-release" ]')
distro = 'fedora'
console.log(`${greenChalk('Fedora')} OS detected.`)
} catch(err) {}
try {
execSync('[ $(uname | grep -c "Darwin") -eq 1 ]')
distro = 'mac'
console.log(`${greenChalk('MacOS')} detected.`)
} catch(err) {}
if(!distro) {
console.log("Your OS was not recognized, sorry.")
process.exit(1)
}
return distro
}
// Runs the correct command to update all your software for any recognized OS
export function updateSoftware() {
detectOS()
if(!distro) {
console.log("Your OS was not recognized, so nothing was updated, sorry.")
return false
}
console.log('Updating your software from repositories...')
console.log(`(You may need to input your ${chalk.blue.bold("'sudo' password")} here)`)
switch(distro) {
case 'debian':
execSync('sudo apt update -yqqq && sudo apt autoremove -yqqq && sudo apt upgrade -yqqq')
break
case 'arch':
execSync('sudo pacman -Syu --noconfirm')
break
case 'fedora':
execSync('sudo dnf update -yqqq 2>/dev/null && && sudo dnf autoremove -yqqq && sudo dnf upgrade -yqqq')
break
case 'mac':
// Install homebrew (todo: if not installed)
execSync('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"')
execSync('install && sudo brew update') // is this right? can it replicate all commands such as update upgrade autoremove?
break
}
return true
}
// Checks if the given package is installed using the standard repo for your detected OS
export function isInstalled(packageName, group) {
detectOS()
if(!distro) {
console.log("Your OS was not recognized, so nothing was updated, sorry.")
return false
}
switch(distro) {
case 'debian':
return execSync("dpkg-query -W -f='${Status}' " + packageName + " 2>/dev/null").includes('ok installed')
case 'arch':
try {
if(!group) {
const stdout = execSync("pacman -Qi " + packageName)
if(stdout.includes('was not found')) {
return false
}
} else {
const stdout = execSync("pacman -Qg " + packageName).toString()
if(!stdout.includes(packageName)) {
return false
}
}
return true
} catch(error) {
break
}
case 'fedora':
case 'mac':
}
}
// Uses the standard app repo for your detected OS to install the named package
function installPackage(packageName) {
detectOS()
if(!distro) {
console.log("Your OS was not recognized, so nothing was updated, sorry.")
return false
}
switch(distro) {
case 'debian':
try {
execSync('sudo apt install -y ' + packageName)
return true
} catch(error) {
break
}
case 'arch':
try {
const result = execSync("sudo pacman -S " + packageName + " --noconfirm").toString()
console.log(packageName, 'installed result is', result)
return true
} catch(error) {
break
}
case 'fedora':
case 'mac':
console.log('Install on this OS not yet implemented, sorry.')
}
console.log('Failed to install', packageName + '. Error:', error)
return false
}
// Installs the specified package or packages using the standard repos for your detected OS. Prints console messages if verbose.
export function installIfNotInstalled(packageNameOrNames, verbose = true, group = false) {
if(!Array.isArray(packageNameOrNames)) {
if(typeof packageNameOrNames !== 'string') {
if(verbose) console.log('Invalid package name provided. Doing nothing.')
return null
}
packageNameOrNames = [ packageNameOrNames ]
}
let packagesInstalled = 0
let packagesFailed = 0
packageNameOrNames.forEach(packageName => {
if(!isInstalled(packageName, group)) {
const success = installPackage(packageName)
if(success) {
if(verbose) console.log('Installed', packageName + '.')
packagesInstalled++
} else {
if(verbose) console.log('Failed to install', packageName + '.')
packagesFailed++
}
} else {
if(verbose) console.log(packageName, 'already installed.')
}
})
return { installed: packagesInstalled, failed: packagesFailed }
}
// Returns true if npm install (npm i) has been run in the given path, otherwise false. Also returns false if the folder does not exist.
// Note that npm packages are totally different from (normal) packages installed the OS's bundled package manager. They are node packages.
export async function isNpmPackageInstalled(path, verbose = false) {
try {
const stdout = execSync('cd ' + folderPath + ' && npm list 2>&1')
return true
} catch(error) {
const missingLocalPackagesCount = error.output.toString().match(/npm ERR! missing:/g).length || 0
if(missingLocalPackagesCount) {
if(verbose) console.log(path, 'has', missingLocalPackagesCount, 'npm packages left to install.')
} else {
if(verbose) console.log('Error while checking if npm package at', path, 'is installed:', error)
}
}
return false
}