import axios from 'axios' ; import * as Mocha from 'mocha' ; import * as mkdirpCallback from 'mkdirp' ; import * as rimrafCallback from 'rimraf' ; import * as qrcode from 'qrcode-terminal' ; import { writeFile as writeFileCallback , exists as existsCallback , readFile as readFileCallback , } from 'fs' ; import * as globCallback from 'glob' ; import * as path from 'path' ; import { promisify } from 'util' ; const exists = promisify ( existsCallback ) ; const glob = promisify ( globCallback ) ; const mkdirp = promisify ( mkdirpCallback ) ; const rimraf = promisify ( rimrafCallback ) ; const writeFile = promisify ( writeFileCallback ) ; const readFile = promisify ( readFileCallback ) ; import 'trace' ; import 'clarify' ; Error . stackTraceLimit = 20 ; import { Docker } from 'docker-cli-js' ; export const docker = new Docker ( ) ; import { EOSManager } from '../eosManager' ; import { sleep } from '../utils' ; import { generateTypes } from '../contracts' ; import { ConfigManager } from '../configManager' ; import * as spinner from './logIndicator' ; hidden const WORKING_DIRECTORY = process . cwd ( ) ; hidden const CONFIG_DIRECTORY = path . join ( __dirname , '../eosio-config' ) ; hidden const TEMP_DOCKER_DIRECTORY = path . join ( __dirname , '.temp-docker' ) ; hidden const TEST_EXPECTED_DURATION = 5000 ; hidden const TEST_TIMEOUT_DURATION = 80000 ; hidden const MAX_CONNECTION_ATTEMPTS = 20 ; author Kevin Brown <github.com/thekevinbrown>
 *  returns const versionFromUrl = ( url ) => { const pattern = /\/(v\d+\.\d+\.\d+)\//g ; const result = pattern . exec ( url ) ; if ( ! result ) throw new Error ( `Could not extract version number from url: '${ url }'` ) ; return result [ 1 ] ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  author Johan Nordberg <github.com/jnordberg>
 *  returns const dockerImageName = async ( ) => { await ConfigManager . loadConfigFromDisk ( ) ; return `lamington:eos.${ versionFromUrl ( ConfigManager . eos ) }-cdt.${ versionFromUrl ( ConfigManager . cdt ) }-contracts.${ ConfigManager . contracts }` ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  returns export const imageExists = async ( ) => { const result = await docker . command ( `images ${ await dockerImageName ( ) }` ) ; return result . images . length > 0 ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
 *  author Johan Nordberg <github.com/jnordberg>
  export const buildImage = async ( ) => { spinner . create ( 'Building docker image' ) ; await rimraf ( TEMP_DOCKER_DIRECTORY ) ; await mkdirp ( TEMP_DOCKER_DIRECTORY , { } ) ; const systemDeps = [ 'build-essential' , 'ca-certificates' , 'cmake' , 'curl' , 'git' , 'wget' ] ; await writeFile ( path . join ( TEMP_DOCKER_DIRECTORY , 'Dockerfile' ) , `
		FROM ubuntu:18.04

		RUN apt-get update --fix-missing && apt-get install -y --no-install-recommends ${ systemDeps . join ( ' ' ) }
		RUN wget ${ ConfigManager . cdt } && apt-get install -y ./*.deb && rm -f *.deb
		RUN wget ${ ConfigManager . eos } && apt-get install -y ./*.deb && rm -f *.deb
		RUN eos_ver=$(ls /usr/opt/eosio | head -n 1); \
			git clone --depth 1 --branch ${ ConfigManager . contracts } https://github.com/EOSIO/eosio.contracts.git /usr/opt/eosio.contracts &&\
			cd /usr/opt/eosio.contracts && ./build.sh -e "/usr/opt/eosio/$eos_ver" -c /usr/opt/eosio.cdt
		RUN apt-get clean && rm -rf /tmp/* /var/tmp/* && rm -rf /var/lib/apt/lists/*
		` . replace ( /\t/gm , '' ) ) ; await docker . command ( `build -t ${ await dockerImageName ( ) } "${ TEMP_DOCKER_DIRECTORY }"` ) ; await rimraf ( TEMP_DOCKER_DIRECTORY ) ; spinner . end ( 'Built docker image' ) ; } ; author Kevin Brown <github.com/thekevinbrown>
  export const startContainer = async ( ) => { await docker . command ( `run
				--rm
				--name lamington
				-d
				-p 8888:8888
				-p 9876:9876
				--mount type=bind,src="${ WORKING_DIRECTORY }",dst=/opt/eosio/bin/project
				--mount type=bind,src="${ __dirname }/../scripts",dst=/opt/eosio/bin/scripts
				--mount type=bind,src="${ CONFIG_DIRECTORY }",dst=/mnt/dev/config
				-w "/opt/eosio/bin/"
				${ await dockerImageName ( ) }
				/bin/bash -c "./scripts/init_blockchain.sh"` . replace ( /\n/gm , '' ) . replace ( /\t/gm , ' ' ) ) ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
 *  returns export const stopContainer = async ( ) => { spinner . create ( 'Stopping EOS Docker Container' ) ; try { await docker . command ( 'stop lamington' ) ; spinner . end ( 'Stopped EOS Docker Container' ) ; } catch ( err ) { spinner . fail ( err ) ; } } ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
 *  returns export const untilEosIsReady = async ( attempts = MAX_CONNECTION_ATTEMPTS ) => { spinner . create ( 'Waiting for EOS' ) ; let attempt = 0 ; while ( attempt < attempts ) { attempt ++ ; if ( await eosIsReady ( ) ) { spinner . end ( 'EOS is ready' ) ; return true ; } await sleep ( 1000 ) ; } spinner . fail ( `Failed to connect with an EOS instance` ) ; throw new Error ( `Could not contact EOS after trying for ${ attempts } second(s).` ) ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  returns export const eosIsReady = async ( ) => { try { const info = await axios . get ( 'http://localhost:8888/v1/chain/get_info' ) ; return info && info . status === 200 ; } catch ( error ) { return false ; } } ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
  export const startEos = async ( ) => { spinner . create ( 'Starting EOS docker container' ) ; if ( ! ( await imageExists ( ) ) ) { console . log ( '--------------------------------------------------------------' ) ; console . log ( 'Docker image does not yet exist. Building...' ) ; console . log ( 'Note: This will take a few minutes but only happens once for each version of the EOS tools you use.' ) ; console . log ( ) ; console . log ( `We've prepared some hold music for you: https://youtu.be/6g4dkBF5anU` ) ; console . log ( ) ; qrcode . generate ( 'https://youtu.be/6g4dkBF5anU' ) ; await buildImage ( ) ; } try { await startContainer ( ) ; await untilEosIsReady ( ) ; console . log ( '                                        \n\
==================================================== \n\
                                                     \n\
      EOS running, admin account created.            \n\
                                                     \n\
      RPC: http://localhost:8888                     \n\
	  Docker Container: lamington                    \n\
                                                     \n\
==================================================== \n' ) ; spinner . end ( 'Started EOS docker container' ) ; } catch ( error ) { spinner . fail ( 'Failed to start the EOS container' ) ; console . log ( ` --> ${ error }` ) ; process . exit ( 1 ) ; } } ; author Kevin Brown <github.com/thekevinbrown>
 *  note export const runTests = async ( ) => { EOSManager . initWithDefaults ( ) ; if ( await exists ( path . join ( WORKING_DIRECTORY , 'tsconfig.json' ) ) ) { require ( 'ts-mocha' ) ; } if ( await exists ( path . join ( WORKING_DIRECTORY , '.env' ) ) ) { require ( 'dotenv' ) . config ( { path : path . join ( WORKING_DIRECTORY , '.env' ) } ) ; } const files = [ ... ( await glob ( '{test,tests}/**/*.{js,ts}' ) ) , ... ( await glob ( '!(node_modules)/**/*.{test,spec}.{js,ts}' ) ) , ] ; const mocha = new Mocha ( ) ; for ( const testFile of files ) { mocha . addFile ( path . join ( WORKING_DIRECTORY , testFile ) ) ; } mocha . slow ( TEST_EXPECTED_DURATION ) ; mocha . timeout ( TEST_TIMEOUT_DURATION ) ; mocha . reporter ( ConfigManager . testReporter ) ; mocha . bail ( ConfigManager . bailOnFailure ) ; await new Promise ( ( resolve , reject ) => mocha . run ( ( failures ) => { if ( failures ) return reject ( failures ) ; return resolve ( ) ; } ) ) ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
 *  param match export const buildAll = async ( match ? ) => { const errors = [ ] ; let contracts = await glob ( '!(node_modules)/**/*.cpp' ) ; contracts = filterMatches ( includeMatches ( onlyMatches ( contracts , match || [ '\\.cpp$' ] ) ) ) ; if ( contracts . length === 0 ) { console . error ( ) ; console . error ( 'Could not find any smart contracts to build.' ) ; process . exit ( 1 ) ; } console . log ( `BUILDING ${ contracts . length } SMART CONTRACT${ contracts . length > 1 ? 'S' : '' }\n` ) ; for ( const contract of contracts ) { try { await build ( contract ) ; } catch ( error ) { errors . push ( { message : `Failed to compile contract ${ contract }` , error , } ) ; } } if ( errors . length > 0 ) { for ( const error of errors ) console . error ( error . message , '\n' , ' -> ' , error . error ) ; throw new Error ( `${ errors . length } contract${ errors . length > 0 ? 's' : '' } failed to compile. Quitting.` ) ; } } ; const onlyMatches = ( paths , matches = [ ] ) => { return paths . filter ( ( filePath ) => { return matches . reduce < boolean > ( ( result , str ) => { const pattern = new RegExp ( str , 'gi' ) ; return result || pattern . test ( filePath ) ; } , false ) ; } ) ; } ; const includeMatches = ( paths ) => { return paths . filter ( ( filePath ) => { return ConfigManager . include . reduce < boolean > ( ( result , match ) => { const pattern = new RegExp ( match , 'gi' ) ; return result || pattern . test ( filePath ) ; } , false ) ; } ) ; } ; const filterMatches = ( paths ) => { return paths . filter ( ( filePath ) => { return ! ConfigManager . exclude . reduce < boolean > ( ( result , match ) => { const pattern = new RegExp ( match , 'gi' ) ; return result || pattern . test ( filePath ) ; } , false ) ; } ) ; } ; author Kevin Brown <github.com/thekevinbrown>
 *  note param filePath returns export const pathToIdentifier = ( filePath ) => filePath . substr ( 0 , filePath . lastIndexOf ( '.' ) ) ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
 *  param contractPath export const build = async ( contractPath ) => { await compileContract ( contractPath ) ; spinner . create ( `Generating type definitions:` + contractPath ) ; try { await generateTypes ( pathToIdentifier ( contractPath ) ) ; spinner . end ( `Generated type definitions: ` + contractPath ) ; } catch ( error ) { spinner . fail ( `Failed to generate type definitions: ` + contractPath ) ; console . log ( ` --> ${ error . message }` ) ; } } ; author Kevin Brown <github.com/thekevinbrown>
 *  param contractPath returns export const outputPathForContract = ( contractPath ) => path . join ( ConfigManager . outDir , 'compiled_contracts' , path . dirname ( contractPath ) ) ; author Kevin Brown <github.com/thekevinbrown>
 *  author Mitch Pierias <github.com/MitchPierias>
 *  param contractPath export const compileContract = async ( contractPath ) => { spinner . create ( `Compiling contract: ` + contractPath ) ; const basename = path . basename ( contractPath , '.cpp' ) ; if ( ! ( await exists ( contractPath ) ) ) { spinner . fail ( `Couldn't locate contract at ${ contractPath }. Are you sure used the correct contract identifier when trying to build the contract?` ) ; throw new Error ( "Contract doesn't exist on disk." ) ; } const outputPath = outputPathForContract ( contractPath ) ; const buildFlagsPathComponents = path . parse ( contractPath ) ; const buildFlagsPath = buildFlagsPathComponents . dir + '/' + buildFlagsPathComponents . name + '.lamflags' ; let buildFlags = '' ; if ( await exists ( buildFlagsPath ) ) { const data = await readFile ( buildFlagsPath ) ; console . log ( '\n Adding build flags to compile command: ' + data . toString ( ) ) ; buildFlags = data . toString ( ) ; } await docker . command ( `exec lamington /opt/eosio/bin/scripts/compile_contract.sh "/${ path . join ( 'opt' , 'eosio' , 'bin' , 'project' , contractPath ) }" "${ outputPath }" "${ basename }" ${ buildFlags }` ) . catch ( ( err ) => { spinner . fail ( 'Failed to compile' ) ; console . log ( ` --> ${ err }` ) ; throw err ; } ) ; spinner . end ( `Compiled contract output into folder: ` + outputPath ) ; } ;