Taskman, a friendly tasks runner

  |   |  4 minutes  |  809 words
Taskman, a friendly tasks runner
Image from Pol Dellaiera

It’s been 2 months now that I left aside my regular work with Drupal 8 and switched back to Atomium development for Drupal 7.

I will soon publish another post on all the new stuff that has been made there, but for now, I will focus on a side project I’ve been doing.

Started as a proof-of-concept a couple of weeks ago, Taskman is tasks runner based on Robo, extendable at will through YAML files.

It has been inspired by what’s already existing so far, but not tied to Drupal or whatsoever.

History

Taskman is a tasks runner written in PHP, for PHP. It’s based on Robo, a PHP framework widely used in many communities.

Robo is a console application that list all the available commands that a user can run. Those commands contains tasks that are executed one after the other.

When shipping a Robofile.php file in your projects, Robo will provide new commands based on what’s inside it.

I invite you to read the documentation if you want to know more on how to extend it or use it as a framework, just like Taskman does.

The only downside of Robo is that you need to code if you want to provides commands and tasks.

The goal of Taskman is to remove this annoyance and let user defines custom commands in YAML.

Of course, as Taskman is based on Robo, custom commands using PHP could still be created and used.

As YAML is quite friendly, it’s rather convenient to define new commands, it’s fast and easy.

Usage

To use Taskman in your project today, two steps are required:

composer require phptaskman/core --dev

Then, create a file taskman.yml.dist that contains your custom commands:

commands:
  hello-world:
    - ls -la
    - touch example.md
    - ls -la
    - rm example.md
    - ls -la

Now, when you run:

./vendor/bin/taskman

Your custom command should be available:

Taskman dev-master

Usage:
  command [options] [arguments]

Options:
  -h, --help                           Display this help message
  -q, --quiet                          Do not output any message
  -V, --version                        Display this application version
      --ansi                           Force ANSI output
      --no-ansi                        Disable ANSI output
  -n, --no-interaction                 Do not ask any interactive question
      --simulate                       Run in simulated mode (show what would have happened).
      --progress-delay=PROGRESS-DELAY  Number of seconds before progress bar is displayed in long-running task collections. Default: 2s. [default: 2]
  -D, --define=DEFINE                  Define a configuration item value. (multiple values allowed)
      --working-dir=WORKING-DIR        Working directory, defaults to current working directory. [default: "/home/pol/dev/git/taskman/core"]
  -v|vv|vvv, --verbose                 Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  hello-world               Run a task.
  help                      Displays help for a command
  list                      Lists commands

Taskman includes a mechanism to automatically detects configuration files in the dependencies of your project.

That means that you could have a PHP package that contains only a YAML file with your custom commands, Taskman will find it automagically.

(Almost) real life example

Let’s say that your project ship with a file: taskman.yml.dist containing:

directories:
  build: "build"
  tests: "tests"
  log: "logs"

commands:
  create-directories:
    - mkdir -p ${directories.build}
    - mkdir -p ${directories.tests}
    - mkdir -p ${directories.log}

This YAML files provides a single command that contains variables.

If for some reason those variables are not the one you expect, you can still override them manually.

To override manually those variables, you have to create a file taskman.yml which should not be committed to your project and ideally added to .gitignore.

That file could be:

directories:
  tests: "${directories.build}/tests"
  log: "${directories.build}/logs"

It’s also possible to define more advanced commands, but the documentation is not yet ready.

Testing framework

I’ve always been using PHPSpec for unit testing, here Taskman is a kind of plugin for Robo, so I’ve decided to use Codeception, mostly for functional tests.

As I don’t want to do tests for the Robo plugin stuff (it’s already done in Robo), simple and functional tests using Codeception was a good choice, I really enjoyed doing it.

I really liked the ease of testing console application, see it by yourself:

<?php

declare(strict_types = 1);

$I = new FunctionalTester($scenario);
$I->wantTo('Check if the executable is found.');
$I->amInPath(\realpath(__DIR__ . '/fixtures/test0'));
$I->runShellCommand('composer install --ansi -n --no-progress --no-scripts --no-dev --no-suggest');
$I->canSeeFileFound('../../../_output/vendor/bin/taskman');
$I->runShellCommand('../../../_output/vendor/bin/taskman');

During the making of these, I also noticed that Composer doesn’t allow you to symlink the package source inside it.

While trying to get around this by writing my own composer plugin, I found this socialengine/composer-symlinker which is exactly doing what I was looking for, this really helped me for the tests.

Future development

I have quite a few ideas to improve Taskman, but as they are not yet mature, I cannot really implement them.

One of the first idea was to get rid of the Taskman executable (./vendor/bin/taskman) and use exclusively the Robo executable (./vendor/bin/robo).

After some discussions with Greg Anderson, it would be possible to do it using CGR, but I still need to look deeper into it.