Преглед на файлове

Add relaxed schema for application packages that do not need to be distributed

Jordi Boggiano преди 13 години
родител
ревизия
82dd1c1f7f
променени са 5 файла, в които са добавени 254 реда и са изтрити 7 реда
  1. 190 0
      res/composer-schema-lax.json
  2. 17 0
      src/Composer/Command/ValidateCommand.php
  3. 1 1
      src/Composer/Factory.php
  4. 12 6
      src/Composer/Json/JsonFile.php
  5. 34 0
      src/Composer/Json/JsonValidationException.php

+ 190 - 0
res/composer-schema-lax.json

@@ -0,0 +1,190 @@
+{
+    "name": "Package",
+    "type": "object",
+    "additionalProperties": true,
+    "properties": {
+        "name": {
+            "type": "string",
+            "description": "Package name, including 'vendor-name/' prefix."
+        },
+        "type": {
+            "description": "Package type, either 'library' for common packages, 'composer-installer' for custom installers, or a custom type defined by whatever project this package applies to.",
+            "type": "string"
+        },
+        "target-dir": {
+            "description": "Forces the package to be installed into the given subdirectory path. This is used for autoloading PSR-0 packages that do not contain their full path. Use forward slashes for cross-platform compatibility.",
+            "type": "string"
+        },
+        "description": {
+            "type": "string",
+            "description": "Short package description."
+        },
+        "keywords": {
+            "type": "array",
+            "items": {
+                "type": "string",
+                "description": "A tag/keyword that this package relates to."
+            }
+        },
+        "homepage": {
+            "type": "string",
+            "description": "Homepage URL for the project.",
+            "format": "uri"
+        },
+        "version": {
+            "type": "string",
+            "description": "Package version, see http://packagist.org/about for more info on valid schemes."
+        },
+        "time": {
+            "type": "string",
+            "description": "Package release date, in 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' format."
+        },
+        "license": {
+            "type": ["string", "array"],
+            "description": "License name. Or an array of license names."
+        },
+        "authors": {
+            "type": "array",
+            "description": "List of authors that contributed to the package. This is typically the main maintainers, not the full list.",
+            "items": {
+                "type": "object",
+                "additionalProperties": false,
+                "properties": {
+                    "name": {
+                        "type": "string",
+                        "description": "Full name of the author.",
+                        "required": true
+                    },
+                    "email": {
+                        "type": "string",
+                        "description": "Email address of the author.",
+                        "format": "email"
+                    },
+                    "homepage": {
+                        "type": "string",
+                        "description": "Homepage URL for the author.",
+                        "format": "uri"
+                    }
+                }
+            }
+        },
+        "require": {
+            "type": "object",
+            "description": "This is a hash of package name (keys) and version constraints (values) that are required to run this package.",
+            "additionalProperties": true
+        },
+        "replace": {
+            "type": "object",
+            "description": "This is a hash of package name (keys) and version constraints (values) that can be replaced by this package.",
+            "additionalProperties": true
+        },
+        "conflict": {
+            "type": "object",
+            "description": "This is a hash of package name (keys) and version constraints (values) that conflict with this package.",
+            "additionalProperties": true
+        },
+        "provide": {
+            "type": "object",
+            "description": "This is a hash of package name (keys) and version constraints (values) that this package provides in addition to this package's name.",
+            "additionalProperties": true
+        },
+        "recommend": {
+            "type": "object",
+            "description": "This is a hash of package name (keys) and version constraints (values) that this package recommends to be installed (typically this will be installed as well).",
+            "additionalProperties": true
+        },
+        "suggest": {
+            "type": "object",
+            "description": "This is a hash of package name (keys) and version constraints (values) that this package suggests work well with it (typically this will only be suggested to the user).",
+            "additionalProperties": true
+        },
+        "config": {
+            "type": ["object"],
+            "description": "Composer options.",
+            "properties": {
+                "vendor-dir": {
+                    "type": "string",
+                    "description": "The location where all packages are installed, defaults to \"vendor\"."
+                },
+                "bin-dir": {
+                    "type": "string",
+                    "description": "The location where all binaries are linked, defaults to \"vendor/bin\"."
+                }
+            }
+        },
+        "extra": {
+            "type": ["object", "array"],
+            "description": "Arbitrary extra data that can be used by custom installers, for example, package of type composer-installer must have a 'class' key defining the installer class name.",
+            "additionalProperties": true
+        },
+        "autoload": {
+            "type": "object",
+            "description": "Description of how the package can be autoloaded.",
+            "properties": {
+                "psr-0": {
+                    "type": "object",
+                    "description": "This is a hash of namespaces (keys) and the directories they can be found into (values) by the autoloader.",
+                    "additionalProperties": true
+                }
+            }
+        },
+        "repositories": {
+            "type": ["object", "array"],
+            "description": "A set of additional repositories where packages can be found.",
+            "additionalProperties": true
+        },
+        "bin": {
+            "type": ["array"],
+            "description": "A set of files that should be treated as binaries and symlinked into bin-dir (from config).",
+            "items": {
+                "type": "string"
+            }
+        },
+        "scripts": {
+            "type": ["object"],
+            "description": "Scripts listeners that will be executed before/after some events.",
+            "properties": {
+                "pre-install-cmd": {
+                    "type": ["array", "string"],
+                    "description": "Occurs before the install command is executed, contains one or more Class::method callables."
+                },
+                "post-install-cmd": {
+                    "type": ["array", "string"],
+                    "description": "Occurs after the install command is executed, contains one or more Class::method callables."
+                },
+                "pre-update-cmd": {
+                    "type": ["array", "string"],
+                    "description": "Occurs before the update command is executed, contains one or more Class::method callables."
+                },
+                "post-update-cmd": {
+                    "type": ["array", "string"],
+                    "description": "Occurs after the update command is executed, contains one or more Class::method callables."
+                },
+                "pre-package-install": {
+                    "type": ["array", "string"],
+                    "description": "Occurs before a package is installed, contains one or more Class::method callables."
+                },
+                "post-package-install": {
+                    "type": ["array", "string"],
+                    "description": "Occurs after a package is installed, contains one or more Class::method callables."
+                },
+                "pre-package-update": {
+                    "type": ["array", "string"],
+                    "description": "Occurs before a package is updated, contains one or more Class::method callables."
+                },
+                "post-package-update": {
+                    "type": ["array", "string"],
+                    "description": "Occurs after a package is updated, contains one or more Class::method callables."
+                },
+                "pre-package-uninstall": {
+                    "type": ["array", "string"],
+                    "description": "Occurs before a package has been uninstalled, contains one or more Class::method callables."
+                },
+                "post-package-uninstall": {
+                    "type": ["array", "string"],
+                    "description": "Occurs after a package has been uninstalled, contains one or more Class::method callables."
+                }
+            }
+        }
+    }
+}

+ 17 - 0
src/Composer/Command/ValidateCommand.php

@@ -16,6 +16,7 @@ use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Output\OutputInterface;
 use Composer\Json\JsonFile;
+use Composer\Json\JsonValidationException;
 
 /**
  * @author Robert Schönthal <seroscho@googlemail.com>
@@ -52,11 +53,27 @@ EOT
             return 1;
         }
 
+        $laxValid = false;
         try {
             $json = new JsonFile($file);
             $json->read();
+
+            $json->validateSchema(JsonFile::LAX_SCHEMA);
+            $laxValid = true;
             $json->validateSchema();
+        } catch (JsonValidationException $e) {
+            if ($laxValid) {
+                $output->writeln('<info>'.$file.' is valid for simple usage with composer but has</info>');
+                $output->writeln('<info>strict errors that make it unable to be published as a package:</info>');
+            } else {
+                $output->writeln('<error>'.$file.' is invalid, the following errors were found:</error>');
+            }
+            foreach ($e->getErrors() as $message) {
+                $output->writeln('<error>'.$message.'</error>');
+            }
+            return 1;
         } catch (\Exception $e) {
+            $output->writeln('<error>'.$file.' contains a JSON Syntax Error:</error>');
             $output->writeln('<error>'.$e->getMessage().'</error>');
             return 1;
         }

+ 1 - 1
src/Composer/Factory.php

@@ -56,7 +56,7 @@ class Factory
         );
 
         $packageConfig = $file->read();
-        $file->validateSchema();
+        $file->validateSchema(JsonFile::LAX_SCHEMA);
 
         if (isset($packageConfig['config']) && is_array($packageConfig['config'])) {
             $packageConfig['config'] = array_merge($composerConfig, $packageConfig['config']);

+ 12 - 6
src/Composer/Json/JsonFile.php

@@ -36,6 +36,9 @@ if (!defined('JSON_UNESCAPED_UNICODE')) {
  */
 class JsonFile
 {
+    const LAX_SCHEMA = 1;
+    const STRICT_SCHEMA = 2;
+
     private $path;
 
     /**
@@ -111,11 +114,11 @@ class JsonFile
     /**
      * Validates the schema of the current json file according to composer-schema.json rules
      *
-     * @param string $json
+     * @param int $schema a JsonFile::*_SCHEMA constant
      * @return Boolean true on success
      * @throws \UnexpectedValueException
      */
-    public function validateSchema()
+    public function validateSchema($schema = self::STRICT_SCHEMA)
     {
         $content = file_get_contents($this->path);
         $data = json_decode($content);
@@ -124,17 +127,20 @@ class JsonFile
             self::validateSyntax($content);
         }
 
-        $schema = json_decode(file_get_contents(__DIR__ . '/../../../res/composer-schema.json'));
+        $schemaFile = __DIR__ . '/../../../res/composer-schema'.($schema === self::LAX_SCHEMA ? '-lax' : '').'.json';
+        $schema = json_decode(file_get_contents($schemaFile));
 
         $validator = new Validator();
         $validator->check($data, $schema);
 
+        // TODO add more specific checks for common errors if needed
+
         if (!$validator->isValid()) {
-            $msg = "\n";
+            $errors = array();
             foreach ((array) $validator->getErrors() as $error) {
-                $msg .= ($error['property'] ? $error['property'].' : ' : '').$error['message']."\n";
+                $errors[] = ($error['property'] ? $error['property'].' : ' : '').$error['message'];
             }
-            throw new \UnexpectedValueException('Your composer.json is invalid. The following errors were found:' . $msg);
+            throw new JsonValidationException($errors);
         }
 
         return true;

+ 34 - 0
src/Composer/Json/JsonValidationException.php

@@ -0,0 +1,34 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Json;
+
+use Exception;
+
+/**
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ */
+class JsonValidationException extends Exception
+{
+    protected $errors;
+
+    public function __construct(array $errors)
+    {
+        parent::__construct(implode("\n", $errors));
+        $this->errors = $errors;
+    }
+
+    public function getErrors()
+    {
+        return $this->errors;
+    }
+}