diff --git a/src/lib/dev-environment/dev-environment-cli.js b/src/lib/dev-environment/dev-environment-cli.js index a32c5d44b..7fc37dd56 100644 --- a/src/lib/dev-environment/dev-environment-cli.js +++ b/src/lib/dev-environment/dev-environment-cli.js @@ -184,7 +184,7 @@ export const validateDependencies = async ( lando: Lando, slug: string, quiet?: } try { - await validateDockerInstalled( lando ); + validateDockerInstalled( lando ); } catch ( exception ) { throw new UserError( exception.message ); } diff --git a/src/lib/dev-environment/dev-environment-core.ts b/src/lib/dev-environment/dev-environment-core.ts index 8c19ee6e3..50e217ee2 100644 --- a/src/lib/dev-environment/dev-environment-core.ts +++ b/src/lib/dev-environment/dev-environment-core.ts @@ -25,6 +25,7 @@ import { landoStop, landoRebuild, landoLogs, + LandoLogsOptions, } from './dev-environment-lando'; import { searchAndReplace } from '../search-and-replace'; import { @@ -306,7 +307,7 @@ function parseComponentForInfo( component: ComponentConfig | WordPressConfig ): export async function showLogs( lando: Lando, slug: string, - options: Record< string, string > = {} + options: LandoLogsOptions = {} ): Promise< unknown > { debug( 'Will display logs command on env', slug, 'with options', options ); @@ -315,10 +316,12 @@ export async function showLogs( debug( 'Instance path for', slug, 'is:', instancePath ); if ( options.service ) { - const appInfo: { services: string } = await landoInfo( lando, instancePath, false ); + const appInfo = await landoInfo( lando, instancePath ); if ( ! appInfo.services.includes( options.service ) ) { throw new UserError( - `Service '${ options.service }' not found. Please choose from one: ${ appInfo.services }` + `Service '${ + options.service + }' not found. Please choose from one: ${ appInfo.services.toString() }` ); } } diff --git a/src/lib/dev-environment/dev-environment-lando.js b/src/lib/dev-environment/dev-environment-lando.ts similarity index 82% rename from src/lib/dev-environment/dev-environment-lando.js rename to src/lib/dev-environment/dev-environment-lando.ts index 2e16b29fa..a886be643 100644 --- a/src/lib/dev-environment/dev-environment-lando.js +++ b/src/lib/dev-environment/dev-environment-lando.ts @@ -1,21 +1,19 @@ -// @flow -// @format - /** * External dependencies */ +import { tmpdir } from 'node:os'; +import { mkdir, rename } from 'node:fs/promises'; +import { lookup } from 'node:dns/promises'; +import path from 'node:path'; import debugLib from 'debug'; -import os from 'os'; -import fs from 'fs'; -import path from 'path'; -import Lando from 'lando/lib/lando'; +import Lando, { LandoConfig } from 'lando/lib/lando'; import { buildConfig } from 'lando/lib/bootstrap'; -import landoUtils from 'lando/plugins/lando-core/lib/utils'; +import landoUtils, { AppInfo } from 'lando/plugins/lando-core/lib/utils'; import landoBuildTask from 'lando/plugins/lando-tooling/lib/build'; import chalk from 'chalk'; import App from 'lando/lib/app'; -import dns from 'dns'; import xdgBasedir from 'xdg-basedir'; +import type { NetworkInspectInfo } from 'dockerode'; /** * Internal dependencies @@ -36,14 +34,10 @@ import UserError from '../user-error'; const DEBUG_KEY = '@automattic/vip:bin:dev-environment'; const debug = debugLib( DEBUG_KEY ); -type Record< T, V > = { - [T]: V, -}; - /** - * @return {Promise} Lando configuration + * @return {Promise} Lando configuration */ -async function getLandoConfig() { +async function getLandoConfig(): Promise< LandoConfig > { const nodeModulesPath = path.join( __dirname, '..', '..', '..', 'node_modules' ); const landoPath = path.join( nodeModulesPath, 'lando' ); const atLandoPath = path.join( nodeModulesPath, '@lando' ); @@ -61,12 +55,13 @@ async function getLandoConfig() { logLevelConsole = 'warn'; } - const vipDir = path.join( xdgBasedir.data || os.tmpdir(), 'vip' ); + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + const vipDir = path.join( xdgBasedir.data || tmpdir(), 'vip' ); // NOSONAR const landoDir = path.join( vipDir, 'lando' ); const fakeHomeDir = path.join( landoDir, 'home' ); try { - await fs.promises.mkdir( fakeHomeDir, { recursive: true } ); + await mkdir( fakeHomeDir, { recursive: true } ); } catch ( err ) { // Ignore } @@ -96,7 +91,7 @@ async function getLandoConfig() { return buildConfig( config ); } -const appMap: Map< string, App > = new Map(); +const appMap = new Map< string, App >(); async function regenerateLandofile( instancePath: string ): Promise< void > { const landoFile = path.join( instancePath, '.lando.yml' ); @@ -104,7 +99,7 @@ async function regenerateLandofile( instancePath: string ): Promise< void > { try { const now = new Date().toISOString().replace( /[^\d]/g, '' ).slice( 0, -3 ); const backup = `${ landoFile }.${ now }`; - await fs.promises.rename( landoFile, backup ); + await rename( landoFile, backup ); console.warn( chalk.yellow( 'Backed up %s to %s' ), landoFile, backup ); } catch ( err ) { // Rename failed - possibly the file does not exist. Silently ignoring. @@ -116,7 +111,7 @@ async function regenerateLandofile( instancePath: string ): Promise< void > { await updateEnvironment( currentInstanceData ); } -async function landoRecovery( lando: Lando, instancePath: string, error: Error ): Promise< App > { +async function landoRecovery( lando: Lando, instancePath: string, error: unknown ): Promise< App > { debug( 'Error initializing Lando app', error ); console.warn( chalk.yellow( 'There was an error initializing Lando, trying to recover...' ) ); try { @@ -127,6 +122,7 @@ async function landoRecovery( lando: Lando, instancePath: string, error: Error ) 'Recovery failed, aborting.' ) } Please recreate the environment or contact support.` ); + throw err; } @@ -149,7 +145,7 @@ async function getLandoApplication( lando: Lando, instancePath: string ): Promis const started = new Date(); try { if ( appMap.has( instancePath ) ) { - return Promise.resolve( appMap.get( instancePath ) ); + return Promise.resolve( appMap.get( instancePath )! ); } if ( ! ( await doesEnvironmentExist( instancePath ) ) ) { @@ -188,14 +184,14 @@ export async function bootstrapLando(): Promise< Lando > { let registryResolvable = false; try { - registryResolvable = ( await dns.promises.lookup( 'ghcr.io' ) ).address || false; + registryResolvable = ( await lookup( 'ghcr.io' ) ).address.length > 0 || false; debug( 'Registry ghcr.io is resolvable' ); } catch ( err ) { debug( 'Registry ghcr.io is not resolvable, image pull might be broken.' ); registryResolvable = false; } - const pull = registryResolvable && ( instanceData.pullAfter || 0 ) < Date.now(); + const pull = registryResolvable && ( instanceData.pullAfter ?? 0 ) < Date.now(); if ( Array.isArray( data.opts.pullable ) && Array.isArray( data.opts.local ) && @@ -236,9 +232,9 @@ export async function landoStart( lando: Lando, instancePath: string ): Promise< } export interface LandoLogsOptions { - follow: boolean; - service: string; - timestamps: boolean; + follow?: boolean; + service?: string; + timestamps?: boolean; } export async function landoLogs( lando: Lando, instancePath: string, options: LandoLogsOptions ) { @@ -249,7 +245,7 @@ export async function landoLogs( lando: Lando, instancePath: string, options: La const app = await getLandoApplication( lando, instancePath ); const logTask = lando.tasks.find( task => task.command === 'logs' ); - await logTask.run( { + await logTask?.run( { follow: options.follow, service: options.service, timestamps: options.timestamps, @@ -278,15 +274,14 @@ export async function landoRebuild( lando: Lando, instancePath: string ): Promis } } -/** - * @return {Promise} - */ -async function getBridgeNetwork( lando: Lando ) { - const networkName = lando.config.networkBridge || 'lando_bridge_network'; +async function getBridgeNetwork( lando: Lando ): Promise< NetworkInspectInfo | null > { + const networkName = lando.config.networkBridge ?? 'lando_bridge_network'; try { - return await lando.engine.getNetwork( networkName ).inspect(); + return ( await lando.engine + .getNetwork( networkName ) + .inspect() ) as Promise< NetworkInspectInfo | null >; } catch ( err ) { - debug( 'Error getting network %s: %s', networkName, err.message ); + debug( 'Error getting network %s: %s', networkName, ( err as Error ).message ); return null; } } @@ -294,11 +289,11 @@ async function getBridgeNetwork( lando: Lando ) { async function cleanUpLandoProxy( lando: Lando ): Promise< void > { const network = await getBridgeNetwork( lando ); if ( network?.Containers && ! Object.keys( network.Containers ).length ) { - const proxy = lando.engine.docker.getContainer( lando.config.proxyContainer ); + const proxy = lando.engine.docker.getContainer( lando.config.proxyContainer! ); try { await proxy.remove( { force: true } ); } catch ( err ) { - debug( 'Error removing proxy container: %s', err.message ); + debug( 'Error removing proxy container: %s', ( err as Error ).message ); } } } @@ -336,29 +331,37 @@ interface LandoInfoOptions { autologinKey?: string; } +interface LandoInfoResult extends AppInfo { + slug: string; + status: string; + 'Login URL'?: string; + 'Default username'?: string; + 'Default password'?: string; + 'Health warnings'?: string; + Documentation: string; +} + export async function landoInfo( lando: Lando, instancePath: string, options: LandoInfoOptions = {} -): Promise< Record< string, any > > { +): Promise< LandoInfoResult > { const started = new Date(); try { const app = await getLandoApplication( lando, instancePath ); - let appInfo = landoUtils.startTable( app ); + const info = landoUtils.startTable( app ); const reachableServices = app.info.filter( service => service.urls.length ); - reachableServices.forEach( - service => ( appInfo[ `${ service.service } urls` ] = service.urls ) - ); + reachableServices.forEach( service => ( info[ `${ service.service } urls` ] = service.urls ) ); const health = await checkEnvHealth( lando, instancePath ); - const frontEndUrl = app.info.find( service => 'nginx' === service.service )?.urls[ 0 ]; + const frontEndUrl = app.info.find( service => 'nginx' === service.service )?.urls[ 0 ] ?? ''; const extraService = await getExtraServicesConnections( lando, app ); - appInfo = { - slug: appInfo.name.replace( /^vipdev/, '' ), - ...appInfo, + const appInfo: Partial< LandoInfoResult > = { + slug: info.name.replace( /^vipdev/, '' ), + ...info, ...extraService, }; @@ -400,7 +403,7 @@ export async function landoInfo( appInfo.Documentation = 'https://docs.wpvip.com/technical-references/vip-local-development-environment/'; - return appInfo; + return appInfo as LandoInfoResult; } finally { const duration = new Date().getTime() - started.getTime(); debug( 'landoInfo() took %d ms', duration ); @@ -436,23 +439,24 @@ async function getExtraServicesConnections( const allServices = await lando.engine.list( { project: app.project } ); for ( const service of allServices ) { - const displayConfiguration = - extraServiceDisplayConfiguration.find( conf => conf.name === service.service ) || {}; + const displayConfiguration = extraServiceDisplayConfiguration.find( + conf => conf.name === service.service + ); - if ( displayConfiguration.skip ) { + if ( ! displayConfiguration || displayConfiguration.skip ) { continue; } // eslint-disable-next-line no-await-in-loop - const containerScan = service?.id ? await lando.engine.docker.scan( service?.id ) : null; - if ( containerScan?.NetworkSettings?.Ports ) { + const containerScan = service.id ? await lando.engine.docker.scan( service.id ) : null; + if ( containerScan?.NetworkSettings.Ports ) { const mappings = Object.keys( containerScan.NetworkSettings.Ports ) .map( internalPort => containerScan.NetworkSettings.Ports[ internalPort ] ) - .filter( externalMapping => externalMapping?.length ); + .filter( externalMapping => externalMapping.length ); - if ( mappings?.length ) { + if ( mappings.length ) { const { HostIp: host, HostPort: port } = mappings[ 0 ][ 0 ]; - const label = displayConfiguration.label || service.service; + const label = displayConfiguration.label ?? service.service; const value = ( displayConfiguration.protocol ? `${ displayConfiguration.protocol }://` : '' ) + `${ host }:${ port }`; @@ -468,12 +472,6 @@ export async function checkEnvHealth( lando: Lando, instancePath: string ): Promise< Record< string, boolean > > { - type ScanResult = { - url: string, - status: boolean, - color: 'green' | 'yellow' | 'red', - }; - const urls: Record< string, string > = {}; const now = new Date(); @@ -487,7 +485,7 @@ export async function checkEnvHealth( } ); } ); - const scanResults: ScanResult[] = await app.scanUrls( Object.keys( urls ), { max: 1 } ); + const scanResults = await app.scanUrls( Object.keys( urls ), { max: 1 } ); const result: Record< string, boolean > = {}; scanResults.forEach( scanResult => { @@ -508,7 +506,7 @@ export async function isEnvUp( lando: Lando, instancePath: string ): Promise< bo const webUrls = reachableServices .map( service => service.urls ) .flat() - .filter( url => ! url.match( /^https?:\/\/(localhost|127\.0\.0\.1):/ ) ); + .filter( url => ! /^https?:\/\/(localhost|127\.0\.0\.1):/.exec( url ) ); const scanResult = await app.scanUrls( webUrls, { max: 1 } ); const duration = new Date().getTime() - now.getTime(); @@ -516,7 +514,8 @@ export async function isEnvUp( lando: Lando, instancePath: string ): Promise< bo // If all the URLs are reachable then the app is considered 'up' return ( - scanResult.length && scanResult.filter( result => result.status ).length === scanResult.length + scanResult.length > 0 && + scanResult.filter( result => result.status ).length === scanResult.length ); } @@ -524,12 +523,12 @@ export async function landoExec( lando: Lando, instancePath: string, toolName: string, - args: Array< string >, - options: any + args: string[], + options: Record< string, unknown > ) { const app = await getLandoApplication( lando, instancePath ); - const tool = app.config.tooling[ toolName ]; + const tool = app.config.tooling![ toolName ] as Record< string, unknown > | undefined; if ( ! tool ) { throw new UserError( `${ toolName } is not a known lando task` ); } @@ -585,7 +584,7 @@ export async function landoShell( } debug( 'Running command "%o" in service "%s" as user "%s"', command, service, user ); - await shellTask.run( { + await shellTask?.run( { command, service, user, @@ -605,8 +604,7 @@ async function removeDevToolsVolumes( lando: Lando, app: App ) { debug( 'Attempting to removing dev-tools volumes' ); const scanResult = await lando.engine.docker.listVolumes(); - const devToolsVolumeNames = ( scanResult?.Volumes || [] ) - .map( volume => volume.Name ) + const devToolsVolumeNames = scanResult.Volumes.map( volume => volume.Name ) // eslint-disable-next-line security/detect-non-literal-regexp .filter( volumeName => new RegExp( `${ app.project }.*devtools` ).test( volumeName ) ); @@ -620,18 +618,10 @@ async function removeDevToolsVolumes( lando: Lando, app: App ) { /** * Remove volume - * - * @param {Lando} lando - * @param {string} volumeName */ -async function removeVolume( lando: Lando, volumeName: string ) { +async function removeVolume( lando: Lando, volumeName: string ): Promise< void > { debug( `Removing devtools volume ${ volumeName }` ); - const devToolsVolume = await lando.engine.docker.getVolume( volumeName ); - if ( ! devToolsVolume ) { - debug( `Volume ${ volumeName } not found` ); - return; - } - + const devToolsVolume = lando.engine.docker.getVolume( volumeName ); try { await devToolsVolume.remove(); debug( `${ volumeName } volume removed` ); @@ -646,12 +636,10 @@ async function removeVolume( lando: Lando, volumeName: string ) { * to start due to missing network. * * This function tries to detect such scenario and remove the orphant. So that regular flow - * can safelly add a network and a new proxy container. - * - * @param {Object} lando Bootstrapped Lando object + * can safely add a network and a new proxy container. */ async function ensureNoOrphantProxyContainer( lando: Lando ): Promise< void > { - const proxyContainerName = lando.config.proxyContainer; + const proxyContainerName = lando.config.proxyContainer!; const docker = lando.engine.docker; const containers = await docker.listContainers( { all: true } ); @@ -663,24 +651,24 @@ async function ensureNoOrphantProxyContainer( lando: Lando ): Promise< void > { return; } - const proxyContainer = await docker.getContainer( proxyContainerName ); + const proxyContainer = docker.getContainer( proxyContainerName ); const status = await proxyContainer.inspect(); - if ( status?.State?.Running ) { + if ( status.State.Running ) { return; } await proxyContainer.remove(); } -export async function validateDockerInstalled( lando: Lando ): Promise< void > { +export function validateDockerInstalled( lando: Lando ): void { lando.log.verbose( 'docker-engine exists: %s', lando.engine.dockerInstalled ); - if ( lando.engine.dockerInstalled === false ) { + if ( ! lando.engine.dockerInstalled ) { throw Error( 'docker could not be located! Please follow the following instructions to install it - https://docs.docker.com/engine/install/' ); } lando.log.verbose( 'docker-compose exists: %s', lando.engine.composeInstalled ); - if ( lando.engine.composeInstalled === false ) { + if ( ! lando.engine.composeInstalled ) { throw Error( 'docker-compose could not be located! Please follow the following instructions to install it - https://docs.docker.com/compose/install/' ); diff --git a/src/lib/dev-environment/docker-utils.js b/src/lib/dev-environment/docker-utils.ts similarity index 83% rename from src/lib/dev-environment/docker-utils.js rename to src/lib/dev-environment/docker-utils.ts index 3ff63df41..eb9110bb3 100644 --- a/src/lib/dev-environment/docker-utils.js +++ b/src/lib/dev-environment/docker-utils.ts @@ -1,17 +1,11 @@ /* eslint-disable no-await-in-loop */ -// @flow - /** * External dependencies */ import { constants } from 'node:fs'; import { access, readFile, stat } from 'node:fs/promises'; import { homedir, platform } from 'node:os'; -import path from 'node:path'; - -type Record< T, V > = { - [T]: V, -}; +import { join } from 'node:path'; /** * Reads a Certificate Authority file and returns it as an array of certificates @@ -22,7 +16,7 @@ type Record< T, V > = { export async function splitca( filepath: string ): Promise< string[] > { const ca: string[] = []; const data = await readFile( filepath, 'utf-8' ); - if ( data.indexOf( '-END CERTIFICATE-' ) < 0 || data.indexOf( '-BEGIN CERTIFICATE-' ) < 0 ) { + if ( ! data.includes( '-END CERTIFICATE-' ) || ! data.includes( '-BEGIN CERTIFICATE-' ) ) { throw new Error( "File does not contain 'BEGIN CERTIFICATE' or 'END CERTIFICATE'" ); } @@ -31,7 +25,7 @@ export async function splitca( filepath: string ): Promise< string[] > { for ( const line of chain ) { if ( line ) { cert.push( line ); - if ( line.match( /-END CERTIFICATE-/ ) ) { + if ( /-END CERTIFICATE-/.exec( line ) ) { ca.push( cert.join( '\n' ) ); cert = []; } @@ -55,7 +49,7 @@ export async function getDockerSocket(): Promise< string | null > { if ( platform() !== 'win32' ) { const possibleSocket = process.env.DOCKER_HOST ?? ''; // If `DOCKER_HOST` is set and not empty, and if it does not point to a unix socket, return - not much that we can do here. - if ( possibleSocket && ! /^unix:\/\//.test( possibleSocket ) ) { + if ( possibleSocket && ! possibleSocket.startsWith( 'unix://' ) ) { return possibleSocket; } @@ -69,7 +63,7 @@ export async function getDockerSocket(): Promise< string | null > { // Try the default location paths.push( '/var/run/docker.sock' ); // Try an alternative location - paths.push( path.join( homedir(), '.docker', 'run', 'docker.sock' ) ); + paths.push( join( homedir(), '.docker', 'run', 'docker.sock' ) ); for ( const socketPath of paths ) { try { @@ -87,8 +81,8 @@ export async function getDockerSocket(): Promise< string | null > { return null; } -export async function getEngineConfig( dockerHost: string ): Promise< Record< string, any > > { - const opts: Record< string, any > = {}; +export async function getEngineConfig( dockerHost: string ): Promise< Record< string, unknown > > { + const opts: Record< string, unknown > = {}; if ( dockerHost.startsWith( 'tcp://' ) ) { const split = /(?:tcp:\/\/)?(.*?):(\d+)/g.exec( dockerHost ); if ( split && split.length === 3 ) { @@ -110,9 +104,9 @@ export async function getEngineConfig( dockerHost: string ): Promise< Record< st const certPath = process.env.DOCKER_CERT_PATH; if ( certPath ) { const [ ca, cert, key ] = await Promise.all( [ - splitca( path.join( certPath, 'ca.pem' ) ), - readFile( path.join( certPath, 'cert.pem' ), 'utf-8' ), - readFile( path.join( certPath, 'key.pem' ), 'utf-8' ), + splitca( join( certPath, 'ca.pem' ) ), + readFile( join( certPath, 'cert.pem' ), 'utf-8' ), + readFile( join( certPath, 'key.pem' ), 'utf-8' ), ] ); opts.ca = ca; diff --git a/types/lando/lib/app.d.ts b/types/lando/lib/app.d.ts index 4bf630359..1db9afc51 100644 --- a/types/lando/lib/app.d.ts +++ b/types/lando/lib/app.d.ts @@ -1,4 +1,20 @@ +import { LandoConfig } from "./lando"; + export = App; + +export interface ServiceInfo { + service: string; + urls: string[]; + type: string; + healthy: boolean; +} + +interface ScanResult { + url: string; + status: boolean; + color: 'green' | 'yellow' | 'red'; +} + declare class App { constructor( name: any, config: any, lando?: {} ); /** @@ -7,8 +23,8 @@ declare class App { * @since 3.0.0 * @alias app.name */ - name: any; - project: any; + name: string; + project: string; _config: any; _dir: string; _lando: {}; @@ -54,15 +70,15 @@ declare class App { engine: any; metrics: any; Promise: any; - events: any; - scanUrls: any; + events: import("lando/lib/events"); + scanUrls: (urls: string[], options?: { max?: number, waitCodes?: number[] }) => Promise; /** * The apps configuration * * @since 3.0.0 * @alias app.config */ - config: any; + config: LandoConfig; configFiles: any; configHash: any; ComposeService: any; @@ -73,9 +89,9 @@ declare class App { * @since 3.0.0 * @alias app.info */ - info: any[]; + info: ServiceInfo[]; labels: any; - opts: {}; + opts: Record; plugins: any; metaCache: string; meta: any; diff --git a/types/lando/lib/art.d.ts b/types/lando/lib/art.d.ts new file mode 100644 index 000000000..a5f34ee37 --- /dev/null +++ b/types/lando/lib/art.d.ts @@ -0,0 +1,44 @@ +export function appDestroy({ name, phase }?: { + name: any; + phase?: string; +}): any; +export function appRebuild({ name, phase, warnings }?: { + name: any; + phase?: string; + warnings?: {}; +}): any; +export function appRestart({ name, phase, warnings }?: { + name: any; + phase?: string; + warnings?: {}; +}): any; +export function appStart({ name, phase, warnings }?: { + name: any; + phase?: string; + warnings?: {}; +}): any; +export function appStop({ name, phase }?: { + name: any; + phase?: string; +}): any; +export function crash(): string; +export function experimental(on?: boolean): string; +export function init(): string; +export function newContent(type?: string): string; +export function noDockerDep(dep?: string): string; +export function poweroff({ phase }?: { + phase?: string; +}): any; +export function print({ text, color }?: { + text: any; + color?: string; +}): any; +export function printFont({ text, color, font }?: { + text: any; + color?: string; + font?: string; +}): any; +export function releaseChannel(channel?: string): string; +export function secretToggle(on?: boolean): string; +export function secretToggleDenied(on?: boolean): string; +export function badToken(): string; diff --git a/types/lando/lib/bootstrap.d.ts b/types/lando/lib/bootstrap.d.ts new file mode 100644 index 000000000..94fb9a2b7 --- /dev/null +++ b/types/lando/lib/bootstrap.d.ts @@ -0,0 +1,14 @@ +import { LandoConfig } from "./lando"; + +export function buildConfig(options: Partial): LandoConfig; +export function dc(shell: any, bin: any, cmd: any, { compose, project, opts }: { + compose: any; + project: any; + opts?: {}; +}): any; +export function getApp(files: any, userConfRoot: any): any; +export function getLandoFiles(files?: any[], startFrom?: string): any; +export function getTasks(config?: {}, argv?: {}, tasks?: any[]): any[]; +export function setupCache(log: any, config: any): import("lando/lib/cache"); +export function setupEngine(config: any, cache: any, events: any, log: any, shell: any, id: any): import("lando/lib/engine"); +export function setupMetrics(log: any, config: any): import("lando/lib/metrics"); diff --git a/types/lando/lib/cache.d.ts b/types/lando/lib/cache.d.ts new file mode 100644 index 000000000..b644e2825 --- /dev/null +++ b/types/lando/lib/cache.d.ts @@ -0,0 +1,64 @@ +export = Cache; +declare class Cache extends NodeCache { + constructor({ log, cacheDir }?: { + log?: Log; + cacheDir?: string; + }); + log: Log; + cacheDir: string; + /** + * Sets an item in the cache + * + * @since 3.0.0 + * @alias lando.cache.set + * @param {String} key The name of the key to store the data with. + * @param {Any} data The data to store in the cache. + * @param {Object} [opts] Options to pass into the cache + * @param {Boolean} [opts.persist=false] Whether this cache data should persist between processes. Eg in a file instead of memory + * @param {Integer} [opts.ttl=0] Seconds the cache should live. 0 mean forever. + * @example + * // Add a string to the cache + * lando.cache.set('mykey', 'mystring'); + * + * // Add an object to persist in the file cache + * lando.cache.set('mykey', data, {persist: true}); + * + * // Add an object to the cache for five seconds + * lando.cache.set('mykey', data, {ttl: 5}); + */ + set(key: string, data: Any, { persist, ttl }?: { + persist?: boolean; + ttl?: Integer; + }): void; + /** + * Gets an item in the cache + * + * @since 3.0.0 + * @alias lando.cache.get + * @param {String} key The name of the key to retrieve the data. + * @return {Any} The data stored in the cache if applicable. + * @example + * // Get the data stored with key mykey + * const data = lando.cache.get('mykey'); + */ + get(key: string): Any; + /** + * Manually remove an item from the cache. + * + * @since 3.0.0 + * @alias lando.cache.remove + * @param {String} key The name of the key to remove the data. + * @example + * // Remove the data stored with key mykey + * lando.cache.remove('mykey'); + */ + remove(key: string): void; + __get: (key: string | number, cb?: NodeCache.Callback) => T; + __set: { + (key: string | number, value: T_1, ttl: string | number, cb?: NodeCache.Callback): boolean; + (key: string | number, value: T_2, cb?: NodeCache.Callback): boolean; + }; + __del: (keys: (string | number) | (string | number)[], cb?: NodeCache.Callback) => number; +} +import NodeCache = require("node-cache"); +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/cli.d.ts b/types/lando/lib/cli.d.ts new file mode 100644 index 000000000..29cb1aef8 --- /dev/null +++ b/types/lando/lib/cli.d.ts @@ -0,0 +1,105 @@ +export = Cli; +declare class Cli { + constructor(prefix?: string, logLevel?: string, userConfRoot?: string); + prefix: string; + logLevel: string; + userConfRoot: string; + /** + * Returns a parsed array of CLI arguments and options + * + * @since 3.0.0 + * @alias lando.cli.argv + * @return {Object} Yarg parsed options + * @example + * const argv = lando.cli.argv(); + * @todo make this static and then fix all call sites + */ + argv(): any; + /** + * Checks to see if lando is running with sudo. If it is it + * will exit the process with a stern warning + * + * @since 3.0.0 + * @alias lando.cli.checkPerms + * @example + * lando.cli.checkPerms() + */ + checkPerms(): void; + clearTaskCaches(): void; + confirm(message?: string): { + describe: string; + alias: string[]; + default: boolean; + boolean: boolean; + interactive: { + type: string; + default: boolean; + message: string; + }; + }; + /** + * Returns a config object with some good default settings for bootstrapping + * lando as a command line interface + * + * @since 3.5.0 + * @alias lando.cli.defaultConfig + * @param {Object} [appConfig={}] Optional raw landofile + * @return {Object} Config that can be used in a Lando CLI bootstrap + * @example + * const config = lando.cli.defaultConfig(); + * // Kick off our bootstrap + * bootstrap(config).then(lando => console.log(lando)); + */ + defaultConfig(appConfig?: any): any; + formatData(data: any, { path, format, filter }?: { + path?: string; + format?: string; + filter?: any[]; + }, opts?: {}): any; + formatOptions(omit?: any[]): any; + /** + * Cli wrapper for error handler + * + * @since 3.0.0 + * @alias lando.cli.handleError + * @param {Error} error The error + * @param {Function} handler The error handler function + * @param {Integer} verbose [verbose=this.argv().verbose] The verbosity level + * @param {Object} lando The Lando object + * @return {Integer} The exit codes + */ + handleError(error: Error, handler: Function, verbose?: Integer, lando?: any): Integer; + init(yargs: any, tasks: any, config: any, userConfig: any): void; + /** + * Returns some cli "art" + * + * @since 3.0.0 + * @alias lando.cli.makeArt + * @param {String} [func='start'] The art func you want to call + * @param {Object} [opts] Func options + * @return {String} Usually a printable string + * @example + * console.log(lando.cli.makeArt('secretToggle', true); + */ + makeArt(func?: string, opts?: any): string; + /** + * Parses a lando task object into something that can be used by the [yargs](http://yargs.js.org/docs/) CLI. + * + * A lando task object is an abstraction on top of yargs that also contains some + * metadata about how to interactively ask questions on both a CLI and GUI. + * + * @since 3.5.0 + * @alias lando.cli.parseToYargs + * @see [yargs docs](http://yargs.js.org/docs/) + * @see [inquirer docs](https://github.com/sboudrias/Inquirer.js) + * @param {Object} task A Lando task object (@see add for definition) + * @param {Object} [config={}] The landofile + * @return {Object} A yargs command object + * @example + * // Add a task to the yargs CLI + * yargs.command(lando.tasks.parseToYargs(task)); + */ + parseToYargs({ command, describe, options, run, level }: any, config?: any): any; + run(tasks?: any[], config?: {}): void; + updateUserConfig(data?: {}): any; +} diff --git a/types/lando/lib/compose.d.ts b/types/lando/lib/compose.d.ts new file mode 100644 index 000000000..8bd1f2e01 --- /dev/null +++ b/types/lando/lib/compose.d.ts @@ -0,0 +1,64 @@ +export function build(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function getId(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function logs(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function pull(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function remove(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function run(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function start(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; +export function stop(compose: any, project: any, opts?: {}): { + cmd: any; + opts: { + mode: string; + cstdio: any; + silent: any; + }; +}; diff --git a/types/lando/lib/config.d.ts b/types/lando/lib/config.d.ts new file mode 100644 index 000000000..a6f25967f --- /dev/null +++ b/types/lando/lib/config.d.ts @@ -0,0 +1,11 @@ +export function tryConvertJson(value: string): any; +export function merge(old: any, ...fresh: any): any; +export function stripEnv(prefix: string): any; +export function defaults(): any; +export function getEngineConfig({ engineConfig, env }: { + engineConfig?: {}; + env?: {}; +}): {}; +export function getOclifCacheDir(product?: string): string; +export function loadFiles(files: any[]): any; +export function loadEnvs(prefix: string): any; diff --git a/types/lando/lib/daemon.d.ts b/types/lando/lib/daemon.d.ts new file mode 100644 index 000000000..7abf85a46 --- /dev/null +++ b/types/lando/lib/daemon.d.ts @@ -0,0 +1,18 @@ +export = LandoDaemon; +declare class LandoDaemon { + constructor(cache?: Cache, events?: Events, docker?: string | boolean, log?: Log, context?: string, compose?: string | boolean); + cache: Cache; + compose: string | boolean; + context: string; + docker: string | boolean; + events: Events; + log: Log; + up(): any; + down(): any; + isUp(log?: Log, cache?: Cache, docker?: string | boolean): any; + getVersions(): any; + getComposeSeparator(): any; +} +import Cache = require("lando/lib/cache"); +import Events = require("lando/lib/events"); +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/docker.d.ts b/types/lando/lib/docker.d.ts new file mode 100644 index 000000000..54749147b --- /dev/null +++ b/types/lando/lib/docker.d.ts @@ -0,0 +1,15 @@ +export = Landerode; +declare class Landerode extends Dockerode { + constructor(opts?: {}, id?: string, promise?: any); + id: string; + createNet(name: any, opts?: {}): Promise; + scan(cid: string): Promise; + isRunning(cid: any): any; + list(options?: {}): any; + remove(cid: any, opts?: { + v: boolean; + force: boolean; + }): any; + stop(cid: any, opts?: {}): any; +} +import Dockerode = require("dockerode"); diff --git a/types/lando/lib/engine.d.ts b/types/lando/lib/engine.d.ts new file mode 100644 index 000000000..6123f517c --- /dev/null +++ b/types/lando/lib/engine.d.ts @@ -0,0 +1,456 @@ +export = Engine; + +export interface LandoService { + id: string; + service: string; + name: string; + app: string; + src: string[]; + kind: string; + lando: boolean; + instance: string; + status: string; +} + +declare class Engine { + constructor(daemon?: LandoDaemon, docker?: Landerode, compose?: () => void, config?: {}); + docker: Landerode; + daemon: LandoDaemon; + compose: () => void; + engineCmd: (name: any, data: any, run?: () => any) => any; + composeInstalled: boolean; + dockerInstalled: boolean; + supportedVersions: any; + /** + * Event that allows you to do some things before a `compose` object's containers are + * built + * + * @since 3.0.0 + * @alias lando.events:pre-engine-build + * @event pre_engine_build + */ + /** + * Event that allows you to do some things after a `compose` object's containers are + * built + * + * @since 3.0.0 + * @alias lando.events:post-engine-build + * @event post_engine_build + */ + /** + * Tries to pull the services for a `compose` object, and then tries to build them if they are found + * locally. This is a wrapper around `docker pull` and `docker build`. + * + * **NOTE:** Generally an instantiated `App` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.build + * @fires pre_engine_build + * @fires post_engine_build + * @param {Object} data A `compose` Object or an Array of `compose` Objects if you want to build more than one set of services. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} [data.opts] Options on how to build the `compose` objects containers. + * @param {Array} [data.opts.services='all services'] The services to build. + * @param {Boolean} [data.opts.nocache=true] Ignore the build cache. + * @param {Boolean} [data.opts.pull=true] Try to pull first. + * @return {Promise} A Promise. + * @example + * return lando.engine.build(app); + */ + build(data: { + compose: any[]; + project: string; + opts?: { + services?: any[]; + nocache?: boolean; + pull?: boolean; + }; + }): Promise; + /** + * Creates a Docker network + * + * @since 3.0.0. + * @function + * @alias lando.engine.createNetwork + * @see [docker api network docs](https://docs.docker.com/engine/api/v1.35/#operation/NetworkCreate) for info on opts. + * @param {String} name The name of the networks + * @return {Promise} A Promise with inspect data. + * @example + * return lando.engine.createNetwork('mynetwork') + */ + createNetwork(name: string): Promise; + /** + * Event that allows you to do some things before some containers are destroyed. + * + * @since 3.0.0 + * @alias lando.events:pre-engine-destroy + * @event pre_engine_destroy + */ + /** + * Event that allows you to do some things after some containers are destroyed. + * + * @since 3.0.0 + * @alias lando.events:post-engine-destroy + * @event post_engine_destroy + */ + /** + * Removes containers for a `compose` object or a particular container. + * + * There are two ways to remove containers: + * + * 1. Using an object with `{id: id}` where `id` is a docker recognizable id + * 2. Using a `compose` object with `{compose: compose, project: project, opts: opts}` + * + * These are detailed more below. + * + * **NOTE:** Generally an instantiated `App` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.destroy + * @fires pre_engine_destroy + * @fires post_engine_destroy + * @param {Object} data Remove criteria, Need eithers an ID or a service within a compose context + * @param {String} data.id An id that docker can recognize such as a container hash or name. Can also use `data.name` or `data.cid`. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} [data.opts] Options on what services to remove. + * @param {Array} [data.opts.services='all services'] An Array of services to remove. + * @param {Boolean} [data.opts.volumes=true] Also remove volumes associated with the container(s). + * @param {Boolean} [data.opts.force=false] Force remove the containers. + * @param {Boolean} [data.opts.purge=false] Implies `volumes` and `force`. + * @return {Promise} A Promise. + * @example + * return lando.engine.destroy(app); + * + */ + destroy(data: { + id: string; + compose: any[]; + project: string; + opts?: { + services?: any[]; + volumes?: boolean; + force?: boolean; + purge?: boolean; + }; + }): Promise; + /** + * Checks whether a specific service exists or not. + * + * There are two ways to check whether a container exists: + * + * 1. Using an object with `{id: id}` where `id` is a docker recognizable id + * 2. Using a `compose` object with `{compose: compose, project: project, opts: opts}` + * + * These are detailed more below. + * + * **NOTE:** Generally an instantiated `app` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.exists + * @param {Object} data Search criteria, Need eithers an ID or a service within a compose context + * @param {String} data.id An id that docker can recognize such as a container hash or name. Can also use `data.name` or `data.cid`. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} data.opts Options on what service to check + * @param {Array} data.opts.services An Array of services to check + * @return {Promise} A Promise with a Boolean of whether the service exists or not. + * @example + * return lando.engine.exists(compose); + */ + exists(data: { + id: string; + compose: any[]; + project: string; + opts: { + services: any[]; + }; + }): Promise; + /** + * Gets a Docker network + * + * @since 3.0.0. + * @function + * @alias lando.engine.getNetwork + * @param {String} id The id of the network + * @return {Object} A Dockerode Network object . + * @example + * const network = lando.engine.getNetwork('mynetwork') + */ + getNetwork(id: string): Dockerode.Network; + /** + * Gets the docker networks. + * + * @since 3.0.0 + * @function + * @alias lando.engine.getNetworks + * @see [docker api network docs](https://docs.docker.com/engine/api/v1.27/#operation/NetworkList) for info on filters option. + * @param {Object} [opts] Options to pass into the docker networks call + * @param {Object} [opts.filters] Filters options + * @return {Promise} A Promise with an array of network objects. + */ + getNetworks(opts?: { + filters?: any; + }): Promise; + /** + * Determines whether a container is running or not + * + * @since 3.0.0 + * @alias lando.engine.isRunning + * @param {String} data An ID that docker can recognize such as the container id or name. + * @return {Promise} A Promise with a boolean of whether the container is running or not + * @example + * + * // Check to see if our app's web service is running + * return lando.engine.isRunning('myapp_web_1').then(isRunning) { + * lando.log.info('Container %s is running: %s', 'myapp_web_1', isRunning); + * }); + */ + isRunning(data: string): Promise; + /** + * Lists all the Lando containers. Optionally filter by app name. + * + * @since 3.0.0 + * @alias lando.engine.list + * @param {Object} [options] Options to filter the list. + * @param {Boolean} [options.all=false] Show even stopped containers + * @param {String} [options.app] Show containers for only a certain app + * @param {Array} [options.filter] Filter by additional key=value pairs + * @return {Promise} A Promise with an Array of container Objects. + * @example + * return lando.engine.list().each(function(container) { + * lando.log.info(container); + * }); + */ + list(options?: { + all?: boolean; + app?: string; + project?: string; + filter?: any[]; + }): Promise; + /** + * Returns logs for a given `compose` object + * + * **NOTE:** Generally an instantiated `app` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.logs + * @param {Object} data A `compose` Object or an Array of `compose` Objects if you want to get logs for more than one set of services. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} [data.opts] Options on how to build the `compose` objects containers. + * @param {Boolean} [data.opts.follow=false] Whether to follow the log. Works like `tail -f`. + * @param {Boolean} [data.opts.timestamps=true] Show timestamps in log. + * @return {Promise} A Promise. + * @example + * + * // Get logs for an app + * return lando.engine.logs(app); + */ + logs(data: { + compose: any[]; + project: string; + opts?: { + follow?: boolean; + timestamps?: boolean; + }; + }): Promise; + /** + * Event that allows you to do some things before a command is run on a particular + * container. + * + * @since 3.0.0 + * @alias lando.events:pre-engine-run + * @event pre_engine_run + */ + /** + * Event that allows you to do some things after a command is run on a particular + * container. + * + * @since 3.0.0 + * @alias lando.events:post-engine-run + * @event post_engine_run + */ + /** + * Runs a command on a given service/container. This is a wrapper around `docker exec`. + * + * UNTIL the resolution of https://github.com/apocas/docker-modem/issues/83 data needs to also be or be an + * array of compose objects for this to work correctly on Windows as well. See some of the other engine + * documentation for what a compose object looks like. + * + * @since 3.0.0 + * @alias lando.engine.run + * @fires pre_engine_run + * @fires post_engine_run + * @param {Object} data A run Object or an Array of run Objects if you want to run more than one command. + * @param {String} data.id The container to run the command on. Must be an id that docker can recognize such as a container hash or name. + * @param {String} data.cmd A String of a command or an Array whose elements are the parts of the command. + * @param {Object} [data.opts] Options on how to run the command. + * @param {String} [data.opts.mode='collect'] Either `collect` or `attach`. Attach will connect to the run `stdin`. + * @param {String} [data.opts.pre] A String or Array of additional arguments or options to append to the `cmd` before the user specified args and options are added. + * @param {Array} [data.opts.env=[]] Additional environmental variables to set for the cmd. Must be in the form `KEY=VALUE`. + * @param {String} [data.opts.user='root'] The user to run the command as. Can also be `user:group` or `uid` or `uid:gid`. + * @param {String} [data.opts.detach=false] Run the process in the background + * @param {String} [data.opts.autoRemove=false] Automatically removes the container + * @return {Promise} A Promise with a string containing the command's output. + * @example + * + * // Run composer install on the appserver container for an app called myapp + * return lando.engine.run({id: 'myapp_appserver_1', cmd: ['composer', 'install']}); + * + * // Drop into an interactive bash shell on the database continer for an app called myapp + * return lando.engine.run({ + * id: 'myapp_database_1', + * cmd: ['bash'], + * opts: { + * mode: 'attach' + * } + * }); + */ + run(data: { + id: string; + cmd: string; + opts?: { + mode?: string; + pre?: string; + env?: any[]; + user?: string; + detach?: string; + autoRemove?: string; + }; + }): Promise; + /** + * Returns comprehensive service metadata. This is a wrapper around `docker inspect`. + * + * There are two ways to get container metadata: + * + * 1. Using an object with `{id: id}` where `id` is a docker recognizable id + * 2. Using a `compose` object with `{compose: compose, project: project, opts: opts}` + * + * These are detailed more below. + * + * **NOTE:** Generally an instantiated `app` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.scan + * @param {Object} data Search criteria, Need eithers an ID or a service within a compose context + * @param {String} data.id An id that docker can recognize such as a container hash or name. Can also use `data.name` or `data.cid`. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} data.opts Options on what service to scan + * @param {Array} data.opts.services An Array of services to scan. + * @return {Promise} A Promise with an Object of service metadata. + * @example + * // Log scan data using an id + * return lando.engine.scan({id: '146d321f212d'}).then(function(data) { + * lando.log.info('Container data is %j', data); + * }); + */ + scan(data: { + id: string; + compose: any[]; + project: string; + opts: { + services: any[]; + }; + }): Promise; + /** + * Event that allows you to do some things before a `compose` Objects containers are + * started + * + * @since 3.0.0 + * @alias lando.events:pre-engine-start + * @event pre_engine_start + */ + /** + * Event that allows you to do some things after a `compose` Objects containers are + * started + * + * @since 3.0.0 + * @alias lando.events:post-engine-start + * @event post_engine_start + */ + /** + * Starts the containers/services for the specified `compose` object. + * + * **NOTE:** Generally an instantiated `app` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.start + * @fires pre_engine_start + * @fires post_engine_start + * @param {Object} data A `compose` Object or an Array of `compose` Objects if you want to start more than one set of services. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} [data.opts] Options on how to start the `compose` Objects containers. + * @param {Array} [data.opts.services='all services'] The services to start. + * @param {Boolean} [data.opts.background=true] Start the services in the background. + * @param {Boolean} [data.opts.recreate=false] Recreate the services. + * @param {Boolean} [data.opts.removeOrphans=true] Remove orphaned containers. + * @return {Promise} A Promise. + * @example + * return lando.engine.start(app); + */ + start(data: { + compose: any[]; + project: string; + opts?: { + services?: any[]; + background?: boolean; + recreate?: boolean; + removeOrphans?: boolean; + }; + }): Promise; + /** + * Event that allows you to do some things before some containers are stopped. + * + * @since 3.0.0 + * @alias lando.events:pre-engine-stop + * @event pre_engine_stop + */ + /** + * Event that allows you to do some things after some containers are stopped. + * + * @since 3.0.0 + * @alias lando.events:post-engine-stop + * @event post_engine_stop + */ + /** + * Stops containers for a `compose` object or a particular container. + * + * There are two ways to stop containers: + * + * 1. Using an object with `{id: id}` where `id` is a docker recognizable id + * 2. Using a `compose` object with `{compose: compose, project: project, opts: opts}` + * + * These are detailed more below. + * + * **NOTE:** Generally an instantiated `app` instance is a valid `compose` object + * + * @since 3.0.0 + * @alias lando.engine.stop + * @fires pre_engine_stop + * @fires post_engine_stop + * @param {Object} data Stop criteria, Need eithers an ID or a service within a compose context + * @param {String} data.id An id that docker can recognize such as a container hash or name. Can also use `data.name` or `data.cid`. + * @param {Array} data.compose An Array of paths to Docker compose files + * @param {String} data.project A String of the project name (Usually this is the same as the app name) + * @param {Object} [data.opts] Options on what services to setop + * @param {Array} [data.opts.services='all services'] An Array of services to stop. + * @return {Promise} A Promise. + * @example + * return lando.engine.stop(app); + */ + stop(data: { + id: string; + compose: any[]; + project: string; + opts?: { + services?: any[]; + }; + }): Promise; +} +import Landerode = require("lando/lib/docker"); +import LandoDaemon = require("lando/lib/daemon"); +import Dockerode = require("dockerode"); diff --git a/types/lando/lib/env.d.ts b/types/lando/lib/env.d.ts new file mode 100644 index 000000000..6bcdb949f --- /dev/null +++ b/types/lando/lib/env.d.ts @@ -0,0 +1,5 @@ +export function getDockerBinPath(): string | false; +export function getDockerComposeBinPath(): string | false; +export function getComposeExecutable(): string | false; +export function getDockerExecutable(): string | false; +export function getOclifCacheDir(product: any): string; diff --git a/types/lando/lib/error.d.ts b/types/lando/lib/error.d.ts new file mode 100644 index 000000000..5923dd12b --- /dev/null +++ b/types/lando/lib/error.d.ts @@ -0,0 +1,24 @@ +export = ErrorHandler; +declare class ErrorHandler { + constructor(log?: Log, metrics?: Metrics); + log: Log; + metrics: Metrics; + /** + * Returns the lando options + * + * This means all the options passed in before the `--` flag. + * + * @since 3.0.0 + * @alias lando.error.handle + * @param {Object} error Error object + * @param {Boolean} report Whether to report the error or not + * @return {Integer} the error code + * @example + * // Gets all the pre-global options that have been specified. + * const argv = lando.tasks.argv(); + * @todo make this static and then fix all call sites + */ + handle({ message, stack, code, hide, verbose }?: any, report?: boolean): Integer; +} +import Log = require("lando/lib/logger"); +import Metrics = require("lando/lib/metrics"); diff --git a/types/lando/lib/events.d.ts b/types/lando/lib/events.d.ts new file mode 100644 index 000000000..e1ea89c79 --- /dev/null +++ b/types/lando/lib/events.d.ts @@ -0,0 +1,55 @@ +export = AsyncEvents; +declare class AsyncEvents extends EventEmitter { + constructor(log?: Log); + log: Log; + _listeners: any[]; + /** + * Our overridden event on method. + * + * This optionally allows a priority to be specified. Lower priorities run first. + * + * @since 3.0.0 + * @alias lando.events.on + * @param {String} name The name of the event + * @param {Integer} [priority=5] The priority the event should run in. + * @param {Function} fn The function to call. Should get the args specified in the corresponding `emit` declaration. + * @return {Promise} A Promise + * @example + * // Print out all our apps as they get instantiated and do it before other `post-instantiate-app` events + * lando.events.on('post-instantiate-app', 1, app => { + * console.log(app); + * }); + * + * // Log a helpful message after an app is started, don't worry about whether it runs before or + * // after other `post-start` events + * return app.events.on('post-start', () => { + * lando.log.info('App %s started', app.name); + * }); + */ + on(name: string, priority: number, fn: (...args: any[]) => unknown | Promise): this; + on(name: string, fn: (...args: any[]) => unknown | Promise): this; + once(eventName: string | symbol, listener: (...args: any[]) => unknown | Promise): this; + /** + * Reimplements event emit method. + * + * This makes events blocking and promisified. + * + * @since 3.0.0 + * @alias lando.events.emit + * @param {String} name The name of the event + * @param {...Any} [args] Options args to pass. + * @return {Promise} A Promise + * @example + * // Emits a global event with a config arg + * return lando.events.emit('wolf359', config); + * + * // Emits an app event with a config arg + * return app.events.emit('sector001', config); + */ + emit(...args?: any[]): Promise; + __on: (eventName: string | symbol, listener: (...args: any[]) => void) => EventEmitter; + __emit: (eventName: string | symbol, ...args: any[]) => boolean; +} +import EventEmitter_1 = require("events"); +import EventEmitter = EventEmitter_1.EventEmitter; +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/factory.d.ts b/types/lando/lib/factory.d.ts new file mode 100644 index 000000000..3bc7793c0 --- /dev/null +++ b/types/lando/lib/factory.d.ts @@ -0,0 +1,54 @@ +export = Factory; +declare class Factory { + constructor(classes?: ({ + name: string; + builder: { + new (id: any, info?: {}, ...sources: any[]): { + id: any; + info: {}; + data: any; + }; + }; + } | { + name: string; + builder: { + new (id: any, config?: {}): { + id: any; + config: { + proxy: any; + services: any; + tooling: any; + }; + }; + }; + })[]); + registry: ({ + name: string; + builder: { + new (id: any, info?: {}, ...sources: any[]): { + id: any; + info: {}; + data: any; + }; + }; + } | { + name: string; + builder: { + new (id: any, config?: {}): { + id: any; + config: { + proxy: any; + services: any; + tooling: any; + }; + }; + }; + })[]; + add({ name, builder, config, parent }: { + name: any; + builder: any; + config?: {}; + parent?: any; + }): any; + get(name?: string): any; +} diff --git a/types/lando/lib/formatters.d.ts b/types/lando/lib/formatters.d.ts new file mode 100644 index 000000000..fd80cb6c8 --- /dev/null +++ b/types/lando/lib/formatters.d.ts @@ -0,0 +1,9 @@ +export function formatData(data: any, { path, format, filter }?: { + path?: string; + format?: string; + filter?: any[]; +}, opts?: {}): any; +export function formatOptions(omit?: any[]): any; +export function getInteractive(options: any, argv: any): any; +export function handleInteractive(inquiry: any, argv: any, command: any, lando: any): any; +export function sortOptions(options: any): any; diff --git a/types/lando/lib/lando.d.ts b/types/lando/lib/lando.d.ts index fd2872b8d..abc0495e3 100644 --- a/types/lando/lib/lando.d.ts +++ b/types/lando/lib/lando.d.ts @@ -1,79 +1,128 @@ -declare class Lando { - constructor( options?: {} ); - BOOTSTRAP_LEVELS: { - config: number; - tasks: number; - engine: number; - app: number; - }; - config: any; - Promise: any; - tasks: any[]; - cache: any; - cli: any; - log: any; - metrics: any; - error: any; - events: any; - user: any; - /** - * Bootstraps Lando, this should - * - * 1. Emit bootstrap events - * 2. Auto detect and then load any plugins - * 3. Augment the lando object with additional methods - * - * You will want to use this after you instantiate `lando` via `new Lando(config)`. There - * are four available bootstrap levels and each provides different things. The run in - * the order presented. - * - * config Autodetects and loads any plugins and merges their returns into - * the global config - * - * tasks Autodetects and loads in any tasks along with recipe inits and - * init sources - * - * engine Autodetects and moves any plugin scripts, adds `engine`, `shell`, - * `scanUrls` and `utils` to the lando instance - * - * app Autodetects and loads in any `services` and `recipes` and also adds `yaml - * and `factory` to the lando instance. - * - * Check out `./bin/lando.js` in this repository for an example of bootstraping - * `lando` for usage in a CLI. - * - * @since 3.0.0 - * @alias lando.bootstrap - * @fires pre_bootstrap_config - * @fires pre_bootstrap_tasks - * @fires pre_bootstrap_engine - * @fires pre_bootstrap_app - * @fires post_bootstrap_config - * @fires post_bootstrap_tasks - * @fires post_bootstrap_engine - * @fires post_bootstrap_app - * @param {String} [level=app] Level with which to bootstrap Lando - * @return {Promise} A Promise - * @example - * // Bootstrap lando at default level and then exit - * lando.bootstrap().then(() => process.exit(0))l - */ - bootstrap( level?: string ): Promise< this >; - _bootstrap: any; - /** - * Gets a fully instantiated App instance. - * - * Lando will also scan parent directories if no app is found in `startFrom` - * - * @since 3.0.0 - * @alias lando.getApp - * @param {String} [startFrom=process.cwd()] The directory to start looking for an app - * @param {Boolean} [warn=true] Show a warning if we can't find an app - * @return {App} Returns an instantiated App instandce. - * @example - * const app = lando.getApp('/path/to/my/app') - */ - getApp( startFrom?: string, warn?: boolean ): import('./app'); +type PluginDirEntry = string | { + path: string; + subdir: string; + namespace: string; +}; + +export interface LandoConfig extends Record { + composeBin: string; + disablePlugins: string[]; + dockerBin: string; + dockerBinPath: string; + env: NodeJS.ProcessEnv; + home: string; + isArmed: boolean; + logLevel: string; + node: string; + os: { + type: string; + platform: NodeJS.Platform; + release: string; + arch: string; + }, + pluginDirs: PluginDirEntry[]; + plugins: string[]; + userConfRoot: string; + + configSources?: string[]; + landoFile?: string; + preLandoFiles?: string[]; + postLandoFiles?: string[]; + proxyName?: string; + proxyContainer?: string; + domain?: string; + version?: string; + networkBridge?: string; + + tooling?: Record>; +} + +export interface LandoTask { + command: string; + level?: string; + describe: string; + options: Record; + run: (options: Record) => Promise; } export = Lando; +declare class Lando { + constructor(options?: Partial); + BOOTSTRAP_LEVELS: { + config: number; + tasks: number; + engine: number; + app: number; + }; + config: LandoConfig; + Promise: any; + tasks: LandoTask[]; + cache: import("lando/lib/cache"); + cli: import("lando/lib/cli"); + log: import("lando/lib/logger"); + metrics: import("lando/lib/metrics"); + error: import("lando/lib/error"); + events: import("lando/lib/events"); + user: typeof import("lando/lib/user"); + + engine: import("lando/lib/engine"); + + /** + * Bootstraps Lando, this should + * + * 1. Emit bootstrap events + * 2. Auto detect and then load any plugins + * 3. Augment the lando object with additional methods + * + * You will want to use this after you instantiate `lando` via `new Lando(config)`. There + * are four available bootstrap levels and each provides different things. The run in + * the order presented. + * + * config Autodetects and loads any plugins and merges their returns into + * the global config + * + * tasks Autodetects and loads in any tasks along with recipe inits and + * init sources + * + * engine Autodetects and moves any plugin scripts, adds `engine`, `shell`, + * `scanUrls` and `utils` to the lando instance + * + * app Autodetects and loads in any `services` and `recipes` and also adds `yaml + * and `factory` to the lando instance. + * + * Check out `./bin/lando.js` in this repository for an example of bootstraping + * `lando` for usage in a CLI. + * + * @since 3.0.0 + * @alias lando.bootstrap + * @fires pre_bootstrap_config + * @fires pre_bootstrap_tasks + * @fires pre_bootstrap_engine + * @fires pre_bootstrap_app + * @fires post_bootstrap_config + * @fires post_bootstrap_tasks + * @fires post_bootstrap_engine + * @fires post_bootstrap_app + * @param {String} [level=app] Level with which to bootstrap Lando + * @return {Promise} A Promise + * @example + * // Bootstrap lando at default level and then exit + * lando.bootstrap().then(() => process.exit(0))l + */ + bootstrap(level?: string): Promise; + _bootstrap: any; + /** + * Gets a fully instantiated App instance. + * + * Lando will also scan parent directories if no app is found in `startFrom` + * + * @since 3.0.0 + * @alias lando.getApp + * @param {String} [startFrom=process.cwd()] The directory to start looking for an app + * @param {Boolean} [warn=true] Show a warning if we can't find an app + * @return {App} Returns an instantiated App instandce. + * @example + * const app = lando.getApp('/path/to/my/app') + */ + getApp(startFrom?: string, warn?: boolean): import("lando/lib/app"); +} diff --git a/types/lando/lib/logger.d.ts b/types/lando/lib/logger.d.ts new file mode 100644 index 000000000..125bd5017 --- /dev/null +++ b/types/lando/lib/logger.d.ts @@ -0,0 +1,12 @@ +import winston = require("winston"); +export = Log; +declare class Log extends winston.Logger { + constructor({ logDir, logLevelConsole, logLevel, logName }?: { + logDir: any; + logLevelConsole?: string; + logLevel?: string; + logName?: string; + }); + sanitizedKeys: string[]; + alsoSanitize(key: any): void; +} diff --git a/types/lando/lib/metrics.d.ts b/types/lando/lib/metrics.d.ts new file mode 100644 index 000000000..257a1b546 --- /dev/null +++ b/types/lando/lib/metrics.d.ts @@ -0,0 +1,15 @@ +export = Metrics; +declare class Metrics { + constructor({ id, log, endpoints, data }?: { + id?: string; + log?: Log; + endpoints?: any[]; + data?: {}; + }); + id: string; + log: Log; + endpoints: any[]; + data: {}; + report(action?: string, data?: {}): any; +} +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/plugins.d.ts b/types/lando/lib/plugins.d.ts new file mode 100644 index 000000000..b84aee8e9 --- /dev/null +++ b/types/lando/lib/plugins.d.ts @@ -0,0 +1,33 @@ +export = Plugins; +declare class Plugins { + constructor(log?: Log); + registry: any[]; + log: Log; + /** + * Finds plugins + * + * @since 3.5.0 + * @alias lando.plugins.find + * @param {Array} dirs Directories to scan for plugins + * @param {Object} options Options to pass in + * @param {Array} [options.disablePlugins=[]] Array of plugin names to not load + * @param {Array} [options.plugins=[]] Array of additional plugins to consider loading + * @return {Array} Array of plugin metadata + */ + find(dirs: any[], { disablePlugins, plugins }?: { + disablePlugins?: any[]; + plugins?: any[]; + }): any[]; + /** + * Loads a plugin. + * + * @since 3.0.0 + * @alias lando.plugins.load + * @param {String} plugin The name of the plugin + * @param {String} [file=plugin.path] That path to the plugin + * @param {Object} [...injected] Something to inject into the plugin + * @return {Object} Data about our plugin. + */ + load(plugin: string, file?: string, ...injected: any[]): any; +} +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/promise.d.ts b/types/lando/lib/promise.d.ts new file mode 100644 index 000000000..3fb19bddc --- /dev/null +++ b/types/lando/lib/promise.d.ts @@ -0,0 +1 @@ +export = Promise; diff --git a/types/lando/lib/router.d.ts b/types/lando/lib/router.d.ts new file mode 100644 index 000000000..74ce36b97 --- /dev/null +++ b/types/lando/lib/router.d.ts @@ -0,0 +1,9 @@ +export function eventWrapper(name: any, daemon: any, events: any, data: any, run: any): any; +export function build(data: any, compose: any): any; +export function destroy(data: any, compose: any, docker: any): any; +export function exists(data: any, compose: any, docker: any, ids?: any[]): any; +export function logs(data: any, compose: any): any; +export function run(data: any, compose: any, docker: any, started?: boolean): any; +export function scan(data: any, compose: any, docker: any): any; +export function start(data: any, compose: any): any; +export function stop(data: any, compose: any, docker: any): any; diff --git a/types/lando/lib/scan.d.ts b/types/lando/lib/scan.d.ts new file mode 100644 index 000000000..f7618d22e --- /dev/null +++ b/types/lando/lib/scan.d.ts @@ -0,0 +1,6 @@ +declare function _exports(log?: Log): (urls: any[], { max, waitCodes }?: { + max?: Integer; + waitCode?: any[]; +}) => any[]; +export = _exports; +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/shell.d.ts b/types/lando/lib/shell.d.ts new file mode 100644 index 000000000..dd5679379 --- /dev/null +++ b/types/lando/lib/shell.d.ts @@ -0,0 +1,68 @@ +export = Shell; +declare class Shell { + constructor(log?: Log); + log: Log; + running: any[]; + stdout: any; + stderr: any; + /** + * Gets running processes. + * + * @since 3.0.0 + * @alias lando.shell.get + * @return {Array} An array of the currently running processes + */ + get(): any[]; + /** + * Runs a command. + * + * This is an abstraction method that: + * + * 1. Delegates to either node's native `spawn` or `exec` methods. + * 2. Promisifies the calling of these function + * 3. Handles `stdout`, `stdin` and `stderr` + * + * @since 3.0.0 + * @alias lando.shell.sh + * @see [extra exec options](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback) + * @see [extra spawn options](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) + * @param {Array} cmd The command to run as elements in an array. + * @param {Object} [opts] Options to help determine how the exec is run. + * @param {Boolean} [opts.mode='exec'] The mode to run in + * @param {Boolean} [opts.detached=false] Whether we are running in detached mode or not (deprecated) + * @param {Boolean} [opts.cwd=process.cwd()] The directory to run the command from + * @return {Promise} A promise with collected results if applicable. + * @example + * // Run a command in collect mode + * return lando.shell.sh(['ls', '-lsa', '/'], {mode: 'collect'}) + * + * // Catch and log any errors + * .catch(err => { + * lando.log.error(err); + * }) + * + * // Print the collected results of the command + * .then(results => { + * console.log(results); + * }); + */ + sh(cmd: any[], { mode, detached, cwd, cstdio, silent }?: { + mode?: boolean; + detached?: boolean; + cwd?: boolean; + }): Promise; + /** + * Returns the path of a specific command or binary. + * + * @since 3.0.0 + * @function + * @alias lando.shell.which + * @param {String} cmd A command to search for. + * @return {String|null} The path to the command or null. + * @example + * // Determine the location of the 'docker' command + * const which = lando.shell.which(DOCKER_EXECUTABLE); + */ + which(cmd: string): string | null; +} +import Log = require("lando/lib/logger"); diff --git a/types/lando/lib/table.d.ts b/types/lando/lib/table.d.ts new file mode 100644 index 000000000..fc878a119 --- /dev/null +++ b/types/lando/lib/table.d.ts @@ -0,0 +1,17 @@ +export = Table; +declare class Table { + constructor(data: any, { border, keyColor, joiner, sort }?: { + border?: boolean; + keyColor?: string; + joiner?: string; + sort?: boolean; + }, opts?: {}); + border: boolean; + joiner: string; + keyColor: string; + sort: boolean; + add(data: any, { joiner, sort }?: { + joiner?: string; + sort?: boolean; + }): void; +} diff --git a/types/lando/lib/user.d.ts b/types/lando/lib/user.d.ts new file mode 100644 index 000000000..c694cf158 --- /dev/null +++ b/types/lando/lib/user.d.ts @@ -0,0 +1,3 @@ +export function getUid(): string; +export function getGid(): string; +export function getUsername(): string; diff --git a/types/lando/lib/utils.d.ts b/types/lando/lib/utils.d.ts new file mode 100644 index 000000000..94dc134c6 --- /dev/null +++ b/types/lando/lib/utils.d.ts @@ -0,0 +1,37 @@ +export function getAppMounts(app: any): any; +export function dockerComposify(data: any): any; +export function appMachineName(data: any): string; +export function dumpComposeData(data: any, dir: any): any; +export function loadComposeFiles(files: any, dir: any): any; +export function getCliEnvironment(more?: {}): any; +export function getId(c: any): any; +export function getInfoDefaults(app: any): any; +export function getGlobals(app: any): any; +export function getServices(composeData: any): any; +export function getUser(service: any, info?: any[]): any; +export function metricsParse(app: any): { + app: any; + type: any; +}; +export function normalizer(data: any): any; +export function makeExecutable(files: any, base?: string): void; +export function moveConfig(src: any, dest?: string): string; +export function shellEscape(command: any, wrap?: boolean, args?: string[]): any; +export function toLandoContainer({ Names, Labels, Id, Status }: { + Names: any; + Labels: any; + Id: any; + Status: any; +}): { + id: any; + service: any; + name: any; + app: any; + src: any; + kind: string; + lando: boolean; + instance: any; + status: any; +}; +export function toObject(keys: any, data?: {}): any; +export function validateFiles(files?: any[], base?: string): any; diff --git a/types/lando/lib/yaml.d.ts b/types/lando/lib/yaml.d.ts new file mode 100644 index 000000000..1e5cb72cb --- /dev/null +++ b/types/lando/lib/yaml.d.ts @@ -0,0 +1,28 @@ +export = Yaml; +declare class Yaml { + constructor(log?: Log); + log: Log; + /** + * Loads a yaml object from a file. + * + * @since 3.0.0 + * @alias lando.yaml.load + * @param {String} file The path to the file to be loaded + * @return {Object} The loaded object + * @example + * // Add a string to the cache + * const thing = lando.yaml.load('/tmp/myfile.yml'); + */ + load(file: string): any; + /** + * Dumps an object to a YAML file + * + * @since 3.0.0 + * @alias lando.yaml.dump + * @param {String} file The path to the file to be loaded + * @param {Object} data The object to dump + * @return {String} Flename + */ + dump(file: string, data?: any): string; +} +import Log = require("lando/lib/logger"); diff --git a/types/lando/plugins/lando-core/lib/utils.d.ts b/types/lando/plugins/lando-core/lib/utils.d.ts new file mode 100644 index 000000000..e5e441e0f --- /dev/null +++ b/types/lando/plugins/lando-core/lib/utils.d.ts @@ -0,0 +1,16 @@ +import App from "lando/lib/app"; + +export interface AppInfo { + name: string; + location: string; + services: string[]; + [key: string]: unknown; +} + +export function getHostPath(mount: any): any; +export function getUrls(data: any, scan?: string[], secured?: string[], bindAddress?: string): any; +export function normalizePath(local: any, base?: string, excludes?: any[]): any; +export function normalizeOverrides(overrides: any, base?: string, volumes?: {}): any; +export function startTable(app: App): AppInfo; +export function stripPatch(version: any): any; +export function stripWild(versions: any): any; diff --git a/types/lando/plugins/lando-tooling/lib/build.d.ts b/types/lando/plugins/lando-tooling/lib/build.d.ts new file mode 100644 index 000000000..7b8df778d --- /dev/null +++ b/types/lando/plugins/lando-tooling/lib/build.d.ts @@ -0,0 +1,7 @@ +declare function _exports(config: any, injected: any): { + command: string; + describe: string; + run: (answers: Record) => Promise; + options: Record; +}; +export = _exports;