Browse Source

prettier install wizard

main
deicidus 2 years ago
parent
commit
9a6a27e306
  1. 6
      index.js
  2. 6
      scripts/ao.js
  3. 18
      scripts/features/index.js
  4. 2
      scripts/strings.js
  5. 26
      scripts/styles.js
  6. 2
      scripts/welcome.js
  7. 68
      scripts/wizard.js

6
index.js

@ -177,14 +177,16 @@ async function main() {
exitIfRoot() exitIfRoot()
// Loading screen, display some quick info during the fun animation // Loading screen, display some quick info during the fun animation
console.log('ao-cli v' + (await aoCli.version()))
const DISTRO = aoEnv('DISTRO') const DISTRO = aoEnv('DISTRO')
if(!DISTRO) { if(!DISTRO) {
setAoEnv('DISTRO', detectOS()) setAoEnv('DISTRO', detectOS())
} }
const friendlyEnvPath = AO_ENV_FILE_PATH.replace(/^\/home\/\w+/, '~')
if(checkAoEnvFile()) { if(checkAoEnvFile()) {
console.log('AO .env file exists at', AO_ENV_FILE_PATH) console.log(friendlyEnvPath, 'exists')
} else { } else {
console.log('AO .env file does not exist at', AO_ENV_FILE_PATH) console.log(friendlyEnvPath, 'does not exist')
} }
await unicornPortal(650) await unicornPortal(650)

6
scripts/ao.js

@ -85,11 +85,11 @@ async function chatMenu() {
// They previously enabled public bootstrapping, so check to make sure it is working and then hide the option // They previously enabled public bootstrapping, so check to make sure it is working and then hide the option
// todo: start and then verify functioning of p2p boostrap method here // todo: start and then verify functioning of p2p boostrap method here
// if it's already started don't start it again // if it's already started don't start it again
if(!publicBootstrapStarted) { //if(!publicBootstrapStarted) {
console.log("\nBootstrapping public AO swarm...") console.log("\nBootstrapping public AO swarm...")
startPublicBootstrap() startPublicBootstrap()
console.log("Bootstrapped (just kidding)") console.log("Bootstrapped (just kidding)")
} //}
//answers['chat_menu'] = 'Enable p2p bootstrap' //answers['chat_menu'] = 'Enable p2p bootstrap'
} }
const chatChoices = [ const chatChoices = [
@ -130,4 +130,4 @@ async function chatMenu() {
return false return false
} }
return true return true
} }

18
scripts/features/index.js

@ -6,7 +6,7 @@ import fs from 'fs'
import { lsFolder } from '../files.js' import { lsFolder } from '../files.js'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import path from 'path' import path from 'path'
import { headerStyle, greenChalk } from '../styles.js' import { headerStyle, greenChalk, styledStatus } from '../styles.js'
import { spinner } from '../console.js' import { spinner } from '../console.js'
const __filename = fileURLToPath(import.meta.url) const __filename = fileURLToPath(import.meta.url)
@ -29,20 +29,6 @@ const loadFeatures = async () => {
const features = await loadFeatures() const features = await loadFeatures()
export default features export default features
// Returns a colored capitalized status word
const styledStatus = (fullWord) => {
const lookup = {
unknown: ' ' + chalk.grey('Unknown') + ' ',
off: ' ' + chalk.grey('Off') + ' ',
installed: chalk.blue('Installed') + ' ',
enabled: ' ' + greenChalk('Enabled') + ' ',
running: ' ' + greenChalk('Running') + ' ',
synced: ' ' + greenChalk('Synced') + ' ',
error: ' ' + chalk.red('Error') + ' '
}
return lookup[fullWord.toLowerCase()] + ' '
}
// Prints the Configure AO Features menu and executes the user's choice // Prints the Configure AO Features menu and executes the user's choice
let featuresChoices let featuresChoices
export async function featuresMenu(previousMenuChoice = 0) { export async function featuresMenu(previousMenuChoice = 0) {
@ -60,7 +46,7 @@ export async function featuresMenu(previousMenuChoice = 0) {
if(status !== 'Unknown') { if(status !== 'Unknown') {
loadedFeatures++ loadedFeatures++
} }
const statusColumn = styledStatus(status) const statusColumn = styledStatus(status) + ' '
const descriptionColumn = feature.description || '' const descriptionColumn = feature.description || ''
const choice = { name: nameColumn + statusColumn + descriptionColumn, value: featureKey, short: featureKey} const choice = { name: nameColumn + statusColumn + descriptionColumn, value: featureKey, short: featureKey}
return choice return choice

2
scripts/strings.js

@ -15,6 +15,8 @@ export const repeatString = (str, n) => {
return new Array(1 + (n || 0)).join(str) return new Array(1 + (n || 0)).join(str)
} }
String.prototype.repeat = repeatString
String.prototype.centerInConsole = function (width = 80) { String.prototype.centerInConsole = function (width = 80) {
const consoleWidth = process.stdout.columns const consoleWidth = process.stdout.columns
const padding = Math.floor((consoleWidth - width) / 2) const padding = Math.floor((consoleWidth - width) / 2)

26
scripts/styles.js

@ -1,4 +1,5 @@
import chalk from 'chalk' import chalk from 'chalk'
import { repeatString } from './strings.js'
// Chalk styles // Chalk styles
export const greenChalk = chalk.hex('#008800') export const greenChalk = chalk.hex('#008800')
@ -10,3 +11,28 @@ export const heading2 = chalk.underline
// Preformatted phrases that can be used in backticked console.log strings // Preformatted phrases that can be used in backticked console.log strings
export const theAO = `the ${greenChalk.bold('AO')}` export const theAO = `the ${greenChalk.bold('AO')}`
export const theMenu = `the ${greenChalk.bold('Menu')}` export const theMenu = `the ${greenChalk.bold('Menu')}`
// Returns a colored capitalized status word
export const styledStatus = (fullWord, width) => {
const title = fullWord.toTitleCase()
const lookup = {
unknown: chalk.grey(title),
off: chalk.grey(title),
'not installed': chalk.grey(title),
blank: chalk.grey(title),
downloaded: chalk.yellow(title),
installed: chalk.blue(title),
initialized: chalk.green(title),
created: chalk.green(title),
enabled: greenChalk(title),
running: greenChalk(title),
synced: greenChalk(title),
error: chalk.red(title),
missing: chalk.red(title)
}
let styledWord = lookup[fullWord.toLowerCase()]
const halfDiff = (width - fullWord.length) / 2
const left = halfDiff % 2 === 0 ? repeatString(' ', halfDiff) : repeatString(' ', Math.ceil(halfDiff))
const right = repeatString(' ', Math.floor(halfDiff))
return left + styledWord + right
}

2
scripts/welcome.js

@ -22,7 +22,7 @@ const welcomeMessages = [
`Dipping your brush in ink, you draw a perfect circle. This is ${theAO}.`, `Dipping your brush in ink, you draw a perfect circle. This is ${theAO}.`,
`You are offered a choice between two pills. However, you have secretly built up an immunity to both pills, and trick your opponent into taking one. Inconceviably, you are in ${theAO}.`, `You are offered a choice between two pills. However, you have secretly built up an immunity to both pills, and trick your opponent into taking one. Inconceviably, you are in ${theAO}.`,
`A young man with spiky hair and golden skin appears before you in a halo of light. He guides you to ${theAO}.`, `A young man with spiky hair and golden skin appears before you in a halo of light. He guides you to ${theAO}.`,
`You enter a gap between two hedges and, after struggling through the brush, emerge into a sunny estate garden. You've found the AO.`, `Looking for a shortcut, you worm your way through through the hedges, and, after struggling through the brush, emerge into a sunny estate garden. You've found the AO.`,
`You find a small animal burrow dug-out near the riverside. Crawling in, you find a network of caves that lead to ${theAO}.`, `You find a small animal burrow dug-out near the riverside. Crawling in, you find a network of caves that lead to ${theAO}.`,
`You receive a handwritten letter in the mail, which reads, in fine calligraphy:, "Dear —, You are in ${theAO}."`, `You receive a handwritten letter in the mail, which reads, in fine calligraphy:, "Dear —, You are in ${theAO}."`,
`An unexpected calm settles over you. The ${greenChalk.bold('AO')} is here: Here is ${theAO}.`, `An unexpected calm settles over you. The ${greenChalk.bold('AO')} is here: Here is ${theAO}.`,

68
scripts/wizard.js

@ -7,10 +7,12 @@ import { detectOS, updateSoftware, isInstalled } from './system.js'
import { isFolder, isFile } from './files.js' import { isFolder, isFile } from './files.js'
import { aoIsInstalled } from './features/ao-server.js' import { aoIsInstalled } from './features/ao-server.js'
import { asciiArt } from './console.js' import { asciiArt } from './console.js'
import { headerStyle, heading2 } from './styles.js' import { headerStyle, heading2, styledStatus } from './styles.js'
import features from './features/index.js' import features from './features/index.js'
import { yesOrNo } from './welcome.js'
const AO_MEMES_PATH = path.join(process.env.HOME, '.ao/memes') const AO_PATH = path.join(process.env.HOME, '.ao')
const AO_MEMES_PATH = path.join(AO_PATH, 'memes')
const commonPackages = ['curl', 'wget', 'git', 'make', 'sqlite3', 'python', 'autoconf-archive'] const commonPackages = ['curl', 'wget', 'git', 'make', 'sqlite3', 'python', 'autoconf-archive']
const debianPackages = ['build-essential', 'zlib1g-dev', 'libtool-bin', 'autoconf', 'automake autotools-dev', 'libgmp-dev', 'libsqlite3-dev', 'python3', 'python3-mako', 'libsodium-dev', 'pkg-config', 'libev-dev', 'libcurl4-gnutls-dev', 'libssl-dev', 'fakeroot', 'devscripts'] const debianPackages = ['build-essential', 'zlib1g-dev', 'libtool-bin', 'autoconf', 'automake autotools-dev', 'libgmp-dev', 'libsqlite3-dev', 'python3', 'python3-mako', 'libsodium-dev', 'pkg-config', 'libev-dev', 'libcurl4-gnutls-dev', 'libssl-dev', 'fakeroot', 'devscripts']
@ -183,37 +185,57 @@ export function checkRequired() {
} }
// Prints out a summary of the AO's installation status. Returns true if the AO is installed (Standard + ao-svelte or ao-3, not ao-cli-only) // Prints out a summary of the AO's installation status. Returns true if the AO is installed (Standard + ao-svelte or ao-3, not ao-cli-only)
export function checkAo() { export async function checkAo() {
const width = 24 const width = 19
const columnize = (status) => styledStatus(status, width)
// Print status of each required package individually // Print status of each required package individually
console.log(`\n${heading2('Required Software Packages')}`) console.log(`\n${heading2('Required Software Packages')}`)
const summary = checkRequired() const summary = Object.entries(checkRequired())
Object.entries(summary).forEach(([packageName, isInstalled]) => { let installedRequirementCount = 0
console.log(packageName.padEnd(width) + (isInstalled ? 'Installed' : 'Missing')) summary.forEach(([packageName, isInstalled]) => {
if(isInstalled) installedRequirementCount++
console.log(packageName.padEnd(width) + columnize(isInstalled ? 'Installed' : 'Missing'))
}) })
const prerequisitesInstalled = Object.entries(summary).every(([packageName, isInstalled]) => isInstalled) const prerequisitesInstalled = summary.every(([packageName, isInstalled]) => isInstalled)
// Check for existence of required directories // Check for existence of required directories
console.log(`\n${heading2('Folders & Config File')}`) console.log(`\n${heading2('Folders & Config File')}`)
const requiredDirectoriesExist = checkAoDirectories() const requiredDirectoriesExist = checkAoDirectories()
console.log('~/.ao & ~/.ao/memes'.padEnd(width) + (requiredDirectoriesExist ? ' Created ' : 'Missing')) console.log('~/.ao'.padEnd(width) + (isFolder(AO_PATH) ? columnize('Created') : columnize('Missing')))
// Check for .env file // Check for .env file
const aoEnvFilePath = path.join(process.env.HOME, '.ao/.env') const aoEnvFilePath = path.join(process.env.HOME, '.ao/.env')
const hasEnvFile = isFile(aoEnvFilePath) const hasEnvFile = isFile(aoEnvFilePath)
console.log('~/.ao/.env'.padEnd(width) + (hasEnvFile ? 'Initialized' : 'Blank'), '\n') console.log('~/.ao/.env'.padEnd(width) + columnize(hasEnvFile ? 'Initialized' : 'Blank'))
console.log('~/.ao/memes'.padEnd(width) + columnize(requiredDirectoriesExist ? 'Created' : 'Missing'))
// Check for ao-server folder with node_modules folder (can do a better check?) // Check for node project folders and locally installed packages (npm i)
console.log(`\n${heading2('AO Server + Client Version Installed')}`) console.log(`\n${heading2('AO Server + Client Version Installed')}`)
const homePathsToCheck = ['ao-server', 'ao-svelte', 'ao-3'] const homePathsToCheck = ['ao-server', 'ao-svelte', 'ao-3']
let homePathsExist = [] let homePathsExist = []
let npmInstalled = []
homePathsToCheck.forEach(folderName => { homePathsToCheck.forEach(folderName => {
const nodeModulesPath = path.join(process.env.HOME, folderName, 'node_modules') const folderPath = path.join(process.env.HOME, folderName)
const exists = isFolder(nodeModulesPath) const exists = isFolder(folderPath)
console.log(folderName.padEnd(width) + (exists ? 'Installed' : 'Not Installed')) let npmI = false
if(exists) { if(exists) {
homePathsExist.push(folderName) homePathsExist.push(folderName)
try {
const stdout = execSync('cd ' + folderPath + ' && npm list 2>&1')
npmInstalled.push(folderName)
npmI = true
} catch(error) {
const missingLocalPackagesCount = error.output.toString().match(/npm ERR! missing:/g).length || 0
if(missingLocalPackagesCount) {
// Confirmation that not all dependencies have been installed
//console.log(folderName, 'has', missingLocalPackagesCount, 'npm packages left to install.')
} else {
// Unknown error
}
}
} }
console.log(folderName.padEnd(width) + columnize(exists ? npmI ? 'Installed' : 'Downloaded' : 'Not Installed'))
}) })
const hasAnAo = (homePathsExist.includes('ao-server') && homePathsExist.includes('ao-svelte')) || homePathsExist.includes('ao-3') const hasAnAo = (homePathsExist.includes('ao-server') && homePathsExist.includes('ao-svelte')) || homePathsExist.includes('ao-3')
@ -224,7 +246,7 @@ export function checkAo() {
const name = feature?.name || shortname const name = feature?.name || shortname
const isInstalled = feature.hasOwnProperty('isInstalled') ? feature.isInstalled() : ['installed', 'enabled', 'running', 'synced'].includes(feature.status()) const isInstalled = feature.hasOwnProperty('isInstalled') ? feature.isInstalled() : ['installed', 'enabled', 'running', 'synced'].includes(feature.status())
if(isInstalled) optionalInstalls.push(shortname) if(isInstalled) optionalInstalls.push(shortname)
console.log(name.padEnd(width) + (isInstalled ? 'Installed' : 'Not Installed')) console.log(name.padEnd(width) + columnize(isInstalled ? 'Installed' : 'Not Installed'))
}) })
console.log(`\n${heading2('Summary')}`) console.log(`\n${heading2('Summary')}`)
@ -239,15 +261,23 @@ export function checkAo() {
if(installAttained === 'Standard' && fullFeatures.every(shortname => optionalInstalls.includes(shortname))) { if(installAttained === 'Standard' && fullFeatures.every(shortname => optionalInstalls.includes(shortname))) {
installAttained = 'Full' installAttained = 'Full'
} }
console.log(installedRequirementCount + '/' + summary.length, 'required packages installed.')
console.log(optionalInstalls.length + '/' + Object.keys(features).length, 'optional features installed.') console.log(optionalInstalls.length + '/' + Object.keys(features).length, 'optional features installed.')
if(!installAttained) { if(!installAttained) {
console.log("You have not installed the AO; the required packages were not detected.") console.log("You have not installed the AO; the required packages were not detected.")
} else { } else {
console.log('You have the packages installed for a', installAttained, 'install.') console.log('Recognized this set of packages as a', installAttained, 'install.')
} }
console.log('Selected AO_VERSION is', aoEnv('AO_VERSION')) console.log('Selected AO_VERSION is', aoEnv('AO_VERSION') + '\n')
// Is it possible to check if npm i has already been called in all the node project folders? if(installAttained) console.log('The AO is installed.')
else {
console.log('The AO is not installed yet. Would you like to install it now?')
const answer = await yesOrNo('Start AO install wizard?', true)
if(answer) {
await aoInstallWizard()
}
}
} }
// Installs core dependencies required by Alchemy and the AO // Installs core dependencies required by Alchemy and the AO

Loading…
Cancel
Save