Skip to content

Commit 248e12b

Browse files
authored
Merge pull request #56 from madewithlove/feature/config-restructure
Restructure config: support allowed and denied license modes
2 parents f11512d + 4e53ec8 commit 248e12b

22 files changed

Lines changed: 764 additions & 146 deletions

.allowed-licenses

Lines changed: 0 additions & 5 deletions
This file was deleted.

.license-checker.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
allowed:
2+
- Apache-2.0
3+
- BSD-3-Clause
4+
- ISC
5+
- MIT
6+
- OSL-3.0

README.md

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,78 @@
11
# CLI Licence checker for composer dependencies
22
This library offers a simple CLI tool to show the licenses used by composer dependencies in your project.
3-
These licenses can be verified against a list of allowed licenses to offer a way for your continuous integration
3+
These licenses can be verified against a list of allowed (or denied) licenses to offer a way for your continuous integration
44
pipeline to block merging when a non-verified license is being introduced to the codebase.
55

6+
## Upgrading from 2.x
7+
8+
Version 3.x introduces a new structured configuration format. Run the migration command to upgrade:
9+
10+
```
11+
vendor/bin/license-checker migrate-config --remove-old
12+
```
13+
14+
This converts your `.allowed-licenses` file to the new `.license-checker.yml` format. See [full migration details](#migrating-from-2x) below.
15+
616
## Installation
717
Installing should be a breeze thanks to `composer`:
8-
Note that you need PHP 8.3 to install the latest version (2.x).
9-
If you are using an older version of PHP, older versions can be installed.
18+
Note that you need PHP 8.4 to install the latest version (3.x).
1019

1120
```
1221
composer require madewithlove/license-checker
1322
```
1423

1524
## Configuration
16-
To configure a list of allowed licenses, simply create an `.allowed-licenses` file in the root of your project (where `composer.json` is located).
17-
The file could look like this:
25+
Create a `.license-checker.yml` file in the root of your project (where `composer.json` is located).
26+
27+
### Allowlist mode
28+
Only the listed licenses are permitted. Any dependency using a license not on this list will be flagged:
29+
```yaml
30+
# .license-checker.yml
31+
allowed:
32+
- MIT
33+
- BSD-3-Clause
34+
- Apache-2.0
1835
```
19-
# contents of .allowed-licenses
20-
- MIT
21-
- BSD-3-Clause
22-
- New BSD License
36+
37+
### Denylist mode
38+
All licenses are permitted **except** the ones listed. Use this when you want to block specific licenses:
39+
```yaml
40+
# .license-checker.yml
41+
denied:
42+
- GPL-3.0
43+
- AGPL-3.0
2344
```
2445
46+
> **Note:** `allowed` and `denied` are mutually exclusive — you must use one or the other, not both.
47+
2548
It's possible to use a custom configuration file by passing the `--filename` (or `-f`) option to the CLI commands.
2649

2750
## Usage
28-
These are the different CLI commands
51+
These are the different CLI commands:
52+
53+
### Check licenses
54+
```
55+
vendor/bin/license-checker check
56+
```
2957
3058
### List used licenses
3159
```
3260
vendor/bin/license-checker used
3361
```
3462
35-
### List allowed licenses
63+
### List configured licenses
64+
Shows the configured allowed or denied licenses:
3665
```
37-
vendor/bin/license-checker allowed
66+
vendor/bin/license-checker list-config
3867
```
3968
40-
### Check licenses
69+
### Count used licenses
4170
```
42-
vendor/bin/license-checker check
71+
vendor/bin/license-checker count
4372
```
4473
4574
### Automatically generate configuration
46-
This command will automatically generate an `.allowed-licenses` configuration based on the currently used licenses.
75+
This command will automatically generate a `.license-checker.yml` configuration in allowlist mode based on the currently used licenses:
4776
```
4877
vendor/bin/license-checker generate-config
4978
```
@@ -52,23 +81,28 @@ vendor/bin/license-checker generate-config
5281
Passing the `--no-dev` option to the CLI commands will scope all checks to production dependencies only.
5382
Checking production and development dependencies against separate configuration files is possible by passing options:
5483
```
55-
vendor/bin/license-checker check --no-dev --filename .allowed-licenses-production
56-
vendor/bin/license-checker check --filename .allowed-licenses-including-dev
84+
vendor/bin/license-checker check --no-dev --filename .license-checker-production.yml
85+
vendor/bin/license-checker check --filename .license-checker-including-dev.yml
5786
```
5887
5988
### Output Formats (--format option)
60-
You can now choose how license information is displayed either as a human-readable table (text) or in machine-readable JSON format.
89+
You can choose how license information is displayed either as a human-readable table (text) or in machine-readable JSON format.
6190
6291
```
6392
vendor/bin/license-checker check --format=json
6493
```
6594
6695
```json
6796
{
68-
"laravel/framework": "MIT",
69-
"phpunit/phpunit": "BSD-3-Clause"
97+
"laravel/framework": {
98+
"license": "MIT",
99+
"is_allowed": true
100+
},
101+
"phpunit/phpunit": {
102+
"license": "BSD-3-Clause",
103+
"is_allowed": false
104+
}
70105
}
71-
72106
```
73107

74108
```
@@ -78,8 +112,49 @@ vendor/bin/license-checker check --format=text
78112
```
79113
✓ phpunit/phpunit [BSD-3-Clause]
80114
✓ symfony/console [MIT]
81-
✓ vimeo/psalm [MIT]
82115
```
83116

84117
By default, results are printed as human-readable text.
85-
Use --format=json for structured machine-readable output.
118+
Use `--format=json` for structured machine-readable output.
119+
120+
## Migrating from 2.x
121+
122+
Version 3.x introduces a new structured configuration format. Here's what changed:
123+
124+
### Configuration file format
125+
The old format was a plain YAML list in `.allowed-licenses`:
126+
```yaml
127+
# OLD format (.allowed-licenses) — no longer supported
128+
- MIT
129+
- BSD-3-Clause
130+
```
131+
132+
The new format uses a structured YAML file (`.license-checker.yml`) with an explicit `allowed` or `denied` key:
133+
```yaml
134+
# NEW format (.license-checker.yml)
135+
allowed:
136+
- MIT
137+
- BSD-3-Clause
138+
```
139+
140+
### Automatic migration
141+
Use the `migrate-config` command to convert your old configuration:
142+
```
143+
vendor/bin/license-checker migrate-config
144+
```
145+
146+
This reads `.allowed-licenses` and writes `.license-checker.yml` with the `allowed:` key.
147+
148+
To also remove the old file:
149+
```
150+
vendor/bin/license-checker migrate-config --remove-old
151+
```
152+
153+
### Renamed commands
154+
| 2.x | 3.x |
155+
|-----|-----|
156+
| `allowed` | `list-config` |
157+
158+
### Other breaking changes
159+
- Minimum PHP version is now 8.4 (was 8.3)
160+
- Minimum Symfony version is now 7.4 (was 4.0)

bin/license-checker

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
use LicenseChecker\Commands\CheckLicenses;
55
use LicenseChecker\Commands\CountUsedLicenses;
66
use LicenseChecker\Commands\GenerateConfig;
7-
use LicenseChecker\Commands\ListAllowedLicenses;
7+
use LicenseChecker\Commands\ListConfiguredLicenses;
88
use LicenseChecker\Commands\ListUsedLicenses;
9+
use LicenseChecker\Commands\MigrateConfig;
910
use LicenseChecker\Commands\Output\TableRenderer;
1011
use LicenseChecker\Composer\DependencyTree;
1112
use LicenseChecker\Composer\DependencyTreeRetriever;
1213
use LicenseChecker\Composer\UsedLicensesParser;
1314
use LicenseChecker\Composer\UsedLicensesRetriever;
14-
use LicenseChecker\Configuration\AllowedLicensesParser;
15+
use LicenseChecker\Configuration\LicenseConfigurationParser;
1516
use Symfony\Component\Console\Application;
1617

1718
$potentialAutoLoaderFiles = [
@@ -25,14 +26,15 @@ foreach ($potentialAutoLoaderFiles as $potentialAutoLoaderFile) {
2526
}
2627
}
2728

28-
$application = new Application('License Checker', '2.0');
29+
$application = new Application('License Checker', '3.0');
2930

30-
$allowedLicensesParser = new AllowedLicensesParser((string) getcwd());
31+
$workingDirectory = (string) getcwd();
32+
$configParser = new LicenseConfigurationParser($workingDirectory);
3133
$usedLicensesParser = new UsedLicensesParser(new UsedLicensesRetriever());
3234

3335
$checkLicenses = new CheckLicenses(
3436
$usedLicensesParser,
35-
$allowedLicensesParser,
37+
$configParser,
3638
new DependencyTree(
3739
new DependencyTreeRetriever(),
3840
$usedLicensesParser
@@ -42,8 +44,9 @@ $checkLicenses = new CheckLicenses(
4244

4345
$application->addCommand($checkLicenses);
4446
$application->addCommand(new ListUsedLicenses($usedLicensesParser));
45-
$application->addCommand(new ListAllowedLicenses($allowedLicensesParser));
46-
$application->addCommand(new GenerateConfig($allowedLicensesParser, $usedLicensesParser));
47+
$application->addCommand(new ListConfiguredLicenses($configParser));
48+
$application->addCommand(new GenerateConfig($configParser, $usedLicensesParser));
4749
$application->addCommand(new CountUsedLicenses($usedLicensesParser));
50+
$application->addCommand(new MigrateConfig($configParser, $workingDirectory));
4851

4952
$application->run();

src/Commands/CheckLicenses.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,27 @@
88
use LicenseChecker\Commands\Output\TableRenderer;
99
use LicenseChecker\Composer\DependencyTree;
1010
use LicenseChecker\Composer\UsedLicensesParser;
11-
use LicenseChecker\Configuration\AllowedLicensesParser;
11+
use LicenseChecker\Configuration\InvalidConfiguration;
12+
use LicenseChecker\Configuration\LicenseConfigurationParser;
13+
use LicenseChecker\Output\OutputFormat;
14+
use LicenseChecker\Output\OutputFormatterFactory;
1215
use Symfony\Component\Console\Command\Command;
1316
use Symfony\Component\Console\Input\InputInterface;
1417
use Symfony\Component\Console\Input\InputOption;
1518
use Symfony\Component\Console\Output\OutputInterface;
1619
use Symfony\Component\Console\Style\SymfonyStyle;
1720
use Symfony\Component\Process\Exception\ProcessFailedException;
1821
use Symfony\Component\Yaml\Exception\ParseException;
19-
use LicenseChecker\Output\OutputFormatterFactory;
20-
use LicenseChecker\Output\OutputFormat;
2122

2223
final class CheckLicenses extends Command
2324
{
2425
private const string NAME = 'check';
2526

2627
public function __construct(
2728
private readonly UsedLicensesParser $usedLicensesParser,
28-
private readonly AllowedLicensesParser $allowedLicensesParser,
29+
private readonly LicenseConfigurationParser $configParser,
2930
private readonly DependencyTree $dependencyTree,
30-
private readonly TableRenderer $tableRenderer
31+
private readonly TableRenderer $tableRenderer,
3132
) {
3233
parent::__construct(self::NAME);
3334
}
@@ -66,13 +67,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6667
try {
6768
/** @var string|null $fileName */
6869
$fileName = is_string($input->getOption('filename')) ? $input->getOption('filename') : null;
69-
$allowedLicenses = $this->allowedLicensesParser->getAllowedLicenses($fileName);
70-
} catch (ParseException $e) {
70+
$config = $this->configParser->parse($fileName);
71+
} catch (ParseException | InvalidConfiguration $e) {
7172
$output->writeln($e->getMessage());
7273
return 1;
7374
}
7475

75-
$notAllowedLicenses = array_diff($usedLicenses, $allowedLicenses);
76+
$notAllowedLicenses = $config->findViolations($usedLicenses);
7677
$dependencies = $this->dependencyTree->getDependencies((bool)$input->getOption('no-dev'));
7778

7879
$dependencyChecks = [];

src/Commands/GenerateConfig.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
namespace LicenseChecker\Commands;
66

77
use LicenseChecker\Composer\UsedLicensesParser;
8-
use LicenseChecker\Configuration\AllowedLicensesParser;
98
use LicenseChecker\Configuration\ConfigurationExists;
9+
use LicenseChecker\Configuration\LicenseConfiguration;
10+
use LicenseChecker\Configuration\LicenseConfigurationParser;
1011
use Symfony\Component\Console\Command\Command;
1112
use Symfony\Component\Console\Input\InputInterface;
1213
use Symfony\Component\Console\Input\InputOption;
@@ -19,15 +20,15 @@ final class GenerateConfig extends Command
1920
private const string NAME = 'generate-config';
2021

2122
public function __construct(
22-
private readonly AllowedLicensesParser $allowedLicensesParser,
23-
private readonly UsedLicensesParser $usedLicensesParser
23+
private readonly LicenseConfigurationParser $configParser,
24+
private readonly UsedLicensesParser $usedLicensesParser,
2425
) {
2526
parent::__construct(self::NAME);
2627
}
2728

2829
protected function configure(): void
2930
{
30-
$this->setDescription('Generates allowed licenses config based on used licenses');
31+
$this->setDescription('Generates license configuration based on used licenses');
3132
$this->addOption('no-dev', null, InputOption::VALUE_NONE, 'Do not include dev dependencies');
3233
$this->addOption(
3334
'filename',
@@ -42,20 +43,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int
4243
$io = new SymfonyStyle($input, $output);
4344

4445
try {
45-
$usedLicenses = $this->usedLicensesParser->parseLicenses((bool)$input->getOption('no-dev'));
46+
$usedLicenses = $this->usedLicensesParser->parseLicenses((bool) $input->getOption('no-dev'));
4647
} catch (ProcessFailedException $e) {
4748
$io->error($e->getMessage());
4849
return 1;
4950
}
5051

51-
sort($usedLicenses);
52-
5352
try {
5453
/** @var string|null $fileName */
5554
$fileName = is_string($input->getOption('filename')) ? $input->getOption('filename') : null;
56-
$this->allowedLicensesParser->writeConfiguration($usedLicenses, $fileName);
55+
$this->configParser->writeConfiguration(LicenseConfiguration::allowed($usedLicenses), $fileName);
5756
$io->success('Configuration file successfully written');
58-
} catch (ConfigurationExists $e) {
57+
} catch (ConfigurationExists) {
5958
$io->error('The configuration file already exists. Please remove it before generating a new one.');
6059
return 1;
6160
}

0 commit comments

Comments
 (0)