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+" }