Преглед на файлове

Merge pull request #91 from ckeditor/t/78

Introduced simple log mechanism for dev tasks output.
Piotrek Koszuliński преди 9 години
родител
ревизия
d5ad6a2c52

+ 10 - 6
dev/tasks/dev/tasks.js

@@ -13,17 +13,21 @@ const installTask = require( './tasks/install' );
 const pluginCreateTask = require( './tasks/create-package' );
 const updateTask = require( './tasks/update' );
 const relinkTask = require( './tasks/relink' );
+const log = require( './utils/log' );
 
 module.exports = ( config ) => {
 	const ckeditor5Path = process.cwd();
 	const packageJSON = require( '../../../package.json' );
 
+	// Configure logging.
+	log.configure( console.log, console.error );
+
 	gulp.task( 'init', () => {
-		initTask( installTask, ckeditor5Path, packageJSON, config.WORKSPACE_DIR, console.log );
+		initTask( installTask, ckeditor5Path, packageJSON, config.WORKSPACE_DIR );
 	} );
 
 	gulp.task( 'create-package', ( done ) => {
-		pluginCreateTask( ckeditor5Path, config.WORKSPACE_DIR, console.log )
+		pluginCreateTask( ckeditor5Path, config.WORKSPACE_DIR )
 			.then( done )
 			.catch( ( error )  => done( error ) );
 	} );
@@ -36,15 +40,15 @@ module.exports = ( config ) => {
 			}
 		} );
 
-		updateTask( ckeditor5Path, packageJSON, config.WORKSPACE_DIR, console.log, options[ 'npm-update' ] );
+		updateTask( ckeditor5Path, packageJSON, config.WORKSPACE_DIR, options[ 'npm-update' ] );
 	} );
 
 	gulp.task( 'status', () => {
-		statusTask( ckeditor5Path, packageJSON, config.WORKSPACE_DIR, console.log, console.error );
+		statusTask( ckeditor5Path, packageJSON, config.WORKSPACE_DIR );
 	} );
 
 	gulp.task( 'relink', () => {
-		relinkTask( ckeditor5Path, packageJSON, config.WORKSPACE_DIR, console.log, console.error );
+		relinkTask( ckeditor5Path, packageJSON, config.WORKSPACE_DIR );
 	} );
 
 	gulp.task( 'install', () => {
@@ -56,7 +60,7 @@ module.exports = ( config ) => {
 		} );
 
 		if ( options.package ) {
-			installTask( ckeditor5Path, config.WORKSPACE_DIR, options.package, console.log );
+			installTask( ckeditor5Path, config.WORKSPACE_DIR, options.package );
 		} else {
 			throw new Error( 'Please provide a package to install: gulp dev-install --plugin <path|GitHub URL|name>' );
 		}

+ 8 - 8
dev/tasks/dev/tasks/create-package.js

@@ -9,6 +9,7 @@ const inquiries = require( '../utils/inquiries' );
 const git = require( '../utils/git' );
 const tools = require( '../utils/tools' );
 const path = require( 'path' );
+const log = require( '../utils/log' );
 
 /**
  * 1. Ask for new package name.
@@ -24,10 +25,9 @@ const path = require( 'path' );
  *
  * @param {String} ckeditor5Path Path to main CKEditor5 repository.
  * @param {String} workspaceRoot Relative path to workspace root.
- * @param {Function} writeln Function for log output.
  * @returns {Promise} Returns promise fulfilled after task is done.
  */
-module.exports = ( ckeditor5Path, workspaceRoot, writeln ) => {
+module.exports = ( ckeditor5Path, workspaceRoot ) => {
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
 	const fileStructure = {
 		'./': [
@@ -68,16 +68,16 @@ module.exports = ( ckeditor5Path, workspaceRoot, writeln ) => {
 		.then( result => {
 			gitHubUrl = result;
 
-			writeln( `Initializing repository ${ repositoryPath }...` );
+			log.out( `Initializing repository ${ repositoryPath }...` );
 			git.initializeRepository( repositoryPath );
 
-			writeln( `Copying files into ${ repositoryPath }...` );
+			log.out( `Copying files into ${ repositoryPath }...` );
 
 			for ( let destination in fileStructure ) {
 				tools.copy( fileStructure[ destination ], path.join( repositoryPath, destination ) );
 			}
 
-			writeln( `Updating package.json files...` );
+			log.out( `Updating package.json files...` );
 			tools.updateJSONFile( path.join( repositoryPath, 'package.json' ), ( json ) => {
 				json.name = packageName;
 				json.version = packageVersion;
@@ -95,13 +95,13 @@ module.exports = ( ckeditor5Path, workspaceRoot, writeln ) => {
 				return json;
 			} );
 
-			writeln( `Creating initial commit...` );
+			log.out( `Creating initial commit...` );
 			git.initialCommit( packageName, repositoryPath );
 
-			writeln( `Linking ${ packageName } to node_modules...` );
+			log.out( `Linking ${ packageName } to node_modules...` );
 			tools.linkDirectories( repositoryPath, path.join( ckeditor5Path, 'node_modules', packageName ) );
 
-			writeln( `Running npm install in ${ packageName }.` );
+			log.out( `Running npm install in ${ packageName }.` );
 			tools.npmInstall( repositoryPath );
 		} );
 };

+ 5 - 5
dev/tasks/dev/tasks/init.js

@@ -6,6 +6,7 @@
 'use strict';
 
 const tools = require( '../utils/tools' );
+const log = require( '../utils/log' );
 
 /**
  * 1. Get CKEditor5 dependencies from package.json file.
@@ -15,19 +16,18 @@ const tools = require( '../utils/tools' );
  * @param {String} ckeditor5Path Path to main CKEditor5 repository.
  * @param {Object} packageJSON Parsed package.json file from CKEditor5 repository.
  * @param {String} workspaceRoot Relative path to workspace root.
- * @param {Function} writeln Function for log output.
  */
-module.exports = ( installTask, ckeditor5Path, packageJSON, workspaceRoot, writeln ) => {
+module.exports = ( installTask, ckeditor5Path, packageJSON, workspaceRoot ) => {
 	// Get all CKEditor dependencies from package.json.
 	const dependencies = tools.getCKEditorDependencies( packageJSON.dependencies );
 
 	if ( dependencies ) {
 		for ( let dependency in dependencies ) {
 			const repositoryURL = dependencies[ dependency ];
-			writeln( `\x1b[1m\x1b[36m${ dependency }\x1b[0m` );
-			installTask( ckeditor5Path, workspaceRoot, repositoryURL, writeln );
+			log.out( `\x1b[1m\x1b[36m${ dependency }\x1b[0m` );
+			installTask( ckeditor5Path, workspaceRoot, repositoryURL );
 		}
 	} else {
-		writeln( 'No CKEditor5 dependencies (ckeditor5-) found in package.json file.' );
+		log.out( 'No CKEditor5 dependencies (ckeditor5-) found in package.json file.' );
 	}
 };

+ 11 - 11
dev/tasks/dev/tasks/install.js

@@ -8,6 +8,7 @@
 const git = require( '../utils/git' );
 const tools = require( '../utils/tools' );
 const path = require( 'path' );
+const log = require( '../utils/log' );
 
 /**
  * This tasks install specified package in development mode. It can be executed by typing:
@@ -26,9 +27,8 @@ const path = require( 'path' );
  * @param {String} ckeditor5Path Absolute path to `ckeditor5` repository.
  * @param {String} workspaceRoot Relative path to workspace root directory.
  * @param {String} name Name of the NPM package or GitHub URL.
- * @param {Function} writeln Function used to report progress to the console.
  */
-module.exports = ( ckeditor5Path, workspaceRoot, name, writeln ) => {
+module.exports = ( ckeditor5Path, workspaceRoot, name ) => {
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
 	let repositoryPath;
 	let dependency;
@@ -41,7 +41,7 @@ module.exports = ( ckeditor5Path, workspaceRoot, name, writeln ) => {
 		const packageName = tools.readPackageName( repositoryPath );
 
 		if ( packageName ) {
-			writeln( `Plugin located at ${ repositoryPath }.` );
+			log.out( `Plugin located at ${ repositoryPath }.` );
 			urlInfo = {
 				name: packageName
 			};
@@ -58,7 +58,7 @@ module.exports = ( ckeditor5Path, workspaceRoot, name, writeln ) => {
 
 	// Check if name is NPM package.
 	if ( !urlInfo ) {
-		writeln( `Not a GitHub URL. Trying to get GitHub URL from NPM package...` );
+		log.out( `Not a GitHub URL. Trying to get GitHub URL from NPM package...` );
 		const url = tools.getGitUrlFromNpm( name );
 
 		if ( url ) {
@@ -71,33 +71,33 @@ module.exports = ( ckeditor5Path, workspaceRoot, name, writeln ) => {
 		repositoryPath = path.join( workspaceAbsolutePath, urlInfo.name );
 
 		if ( tools.isDirectory( repositoryPath ) ) {
-			writeln( `Directory ${ repositoryPath } already exists.` );
+			log.out( `Directory ${ repositoryPath } already exists.` );
 		} else {
-			writeln( `Cloning ${ urlInfo.name } into ${ repositoryPath }...` );
+			log.out( `Cloning ${ urlInfo.name } into ${ repositoryPath }...` );
 			git.cloneRepository( urlInfo, workspaceAbsolutePath );
 		}
 
 		// Checkout to specified branch if one is provided.
 		if ( urlInfo.branch ) {
-			writeln( `Checking ${ urlInfo.name } to ${ urlInfo.branch }...` );
+			log.out( `Checking ${ urlInfo.name } to ${ urlInfo.branch }...` );
 			git.checkout( repositoryPath, urlInfo.branch );
 		}
 
 		// Run `npm install` in new repository.
-		writeln( `Running "npm install" in ${ urlInfo.name }...` );
+		log.out( `Running "npm install" in ${ urlInfo.name }...` );
 		tools.npmInstall( repositoryPath );
 
 		const linkPath = path.join( ckeditor5Path, 'node_modules', urlInfo.name );
 
 		if ( tools.isDirectory( linkPath ) ) {
-			writeln( `Uninstalling ${ urlInfo.name } from CKEditor5 node_modules...` );
+			log.out( `Uninstalling ${ urlInfo.name } from CKEditor5 node_modules...` );
 			tools.npmUninstall( ckeditor5Path, urlInfo.name );
 		}
 
-		writeln( `Linking ${ linkPath } to ${ repositoryPath }...` );
+		log.out( `Linking ${ linkPath } to ${ repositoryPath }...` );
 		tools.linkDirectories( repositoryPath, linkPath );
 
-		writeln( `Adding ${ urlInfo.name } dependency to CKEditor5 package.json...` );
+		log.out( `Adding ${ urlInfo.name } dependency to CKEditor5 package.json...` );
 		tools.updateJSONFile( path.join( ckeditor5Path, 'package.json' ), ( json ) => {
 			json.dependencies = json.dependencies || {};
 			json.dependencies[ urlInfo.name ] = dependency;

+ 6 - 7
dev/tasks/dev/tasks/relink.js

@@ -7,6 +7,7 @@
 
 const tools = require( '../utils/tools' );
 const path = require( 'path' );
+const log = require( '../utils/log' );
 
 /**
  * 1. Get CKEditor5 dependencies from package.json file.
@@ -16,10 +17,8 @@ const path = require( 'path' );
  * @param {String} ckeditor5Path Path to main CKEditor5 repository.
  * @param {Object} packageJSON Parsed package.json file from CKEditor5 repository.
  * @param {String} workspaceRoot Relative path to workspace root.
- * @param {Function} writeln Function for log output.
- * @param {Function} writeError Function of error output
  */
-module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, writeln, writeError ) => {
+module.exports = ( ckeditor5Path, packageJSON, workspaceRoot ) => {
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
 
 	// Get all CKEditor dependencies from package.json.
@@ -36,17 +35,17 @@ module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, writeln, writeErro
 				// Check if repository's directory exists.
 				if ( directories.indexOf( dependency ) > -1 ) {
 					try {
-						writeln( `Linking ${ repositoryURL }...` );
+						log.out( `Linking ${ repositoryURL }...` );
 						tools.linkDirectories( repositoryAbsolutePath, path.join( ckeditor5Path, 'node_modules', dependency ) );
 					} catch ( error ) {
-						writeError( error );
+						log.err( error );
 					}
 				}
 			}
 		} else {
-			writeln( 'No CKEditor5 plugins in development mode.' );
+			log.out( 'No CKEditor5 plugins in development mode.' );
 		}
 	} else {
-		writeln( 'No CKEditor5 dependencies found in package.json file.' );
+		log.out( 'No CKEditor5 dependencies found in package.json file.' );
 	}
 };

+ 6 - 7
dev/tasks/dev/tasks/status.js

@@ -8,6 +8,7 @@
 const tools = require( '../utils/tools' );
 const git = require( '../utils/git' );
 const path = require( 'path' );
+const log = require( '../utils/log' );
 
 /**
  * 1. Get CKEditor5 dependencies from package.json file.
@@ -17,10 +18,8 @@ const path = require( 'path' );
  * @param {String} ckeditor5Path Path to main CKEditor5 repository.
  * @param {Object} packageJSON Parsed package.json file from CKEditor5 repository.
  * @param {String} workspaceRoot Relative path to workspace root.
- * @param {Function} writeln Function for log output.
- * @param {Function} writeError Function of error output
  */
-module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, writeln, writeError ) => {
+module.exports = ( ckeditor5Path, packageJSON, workspaceRoot ) => {
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
 
 	// Get all CKEditor dependencies from package.json.
@@ -38,16 +37,16 @@ module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, writeln, writeErro
 				if ( directories.indexOf( dependency ) > -1 ) {
 					try {
 						status = git.getStatus( repositoryAbsolutePath );
-						writeln( `\x1b[1m\x1b[36m${ dependency }\x1b[0m\n${ status.trim() }` );
+						log.out( `\x1b[1m\x1b[36m${ dependency }\x1b[0m\n${ status.trim() }` );
 					} catch ( error ) {
-						writeError( error );
+						log.err( error );
 					}
 				}
 			}
 		} else {
-			writeln( 'No CKEditor5 plugins in development mode.' );
+			log.out( 'No CKEditor5 plugins in development mode.' );
 		}
 	} else {
-		writeln( 'No CKEditor5 dependencies found in package.json file.' );
+		log.out( 'No CKEditor5 dependencies found in package.json file.' );
 	}
 };

+ 8 - 8
dev/tasks/dev/tasks/update.js

@@ -8,6 +8,7 @@
 const tools = require( '../utils/tools' );
 const git = require( '../utils/git' );
 const path = require( 'path' );
+const log = require( '../utils/log' );
 
 /**
  * 1. Get CKEditor5 dependencies from package.json file.
@@ -17,11 +18,10 @@ const path = require( 'path' );
  * @param {String} ckeditor5Path Path to main CKEditor5 repository.
  * @param {Object} packageJSON Parsed package.json file from CKEditor5 repository.
  * @param {String} workspaceRoot Relative path to workspace root.
- * @param {Function} writeln Function for log output.
  * @param {Boolean} runNpmUpdate When set to true `npm update` will be executed inside each plugin repository
  * and inside CKEditor 5 repository.
  */
-module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, writeln, runNpmUpdate ) => {
+module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, runNpmUpdate ) => {
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
 
 	// Get all CKEditor dependencies from package.json.
@@ -38,27 +38,27 @@ module.exports = ( ckeditor5Path, packageJSON, workspaceRoot, writeln, runNpmUpd
 
 				// Check if repository's directory already exists.
 				if ( directories.indexOf( urlInfo.name ) > -1 ) {
-					writeln( `Checking out ${ urlInfo.name } to ${ urlInfo.branch }...` );
+					log.out( `Checking out ${ urlInfo.name } to ${ urlInfo.branch }...` );
 					git.checkout( repositoryAbsolutePath, urlInfo.branch );
 
-					writeln( `Pulling changes to ${ urlInfo.name }...` );
+					log.out( `Pulling changes to ${ urlInfo.name }...` );
 					git.pull( repositoryAbsolutePath, urlInfo.branch );
 
 					if ( runNpmUpdate ) {
-						writeln( `Running "npm update" in ${ urlInfo.name }...` );
+						log.out( `Running "npm update" in ${ urlInfo.name }...` );
 						tools.npmUpdate( repositoryAbsolutePath );
 					}
 				}
 			}
 
 			if ( runNpmUpdate ) {
-				writeln( `Running "npm update" in CKEditor5 repository...` );
+				log.out( `Running "npm update" in CKEditor5 repository...` );
 				tools.npmUpdate( ckeditor5Path );
 			}
 		} else {
-			writeln( 'No CKEditor5 plugins in development mode.' );
+			log.out( 'No CKEditor5 plugins in development mode.' );
 		}
 	} else {
-		writeln( 'No CKEditor5 dependencies found in package.json file.' );
+		log.out( 'No CKEditor5 dependencies found in package.json file.' );
 	}
 };

+ 1 - 1
dev/tasks/dev/utils/git.js

@@ -111,7 +111,7 @@ module.exports = {
 	 * @returns {String} Executed command's result.
 	 */
 	getStatus( repositoryPath ) {
-		return tools.shExec( `cd ${ repositoryPath } && git status --porcelain -sb` );
+		return tools.shExec( `cd ${ repositoryPath } && git status --porcelain -sb`, false );
 	},
 
 	/**

+ 56 - 0
dev/tasks/dev/utils/log.js

@@ -0,0 +1,56 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+let logOut;
+let logErr;
+
+module.exports = {
+	/**
+	 * Configure login output functions.
+	 *
+	 * 		log.configure( logOut, logErr );
+	 *
+	 * 		function logOut( message ) {
+	 * 			// Save output to file.
+	 * 			...
+	 * 		}
+	 *
+	 * 		function logErr( message) {
+	 * 			// Save error to file.
+	 * 			...
+	 * 		}
+	 *
+	 * @param {Function} stdout Function to be used to log standard output.
+	 * @param {Function} stderr Function to be used to log standard error.
+	 */
+	configure( stdout, stderr ) {
+		logOut = stdout;
+		logErr = stderr;
+	},
+
+	/**
+	 * Logs output using function provided in {@link configure}.
+	 *
+	 * @param {String} message Message to be logged.
+	 */
+	out( message ) {
+		if ( logOut ) {
+			logOut( message );
+		}
+	},
+
+	/**
+	 * Logs errors using function provided in {@link #configure}.
+	 *
+	 * @param {String} message Message to be logged.
+	 */
+	err( message ) {
+		if ( logErr ) {
+			logErr( message );
+		}
+	}
+};

+ 19 - 6
dev/tasks/dev/utils/tools.js

@@ -6,29 +6,42 @@
 'use strict';
 
 const dependencyRegExp = /^ckeditor5-/;
+const log = require( '../utils/log' );
 
 module.exports = {
 
 	/**
 	 * Executes a shell command.
 	 *
-	 * @param command {String} The command to be executed.
+	 * @param {String} command The command to be executed.
+	 * @param {Boolean} [logOutput] When set to `false` command's output will not be logged. When set to `true`,
+	 * stdout and stderr will be logged. Defaults to `true`.
 	 * @returns {String} The command output.
 	 */
-	shExec( command ) {
+	shExec( command, logOutput ) {
 		const sh = require( 'shelljs' );
 		sh.config.silent = true;
+		logOutput = logOutput !== false;
 
 		const ret = sh.exec( command );
 
+		if ( logOutput ) {
+			if ( ret.stdout !== '' ) {
+				log.out( ret.stdout );
+			}
+
+			if ( ret.stderr !== '' ) {
+				log.err( ret.stderr );
+			}
+		}
+
 		if ( ret.code ) {
 			throw new Error(
-				'Error while executing `' + command + '`:\n\n' +
-				ret.output
+				`Error while executing ${ command }: ${ ret.stderr }`
 			);
 		}
 
-		return ret.output;
+		return ret.stdout;
 	},
 
 	/**
@@ -246,7 +259,7 @@ module.exports = {
      */
 	getGitUrlFromNpm( name ) {
 		try {
-			const info = JSON.parse( this.shExec( `npm view ${ name } repository --json` ) );
+			const info = JSON.parse( this.shExec( `npm view ${ name } repository --json`, false ) );
 
 			if ( info && info.type == 'git' ) {
 				return info.url;

+ 1 - 2
dev/tests/dev/create-package.js

@@ -16,7 +16,6 @@ const git = require( '../../tasks/dev/utils/git' );
 const path = require( 'path' );
 
 describe( 'dev-package-create', () => {
-	const emptyFn = () => { };
 	let spies;
 
 	const mainRepositoryPath = '/path/to/repository';
@@ -55,7 +54,7 @@ describe( 'dev-package-create', () => {
 	it( 'should exist', () => expect( packageCreateTask ).to.be.a( 'function' ) );
 
 	it( 'should create a package', () => {
-		return packageCreateTask( mainRepositoryPath, workspaceRoot, emptyFn ).then( () => {
+		return packageCreateTask( mainRepositoryPath, workspaceRoot ).then( () => {
 			expect( spies.getPackageName.calledOnce ).to.equal( true );
 			expect( spies.getPackageVersion.calledOnce ).to.equal( true );
 			expect( spies.getPackageGitHubUrl.calledOnce ).to.equal( true );

+ 4 - 5
dev/tests/dev/init.js

@@ -14,7 +14,6 @@ describe( 'dev-init', () => {
 	const initTask = require( '../../tasks/dev/tasks/init' );
 	const ckeditor5Path = 'path/to/ckeditor5';
 	const workspaceRoot = '..';
-	const emptyFn = () => {};
 
 	it( 'should get all ckedtior5- dependencies and execute dev-install on them', () => {
 		const getDependenciesSpy = sinon.spy( tools, 'getCKEditorDependencies' );
@@ -28,15 +27,15 @@ describe( 'dev-init', () => {
 		};
 		const deps = JSON.dependencies;
 
-		initTask( installSpy, ckeditor5Path, JSON, workspaceRoot, emptyFn );
+		initTask( installSpy, ckeditor5Path, JSON, workspaceRoot );
 
 		getDependenciesSpy.restore();
 
 		sinon.assert.calledOnce( getDependenciesSpy );
 		sinon.assert.calledWithExactly( getDependenciesSpy, deps );
 		sinon.assert.calledTwice( installSpy );
-		sinon.assert.calledWithExactly( installSpy.firstCall, ckeditor5Path, workspaceRoot, deps[ 'ckeditor5-core' ], emptyFn );
-		sinon.assert.calledWithExactly( installSpy.secondCall, ckeditor5Path, workspaceRoot, deps[ 'ckeditor5-plugin-devtest' ], emptyFn );
+		sinon.assert.calledWithExactly( installSpy.firstCall, ckeditor5Path, workspaceRoot, deps[ 'ckeditor5-core' ] );
+		sinon.assert.calledWithExactly( installSpy.secondCall, ckeditor5Path, workspaceRoot, deps[ 'ckeditor5-plugin-devtest' ] );
 	} );
 
 	it( 'should not call dev-install if no ckedtior5- dependencies', () => {
@@ -46,7 +45,7 @@ describe( 'dev-init', () => {
 			dependencies: {}
 		};
 
-		initTask( installSpy, ckeditor5Path, JSON, workspaceRoot, emptyFn );
+		initTask( installSpy, ckeditor5Path, JSON, workspaceRoot );
 
 		getDependenciesSpy.restore();
 

+ 6 - 6
dev/tests/dev/install.js

@@ -45,7 +45,7 @@ describe( 'dev-install', () => {
 		spies.isDirectory.onSecondCall().returns( false );
 		spies.isDirectory.onThirdCall().returns( true );
 
-		installTask( ckeditor5Path, workspacePath, repositoryUrl, () => {} );
+		installTask( ckeditor5Path, workspacePath, repositoryUrl );
 
 		sinon.assert.calledThrice( spies.isDirectory );
 		sinon.assert.calledOnce( spies.parseUrl );
@@ -86,7 +86,7 @@ describe( 'dev-install', () => {
 		spies.isDirectory.onSecondCall().returns( true );
 		spies.getGitUrlFromNpm.returns( repositoryUrl );
 
-		installTask( ckeditor5Path, workspacePath, moduleName, () => {} );
+		installTask( ckeditor5Path, workspacePath, moduleName );
 
 		sinon.assert.calledThrice( spies.isDirectory );
 		sinon.assert.calledTwice( spies.parseUrl );
@@ -126,7 +126,7 @@ describe( 'dev-install', () => {
 		spies.isDirectory.onSecondCall().returns( true );
 		spies.readPackageName.returns( moduleName );
 
-		installTask( ckeditor5Path, workspacePath, '../ckeditor5-core', () => {} );
+		installTask( ckeditor5Path, workspacePath, '../ckeditor5-core' );
 
 		sinon.assert.calledThrice( spies.isDirectory );
 		sinon.assert.calledOnce( spies.readPackageName );
@@ -137,7 +137,7 @@ describe( 'dev-install', () => {
 		spies.isDirectory.onSecondCall().returns( true );
 		spies.readPackageName.returns( moduleName );
 
-		installTask( ckeditor5Path, workspacePath, '/ckeditor5-core', () => {} );
+		installTask( ckeditor5Path, workspacePath, '/ckeditor5-core' );
 
 		sinon.assert.calledThrice( spies.isDirectory );
 		sinon.assert.calledOnce( spies.readPackageName );
@@ -148,7 +148,7 @@ describe( 'dev-install', () => {
 		spies.isDirectory.onSecondCall().returns( true );
 
 		expect( () => {
-			installTask( ckeditor5Path, workspacePath, moduleName, () => {} );
+			installTask( ckeditor5Path, workspacePath, moduleName );
 		} ).to.throw();
 
 		sinon.assert.calledOnce( spies.parseUrl );
@@ -162,7 +162,7 @@ describe( 'dev-install', () => {
 		spies.readPackageName.returns( null );
 
 		expect( () => {
-			installTask( ckeditor5Path, workspacePath, moduleName, () => {} );
+			installTask( ckeditor5Path, workspacePath, moduleName );
 		} ).to.throw();
 
 		sinon.assert.calledOnce( spies.parseUrl );

+ 26 - 0
dev/tests/dev/log.js

@@ -0,0 +1,26 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* global describe, it */
+
+'use strict';
+
+const sinon = require( 'sinon' );
+const log = require( '../../tasks/dev/utils/log' );
+
+describe( 'log', () => {
+	it( 'should log using configured functions', () => {
+		const outFn = sinon.spy();
+		const errFn = sinon.spy();
+
+		log.configure( outFn, errFn );
+
+		log.out( 'output' );
+		log.err( 'error' );
+
+		sinon.assert.calledWithExactly( outFn, 'output' );
+		sinon.assert.calledWithExactly( errFn, 'error' );
+	} );
+} );

+ 6 - 5
dev/tests/dev/relink.js

@@ -17,7 +17,6 @@ describe( 'dev-relink', () => {
 	const modulesPath = path.join( ckeditor5Path, 'node_modules' );
 	const workspaceRoot = '..';
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
-	const emptyFn = () => {};
 
 	it( 'should link dev repositories', () => {
 		const dirs = [ 'ckeditor5-core', 'ckeditor5-devtest' ];
@@ -32,7 +31,7 @@ describe( 'dev-relink', () => {
 			}
 		};
 
-		task( ckeditor5Path, json, workspaceRoot, emptyFn, emptyFn );
+		task( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();
@@ -53,7 +52,7 @@ describe( 'dev-relink', () => {
 			}
 		};
 
-		task( ckeditor5Path, json, workspaceRoot, emptyFn, emptyFn );
+		task( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();
@@ -73,7 +72,7 @@ describe( 'dev-relink', () => {
 			}
 		};
 
-		task( ckeditor5Path, json, workspaceRoot, emptyFn, emptyFn );
+		task( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();
@@ -96,8 +95,10 @@ describe( 'dev-relink', () => {
 			}
 		};
 		const writeErrorSpy = sinon.spy();
+		const log = require( '../../tasks/dev/utils/log' );
+		log.configure( () => {}, writeErrorSpy );
 
-		task( ckeditor5Path, json, workspaceRoot, emptyFn, writeErrorSpy );
+		task( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();

+ 6 - 5
dev/tests/dev/status.js

@@ -17,7 +17,6 @@ describe( 'dev-status', () => {
 	const ckeditor5Path = 'path/to/ckeditor5';
 	const workspaceRoot = '..';
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
-	const emptyFn = () => {};
 
 	it( 'should show status of dev repositories', () => {
 		const dirs = [ 'ckeditor5-core', 'ckeditor5-devtest' ];
@@ -32,7 +31,7 @@ describe( 'dev-status', () => {
 			}
 		};
 
-		statusTask( ckeditor5Path, json, workspaceRoot, emptyFn, emptyFn );
+		statusTask( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();
@@ -53,7 +52,7 @@ describe( 'dev-status', () => {
 			}
 		};
 
-		statusTask( ckeditor5Path, json, workspaceRoot, emptyFn, emptyFn );
+		statusTask( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();
@@ -73,7 +72,7 @@ describe( 'dev-status', () => {
 			}
 		};
 
-		statusTask( ckeditor5Path, json, workspaceRoot, emptyFn, emptyFn );
+		statusTask( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();
@@ -96,8 +95,10 @@ describe( 'dev-status', () => {
 			}
 		};
 		const writeErrorSpy = sinon.spy();
+		const log = require( '../../tasks/dev/utils/log' );
+		log.configure( () => {}, writeErrorSpy );
 
-		statusTask( ckeditor5Path, json, workspaceRoot, emptyFn, writeErrorSpy );
+		statusTask( ckeditor5Path, json, workspaceRoot );
 
 		getDependenciesSpy.restore();
 		getDirectoriesStub.restore();

+ 30 - 0
dev/tests/dev/tools.js

@@ -46,6 +46,36 @@ describe( 'utils', () => {
 				} ).to.throw();
 				sinon.assert.calledOnce( execStub );
 			} );
+
+			it( 'should output using log functions', () => {
+				const sh = require( 'shelljs' );
+				const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 0, stdout: 'out', stderr: 'err' } );
+				const log = require( '../../tasks/dev/utils/log' );
+				const outFn = sandbox.spy();
+				const errFn = sandbox.spy();
+				log.configure( outFn, errFn );
+
+				tools.shExec( 'command' );
+
+				sinon.assert.calledOnce( execStub );
+				sinon.assert.calledWithExactly( outFn, 'out' );
+				sinon.assert.calledWithExactly( errFn, 'err' );
+			} );
+
+			it( 'should not log when in silent mode', () => {
+				const sh = require( 'shelljs' );
+				const execStub = sandbox.stub( sh, 'exec' ).returns( { code: 0, stdout: 'out', stderr: 'err' } );
+				const log = require( '../../tasks/dev/utils/log' );
+				const outFn = sandbox.spy();
+				const errFn = sandbox.spy();
+				log.configure( outFn, errFn );
+
+				tools.shExec( 'command', false );
+
+				sinon.assert.calledOnce( execStub );
+				sinon.assert.notCalled( outFn );
+				sinon.assert.notCalled( errFn );
+			} );
 		} );
 
 		describe( 'linkDirectories', () => {

+ 4 - 5
dev/tests/dev/update.js

@@ -17,7 +17,6 @@ describe( 'dev-update', () => {
 	const ckeditor5Path = 'path/to/ckeditor5';
 	const workspaceRoot = '..';
 	const workspaceAbsolutePath = path.join( ckeditor5Path, workspaceRoot );
-	const emptyFn = () => {};
 	const spies = {};
 
 	beforeEach( () => {
@@ -43,7 +42,7 @@ describe( 'dev-update', () => {
 			}
 		};
 
-		updateTask( ckeditor5Path, json, workspaceRoot, emptyFn, true );
+		updateTask( ckeditor5Path, json, workspaceRoot, true );
 
 		sinon.assert.calledTwice( spies.pull );
 		sinon.assert.calledWithExactly( spies.pull.firstCall, path.join( workspaceAbsolutePath, dirs[ 0 ] ), 'master' );
@@ -67,7 +66,7 @@ describe( 'dev-update', () => {
 			}
 		};
 
-		updateTask( ckeditor5Path, json, workspaceRoot, emptyFn, false );
+		updateTask( ckeditor5Path, json, workspaceRoot, false );
 		sinon.assert.calledWithExactly( spies.pull.firstCall, path.join( workspaceAbsolutePath, dirs[ 0 ] ), 'master' );
 		sinon.assert.notCalled( spies.npmUpdate );
 	} );
@@ -80,7 +79,7 @@ describe( 'dev-update', () => {
 			}
 		};
 
-		updateTask( ckeditor5Path, json, workspaceRoot, emptyFn, false );
+		updateTask( ckeditor5Path, json, workspaceRoot, false );
 
 		sinon.assert.notCalled( spies.pull );
 		sinon.assert.notCalled( spies.npmUpdate );
@@ -95,7 +94,7 @@ describe( 'dev-update', () => {
 			}
 		};
 
-		updateTask( ckeditor5Path, json, workspaceRoot, emptyFn, false );
+		updateTask( ckeditor5Path, json, workspaceRoot, false );
 
 		sinon.assert.notCalled( spies.pull );
 		sinon.assert.notCalled( spies.npmUpdate );