Gush 1.8.8 is out: Command Line Tool For Project maintainance/contributing

Gush release 1.8.8 is out!

In case you are new to Gush, Gush is a command line tool that helps you save time and improves your speed dramatically contributing to projects whether at work or Open Source and keeps you healthy.

Gush is a tool that you can install with composer or just download it as a phar. I recommend the phar and the phar is just a single streamlined file that contains all sources and that works just like the regular cloned folder. My recommendation for developers is to clone the project and symlink the bin/gush into their path. For everybody else I just recommend the phar. I do not recommend anymore the global installation because it may have dependencies that would conflict with other packages you have installed into your global composer directories.

Gush was created with a top goal in mind. To be able to rapidly contribute and maintain a package. From the standpoint of a contributor or developer at a company you want to get things streamlined as much as possible. Gush has a built-in flow and it really draws into many developer’s experience with how things work and should work in a collaborative or solo rapid environment dealing with versioned projects/packages.

Gush’s flow starts with configuration. You may configure adapters and issue trackers. Adapters are mechanisms by which you will send your contributions in terms of code changesets. Issue trackers are mechanisms by which you will be handling the book-keeping of your tickets in your project/package. You may decide to open, close, take, assign, or label tickets for instance.

Gush is well abstracted to support various hubs, that is various third party services such as Github, Github Enterprise, Jira, Bitbucket, and Gitlab for example. The abstraction is defined in an interface class so at any time anyone can come and write an adapter implementing that interface and Gush will support that new service. Gush is abstracted in both, at the adapter level and at the issue tracker level, and is well separated so you can track your issues on jira while working your pull code changeset requests in github.
Gush’s configuration let’s you do exactly that.

Gush configuration is per project and global, you can configure several adapters and specify which one will your particular project at hand should use. This is specified into a local .gush.yml file whereas the global configuration uses a folder .gush inside your user home directory.

Once configured Gush has a series of commands, self explanatory and easy to use:

~ gush                                                                                    Luiss-MacBook-Pro-3 [4:17:47]
   _____ _    _  _____ _    _
  / ____| |  | |/ ____| |  | |
 | |  __| |  | | (___ | |__| |
 | | |_ | |  | |\___ \|  __  |
 | |__| | |__| |____) | |  | |
  \_____|\____/|_____/|_|  |_|
 
Gush version @package_version@
 
Usage:
  [options] command [arguments]
 
Options:
  --help           -h Display this help message.
  --quiet          -q Do not output any message.
  --verbose        -v|vv|vvv Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
  --version        -V Display this application version.
  --ansi              Force ANSI output.
  --no-ansi           Disable ANSI output.
  --no-interaction -n Do not ask any interactive question.
 
Available commands:
  help                           Displays help for a command
  list                           Lists commands
branch
  branch:changelog               Reports what got fixed or closed since last release on current branch
  branch:delete                  Deletes remote branch with the current or given name
  branch:fork                    Forks current upstream repository
  branch:push                    Pushes and tracks the current local branch into user own fork
  branch:sync                    Syncs local branch with its upstream version
core
  core:alias                     Outputs help and alias snippet for wrapping gush on git
  core:autocomplete              Create file for Bash autocomplete
  core:configure                 Configure adapter credentials and the cache folder
  core:init                      Configures a local .gush.yml config file
  core:update                    Updates the application.
issue
  issue:assign                   Assigns an issue to a user
  issue:close                    Closes an issue
  issue:create                   Creates an issue
  issue:label:assign             Labels issues/pull requests
  issue:label:list               Lists the issue's labels
  issue:list                     List issues
  issue:list:milestones          Lists the issue's milestones
  issue:show                     Shows given issue
  issue:take                     Takes an issue
meta
  meta:header
pull-request
  pull-request:assign            Assigns a pull-request to a user
  pull-request:close             Closes a pull request
  pull-request:create            Launches a pull request
  pull-request:fabbot-io         Run fabbot-io patches on given PR
  pull-request:fixer             Run cs-fixer and commits fixes
  pull-request:label:list        Lists the pull-request's labels
  pull-request:list              Lists all available pull requests
  pull-request:list:milestones   Lists the pull-request's milestones
  pull-request:merge             Merges the pull request given
  pull-request:pat-on-the-back   Gives a pat on the back to a PR's author
  pull-request:semver            Provides information about the semver version of a pull request
  pull-request:squash            Squashes all commits of a PR
  pull-request:switch-base       Switch the base of the PR to another one
release
  release:create                 Create a new Release
  release:list                   Lists the releases
  release:remove                 Removes a release
version-eye
  version-eye:check              Update composer.json dependency versions from versioneye service

Every command provides help description and information about how it is to be used.

Typical flows in gush involve commands for contributors like:

git clone or composer init or composer install project ...
gush core:configure
cd project
gush core:init
ls -la
gush b:f
gush i:create
gush i:take #
./do_changes_here.sh; git add .; git commit -am "some changes"
gush b:p
gush p:create --issue=#

Maintainer commands could be something like:

gush p:merge #
gush b:d
git checkout master
gush b:s
git branch -d #-branch_name_here

And the cycle repeats. The commands run under best practices and good assumptions based on how composer typically create remotes, it has a fast setup and every contribution’s command gets repetitive meaning it will leave you with less error prone manipulations with git and avoid you to have to go to the web interface to click on the buttons thereby decreasing your speed with the keyboard flow you are carrying in code.

Gush commands are very powerful and complete. If you see a use case please feel free to suggest it opening a ticket on the repositories.
The advantages of Gush against its competitors like hub or others is that you use one command line tool for many hubs, Github Enterprise, Jira, Jira Enterprise, Gitlab, Bitbucket, etc. You do not have to switch setup from work and for FOSS, all is project specific so you just cd into your project folder. Gush is being used in production in companies within the USA and Europe and Latin America successfully. It is written in a language easy to refactor and implement and with a very low barrier for hacking as PHP. It bears a great OO architecture and is improving constantly.

Gush was born to send many PRs to open source projects in matter of seconds. It will not have anything hard to install, like php. It is mainly geared towards developers at work and Open Source, and has a high regard of code quality. Our github adapter has been downloaded more than 2.4k times, it has been featured by Github as starred by staff, and it has been qualified by some top experts as a package of high impact for the community.

Gush is all about flow and that is where its name is derived ‘gush out’. Encouragements!

Matthew 7:12: The DDD Project To Help (uses carefully some Symfony)

So we started Matthew 7:12. It is an open source project. It aims to solve the following situation:

In some countries, the internet is very controlled or minimum. The only way to contribute to an open source project is via email. I have heard that the linux kernel group also work using email and attaching patches. However I don’t think their approach solves this situation in a way that is acceptable for this other people.

Matthew is a server app that takes notices of new changes in a repository and email subscribed users patches attached to it. These patches are zipped and are split so no patch per email is larger than a threshold say 2 MB.

Matthew is also a server app that receives contributions, so not only sends them. Matthew is able to read an email account per project to check whether it has been emailed a patch. Each patch that Matthew reads is converted into a Pull Request to a given branch in a given repo.

The circle of contribution then is pushing code via email from contributors and creating PRs and notifying these users of new changes in the repo so that at any time all they would have to do is to send a patch of their changes or apply a patch received from upstream to continue developing.

Matthew will have options to bootstrap a repo on their locals, so easy download options considering split zips of the specific sections of a repository.

Matthew will use best practices and if you like those you already can tell from the repo here.

Is still under heavy construction and you are summoned to PR it, it is your responsibility how it ends up :)

We will deploy it soon too and test it!

Symfony CS Custom Fixer + Tests: Do focus on your goal!

Once again I received a closing ticket here https://github.com/fabpot/PHP-CS-Fixer/issues/381 in response to a feature request. There was no explanation why it was closed. However @stof kindly provided some reasoning. However what surprised me is what happened afterwards!

But it was great that @sstok picked it up and left a gist. This is amazing how the community can be so encouraging! Really thanks!

So I went and plugged this custom short syntax converter and here is the recipe!

<?php
 
namespace Decoupling\Fixer;
 
use Symfony\CS\FixerInterface;
use Symfony\CS\Tokens;
 
class ShortArraySyntaxFixer implements FixerInterface
{
    public function fix(\SplFileInfo $file, $content)
    {
        $tokens = Tokens::fromCode($content);
 
        for ($index = 0, $c = $tokens->count(); $index < $c; $index++) {
            $token = $tokens[$index];
 
            if (Tokens::isKeyword($token) && T_ARRAY === $token[0] && '(' === $tokens->getNextNonWhitespace($index)) {
                $this->fixArray($tokens, $index);
                continue;
            }
        }
 
        return $tokens->generateCode();
    }
 
    private function fixArray(Tokens $tokens, &$index)
    {
        $bracesLevel = 0;
 
        unset($tokens[$index]);
        $index++;
 
        for ($c = $tokens->count(); $index < $c; $index++) {
            $token = $tokens[$index];
 
            if ('(' === $token) {
                if (0 === $bracesLevel) {
                    $tokens[$index] = '[';
                }
 
                ++$bracesLevel;
                continue;
            }
 
            if (Tokens::isKeyword($token) && T_ARRAY === $token[0] && '(' === $tokens->getNextNonWhitespace($index)) {
                $this->fixArray($tokens, $index);
                continue;
            }
 
            if (')' === $token) {
                --$bracesLevel;
 
                if (0 === $bracesLevel) {
                    $tokens[$index] = ']';
                    break;
                }
            }
        }
    }
 
    public function getLevel()
    {
        return FixerInterface::ALL_LEVEL;
    }
 
    public function getPriority()
    {
        return 0;
    }
 
    public function supports(\SplFileInfo $file)
    {
        return 'php' === pathinfo($file->getFilename(), PATHINFO_EXTENSION);
    }
 
    public function getName()
    {
        return 'short_array_syntax';
    }
 
    public function getDescription()
    {
        return 'PHP array\'s should use the PHP 5.4 short-syntax';
    }
}

And its corresponding test:

<?php
 
namespace Decoupling\Tests\Fixer;
 
use Decoupling\Fixer\ShortArraySyntaxFixer;
 
class ShortArraySyntaxFixerTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider provideExamples
     */
    public function testFix($expected, $input)
    {
        $fixer = new ShortArraySyntaxFixer();
        $file = $this->getTestFile();
 
        $this->assertEquals($expected, $fixer->fix($file, $input));
    }
 
    public function provideExamples()
    {
        return [
            ['<?php $x = [];', '<?php $x = array();'],
            ['<?php $x = []; $y = [];', '<?php $x = array(); $y = array();'],
            ['<?php $x = [ ];', '<?php $x = array( );'],
            ['<?php $x = [\'foo\'];', '<?php $x = array(\'foo\');'],
            ['<?php $x = [ \'foo\' ];', '<?php $x = array( \'foo\' );'],
            ['<?php $x = [($y ? true : false)];', '<?php $x = array(($y ? true : false));'],
            ['<?php $x = [($y ? [true] : [false])];', '<?php $x = array(($y ? array(true) : array(false)));'],//
            ['<?php $x = [($y ? [true] : [ false ])];', '<?php $x = array(($y ? array(true) : array( false )));'],
            ['<?php $x = [($y ? ["t" => true] : ["f" => false])];', '<?php $x = array(($y ? array("t" => true) : array("f" => false)));'],
            ['<?php print_r([($y ? true : false)]);', '<?php print_r(array(($y ? true : false)));'],
            ['<?php $x = [[[]]];', '<?php $x = array(array(array()));'],
            ['<?php $x = [[[]]]; $y = [[[]]];', '<?php $x = array(array(array())); $y = array(array(array()));'],
        ];
    }
 
    private function getTestFile($filename = __FILE__)
    {
        static $files = [];
 
        if (!isset($files[$filename])) {
            $files[$filename] = new \SplFileInfo($filename);
        }
 
        return $files[$filename];
    }
}

This is also in a gist @sstok posted, however i have applied the fixer on the code itself :) and also fixed missing namespaces and opening php tags.

Your typical .php_cs then becomes:

<?php
 
require_once __DIR__.'/src/Decoupling/Fixer/ShortArraySyntaxFixer.php';
 
$finder = Symfony\CS\Finder\DefaultFinder::create()
    ->notName('README.md')
    ->notName('.php_cs')
    ->notName('composer.*')
    ->notName('phpunit.xml*')
    ->notName('*.xml')
    ->exclude('app')
    ->exclude('bin')
    ->exclude('migrations')
    ->exclude('vendor')
    ->exclude('web/bundles')
    ->in(__DIR__)
;
 
return Symfony\CS\Config\Config::create()
    ->addCustomFixer(new Decoupling\Fixer\ShortArraySyntaxFixer)
    ->fixers(
        [
            'encoding',
            'linefeed',
            'indentation',
            'trailing_spaces',
            'object_operator',
            'phpdoc_params',
            'visibility',
            'short_tag',
            'php_closing_tag',
            'return',
            'extra_empty_lines',
            'braces',
            'lowercase_constants',
            'lowercase_keywords',
            'include',
            'function_declaration',
            'controls_spaces',
            'spaces_cast',
            'psr0',
            'elseif',
            'eof_ending',
            'one_class_per_file',
            'unused_use',
            'short_array_syntax',
        ]
    )
    ->finder($finder)
;

Now go and run it:

~ php-cs-fixer fix .

It worked like a charm in my project and I am happy to use it even in gush now, PR coming!

joyfully undeservedly ending this post!

your friend @cordoval