1
0
Fork 0

JSON schema updates (#12123)

* Add composer-lock-schema, update composer-repository-schema with new properties, add lock schema validation in diagnose

Fixes #7823

* Add ref to composer.json schema in the lock one
pull/12103/head^2
Jordi Boggiano 2024-09-26 13:36:25 +02:00 committed by GitHub
parent d37dd5fff1
commit 95b9b54f0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 254 additions and 16 deletions

View File

@ -0,0 +1,101 @@
{
"$schema": "https://json-schema.org/draft-04/schema#",
"title": "Composer Lock File",
"type": "object",
"required": [ "content-hash", "packages", "packages-dev" ],
"additionalProperties": true,
"properties": {
"_readme": {
"type": "array",
"items": {
"type": "string"
},
"description": "Informational text for humans reading the file"
},
"content-hash": {
"type": "string",
"description": "Hash of all relevant properties of the composer.json that was used to create this lock file."
},
"packages": {
"type": "array",
"description": "An array of packages that are required.",
"items": {
"$ref": "./composer-schema.json",
"required": ["name", "version"]
}
},
"packages-dev": {
"type": "array",
"description": "An array of packages that are required in require-dev.",
"items": {
"$ref": "./composer-schema.json"
}
},
"aliases": {
"type": "array",
"description": "Inline aliases defined in the root package.",
"items": {
"type": "object",
"required": [ "package", "version", "alias", "alias_normalized" ],
"properties": {
"package": {
"type": "string"
},
"version": {
"type": "string"
},
"alias": {
"type": "string"
},
"alias_normalized": {
"type": "string"
}
}
}
},
"minimum-stability": {
"type": "string",
"description": "The minimum-stability used to generate this lock file."
},
"stability-flags": {
"type": "object",
"description": "Root package stability flags changing the minimum-stability for specific packages.",
"additionalProperties": {
"type": "integer"
}
},
"prefer-stable": {
"type": "boolean",
"description": "Whether the --prefer-stable flag was used when building this lock file."
},
"prefer-lowest": {
"type": "boolean",
"description": "Whether the --prefer-lowest flag was used when building this lock file."
},
"platform": {
"type": "object",
"description": "Platform requirements of the root package.",
"additionalProperties": {
"type": "string"
}
},
"platform-dev": {
"type": "object",
"description": "Platform dev-requirements of the root package.",
"additionalProperties": {
"type": "string"
}
},
"platform-overrides": {
"type": "object",
"description": "Platform config overrides of the root package.",
"additionalProperties": {
"type": "string"
}
},
"plugin-api-version": {
"type": "string",
"description": "The composer-plugin-api version that was used to generate this lock file."
}
}
}

View File

@ -1,11 +1,12 @@
{
"$schema": "https://json-schema.org/draft-04/schema#",
"description": "A representation of packages metadata.",
"title": "Composer Package Repository",
"type": "object",
"oneOf": [
{ "required": [ "packages" ] },
{ "required": [ "providers" ] },
{ "required": [ "provider-includes", "providers-url" ] }
{ "required": [ "provider-includes", "providers-url" ] },
{ "required": [ "metadata-url" ] }
],
"properties": {
"packages": {
@ -13,19 +14,48 @@
"description": "A hashmap of package names in the form of <vendor>/<name>.",
"additionalProperties": { "$ref": "#/definitions/versions" }
},
"providers-url": {
"metadata-url": {
"type": "string",
"description": "Endpoint to retrieve provider data from, e.g. '/p/%package%$%hash%.json'."
"description": "Endpoint to retrieve package metadata data from, in Composer v2 format, e.g. '/p2/%package%.json'."
},
"provider-includes": {
"type": "object",
"description": "A hashmap of provider listings.",
"additionalProperties": { "$ref": "#/definitions/provider" }
"available-packages": {
"type": "array",
"items": {
"type": "string"
},
"description": "If your repository only has a small number of packages, and you want to avoid serving many 404s, specify all the package names that your repository contains here."
},
"providers": {
"type": "object",
"description": "A hashmap of package names in the form of <vendor>/<name>.",
"additionalProperties": { "$ref": "#/definitions/provider" }
"available-package-patterns": {
"type": "array",
"items": {
"type": "string"
},
"description": "If your repository only has a small number of packages, and you want to avoid serving many 404s, specify package name patterns containing wildcards (*) that your repository contains here."
},
"security-advisories": {
"type": "array",
"items": {
"type": "object",
"required": ["metadata", "api-url"],
"properties": {
"metadata": {
"type": "boolean",
"description": "Whether metadata files contain security advisory data or whether it should always be queried using the API URL."
},
"api-url": {
"type": "string",
"description": "Endpoint to call to retrieve security advisories data."
}
}
}
},
"metadata-changes-url": {
"type": "string",
"description": "Endpoint to retrieve package metadata updates from. This should receive a timestamp since last call to be able to return new changes. e.g. '/metadata/changes.json'."
},
"providers-api": {
"type": "string",
"description": "Endpoint to retrieve package names providing a given name from, e.g. '/providers/%package%.json'."
},
"notify-batch": {
"type": "string",
@ -35,9 +65,73 @@
"type": "string",
"description": "Endpoint that provides search capabilities, e.g. '/search.json?q=%query%&type=%type%'."
},
"list": {
"type": "string",
"description": "Endpoint that provides a full list of packages present in the repository. It should accept an optional `?filter=xx` query param, which can contain `*` as wildcards matching any substring. e.g. '/list.json'."
},
"warnings": {
"type": "array",
"items": {
"type": "object",
"required": ["message", "versions"],
"properties": {
"message": {
"type": "string",
"description": "A message that will be output by Composer as a warning when this source is consulted."
},
"versions": {
"type": "string",
"description": "A version constraint to limit to which Composer versions the warning should be shown."
}
}
}
},
"infos": {
"type": "array",
"items": {
"type": "object",
"required": ["message", "versions"],
"properties": {
"message": {
"type": "string",
"description": "A message that will be output by Composer as info when this source is consulted."
},
"versions": {
"type": "string",
"description": "A version constraint to limit to which Composer versions the info should be shown."
}
}
}
},
"providers-url": {
"type": "string",
"description": "DEPRECATED: Endpoint to retrieve provider data from, e.g. '/p/%package%$%hash%.json'."
},
"provider-includes": {
"type": "object",
"description": "DEPRECATED: A hashmap of provider listings.",
"additionalProperties": { "$ref": "#/definitions/provider" }
},
"providers": {
"type": "object",
"description": "DEPRECATED: A hashmap of package names in the form of <vendor>/<name>.",
"additionalProperties": { "$ref": "#/definitions/provider" }
},
"warning": {
"type": "string",
"description": "A message that will be output by Composer as a warning when this source is consulted."
"description": "DEPRECATED: A message that will be output by Composer as a warning when this source is consulted."
},
"warning-versions": {
"type": "string",
"description": "DEPRECATED: A version constraint to limit to which Composer versions the warning should be shown."
},
"info": {
"type": "string",
"description": "DEPRECATED: A message that will be output by Composer as a info when this source is consulted."
},
"info-versions": {
"type": "string",
"description": "DEPRECATED: A version constraint to limit to which Composer versions the info should be shown."
}
},
"definitions": {

View File

@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft-04/schema#",
"title": "Package",
"title": "Composer Package",
"type": "object",
"properties": {
"name": {

View File

@ -19,6 +19,8 @@ use Composer\Config;
use Composer\Downloader\TransportException;
use Composer\IO\BufferIO;
use Composer\Json\JsonFile;
use Composer\Json\JsonValidationException;
use Composer\Package\Locker;
use Composer\Package\RootPackage;
use Composer\Package\Version\VersionParser;
use Composer\Pcre\Preg;
@ -89,6 +91,12 @@ EOT
$io->write('Checking composer.json: ', false);
$this->outputResult($this->checkComposerSchema());
if ($composer->getLocker()->isLocked()) {
$io->write('Checking composer.lock: ', false);
$this->outputResult($this->checkComposerLockSchema($composer->getLocker()));
}
$this->process = $composer->getLoop()->getProcessExecutor() ?? new ProcessExecutor($io);
} else {
$this->process = new ProcessExecutor($io);
@ -267,6 +275,27 @@ EOT
return true;
}
/**
* @return string|true
*/
private function checkComposerLockSchema(Locker $locker)
{
$json = $locker->getJsonFile();
try {
$json->validateSchema(JsonFile::LOCK_SCHEMA);
} catch (JsonValidationException $e) {
$output = '';
foreach ($e->getErrors() as $error) {
$output .= '<error>'.$error.'</error>'.PHP_EOL;
}
return trim($output);
}
return true;
}
private function checkGit(): string
{
if (!function_exists('proc_open')) {

View File

@ -32,6 +32,7 @@ class JsonFile
public const LAX_SCHEMA = 1;
public const STRICT_SCHEMA = 2;
public const AUTH_SCHEMA = 3;
public const LOCK_SCHEMA = 4;
/** @deprecated Use \JSON_UNESCAPED_SLASHES */
public const JSON_UNESCAPED_SLASHES = 64;
@ -41,6 +42,7 @@ class JsonFile
public const JSON_UNESCAPED_UNICODE = 256;
public const COMPOSER_SCHEMA_PATH = __DIR__ . '/../../../res/composer-schema.json';
public const LOCK_SCHEMA_PATH = __DIR__ . '/../../../res/composer-lock-schema.json';
public const INDENT_DEFAULT = ' ';
@ -228,8 +230,12 @@ class JsonFile
{
$isComposerSchemaFile = false;
if (null === $schemaFile) {
$isComposerSchemaFile = true;
$schemaFile = self::COMPOSER_SCHEMA_PATH;
if ($schema === self::LOCK_SCHEMA) {
$schemaFile = self::LOCK_SCHEMA_PATH;
} else {
$isComposerSchemaFile = true;
$schemaFile = self::COMPOSER_SCHEMA_PATH;
}
}
// Prepend with file:// only when not using a special schema already (e.g. in the phar)

View File

@ -73,6 +73,14 @@ class Locker
$this->process = $process ?? new ProcessExecutor($io);
}
/**
* @internal
*/
public function getJsonFile(): JsonFile
{
return $this->lockFile;
}
/**
* Returns the md5 hash of the sorted content of the composer file.
*