@installdoc/ansible-gas-station
Version:
An Ansible playbook that provisions your network with software from GitHub Awesome lists, developed with disaster recovery in mind ⛽🔥🤤
222 lines (196 loc) • 6.54 kB
JavaScript
/* eslint-disable functional/immutable-data, fp/no-mutating-methods, jsdoc/require-returns-type */
import inquirer from 'inquirer'
import { execSync } from 'node:child_process'
import { readdirSync } from 'node:fs'
import { decorateSystem } from './lib/decorate-system.js'
import { logInstructions, LOG_DECORATOR_REGEX } from './lib/log.js'
const platformMap = {
'Hyper-V': 'hyperv',
KVM: 'libvirt',
Parallels: 'parallels',
'VMWare Fusion': 'vmware_fusion',
'VMWare Workstation': 'vmware_workstation',
VirtualBox: 'virtualbox'
}
const options = {
stdio: 'ignore'
}
const optionsPowerShell = {
shell: 'powershell.exe',
stdio: 'ignore'
}
/**
* Executes a command synchronously via the terminal shell
*
* @param {*} cmd - Command to execute
* @returns Status code from the command executed
*/
const exec = (cmd) => execSync(cmd, options)
/**
* Executes a command synchronously via PowerShell
*
* @param {*} cmd - Command to execute on PowerShell
* @returns Status code from the command executed
*/
const execPowerShell = (cmd) => execSync(cmd, optionsPowerShell)
/**
* Checks if a program is installed
*
* @param {*} program - Program to check
* @returns A boolean indicator of whether or not the program is installed
*/
const isUnixInstalled = (program) => {
if (process.platform === 'linux') {
// eslint-disable-next-line functional/no-try-statement
try {
exec(`hash ${program} 2>/dev/null`)
return true
} catch {
return false
}
} else {
return false
}
}
/**
* Checks if an application is installed and has a `*.desktop` link
*
* @param {*} program - Program to look for
* @returns A boolean indicator of whether or not the program is installed
*/
const isDotDesktopInstalled = (program) => {
if (process.platform === 'linux') {
const directories = [
process.env.XDG_DATA_HOME && `${process.env.XDG_DATA_HOME}/applications`,
process.env.HOME && `${process.env.HOME}/.local/share/applications`,
'/usr/share/applications',
'/usr/local/share/applications'
]
.filter(Boolean)
.filter((path) => Boolean(readdirSync(path)))
const desktopFiles = directories
.flatMap((directory) => readdirSync(directory))
.filter((file) => file.endsWith('.desktop'))
.map((item) => item.replace(/\.desktop$/u, ''))
return desktopFiles.includes(program.replace(/\.desktop$/u, ''))
}
return false
}
/**
* Determines if a program is installed on a Darwin-based OS
*
* @param {*} program - Program to check for
* @returns A boolean indicating if the program is installed
*/
const isMacInstalled = (program) => {
if (process.platform === 'darwin') {
return Boolean(exec(`osascript -e 'id of application "${program}"' 2>&1>/dev/null`))
}
return false
}
/**
* Checks if program is installed in a Windows environment
*
* @param {*} program - Program to check for
* @returns A boolean indicating whether or not the program is installed
*/
const isWindowsInstalled = (program) => {
if (process.platform === 'win32') {
/*
* Try a couple variants, depending on execution environment the .exe
* may or may not be required on both `where` and the program name.
*/
const attempts = [`where ${program}`, `where ${program}.exe`, `where.exe ${program}`, `where.exe ${program}.exe`]
return attempts.map((attempt) => Boolean(exec(attempt))).includes(true)
}
return false
}
/**
* Combines several methods for determining whether or not a program
* is installed.
*
* @param {*} program - Program to check for
* @returns A boolean indicating whether or not a program is installed
*/
const isInstalled = (program) =>
[isUnixInstalled, isMacInstalled, isWindowsInstalled, isDotDesktopInstalled].some((check) => check(program))
/**
* Prompts the user for the operating system they wish to launch and test the
* Ansible play against.
*
* @returns The lowercased operating system choice
*/
async function promptForDesktop() {
const choices = ['Archlinux', 'CentOS', 'Debian', 'Fedora', 'macOS', 'Ubuntu', 'Windows']
const choicesDecorated = choices.map((choice) => decorateSystem(choice))
const response = await inquirer.prompt([
{
choices: choicesDecorated,
message: 'Which desktop operating system would you like to provision?',
name: 'operatingSystem',
type: 'list'
}
])
const DECORATION_LENGTH = 2
return response.operatingSystem.replace(LOG_DECORATOR_REGEX, '').toLowerCase().slice(DECORATION_LENGTH)
}
/**
* Prompts the user for the virtualization platform they wish to use. Before presenting
* the options some basic verification is done to ensure that only the options available
* on the system are presented.
*
* @returns The chosen virtualization provider
*/
// eslint-disable-next-line max-statements, require-jsdoc
async function promptForPlatform() {
const choices = []
if (
process.platform === 'win32' &&
execPowerShell(
'$hyperv = Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-All -Online; $hyperv.State -eq "Enabled"'
)
) {
choices.push('Hyper-V')
}
if ((process.platform === 'darwin' || process.platform === 'linux') && isInstalled('kvm')) {
choices.push('KVM')
}
if (process.platform === 'darwin' && isInstalled('Parallels Desktop.app')) {
choices.push('Parallels')
}
if (isInstalled('virtualbox')) {
choices.push('VirtualBox')
}
if (process.platform === 'darwin' && isInstalled('VMware Fusion.app')) {
choices.push('VMWare Fusion')
}
if (process.platform !== 'darwin' && isInstalled('vmware')) {
choices.push('VMWare Workstation')
}
const response = await inquirer.prompt([
{
choices,
message: 'Which virtualization platform would you like to use?',
name: 'virtualizationPlatform',
type: 'list'
}
])
return platformMap[response.virtualizationPlatform]
}
/**
* Main script logic
*/
async function run() {
logInstructions(
'Launch VM via Vagrant',
'Use the following prompts to select the type of operating system and' +
' the virtualization platform you wish to use with Vagrant. After you make your choice' +
' the corresponding environment will be provisioned with Vagrant.'
)
const operatingSystem = await promptForDesktop()
const virtualizationPlatform = await promptForPlatform()
execSync(`task ansible:test:vagrant:cli -- --provider=${virtualizationPlatform} ${operatingSystem}`, {
stdio: 'inherit'
})
}
run()