hkyss/evocms-extras

Console extras manager for Evolution CMS CE 3: catalog, install, update and removal for both Composer and legacy MODX Evolution Package extras

Maintainers

Package info

github.com/hkyss/evocms-extras

Documentation

Type:evolutioncms-package

pkg:composer/hkyss/evocms-extras

Transparency log

Statistics

Installs: 37

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-08-18 15:03 UTC

This package is auto-updated.

Last update: 2026-08-18 15:17:22 UTC


README

Tests Latest version PHP License

Console extras manager for Evolution CMS CE 3 (evocms-community/evolution). Lists, installs, updates and removes extras in both formats the ecosystem uses.

php artisan extra:list --search=commerce
php artisan extra:install evolution-cms-extras/tinymce5
php artisan extra:remove evolution-cms-extras/tinymce5 --dry-run

CE 3.1 ships php artisan extras, an interactive prompt tied to one GitHub organisation. It has no listing, no search, no JSON output and no dry run, and there is no removal command anywhere in the core. package:installrequire also drops Composer's exit code, so a failed install reports success and leaves the requirement in the manifest.

Install

cd core
php artisan package:installrequire hkyss/evocms-extras "^1.0"
php artisan migrate
php artisan extra:doctor

Run migrate. Legacy extras refuse to install without the record table, since there would be no way to remove them afterwards.

extra:doctor checks the things Composer cannot: PHP version, ext-zip, a writable custom/composer.json, whether composer-merge-plugin is enabled, whether GITHUB_PAT is set. Start there when something misbehaves.

Config is optional:

php artisan vendor:publish --tag=extras-config

Formats

Composer extras are packages of type evolutioncms-* on Packagist, about two dozen of them. They go into core/custom/composer.json, and the core's package:discover picks up their providers and assets.

Legacy extras use the MODX Evolution Package format: an assets/ tree copied into the site, plus an install/ directory holding element descriptors and an optional setup.data.sql. Most of the ecosystem is here — extras-evolution has around 130 repositories and evocms-community another 30, none with a composer.json. Commerce and its payment plugins, PageBuilder, ClientSettings, Ditto, eForm, ajaxSearch, Shopkeeper and ManagerManager are all legacy.

The legacy format has no uninstall of its own, which is why this package keeps its own install record.

Interactive mode

Run extra:install, extra:update or extra:remove without a coordinate in a terminal and the command asks instead of failing:

Install which extra?
  → swa  (1/160)
 ❯ ◉ vvvladv/evo-swagger  composer  OpenAPI / Swagger UI manager module and apidocs…

  ↑↓ move  ·  space select  ·  enter confirm  ·  esc clear filter

Arrow keys move, typing narrows the list, space ticks several, enter confirms, escape clears the filter and then backs out. Picking one extra also offers its published versions, and the plan is always confirmed before anything is written.

The read-only commands use the same list where it saves you a lookup:

php artisan extra:list                 # browse; enter opens a card, esc leaves
php artisan extra:list --format=table  # the plain table, whatever the terminal
php artisan extra:info                 # no coordinate: pick one from the catalog

Browsing is not read-only: the card is followed by what can be done with that extra — install for something absent, update and remove for something present, or back to the list. The plan is printed and confirmed before anything is written, exactly as it is for extra:install.

extra:cache --clear and extra:cache --rebuild-snapshot ask first — the cache costs minutes to rebuild, and the snapshot is a file under version control.

This is additive. Passing coordinates, --no-interaction, piping the output, or running where stty is unavailable takes exactly the same path as before — extra:update with no argument still means "everything installed" when unattended.

Setting CI or CONTINUOUS_INTEGRATION also turns every prompt off. A runner allocates a terminal to get coloured logs, and so does docker exec -t in a Makefile; without this a prompt there would wait for a keypress that never comes and the job would hang rather than fail.

Set EXTRAS_ASCII=1 to replace the box drawing and glyphs with plain ASCII; this happens automatically on Windows outside Windows Terminal.

Commands

Start with php artisan extra:help — one screen covering every command, the flags worth knowing and the interactive keys. php artisan extra:<command> --help has the full option list.

extra:list

php artisan extra:list
php artisan extra:list --search=commerce
php artisan extra:list --installed
php artisan extra:list --legacy --verified
php artisan extra:list --format=table
php artisan extra:list --format=json

--composer and --legacy filter by format, --verified keeps only what has been checked on Evo 3.

--installed is answered from custom/composer.json and the install records rather than from the catalog, so it stays complete when the catalog is stale, rate-limited or unreachable. An extra installed here that the catalog does not list is shown as installed; not listed in the catalog.

--format decides how the result is shown:

auto (default) the browsable list in a terminal, the table everywhere else
list the browsable list; degrades to the table when there is no terminal
table the table, always
json machine-readable, never interactive

Anything that is not a terminal — a pipe, a redirect, --no-ansi, --no-interaction, CI — gets the table under auto, so scripts do not need to pass a flag to stay scriptable.

extra:install

php artisan extra:install evolution-cms-extras/tinymce5
php artisan extra:install evolution-cms-extras/tinymce5 --use-version=5.7.1
php artisan extra:install a/one b/two c/three
php artisan extra:install --file=extras.txt --continue-on-error
php artisan extra:install extras-evolution/Ditto --dry-run

Called with no coordinate in a terminal it opens the catalog as a filterable list; see Interactive mode.

Pass as many coordinates as you like; there is no separate batch command. --file reads one per line and treats # as a comment.

--force proceeds past an objection such as an extra that has never been verified on Evo 3. It does not cover a platform requirement — a php constraint the installation does not meet, or a missing extension. Those are checked before the manifest is touched, and Composer would refuse for the same reason anyway. --ignore-platform-reqs is the way past those, for when the catalog is wrong about what a package needs.

--use-version works on a single extra only. Applying one version string to several packages installs the wrong thing more often than not. The flag is not called --version because Symfony defines that one on the console application itself, and a command that shadows it cannot run at all.

extra:update

php artisan extra:update
php artisan extra:update evocms-community/commerce
php artisan extra:update evocms-community/commerce --use-version=1.4.0
php artisan extra:update --dry-run

With no arguments it offers the installed extras as a list to tick, and updates everything installed when run unattended.

extra:remove

php artisan extra:remove evolution-cms-extras/tinymce5
php artisan extra:remove a/one b/two --continue-on-error

extra:info, extra:cache, extra:doctor, extra:help

php artisan extra:help

php artisan extra:info
php artisan extra:info evocms-community/pagebuilder
php artisan extra:info evocms-community/pagebuilder --format=json

php artisan extra:cache
php artisan extra:cache --clear

php artisan extra:doctor

Dry runs

Every operation is assembled as a full plan first. --dry-run prints it and changes nothing:

$ php artisan extra:install evocms-community/pagebuilder --dry-run

install evocms-community/pagebuilder  [legacy]
  version: — → master

  file
    · create assets/plugins/pagebuilder/pagebuilder.php
    · back up assets/plugins/pagebuilder/lang/en.php → .old
    · overwrite assets/plugins/pagebuilder/lang/en.php
  element
    · create plugin PageBuilder
    · create snippet PageBuilder
  sql
    · sql: CREATE TABLE IF NOT EXISTS `evo_pagebuilder` (…)
    · sql: ALTER TABLE evo_pagebuilder ADD COLUMN container varchar(255)…

  compatibility of 'evocms-community/pagebuilder' with Evolution CMS 3 is unknown

Evolution CE keeps core/vendor/ under version control — 5344 files, 51 MB — so any Composer install turns into a diff of thousands of files in your working tree.

Compatibility

Most legacy extras were written for MODX Evolution 1.x. Ditto was last touched in 2021, ManagerManager in 2019. The format has no field for declaring compatibility with Evo 3, so every entry carries a status set by hand:

  • verified — installed and checked on Evolution CMS 3
  • unknown — never checked, which is the default for legacy extras
  • incompatible — known to break

extra:list shows all of them. extra:install refuses unknown and incompatible unless you pass --force.

Removal

Removal takes back what the extra brought and leaves anything you changed:

  • A file is deleted only if it still matches the installed copy byte for byte. Anything you edited stays, and the output tells you what was left.
  • Files overwritten during install are restored from their .old backups.
  • Elements created by the install are deleted. Elements that existed beforehand are reverted to their previous code.
  • Elements marked @overwrite false in their docblock are never touched. Authors use that tag for config chunks people are expected to edit.
  • Schema is not rolled back. Tables and columns an extra created stay, because the format has no down migrations. The plan says so before you confirm.

Catalog

Sources are configured in order, and the first one holding a coordinate wins:

'sources' => [
    ['driver' => 'snapshot',   'name' => 'Bundled snapshot'],
    ['driver' => 'packagist',  'name' => 'Packagist', 'types' => ['evolutioncms-plugin', /* … */]],
    ['driver' => 'github-org', 'name' => 'Extras-Evolution', 'organization' => 'extras-evolution'],
    ['driver' => 'github-org', 'name' => 'EvoCMS Community', 'organization' => 'evocms-community'],
],

The snapshot ships with the package and comes first. Walking the legacy organisations costs hundreds of GitHub requests against an anonymous limit of 60 per hour, and compatibility statuses have nowhere else to live.

Rebuilding it needs a token and is done separately, then committed:

GITHUB_PAT=ghp_… php artisan extra:cache --rebuild-snapshot=vendor/hkyss/evocms-extras/resources/catalog.json

Statuses set by hand survive a rebuild. GITHUB_PAT is the variable the core already reads, so an existing token works as is.

How it plugs into Evolution

The core does the wiring; this package writes the manifest and runs Composer.

composer update in core/
   └─ post-autoload-dump → php artisan package:discover
        ├─ core/custom/composer.json
        ├─ extra.laravel.providers → core/custom/config/app/providers/<Class>.php
        ├─ extra.laravel.files     → assets copied into the site tree
        └─ provider manifest reset
   └─ ProviderRepository loads the providers

Requirements go into core/custom/composer.json, which wikimedia/composer-merge-plugin merges into the core manifest. The core's own composer.json is never modified.

Composer runs in-process, with its exit code checked and the manifest restored if it fails. A subprocess would need a composer binary in PATH, which shared hosting usually lacks — the core bundles composer/composer for the same reason.

Requirements

  • PHP ^8.2
  • Evolution CMS CE 3.1.x, tested on 3.1.30
  • ext-json, ext-zip
  • composer/composer, already a core dependency

The CMS cannot be declared as a Composer dependency: evocms/evolution is published as type: project and evocms/core is not published at all. extra:doctor checks the platform at runtime instead.

Development

composer install
composer check

composer check is php-cs-fixer, phpstan level 6 and phpunit. Unit tests cover the planning side — descriptor parsing, SQL splitting, property merging, tree comparison, catalog assembly — and need no network, database or Evolution install. Fixtures are unmodified files from real extras. Plan execution is covered separately against a real CE checkout.

See CONTRIBUTING.md.

License

MIT, see LICENSE. Changes are listed in CHANGELOG.md; what installing an extra actually does to your server is described in SECURITY.md.