From 14a638721853432b70158e1bbfd6b6b4d638f6a4 Mon Sep 17 00:00:00 2001 From: Stefan Kalscheuer Date: Tue, 2 Jan 2018 20:07:26 +0100 Subject: [PATCH] Added Robo build script Created tasks for building and testing using Robo build environment, installed via Composer. This is supposed to replace Gulp as default build tool and makes NPM obsolete. --- Gulpfile.js | 102 ----------- RoboFile.php | 492 ++++++++++++++++++++++++++++++++++++++++++++++++++ composer.json | 17 +- package.json | 15 +- 4 files changed, 508 insertions(+), 118 deletions(-) delete mode 100644 Gulpfile.js create mode 100644 RoboFile.php diff --git a/Gulpfile.js b/Gulpfile.js deleted file mode 100644 index c0c9dc0..0000000 --- a/Gulpfile.js +++ /dev/null @@ -1,102 +0,0 @@ -const gulp = require('gulp'); -const clean = require('gulp-clean'); -const copy = require('gulp-copy'); -const zip = require('gulp-zip'); -const composer = require('gulp-composer'); -const phpunit = require('gulp-phpunit'); -const exec = require('child_process').exec; -const phpcs = require('gulp-phpcs'); -const cleanCSS = require('gulp-clean-css'); -const minify = require('gulp-minify'); -const argv = require('yargs').argv; - -const config = require('./package.json'); -const dev = argv.dev; -const finalName = config.name + '.' + config.version; - -// Clean the target directory. -gulp.task('clean', function () { - console.log('Cleaning up target directory ...'); - return gulp.src('dist', {read: false}) - .pipe(clean()); -}); - -// Prepare composer. -gulp.task('compose', function () { - console.log('Preparing Composer ...'); - return composer('install'); -}); - -// Execute unit tests. -gulp.task('test', ['compose'], function () { - console.log('Running PHPUnit tests ...'); - return gulp.src('phpunit.xml') - .pipe(phpunit('./vendor/bin/phpunit', {debug: false})); -}); - -// Execute PHP Code Sniffer. -gulp.task('test-cs', function (cb) { - return exec('./vendor/bin/phpcs --config-set installed_paths vendor/wp-coding-standards/wpcs', function (err, stdout, stderr) { - console.log(stdout); - console.log(stderr); - if (null === err) { - console.log('Running PHP Code Sniffer tests ...'); - gulp.src(['statify-blacklist.php', 'inc/**/*.php']) - .pipe(phpcs({bin: './vendor/bin/phpcs', standard: 'phpcs.xml'})) - .pipe(phpcs.reporter('log')); - } - cb(err); - }); -}); - - -// Bundle files as required for plugin distribution. -gulp.task('bundle', ['clean'], function () { - console.log('Collecting files for package dist/' + config.name + config.version + ' ...'); - return gulp.src(['**/*.php', 'styles/*.css', 'scripts/*.js', '!test/**', '!vendor/**', 'README.md', 'LICENSE.md'], {base: './'}) - .pipe(copy('./dist/' + finalName + '/' + config.name)); -}); - - -// Minify CSS. -gulp.task('minify-css', function () { - if (!dev) { - console.log('Minifying CSS ...'); - return gulp.src('./dist/' + finalName + '/' + config.name + '/styles/*.css') - .pipe(cleanCSS({compatibility: 'ie9'})) - .pipe(gulp.dest('./dist/' + finalName + '/' + config.name + '/styles/')); - } else { - console.log('Development flag detected, not minifying CSS ...'); - } -}); - -// Minify JavaScript. -gulp.task('minify-js', function () { - if (!dev) { - console.log('Minifying JS ...'); - return gulp.src('./dist/' + finalName + '/' + config.name + '/scripts/**/*.js') - .pipe(minify({ - ext : { - source: '.js', - min : '.js' - }, - ignoreFiles : ['*.min.js'], - noSource : true, - preserveComments: 'some' - })) - .pipe(gulp.dest('./dist/' + finalName + '/' + config.name + '/scripts')); - } else { - console.log('Development flag detected, not minifying JS ...'); - } -}); - - -// Create a ZIP package of the relevant files for plugin distribution. -gulp.task('package', ['minify-js', 'minify-css', 'bundle'], function () { - console.log('Building package dist/' + config.name + config.version + '.zip ...'); - return gulp.src('./dist/' + config.name + '.' + config.version + '/**') - .pipe(zip(finalName + '.zip')) - .pipe(gulp.dest('./dist')); -}); - -gulp.task('default', ['clean', 'compose', 'test', 'test-cs', 'bundle', 'minify-css', 'minify-css', 'package']); diff --git a/RoboFile.php b/RoboFile.php new file mode 100644 index 0000000..9d30d36 --- /dev/null +++ b/RoboFile.php @@ -0,0 +1,492 @@ + + * + * @package WP Liveticker 2 + * @version 1.0.0 + */ + +use Robo\Exception\TaskException; +use Robo\Tasks; +use Symfony\Component\Finder\Finder; + +/** + * Class RoboFile + */ +class RoboFile extends Tasks { + const PROJECT_NAME = 'wp-liveticker2'; + const SVN_URL = 'https://plugins.svn.wordpress.org/wp-liveticker2'; + + const OPT_TARGET = 'target'; + const OPT_SKIPTEST = 'skipTests'; + const OPT_SKIPSTYLE = 'skipStyle'; + const OPT_MINIFY = 'minify'; + + /** + * Version tag (read from composer.json). + * + * @var string + */ + private $version; + + /** + * Target directory path. + * + * @var string + */ + private $target_dir; + + /** + * Final package name. + * + * @var string + */ + private $final_name; + + /** + * RoboFile constructor + * + * @param array $opts Options. + * + * @return void + */ + public function __construct( $opts = array( self::OPT_TARGET => 'dist' ) ) { + // Read composer configuration and extract version number.. + $composer = json_decode( file_get_contents( __DIR__ . '/composer.json' ) ); + // Extract parameter from options. + $this->version = $composer->version; + $this->target_dir = $opts[ self::OPT_TARGET ]; + $this->final_name = self::PROJECT_NAME . '.' . $this->version; + } + + /** + * Clean up target directory + * + * @param array $opts Options. + * + * @return void + */ + public function clean( $opts = array( self::OPT_TARGET => 'dist' ) ) { + $this->say( 'Cleaning target directory...' ); + if ( is_dir( $this->target_dir . '/' . $this->final_name ) ) { + $this->_deleteDir( array( $this->target_dir . '/' . $this->final_name ) ); + } + if ( is_file( $this->target_dir . '/' . $this->final_name . '.zip' ) ) { + $this->_remove( $this->target_dir . '/' . $this->final_name . '.zip' ); + } + } + + /** + * Run PHPUnit tests + * + * @return void + */ + public function test() { + $this->say( 'Executing PHPUnit tests...' ); + $this->taskPhpUnit()->configFile( __DIR__ . '/phpunit.xml' )->run(); + } + + /** + * Run code style tests + * + * @return void + */ + public function testCS() { + $this->say( 'Executing PHPCS tests...' ); + $this->_exec( __DIR__ . '/vendor/bin/phpcs --standard=phpcs.xml -s' ); + } + + /** + * Build a distributable bundle. + * + * @param array $opts Options. + * + * @return void + */ + public function build( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + $this->clean( $opts ); + if ( isset( $opts[ self::OPT_SKIPTEST ] ) && true === $opts[ self::OPT_SKIPTEST ] ) { + $this->say( 'Tests skipped' ); + } else { + $this->test(); + } + if ( isset( $opts[ self::OPT_SKIPSTYLE ] ) && true === $opts[ self::OPT_SKIPSTYLE ] ) { + $this->say( 'Style checks skipped' ); + } else { + $this->testCS(); + } + $this->bundle(); + $this->minify( $opts ); + } + + /** + * Bundle global resources. + * + * @return void + */ + private function bundle() { + $this->say( 'Bundling resources...' ); + $this->taskCopyDir( array( + 'includes' => $this->target_dir . '/' . $this->final_name . '/includes', + 'scripts' => $this->target_dir . '/' . $this->final_name . '/scripts', + 'styles' => $this->target_dir . '/' . $this->final_name . '/styles', + 'views' => $this->target_dir . '/' . $this->final_name . '/views', + ) )->run(); + $this->_copy( 'wp-liveticker2.php', $this->target_dir . '/' . $this->final_name . '/wp-liveticker2.php' ); + $this->_copy( 'README.md', $this->target_dir . '/' . $this->final_name . '/README.md' ); + $this->_copy( 'LICENSE.md', $this->target_dir . '/' . $this->final_name . '/LICENSE.md' ); + } + + /** + * Minify JavaScript and CSS assets in target director. + * + * @param array $opts Options. + * + * @return void + */ + public function minify( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + if ( $opts[ self::OPT_MINIFY ] ) { + $this->minifyJS( $opts ); + $this->minifyCSS( $opts ); + } else { + $this->say( 'Minification skipped.' ); + } + } + + /** + * Minify CSS assets. + * + * @param array $opts Options. + * + * @return void + */ + public function minifyCSS( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + if ( ! isset( $opts[ self::OPT_MINIFY ] ) ) { + $this->say( 'CSS minification skipped.' ); + + return; + } + + $this->say( 'Minifying CSS...' ); + + $finder = Finder::create()->name( '*.css*' ) + ->notName( '*.min.css' ) + ->in( $this->target_dir . '/' . $this->final_name . '/styles' ); + foreach ( $finder as $file ) { + $this->taskMinify( $file ) + ->run(); + // Replace original file for in-place minification. + $abspath = $file->getPath() . '/' . $file->getFilename(); + $this->_rename( str_replace( '.css', '.min.css', $abspath ), $abspath, true ); + } + } + + /** + * Minify JavaScript assets. + * + * @param array $opts Options. + * + * @return void + */ + public function minifyJS( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + if ( ! isset( $opts[ self::OPT_MINIFY ] ) ) { + $this->say( 'JS minification skipped.' ); + + return; + } + + $this->say( 'Minifying JavaScript...' ); + + // Minify global JavaScripts files except already minified. + $finder = Finder::create()->name( '*.js*' ) + ->notName( '*.min.js' ) + ->in( $this->target_dir . '/' . $this->final_name . '/scripts' ); + foreach ( $finder as $file ) { + $this->taskMinify( $file )->run(); + // Replace original file for in-place minification. + $abspath = $file->getPath() . '/' . $file->getFilename(); + $this->_rename( str_replace( '.js', '.min.js', $abspath ), $abspath, true ); + } + } + + /** + * Create ZIP package from distribution bundle. + * + * @param array $opts Options. + * + * @return void + */ + public function package( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + $this->build( $opts ); + $this->say( 'Packaging...' ); + $this->taskPack( $this->target_dir . '/' . $this->final_name . '.zip' ) + ->addDir( '', $this->target_dir . '/' . $this->final_name ) + ->run(); + } + + /** + * Deploy development version (trunk). + * + * @param array $opts Options. + * + * @return void + * @throws TaskException On errors. + */ + public function deployTrunk( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + // First execute build job. + $this->build( $opts ); + + // Prepare VCS, either checkout or update local copy. + $this->prepareVCS(); + + $this->say( 'Preparing deployment directory...' ); + $this->updateVCStrunk(); + + // Update remote repository. + $this->say( 'Deploying...' ); + $this->commitVCS( + '--force trunk/*', + 'Updated ' . self::PROJECT_NAME . ' trunk' + ); + } + + /** + * Deploy current version tag. + * + * @param array $opts Options. + * + * @return void + * @throws TaskException On errors. + */ + public function deployTag( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + // First execute build job. + $this->build( $opts ); + + // Prepare VCS, either checkout or update local copy. + $this->prepareVCS(); + + $this->say( 'Preparing deployment directory...' ); + $this->updateVCStag(); + + // Update remote repository. + $this->say( 'Deploying...' ); + $this->commitVCS( + 'tags/' . $this->version, + 'Updated ' . self::PROJECT_NAME . ' v' . $this->version + ); + } + + /** + * Deploy current version tag. + * + * @param array $opts Options. + * + * @return void + * @throws TaskException On errors. + */ + public function deployReadme( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + // First execute build job. + $this->build( $opts ); + + // Prepare VCS, either checkout or update local copy. + $this->prepareVCS(); + + $this->updateVCSreadme(); + + // Update remote repository. + $this->say( 'Deploying...' ); + $this->commitVCS( + '--force trunk/README.md', + 'Updated ' . self::PROJECT_NAME . ' ReadMe' + ); + } + + /** + * Deploy current version tag and trunk. + * + * @param array $opts Options. + * + * @return void + * @throws TaskException On errors. + */ + public function deployAll( + $opts = array( + self::OPT_TARGET => 'dist', + self::OPT_SKIPTEST => false, + self::OPT_SKIPSTYLE => false, + self::OPT_MINIFY => true, + ) + ) { + // First execute build job. + $this->build( $opts ); + + // Prepare VCS, either checkout or update local copy. + $this->prepareVCS(); + + $this->say( 'Preparing deployment directory...' ); + $this->updateVCStrunk(); + $this->updateVCStag(); + + // Update remote repository. + $this->say( 'Deploying...' ); + $this->commitVCS( + array( + '--force trunk/*', + '--force tags/' . $this->version, + ), + 'Updated ' . self::PROJECT_NAME . ' v' . $this->version + ); + } + + /** + * Prepare VCS direcory. + * + * Checkout or update local copy of SVN repository. + * + * @return void + * @throws TaskException On errors. + */ + private function prepareVCS() { + if ( is_dir( $this->target_dir . '/svn' ) ) { + $this->taskSvnStack() + ->stopOnFail() + ->dir( $this->target_dir . '/svn/statify-blacklist' ) + ->update() + ->run(); + } else { + $this->_mkdir( $this->target_dir . '/svn' ); + $this->taskSvnStack() + ->dir( $this->target_dir . '/svn' ) + ->checkout( self::SVN_URL ) + ->run(); + } + } + + /** + * Commit VCS changes + * + * @param string|array $to_add Files to add. + * @param string $msg Commit message. + * + * @return void + * @throws TaskException On errors. + */ + private function commitVCS( $to_add, $msg ) { + $task = $this->taskSvnStack() + ->stopOnFail() + ->dir( $this->target_dir . '/svn/statify-blacklist' ); + + if ( is_array( $to_add ) ) { + foreach ( $to_add as $ta ) { + $task = $task->add( $ta ); + } + } else { + $task = $task->add( $to_add ); + } + + $task->commit( $msg )->run(); + } + + /** + * Update SVN readme file. + * + * @return void + */ + private function updateVCSreadme() { + $trunk_dir = $this->target_dir . '/svn/statify-blacklist/trunk'; + $this->_copy( $this->target_dir . '/' . $this->final_name . 'README.md', $trunk_dir . 'README.md' ); + } + + /** + * Update SVN development version (trunk). + * + * @return void + */ + private function updateVCStrunk() { + // Clean trunk directory. + $trunk_dir = $this->target_dir . '/svn/statify-blacklist/trunk'; + $this->taskCleanDir( $trunk_dir )->run(); + + // Copy built bundle to trunk. + $this->taskCopyDir( array( $this->target_dir . '/' . $this->final_name => $trunk_dir ) )->run(); + } + + /** + * Update current SVN version tag. + * + * @return void + */ + private function updateVCStag() { + // Clean tag directory if it exists. + $tag_dir = $this->target_dir . '/svn/statify-blacklist/tags/' . $this->version; + if ( is_dir( $tag_dir ) ) { + $this->taskCleanDir( $this->target_dir . '/svn/statify-blacklist/tags/' . $this->version )->run(); + } else { + $this->_mkdir( $tag_dir ); + } + + // Copy built bundle to trunk. + $this->taskCopyDir( array( $this->target_dir . '/' . $this->final_name => $tag_dir ) )->run(); + } +} diff --git a/composer.json b/composer.json index 25900d4..1785211 100644 --- a/composer.json +++ b/composer.json @@ -22,14 +22,27 @@ }, "require-dev": { "php": ">=5.3", + "consolidation/robo": "^1.0.0", "phpunit/phpunit": "*", "phpunit/php-code-coverage": "*", - "dealerdirect/phpcodesniffer-composer-installer": "^0.4","slowprog/composer-copy-file": "~0.2", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4", + "slowprog/composer-copy-file": "~0.2", "squizlabs/php_codesniffer": "^3.1", "wimg/php-compatibility": "^8.0", - "wp-coding-standards/wpcs": "~0.14" + "wp-coding-standards/wpcs": "~0.14", + "patchwork/jsqueeze": "^2.0.5", + "natxet/CssMin": "^3.0.5" }, "scripts": { + "build": [ + "robo build" + ], + "package": [ + "robo package" + ], + "deploy": [ + "robo deploy:all" + ], "test-all": [ "@test", "@test-cs" diff --git a/package.json b/package.json index 15806f0..24c1b26 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,5 @@ "version": "1.0.0-alpha", "description": "A simple Liveticker for Wordpress.", "author": "Stefan Kalscheuer", - "license": "GPL-2.0+", - "devDependencies": { - "gulp": "^3.9.1", - "gulp-clean": "^0.3.2", - "gulp-copy": "^1.0.1", - "gulp-zip": "^4.0.0", - "gulp-composer": "^0.4.4", - "gulp-phpunit": "^0.24.1", - "gulp-phpcs": "^2.1.0", - "gulp-clean-css": "^3.9.0", - "gulp-minify": "^2.1.0", - "child_process": "^1.0.2", - "yargs": "^10.0.3" - } + "license": "GPL-2.0+" }