From f7c3d428191dd2f5aebb2013d7879c5d4f2919bf Mon Sep 17 00:00:00 2001 From: Stephen Beemsterboer Date: Wed, 24 Jan 2018 20:37:12 -0500 Subject: [PATCH] Initial commit with plugin MVP --- .gitignore | 2 + README.md | 144 ++++++++++++++++++++++++++++++++++++++++ composer.json | 25 +++++++ composer.lock | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Plugin.php | 70 ++++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 src/Plugin.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8dcd98b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# composer artifacts +/vendor/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..df28f5e --- /dev/null +++ b/README.md @@ -0,0 +1,144 @@ +# Drupal Libraries Installer + +_Drupal Libraries Installer_ is a [composer][composer] plugin that allows for easily +managing external libraries required for Drupal modules/themes that are not available +as composer packages. This plugin is another piece of the puzzle towards managing all +external dependencies for a Drupal site in a single place: the `composer.json` file. + +## How to Use + +1. Add _Drupal Libraries Installer_ to your Drupal site project: + + ```sh + composer require balbuf/drupal-libraries-installer + ``` + +1. Ensure composer packages of type `drupal-library` are configured to install to the +appropriate path. By default, [`composer/installers`][installers] (a dependency of +this plugin and likely already included in your project) will install these packages +to `/libraries/{$name}/` in the root of your repo. You may wish to change this via +the `installer-paths` property (within `extra`) of your `composer.json`: + + ```json + { + "extra": { + "installer-paths": { + "web/libraries/{$name}/": ["type:drupal-library"] + } + } + } + ``` + + _See the `composer/installers` [README][installers readme] for more information on + the `installer-paths` property._ + +1. Add libraries to your composer.json file via the `drupal-libraries` property +within `extra`. A library is specified using its name as the key (populated as +the `{$name}` replacement token of the install path) and a URL to its distribution +ZIP file as the value: + + ```json + { + "extra": { + "drupal-libraries": { + "flexslider": "https://github.com/woocommerce/FlexSlider/archive/2.6.4.zip", + "chosen": "https://github.com/harvesthq/chosen/releases/download/v1.8.2/chosen_v1.8.2.zip" + } + } + } + ``` + _See below for how to find the ZIP URL for a GitHub repo._ + +1. Run `composer install`. Libraries are downloaded and unpacked into place upon running +`composer install` or `composer update`. (To upgrade a library, simply swap out its URL +in your `composer.json` file and run `composer install` again.) + +## How to Reference a GitHub Repo as a ZIP File + +If you are directed to download a library from its GitHub repo, follow these instructions +to find a link to the ZIP file version of the code base: + +### Preferred Method + +It is best to reference a specific release of the library so that the same version of +code is downloaded every time for each user of the project. To see what releases are +available: + +1. Click the `X releases` link (where `X` is some number) near the top of the +GitHub repo's home page. You can also reach the release page by appending `/releases/` +to the repo's home page URL, e.g. for `https://github.com/harvesthq/chosen`, you'd +go to `https://github.com/harvesthq/chosen/releases/`. + +1. Identify which release you'd like to use. You'll likely want to use the latest release +unless the module noted a specific version requirement. + +1. For that release, find the "Assets" section. If the repo provides its own distribution +ZIP file, that will be listed as one of the first files in the list. If so, you'll want to +try using that first in case it includes pre-built files not available directly in the repo. +Otherwise, use the "Source code (zip)" link for that release. Simply copy the URL for the +desired link to use within your `composer.json` file. + +### Alternate Method + +If the library does not provide any releases, you can still reference it in ZIP file form. +The downside is that any time you download this ZIP, the contents may change based on the +state of the repo. There is no guarantee that separate users of the project will have the +exact same version of the library. + +1. Click the green `Clone or download` button on the repo's home page. + +1. Copy the URL for the `Download ZIP` link to use within your `composer.json` file. + +## Notes + +- Only ZIP files are supported at this time. +- This plugin is meant to be used with a root package only (i.e. a Drupal site repo) +and will not find libraries listed in the `composer.json` files of dependencies +(e.g. contrib modules). +- This plugin is essentially a shortcut for explicitly declaring the composer package +information for each library zip you need to include in your project, e.g.: + ```json + { + "repositories": [ + { + "type": "package", + "package": { + "name": "harvesthq/chosen", + "version": "1.8.2", + "type": "drupal-library", + "dist": { + "url": "https://github.com/harvesthq/chosen/releases/download/v1.8.2/chosen_v1.8.2.zip", + "type": "zip" + } + } + } + ], + "require": { + "harvesthq/chosen": "1.8.2" + } + } + ``` + While that method is perfectly viable and works right out of the box with no additional + plugin, it is also cumbersome, not very user-friendly, and quite verbose, adding + a lot of additional noise to your `composer.json` file. +- Libraries are installed after actual composer packages are installed and are not +subject to the dependency-resolving algorithm inherent to composer. What this means +is that libraries cannot be specified with a range of compatible versions (rather, +a specific version of a library's distribution file must be chosen), and if libraries +have any other additional library dependencies of their own, these must be explicitly +added to the list. +- Because libraries are installed after composer packages, it's possible that a library +installed by this plugin could overwrite a composer package in the event of an +install-path collision. +- While many libraries are JS- and/or CSS-based and available via [npm][npm], there is +no way to install these packages directly into the proper /libraries/ folder with npm. +As well, modules will often list their external library requirements as links to ZIP +distribution files or GitHub repos, making it easier to reference and pull in these +dependencies in that manner with this plugin. +- This plugin is intended only as a short-term solution for the broader issue of +external library dependency management among modules and themes. + +[composer]: https://getcomposer.org/ +[npm]: https://www.npmjs.com/ +[installers]: https://packagist.org/packages/composer/installers +[installers readme]: https://github.com/composer/installers#custom-install-paths diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..75b9445 --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "balbuf/drupal-libraries-installer", + "description": "Install Drupal libraries via a simple listing in your composer.json file", + "type": "composer-plugin", + "license": "MIT", + "authors": [ + { + "name": "Stephen Beemsterboer", + "homepage": "https://github.com/balbuf" + } + ], + "support": { + "issues": "https://github.com/balbuf/drupal-libraries-installer/issues" + }, + "autoload": { + "psr-4": {"BalBuf\\DrupalLibrariesInstaller\\": "src/"} + }, + "extra": { + "class": "BalBuf\\DrupalLibrariesInstaller\\Plugin" + }, + "require": { + "composer-plugin-api": "^1.0", + "composer/installers": "^1.5" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..f5427b4 --- /dev/null +++ b/composer.lock @@ -0,0 +1,175 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "e3089c2a8a25f2bd283705175775337f", + "packages": [ + { + "name": "composer/installers", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "049797d727261bf27f2690430d935067710049c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/049797d727261bf27f2690430d935067710049c2", + "reference": "049797d727261bf27f2690430d935067710049c2", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0" + }, + "replace": { + "roundcube/plugin-installer": "*", + "shama/baton": "*" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpunit/phpunit": "^4.8.36" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Craft", + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Thelia", + "WolfCMS", + "agl", + "aimeos", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "joomla", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "mediawiki", + "modulework", + "modx", + "moodle", + "osclass", + "phpbb", + "piwik", + "ppi", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "symfony", + "typo3", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "time": "2017-12-29T09:13:20+00:00" + }, + { + "name": "webflo/drupal-finder", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/webflo/drupal-finder.git", + "reference": "8a7886c575d6eaa67a425dceccc84e735c0b9637" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webflo/drupal-finder/zipball/8a7886c575d6eaa67a425dceccc84e735c0b9637", + "reference": "8a7886c575d6eaa67a425dceccc84e735c0b9637", + "shasum": "" + }, + "require-dev": { + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^4.8" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/DrupalFinder.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Florian Weber", + "email": "florian@webflo.org" + } + ], + "description": "Helper class to locate a Drupal installation from a given path.", + "time": "2017-10-24T08:12:11+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 0000000..ffa9228 --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,70 @@ + 'install', + ScriptEvents::POST_UPDATE_CMD => 'install', + ]; + } + + /** + * Upon running composer install or update, install the drupal libraries. + * @param Event $event install/update event + */ + public function install(Event $event) { + // get composer object + $composer = $event->getComposer(); + // get root package extra prop + $extra = $composer->getPackage()->getExtra(); + + // do we have any libraries listed? + if (empty($extra[static::EXTRA_PROP]) || !is_array($extra[static::EXTRA_PROP])) { + return; + } + + // get some services + $downloadManager = $composer->getDownloadManager(); + $installationManager = $composer->getInstallationManager(); + + // install each library + foreach ($extra[static::EXTRA_PROP] as $library => $url) { + // create a virtual package for this library + // we don't ask for a version number, so just use "1.0.0" so the package is considered stable + $package = new Package(static::TYPE . '/' . $library, '1.0.0', $url); + // all URLs are assumed to be zips (for now) + $package->setDistType('zip'); + $package->setDistUrl($url); + $package->setType(static::TYPE); + // let composer download and unpack the library for us! + $downloadManager->download($package, $installationManager->getInstallPath($package)); + } + } + +}