Compare commits

...

18 Commits

4
.gitattributes vendored

@ -1,5 +1,9 @@
/docs/*.py export-ignore
/docs/*.txt export-ignore
/docs/_* export-ignore
/sandbox export-ignore
/tests export-ignore
/.git* export-ignore
/*.yml export-ignore
/*.xml export-ignore
/*.xml.dist export-ignore

3
.gitignore vendored

@ -1,5 +1,6 @@
/.idea
/reports
/docs/html
/docs/build
vendor
composer.lock
/.phpunit.result.cache

@ -1,5 +1,17 @@
# Changelog
## 1.1.0
*Jun 7, 2022*
* Shortcut config for `liceneses.allowed` and `packages.allowed`
## 1.0.1
*Jun 7, 2022*
* Do not remove the root package from the installation pool
## 1.0.0
*Jun 5, 2022*

@ -6,6 +6,12 @@
License management plugin for Composer.
## Installation
```sh
composer require 'arokettu/composer-license-manager'
```
## Features
The plugin is configured in the ``extras`` section of the ``composer.json`` file.
@ -36,12 +42,6 @@ Run ``composer licenses:scan`` to check installed packages for undesired license
With `"enforced": true` (default setting) the plugin will prevent installation of packages with undesired licenses during `composer install` and `composer update`.
## Installation
```sh
composer require 'arokettu/composer-license-manager'
```
## Documentation
Read full documentation here: <https://sandfox.dev/php/composer-license-manager.html>
@ -52,6 +52,8 @@ Also on Read the Docs: <https://composer-license-manager.readthedocs.io/>
Please file issues on our main repo at GitLab: <https://gitlab.com/sandfox/composer-license-manager/-/issues>
Feel free to ask any questions in our room on Gitter: <https://gitter.im/arokettu/community>
## License
The library is available as open source under the terms of the [MIT License].

@ -54,7 +54,7 @@
"require-dev": {
"composer/composer": "^2.0",
"phpunit/phpunit": "^7.5 || ^9.5",
"sandfox.dev/code-standard": "^10@dev",
"sandfox.dev/code-standard": "^1",
"squizlabs/php_codesniffer": "*",
"vimeo/psalm": "^4.23"
}

@ -29,6 +29,8 @@ Licenses
Licenses section configures desired and undesired licenses.
.. versionadded:: 1.1 ``{ "licenses": [...] }`` is a shortcut for ``{ "licenses": { "allowed": [...] } }``
``"allowed"``
Whitelisted licenses. Allows globs in prefix form (``*`` as the last character).
Default: ``["*"]``
@ -45,7 +47,6 @@ Licenses section configures desired and undesired licenses.
``BSD-Protection`` (non GPL-compatible),
``BSD-3-Clause-No-Nuclear-License`` and ``BSD-3-Clause-No-Military-License`` (both non-free)
Check order:
#. exact forbidden licenses
@ -64,6 +65,8 @@ Packages
Package exceptions to the policy enforcement.
.. versionadded:: 1.1 ``{ "packages": [...] }`` is a shortcut for ``{ "packages": { "allowed": [...] } }``
``"allowed"``
Whitelisted packages. Allows globs in prefix form (``*`` as the last character).
Default: ``[]``

@ -6,8 +6,11 @@ Policy Enforcement
Theory
======
The plugin removes non-conforming packages from the package pool before the solver pool.
That to my knowledge is the only stage where writing ``composer.lock`` with an invalid set of packages can be prevented.
The plugin removes non-conforming packages from the package pool before the solver stage.
That, to my knowledge, is the only stage in Composer 2.x where writing ``composer.lock`` with an invalid set of packages can be prevented.
The package never prevents installation of itself so you must accept its license even if you forbid MIT in general.
(But how do you use Composer then?)
Effects
=======

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
convertDeprecationsToExceptions="true"
executionOrder="random">
executionOrder="random"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd">
<testsuites>
<testsuite name="all">
<directory>tests</directory>

@ -1,4 +1,5 @@
{
"name": "sandfox.dev/sandbox",
"repositories": [
{
"type": "path",
@ -10,6 +11,7 @@
"url": "https://asset-packagist.org"
}
],
"license": "0BSD",
"require": {
"arokettu/composer-license-manager": "dev-master",
"laminas/laminas-code": "^4.5",
@ -24,7 +26,7 @@
"extra": {
"arokettu/composer-license-manager": {
"licenses": {
"allowed": ["MIT", "BSD-*", "ISC", "artistic-2.0", "apache-2.0", "(wtfpl*", "*"],
"allowed": ["MIT", "BSD-*", "ISC", "artistic-2.0", "apache-2.0", "(wtfpl*"],
"forbidden": ["BSD-3-Clause"],
"allow-empty": false
},

@ -38,11 +38,21 @@ final class Config
public static function fromArray(array $config): self
{
$licenses = $config['licenses'] ?? [];
if ($licenses !== [] && array_is_list($licenses)) {
$licenses = ['allowed' => $licenses];
}
$packages = $config['packages'] ?? [];
if ($packages !== [] && array_is_list($packages)) {
$packages = ['allowed' => $packages];
}
return new self(
ConfigHelper::valueToArray($config['licenses']['allowed'] ?? ['*']),
$config['licenses']['allow-empty'] ?? false,
ConfigHelper::valueToArray($config['licenses']['forbidden'] ?? []),
ConfigHelper::valueToArray($config['packages']['allowed'] ?? []),
ConfigHelper::valueToArray($licenses['allowed'] ?? ['*']),
$licenses['allow-empty'] ?? false,
ConfigHelper::valueToArray($licenses['forbidden'] ?? []),
ConfigHelper::valueToArray($packages['allowed'] ?? []),
$config['enforced'] ?? true
);
}
@ -108,7 +118,6 @@ final class Config
return $this->allowEmptyLicense;
}
/** @return bool */
public function isEnforced(): bool
{
return $this->enforced;

@ -16,7 +16,7 @@ use Composer\Plugin\PrePoolCreateEvent;
/**
* @internal
*/
class PrePoolCreateEventHandler
final class PrePoolCreateEventHandler
{
/** @var Composer */
private $composer;
@ -32,27 +32,35 @@ class PrePoolCreateEventHandler
public function handle(PrePoolCreateEvent $event): void
{
$config = Config::fromComposer($this->composer);
$rootPackage = $this->composer->getPackage()->getName();
/** @var array<string, array<int, string>> $filtered */
$filtered = [];
$packages = array_filter($event->getPackages(), function (PackageInterface $package) use (&$filtered, $config) {
$packageName = $package->getName();
$packages = array_filter(
$event->getPackages(),
function (PackageInterface $package) use (&$filtered, $config, $rootPackage) {
$packageName = $package->getName();
if ($packageName === LicenseManagerPlugin::PACKAGE || !str_contains($packageName, '/')) {
return true;
}
if ($package instanceof CompletePackageInterface) {
if (LicenseHelper::isPermitted($package, $config)) {
if (
$packageName === $rootPackage || // do not block root package
$packageName === LicenseManagerPlugin::PACKAGE || // do not block the manager itself
!str_contains($packageName, '/') // platform and composer packages
) {
return true;
}
if ($package instanceof CompletePackageInterface) {
if (LicenseHelper::isPermitted($package, $config)) {
return true;
} else {
$filtered[$packageName] = $package->getLicense();
return false;
}
} else {
$filtered[$packageName] = $package->getLicense();
return false;
throw new \LogicException('Filtering can work only on complete packages');
}
} else {
throw new \LogicException('Filtering can work only on complete packages');
}
});
);
if ($filtered !== []) {
$this->io->write('<warning>Some packages do not conform to the license policy:</warning>');

@ -10,7 +10,7 @@ use Composer\Package\CompletePackageInterface;
/**
* @internal
*/
class LicenseHelper
final class LicenseHelper
{
public static function isPermitted(CompletePackageInterface $package, Config $config): bool
{

@ -44,4 +44,20 @@ class ConfigTest extends TestCase
Config::fromArray(['licenses' => ['allowed' => ['MIT' => 'MIT', 123 => 123]]]);
}
public function testLicenseShortcutsEquivalence(): void
{
$config1 = Config::fromArray(['licenses' => ['allowed' => ['MIT', 'BSD-2-Clause', 'GPL-*']]]);
$config2 = Config::fromArray(['licenses' => ['MIT', 'BSD-2-Clause', 'GPL-*']]);
self::assertEquals($config1, $config2);
}
public function testPackageShortcutsEquivalence(): void
{
$config1 = Config::fromArray(['packages' => ['allowed' => ['foo/bar', 'foo/baz', 'bar/*']]]);
$config2 = Config::fromArray(['packages' => ['foo/bar', 'foo/baz', 'bar/*']]);
self::assertEquals($config1, $config2);
}
}

Loading…
Cancel
Save