Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#3 Adding depth flag to speed up check for large directory trees. #4

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This tool can be used to check the licenses of the used packages.

Our package can be installed with composer with the following command:
```bash
composer require best-it/license-check --dev --prefer-dist
composer require best-it/license-check --dev
```

## Usage
Expand Down
49 changes: 25 additions & 24 deletions src/Checker.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,46 @@ public function __construct(iterable $loaders)
$this->loaders = $loaders;
}

/**
* Check if the given package is allowed.
*
* @param string $package
* @param string[] $allowedPackages
*
* @return bool
*/
private function isPackageAllowed(string $package, array $allowedPackages): bool
{
$allowed = false;

foreach ($allowedPackages as $allowedPackage) {
if (preg_match($allowedPackage, $package) === 1) {
$allowed = true;
break;
}
}

return $allowed;
}

/**
* Start the validation.
*
* @param Configuration $configuration
* @param string $path
* @param int|null $depth
*
* @return Result
*/
public function validate(Configuration $configuration, string $path): Result
public function validate(Configuration $configuration, string $path, ?int $depth = null): Result
{
$result = new Result();

$allowedLicenses = $configuration->getAllowedLicenses();
foreach ($this->loaders as $loader) {
$type = $loader->getName();
$allowedPackages = $configuration->getAllowedPackages($type);
foreach ($loader->getLicenses($path) as $package => $licenses) {
foreach ($loader->getLicenses($path, $depth) as $package => $licenses) {
if (!$this->isPackageAllowed($package, $allowedPackages)) {
if (count($licenses) === 0) {
$result->addViolation(
Expand Down Expand Up @@ -84,26 +107,4 @@ public function validate(Configuration $configuration, string $path): Result

return $result;
}

/**
* Check if the given package is allowed.
*
* @param string $package
* @param string[] $allowedPackages
*
* @return bool
*/
private function isPackageAllowed(string $package, array $allowedPackages): bool
{
$allowed = false;

foreach ($allowedPackages as $allowedPackage) {
if (preg_match($allowedPackage, $package) === 1) {
$allowed = true;
break;
}
}

return $allowed;
}
}
20 changes: 19 additions & 1 deletion src/Command/LicenseCheckCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class LicenseCheckCommand extends Command
*/
private const OPTION_CONFIGURATION = 'configuration';

/**
* Constant for the depth cli option.
*
* @var string OPTION_IGNORE_ERRORS
*/
private const OPTION_DEPTH = 'depth';

/**
* Constant for the ignore-errors cli option.
*
Expand Down Expand Up @@ -98,6 +105,12 @@ protected function configure(): void
null,
InputOption::VALUE_NONE,
'Don\'t return an error code.',
)
->addOption(
self::OPTION_DEPTH,
null,
InputOption::VALUE_REQUIRED,
'Set the maximum depth of the directory iterators.',
);
}

Expand Down Expand Up @@ -129,7 +142,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
assert(is_string($configurationPath));
$configuration = $this->configurationLoader->load($configurationPath);

$resultSet = $this->checker->validate($configuration, $workingDirectory);
if (($depth = $input->getOption(self::OPTION_DEPTH)) !== null) {
assert(is_string($depth));
$depth = (int) $depth;
}

$resultSet = $this->checker->validate($configuration, $workingDirectory, $depth);

$resultCode = defined('static::SUCCESS') ? static::SUCCESS : 0;

Expand Down
93 changes: 93 additions & 0 deletions src/LicenseLoader/AbstractLicenseLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace BestIt\LicenseCheck\LicenseLoader;

use BestIt\LicenseCheck\LicenseLoader\Exception\LicenseLoaderException;
use BestIt\LicenseCheck\LicenseLoader\Exception\PatternNotDefinedException;
use BestIt\LicenseCheck\LicenseLoader\Result\Result;
use Symfony\Component\Finder\Finder;

/**
* Abstract license loader.
*
* @author best it AG <[email protected]>
* @package BestIt\LicenseCheck\LicenseLoader
*/
abstract class AbstractLicenseLoader implements LicenseLoaderInterface
{
/**
* Finder to get composer.json files.
*
* @var Finder $finder
*/
private Finder $finder;

/**
* Search pattern to get license files.
*
* @var string|null $searchPattern
*/
protected ?string $searchPattern = null;

/**
* ComposerLicenseLoader constructor.
*
* @param Finder $finder
*/
public function __construct(Finder $finder)
{
$this->finder = $finder;
}

/**
* Get used licenses in the specified path.
*
* @param string $path Search path.
* @param int|null $depth (Optional) Search depth.
*
* @return array<array<string>>
*/
public function getLicenses(string $path, ?int $depth = null): array
{
$result = new Result();

if ($this->searchPattern === null) {
throw new PatternNotDefinedException();
}

$iterator = $this->finder->path($this->searchPattern)->in($path);

if ($depth !== null) {
$iterator->depth($depth);
}

foreach ($iterator as $file) {
$filePath = $file->getPathname();
$content = $file->getContents();
if (!is_string($content)) {
throw new LicenseLoaderException(sprintf('Cannot read content of file %s', $filePath));
}

$decodedContent = json_decode($content, true);
if (!is_array($decodedContent)) {
throw new LicenseLoaderException(sprintf('Cannot decode content of file %s', $filePath));
}

$this->parseFile($decodedContent, $result);
}

return $result->toArray();
}

/**
* Decode file contents.
*
* @param mixed[] $decodedContent Array of json file content.
* @param Result $result License result.
*
* @return void
*/
abstract protected function parseFile(array $decodedContent, Result $result): void;
}
56 changes: 16 additions & 40 deletions src/LicenseLoader/ComposerLicenseLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,40 @@

namespace BestIt\LicenseCheck\LicenseLoader;

use BestIt\LicenseCheck\LicenseLoader\Exception\LicenseLoaderException;
use Symfony\Component\Finder\Finder;
use BestIt\LicenseCheck\LicenseLoader\Result\Result;

/**
* License loader for composer packages.
*
* @author best it AG <[email protected]>
* @package BestIt\LicenseCheck\LicenseLoader
*/
class ComposerLicenseLoader implements LicenseLoaderInterface
class ComposerLicenseLoader extends AbstractLicenseLoader
{
/**
* Finder to get composer.json files.
* Pattern for composer files.
*
* @var Finder $finder
* @var string|null
*/
private Finder $finder;
protected ?string $searchPattern = 'composer.lock';

/**
* ComposerLicenseLoader constructor.
* Parse composer.lock content.
*
* @param Finder $finder
*/
public function __construct(Finder $finder)
{
$this->finder = $finder;
}

/**
* Get used licenses in the specified path.
* @param mixed[] $decodedContent
* @param Result $result
*
* @param string $path
*
* @return array<array<string>>
* @return void
*/
public function getLicenses(string $path): array
protected function parseFile(array $decodedContent, Result $result): void
{
$result = [];

foreach ($this->finder->name('composer.lock')->in($path) as $file) {
$filePath = $file->getPathname();
$content = $file->getContents();
if (!is_string($content)) {
throw new LicenseLoaderException(sprintf('Cannot read content of file %s', $filePath));
}

$decodedContent = json_decode($content, true);
if (!is_array($decodedContent)) {
throw new LicenseLoaderException(sprintf('Cannot decode content of file %s', $filePath));
}

// ToDo: Check json structure.
foreach ($decodedContent['packages'] as $package) {
$result[$package['name']] = $package['license'] ?? [];
}
// ToDo: Check json structure.
foreach ($decodedContent['packages'] as $package) {
$result->add(
(string) $package['name'],
$package['license'] ?? [],
);
}

return $result;
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/LicenseLoader/Exception/PatternNotDefinedException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace BestIt\LicenseCheck\LicenseLoader\Exception;

use BestIt\LicenseCheck\Exception\LicenseCheckException;

/**
* Exception class if no search pattern is defined.
*
* @author best it AG <[email protected]>
* @package BestIt\LicenseCheck\LicenseLoader\Exception
*/
class PatternNotDefinedException extends LicenseCheckException
{
}
5 changes: 3 additions & 2 deletions src/LicenseLoader/LicenseLoaderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ interface LicenseLoaderInterface
/**
* Get used licenses in the specified path.
*
* @param string $path
* @param string $path Search path.
* @param int|null $depth (Optional) Search depth.
*
* @return array<array<string>>
*/
public function getLicenses(string $path): array;
public function getLicenses(string $path, ?int $depth = null): array;

/**
* Get name of the license loader.
Expand Down
Loading