Skip to content

Commit

Permalink
Merge pull request php-pds#21 from afilina/compliance-validator
Browse files Browse the repository at this point in the history
Compliance validator
  • Loading branch information
Paul M. Jones authored Jan 1, 2017
2 parents 84b5113 + 611e299 commit 6605a8a
Show file tree
Hide file tree
Showing 3 changed files with 367 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ A package MUST use these names for these root-level files:
A package SHOULD include a root-level file indicating the licensing and
copyright terms of the package contents.

## Validator

Quickly validate your project's compliance by following these steps:

- Install package in your project: `composer require pds/skeleton @dev`
- Run the validator: `./vendor/pds/skeleton/bin/validate`

## Root-Level Directories

### bin/
Expand Down
291 changes: 291 additions & 0 deletions bin/validate
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
#!/usr/bin/env php

<?php

if (!defined('ENV') || ENV != 'test') {
$lines = scandir(__DIR__ . "/../../../../");
foreach ($lines as $i => $line) {
if (is_dir($line)) {
$lines[$i] .= "/";
}
}
$validator = new ComplianceValidator();
$results = $validator->validate($lines);
$validator->outputResults($results);
}

class ComplianceValidator
{
const STATE_OPTIONAL_NOT_PRESENT = 1;
const STATE_CORRECT_PRESENT = 2;
const STATE_REQUIRED_NOT_PRESENT = 3;
const STATE_INCORRECT_PRESENT = 4;

public function validate($lines)
{
$complianceTests = [
"Command-line executables" => $this->checkBin($lines),
"Configuration files" => $this->checkConfig($lines),
"Documentation files" => $this->checkDocs($lines),
"Web server files" => $this->checkPublic($lines),
"Other resource files" => $this->checkResources($lines),
"PHP source code" => $this->checkSrc($lines),
"Test code" => $this->checkTests($lines),
"Package managers" => $this->checkVendor($lines),
"Log of changes between releases" => $this->checkChangelog($lines),
"Guidelines for contributors" => $this->checkContributing($lines),
"Licensing information" => $this->checkLicense($lines),
"Information about the package itself" => $this->checkReadme($lines),
];

$results = [];
foreach ($complianceTests as $label => $complianceResult) {
$state = $complianceResult[0];
$expected = $complianceResult[1];
$actual = $complianceResult[2];
$results[$expected] = [
'label' => $label,
'state' => $state,
'expected' => $expected,
'actual' => $actual,
];
}
return $results;
}

public function outputResults($results)
{
foreach ($results as $result) {
$this->outputResultLine($result['label'], $result['state'], $result['expected'], $result['actual']);
}
}

protected function outputResultLine($label, $complianceState, $expected, $actual)
{
$messages = [
self::STATE_OPTIONAL_NOT_PRESENT => "Optional {$expected} not present",
self::STATE_CORRECT_PRESENT => "Correct {$actual} present",
self::STATE_INCORRECT_PRESENT => "Incorrect {$actual} present",
self::STATE_REQUIRED_NOT_PRESENT => "Required {$expected} not present",
];
echo $this->colorConsoleText("- " . $label . ": " . $messages[$complianceState], $complianceState) . PHP_EOL;
}

protected function colorConsoleText($text, $complianceState)
{
$colors = [
self::STATE_OPTIONAL_NOT_PRESENT => "\033[43;30m",
self::STATE_CORRECT_PRESENT => "\033[42;30m",
self::STATE_INCORRECT_PRESENT => "\033[41m",
self::STATE_REQUIRED_NOT_PRESENT => "\033[41m",
];
if (!array_key_exists($complianceState, $colors)) {
return $text;
}
return $colors[$complianceState] . " " . $text . " \033[0m";
}

protected function checkDir($lines, $pass, array $fail)
{
foreach ($lines as $line) {
$line = trim($line);
if ($line == $pass) {
return [self::STATE_CORRECT_PRESENT, $pass, $line];
}
if (in_array($line, $fail)) {
return [self::STATE_INCORRECT_PRESENT, $pass, $line];
}
}
return [self::STATE_OPTIONAL_NOT_PRESENT, $pass, null];
}

protected function checkFile($lines, $pass, array $fail)
{
foreach ($lines as $line) {
$line = trim($line);
if (preg_match("/^{$pass}(\.[a-z]+)?$/", $line)) {
return [self::STATE_CORRECT_PRESENT, $pass, $line];
}
foreach ($fail as $regex) {
if (preg_match($regex, $line)) {
return [self::STATE_INCORRECT_PRESENT, $pass, $line];
}
}
}
return [self::STATE_OPTIONAL_NOT_PRESENT, $pass, null];
}

protected function checkVendor($lines, $pass = 'vendor/')
{
foreach ($lines as $line) {
$line = trim($line);
if ($line == $pass) {
return [self::STATE_CORRECT_PRESENT, $pass, $line];
}
}
return [self::STATE_REQUIRED_NOT_PRESENT, $pass, null];
}

protected function checkChangelog($lines)
{
return $this->checkFile($lines, 'CHANGELOG', [
'/^.*CHANGLOG.*$/i',
'/^.*CAHNGELOG.*$/i',
'/^WHATSNEW(\.[a-z]+)?$/i',
'/^RELEASE((_|-)?NOTES)?(\.[a-z]+)?$/i',
'/^RELEASES(\.[a-z]+)?$/i',
'/^CHANGES(\.[a-z]+)?$/i',
'/^CHANGE(\.[a-z]+)?$/i',
'/^HISTORY(\.[a-z]+)?$/i',
]);
}

protected function checkContributing($lines)
{
return $this->checkFile($lines, 'CONTRIBUTING', [
'/^DEVELOPMENT(\.[a-z]+)?$/i',
'/^README\.CONTRIBUTING(\.[a-z]+)?$/i',
'/^DEVELOPMENT_README(\.[a-z]+)?$/i',
'/^CONTRIBUTE(\.[a-z]+)?$/i',
'/^HACKING(\.[a-z]+)?$/i',
]);
}

protected function checkLicense($lines)
{
return $this->checkFile($lines, 'LICENSE', [
'/^.*EULA.*$/i',
'/^.*(GPL|BSD).*$/i',
'/^([A-Z-]+)?LI(N)?(S|C)(E|A)N(S|C)(E|A)(_[A-Z_]+)?(\.[a-z]+)?$/i',
'/^COPY(I)?NG(\.[a-z]+)?$/i',
'/^COPYRIGHT(\.[a-z]+)?$/i',
]);
}

protected function checkReadme($lines)
{
return $this->checkFile($lines, 'README', [
'/^USAGE(\.[a-z]+)?$/i',
'/^SUMMARY(\.[a-z]+)?$/i',
'/^DESCRIPTION(\.[a-z]+)?$/i',
'/^IMPORTANT(\.[a-z]+)?$/i',
'/^NOTICE(\.[a-z]+)?$/i',
'/^GETTING(_|-)STARTED(\.[a-z]+)?$/i',
]);
}

protected function checkBin($lines)
{
return $this->checkDir($lines, 'bin/', [
'cli/',
'scripts/',
'console/',
'shell/',
'script/',
]);
}

protected function checkConfig($lines)
{
return $this->checkDir($lines, 'config/', [
'etc/',
'settings/',
'configuration/',
'configs/',
'_config/',
'conf/',
]);
}

protected function checkDocs($lines)
{
return $this->checkDir($lines, 'docs/', [
'manual/',
'documentation/',
'usage/',
'doc/',
'guide/',
'phpdoc/',
]);
}

protected function checkPublic($lines)
{
return $this->checkDir($lines, 'public/', [
'assets/',
'static/',
'html/',
'httpdocs/',
'media/',
'docroot/',
'css/',
'fonts/',
'styles/',
'style/',
'js/',
'javascript/',
'images/',
'site/',
'mysite/',
'img/',
'web/',
'pub/',
'webroot/',
'www/',
'htdocs/',
'asset/',
'public_html/',
'publish/',
'pages/',
]);
}

protected function checkSrc($lines)
{
return $this->checkDir($lines, 'src/', [
'exception/',
'exceptions/',
'src-files/',
'traits/',
'interfaces/',
'common/',
'sources/',
'php/',
'inc/',
'libraries/',
'autoloads/',
'autoload/',
'source/',
'includes/',
'include/',
'lib/',
'libs/',
'library/',
'code/',
'classes/',
'func/',
]);
}

protected function checkTests($lines)
{
return $this->checkDir($lines, 'tests/', [
'test/',
'unit-tests/',
'phpunit/',
'testing/',
]);
}

protected function checkResources($lines)
{
return $this->checkDir($lines, 'resources/', [
'Resources/',
'res/',
'resource/',
'Resource/',
'ressources/',
'Ressources/',
]);
}
}
69 changes: 69 additions & 0 deletions tests/ComplianceValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

define('ENV', 'test');
require __DIR__ . "/../bin/validate";

$tester = new ComplianceValidatorTest();
// Test all 4 possible states.
$tester->testValidate_WithIncorrectBin_ReturnsIncorrectBin();
$tester->testValidate_WithoutVendor_ReturnsMissingVendor();

echo "Errors: {$tester->numErrors}" . PHP_EOL;

class ComplianceValidatorTest
{
public $numErrors = 0;

public function testValidate_WithIncorrectBin_ReturnsIncorrectBin()
{
$paths = [
'cli/',
'vendor/',
];

$validator = new ComplianceValidator();
$results = $validator->validate($paths);

foreach ($results as $expected => $result) {
if ($expected == "bin/") {
if ($result['state'] != ComplianceValidator::STATE_INCORRECT_PRESENT) {
$this->numErrors++;
echo __FUNCTION__ . ": Expected state of {$result['expected']} to be STATE_INCORRECT_PRESENT" . PHP_EOL;
}
continue;
}
if ($expected == "vendor/") {
if ($result['state'] != ComplianceValidator::STATE_CORRECT_PRESENT) {
$this->numErrors++;
echo __FUNCTION__ . ": Expected state of {$result['expected']} to be STATE_CORRECT_PRESENT" . PHP_EOL;
}
continue;
}
if ($result['state'] != ComplianceValidator::STATE_OPTIONAL_NOT_PRESENT) {
$this->numErrors++;
echo __FUNCTION__ . ": Expected state of {$result['expected']} to be STATE_OPTIONAL_NOT_PRESENT" . PHP_EOL;
continue;
}
}
}

public function testValidate_WithoutVendor_ReturnsMissingVendor()
{
$paths = [
'bin/',
];

$validator = new ComplianceValidator();
$results = $validator->validate($paths);

foreach ($results as $expected => $result) {
if ($expected == "vendor/") {
if ($result['state'] != ComplianceValidator::STATE_REQUIRED_NOT_PRESENT) {
$this->numErrors++;
echo __FUNCTION__ . ": Expected state of {$result['expected']} to be STATE_REQUIRED_NOT_PRESENT" . PHP_EOL;
}
continue;
}
}
}
}

0 comments on commit 6605a8a

Please sign in to comment.