Introducing: DecodeLabs Effigy
Your new go-to CLI tool
Building command line interactions for your application can get complicated quickly - writing standalone tasks is relatively straightforward but you soon realise you need a standardised structure and call signature, handling output gets incredibly fudgy, supporting different environments is a pain in the butt and chaining tasks is a total minefield.
DecodeLabs Effigy and the libraries it was built upon were designed to ease all of these pains in the simplest form we could come up with.
Written as a standalone application in its own right, it's a departure from the usual DecodeLabs library format, instead installed globally via composer so that it can be run directly from the CLI wherever you happen to be.
composer global require decodelabs/effigy
You will also need to have added your global Composer installation bin directory to your
$PATH in your
Note, earlier versions of Composer may store global config in
~/.composer/ - adapt your $PATH as necessary. You can find Composer's home path with
composer global config home
Once installed, you can call Effigy from any location - it will look up the directory tree for the nearest Composer project JSON file.
Talking to Effigy
Effigy's job is to receive an instruction, work out where and how that instruction should be executed, set up the required environment then hand off to that environment to complete the command.
There are a few ways a command can be interpreted:
A built-in Effigy task
An installed Composer bin
A script defined in
Finally, a task handled by your application
effigy format will run the built-in Effigy task to apply Easy Coding Standards formatter across your codebase.
effigy composer update will run the Composer update command on your project. If you have
PHPStan installed in your project,
effigy phpstan would run the
phpstan bin directly, though
effigy analyze offers a more comprehensive analysis support task.
The most interesting bit comes when the command you have asked to run is not a built-in task or script; it's at this point where Effigy will attempt to run your application as a CLI tool and ask it to handle the command itself.
You must tell Effigy how your application expects to do this. This is done by adding some configuration to your
Say for example, you can currently run commands in your project through
webroot/index.php as your primary entry point:
php webroot/index.php run-task
Define your entry point in your composer.json file:
Effigy will then load
webroot/index.php as the entry point and pass the command to whatever structure your application has in place to handle it. See DecodeLabs Clip for the underlying structure that Effigy uses to run CLI tasks in a reproducible structured format - it can be used as a plug-and-play system for CLI tasks in your application.
Should you need per-environment entry files, specify template keys in your composer config entry path:
Then on first run, Effigy will ask for the "env" parameter and save it in a local config file (which gets added to your .gitignore).
Effigy can be configured both through the
extra.effigy structure in
composer.json or your environment-specific
effigy.json config file (make sure to add it to .gitignore if you create it yourself!)
In addition to the entry path explained above, the following configuration directives can be set up to help you control your application:
php- Path to your PHP binary. This should only be configured in
effigy.jsonas it is environment-specific. It can be set via
codeDirs- A list of paths in your project folder that should be considered as having code within them. This is used by built-in tasks such as
formatto distinguish which folders they should process.
params- A list of template keys used in resolving your entry path
exports- A list of paths in your project folder that should be exported in your project's distribution. This is used to create and check your
So, an example
composer.json may contain:
And your example
effigy.json may contain:
Effigy comes with a set of pre-defined tasks that cover a bunch of regularly needed functionality:
analyze- Setup and run
PHPStananalysis on your project
check-executable-permissions- Ensure only files defined in your Composer bin list are executable
check-git-exports- Ensure your
.gitattributesfile exists and makes sense
check-non-ascii- Look for problematic non-ascii characters in your PHP code. Comment line with
// @ignore-non-asciifor non-ascii characters you want to keep
eclint- Setup and run the
eclintlinter tool to check your code conforms to your
format- Setup and run Easy Coding Standards formatter
generate-changelog- Generate a changelog file from a template
generate-composer-config- Generate a
composer.jsonfile from a template
generate-ecs-config- Generate an Easy Coding Standard config from a template
generate-editor-config- Generate a
.editorconfigfile from a template
generate-git-attributes- Generate a
.gitattributesfile from a template
generate-github-workflow- Generate a GitHub workflow file to check, test and analyze your project automatically via GitHub actions
generate-gitignore- Generate a
.gitignorefile from a template
generate-phpstan-config- Generate a
PHPStanconfig from a template
generate-readme- Generate a README.md file from a template
init-repoand all the above file generate tasks
init-repo- Prepare your project's git repository and initialise
git-flowif it's available
remove-local- Install / uninstall a local
effigybin file in your project so Effigy can be called without being available globally
lint- Setup and run PHP
parallel-linton your project code
unmount- Mount / unmount a dependency package with a symbolic link for direct development, see below
prep- Prepare your package for release - update dependencies, analyze, format, test and check everything, thoroughly
self-update- Ensure Effigy is up to date
set-php- Defined a custom version of PHP to use for your project
upgrade- Update Composer packages and tidy up afterwards
veneer-stub- Create stubs (for editor autocompletion) for libraries with Veneer frontages
version- Get the current version of Effigy
Sometimes it is necessary to work on a secondary package in context of your main project - but this usually requires having to set up a specialised testing environment and is a total pain to achieve.
Effigy offers the
unmount tasks which will add special configuration to your
composer.json which enables temporary symbolic links to a local copy of that package to allow development in-situ.
Once your development is complete, you must
unmount before committing anything to your main project's repository as the configuration in your
composer.json is specific to your environment.
For example, say you had this in your
If you needed to add a feature to
mycompany/shared-library you can:
effigy mount shared-library
Effigy will then ask for the path to your local copy of this package and update your
composer.json to include a local repository definition. You may then make your changes to your local copy of
shared-library and test it in context of your project.
This will return your configuration back to normal, reinstalling the latest compatible version of your dependency. You may wish to finalise, commit and release the update to the dependency package before unmounting (no config is changed in
shared-library in the mounting process) so that the updated version is pulled in during the unmount command.
At the time of writing, Effigy is at
v0.4.0, and still in pre-release state. It is however stable and reasonably feature-complete and in use in production environments internally.
v1 release is pending, awaiting final documentation and full test coverage however due to the standalone nature of the package, there's little reason not to give Effigy a try in your current project.