Skip to content
Extraits de code Groupes Projets

Comparer les révisions

Les modifications sont affichées comme si la révision source était fusionnée avec la révision cible. En savoir plus sur la comparaison des révisions.

Source

Sélectionner le projet cible
No results found

Cible

Sélectionner le projet cible
  • spip-league/composer-installer
1 résultat
Afficher les modifications
Validations sur la source (2)
Affichage de
avec 208 ajouts et 180 suppressions
......@@ -5,3 +5,4 @@
/.phpunit.cache
/phpstan.neon
/tmp/
/build/
stages:
- lint
- check
- build
- test
default:
image: php:latest
cache:
paths:
- vendor/
before_script:
# Install git
- apt update -yqq
- apt install git libzip-dev -yqq
- docker-php-ext-install zip
image:
name: spip/tools:8.2
entrypoint:
- "/bin/ash"
- "-c"
# Install composer
- curl -sS https://getcomposer.org/installer | php
# Install all project dependencies
- php composer.phar install
# d’abord vérifier rapidement les fichiers
job:check:php:
stage: check
lint:
stage: lint
when: on_success
only:
- main
- merge_requests
before_script:
- curl -o /usr/local/bin/parallel-lint -fsL https://github.com/php-parallel-lint/PHP-Parallel-Lint/releases/latest/download/parallel-lint.phar
script:
- php /usr/local/bin/parallel-lint --exclude .git --exclude vendor .
- make -f /Makefile lint
artifacts:
paths:
- build/phplint.json
expire_in: 2 days
reports:
codequality: build/phplint.json
job:build:
stage: build
phpstan:
stage: check
when: on_success
only:
- main
- merge_requests
script:
# Security check of dependencies
- php composer.phar audit
- make -f /Makefile analyze
artifacts:
paths:
- build/phpstan.json
expire_in: 2 days
reports:
codequality: build/phpstan.json
job:test:php-latest:
stage: test
coding_standard:
stage: check
when: on_success
only:
- main
- merge_requests
before_script:
# pcov pour le code-coverage
- pecl install pcov
- docker-php-ext-enable pcov
script:
- php -d memory_limit=-1 -d pcov.enabled=1 -d pcov.directory=. vendor/bin/phpunit --coverage-text --colors=never --coverage-cobertura .phpunit.cache/corbertura/report.xml
coverage: /^\s*Lines:\s*\d+.\d+\%/
- make -f /Makefile cs
artifacts:
paths:
- .phpunit.cache/html
expire_in: 2 days
reports:
coverage_report:
coverage_format: cobertura
path: .phpunit.cache/corbertura/report.xml
paths:
- build/ecs.json
expire_in: 2 days
reports:
codequality: build/ecs.json
job:test:php-8.2:
stage: test
needs:
- job:test:php-latest
outdated:
stage: check
when: on_success
only:
- main
- merge_requests
image: php:8.2
script:
- vendor/bin/phpunit --no-coverage
- make -f /Makefile outdated
artifacts:
paths:
- build/gl-outdated.json
expire_in: 2 days
reports:
codequality: build/gl-outdated.json
job:test:ecs:
stage: test
audit:
stage: check
when: on_success
only:
- main
- merge_requests
script:
- php -d display_errors=0 vendor/bin/ecs check --output-format=gitlab > ecs-quality-report.json
- make -f /Makefile audit
artifacts:
paths:
- ecs-quality-report.json
reports:
codequality: ecs-quality-report.json
paths:
- build/gl-audit.json
expire_in: 2 days
reports:
codequality: build/gl-audit.json
job:test:phpstan:
coverage_report:
stage: test
when: on_success
parallel:
matrix:
- VERSION: ["8.2","8.3","latest"]
image:
name: spip/tools:$VERSION
entrypoint:
- "/bin/ash"
- "-c"
only:
- main
- merge_requests
script:
- php -d memory_limit=-1 vendor/bin/phpstan analyse --error-format gitlab > phpstan-quality-report.json
- make -f /Makefile test-coverage
artifacts:
paths:
- phpstan-quality-report.json
reports:
codequality: phpstan-quality-report.json
\ No newline at end of file
paths:
- .phpunit.cache/corbertura/report.xml
expire_in: 2 days
reports:
coverage_report:
coverage_format: cobertura
path: .phpunit.cache/corbertura/report.xml
......@@ -10,6 +10,7 @@
}
],
"require": {
"php": "^8.2",
"composer-plugin-api": "^2.6"
},
"require-dev": {
......@@ -35,6 +36,11 @@
"SpipLeague\\Test\\Composer\\": "tests/"
}
},
"config": {
"platform": {
"php": "8.2.27"
}
},
"extra": {
"branch-alias": {
"dev-main": "0.8.x-dev"
......
src tests ecs.php rector.php
......@@ -3,7 +3,7 @@ includes:
- vendor/composer/composer/phpstan/rules.neon
parameters:
phpVersion: 70400
phpVersion: 80200
paths:
- src
- tests
......
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.3/phpunit.xsd" bootstrap="vendor/autoload.php" cacheDirectory=".phpunit.cache" executionOrder="depends,defects" requireCoverageMetadata="true" beStrictAboutCoverageMetadata="true" beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true">
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd" bootstrap="vendor/autoload.php" cacheDirectory=".phpunit.cache" executionOrder="depends,defects" requireCoverageMetadata="true" beStrictAboutCoverageMetadata="true" beStrictAboutOutputDuringTests="true" failOnRisky="true" failOnWarning="true">
<testsuites>
<testsuite name="default">
<directory>tests</directory>
......@@ -10,6 +10,7 @@
<report>
<html outputDirectory=".phpunit.cache/html"/>
<text outputFile="php://stdout"/>
<cobertura outputFile=".phpunit.cache/corbertura/report.xml"/>
</report>
</coverage>
......
......@@ -19,7 +19,7 @@ class AssetsClearCache
/**
* To delete the cache files dedicated for public compiled assets
*/
public static function clearCache(Event $event)
public static function clearCache(Event $event): void
{
$event->getIO()
->write('Clearing the assets cache ...');
......
......@@ -29,7 +29,7 @@ class BaseDirectories
/**
* To create base directories
*/
public static function createBaseDirectories(Event $event, bool $check = false)
public static function createBaseDirectories(Event $event, bool $check = false): void
{
if ($check) {
$event->getIO()
......
......@@ -27,8 +27,8 @@ abstract class AbstractSwitchCommand extends AbstractSpipCommand
{
try {
$composer = $this->requireComposer();
$distribution = Collection::fromJsonFile(Factory::createRemoteUrls($composer));
$analyzer = new Analyzer($distribution, $composer);
$distribution = Collection::fromJsonFile();
$analyzer = new Analyzer($distribution, $composer, Factory::createRemoteUrls($composer));
$switcher = new Switcher($distribution, $composer);
} catch (\Throwable $th) {
$output->writeln($th->getMessage());
......
......@@ -38,9 +38,9 @@ class ModeDevCommand extends AbstractSpipCommand
if (is_string($tmp)) {
$tmp = [$tmp];
}
if (!\is_array($tmp)) {
$tmp = [];
}
// if (!\is_array($tmp)) {
// $tmp = [];
// }
if (empty(\array_intersect(['spip/*' => 'source'], $tmp))) {
$output->writeln('<warning>Missing preferred-install in ' . $composerFile . '</warning>');
if (!$this->getIO()->askConfirmation(
......@@ -69,17 +69,19 @@ class ModeDevCommand extends AbstractSpipCommand
foreach ($toChange as $path) {
$url = '';
$changer->getProcessor()?->execute($changer->getRemote($path), $url);
$url = trim($url);
if ($output->isVerbose()) {
$output->write('Checking ' . $url . ' in ' . $path . ' ...');
}
$newUrl = $changer->toSsh($url);
if ($newUrl !== $url) {
$output->write(PHP_EOL . 'Converting ' . $url . ' into ' . $newUrl);
$changer->getProcessor()?->execute($changer->setRemote($path, $newUrl));
}
if ($output->isVerbose()) {
$output->writeln(' OK');
$url = is_string($url) ? trim($url) : '';
if (strlen($url)) {
if ($output->isVerbose()) {
$output->write('Checking ' . $url . ' in ' . $path . ' ...');
}
$newUrl = $changer->toSsh($url);
if ($newUrl !== $url) {
$output->write(PHP_EOL . 'Converting ' . $url . ' into ' . $newUrl);
$changer->getProcessor()?->execute($changer->setRemote($path, $newUrl));
}
if ($output->isVerbose()) {
$output->writeln(' OK');
}
}
}
$output->writeln(PHP_EOL . 'All done.');
......
......@@ -8,9 +8,6 @@ use SpipLeague\Composer\Command\ModeDevCommand;
use SpipLeague\Composer\Command\SwitchBackCommand;
use SpipLeague\Composer\Command\SwitchForwardCommand;
/**
* @codeCoverageIgnore
*/
class CommandProvider implements CommandProviderCapability
{
public function getCommands()
......
......@@ -26,7 +26,8 @@ class PreferredInstall
return [];
}
$toCheck = array_keys(array_filter(
/** @var string[] $toCheck */
$toCheck = \array_keys(\array_filter(
$this->composer->getConfig()
->get('preferred-install'),
fn($install) => $install === 'source',
......@@ -42,11 +43,12 @@ class PreferredInstall
$backOffice = $extra['spip']['back_office'] ?? ''; // -> SpipPaths::BACK_OFFICE/
$vendorDir = $this->composer->getConfig()
->get('vendor-dir'); // -> vendor/
$rootDir = realpath($vendorDir . '/..');
$rootDir = \realpath($vendorDir . '/..');
// -> plugins/ ?
$requires = InstalledVersions::getInstalledPackages();
$toChange = array_reduce($requires, function (array $list, string $packageName) use (
/** @var string[] $toChange */
$toChange = \array_reduce($requires, function (array $list, string $packageName) use (
$toCheck,
$extensions,
$template,
......
......@@ -14,12 +14,12 @@ class Collection implements CollectionInterface
{
use CollectionTrait;
private ?string $file;
private string $file;
/**
* @param SpecificationInterface[] $distribution
* @param array<mixed> $distribution
*/
public function __construct(array $distribution, ?string $file = \null)
public function __construct(array $distribution, string $file)
{
$originalCount = \count($distribution);
$distribution = \array_filter(
......@@ -39,12 +39,8 @@ class Collection implements CollectionInterface
$this->file = $file;
}
public static function fromJsonFile(RemoteUrlsInterface $changer, ?string $file = null): self
public static function fromJsonFile(string $file = SpipPaths::LOCAL_PLUGINS_DIST): self
{
if ($file === null) {
$file = SpipPaths::LOCAL_PLUGINS_DIST;
}
if (!(\file_exists($file) || \file_put_contents($file, '{}'))) {
throw new \LogicException(
'<error>File "' . $file . '" is missing and can\' be created. Can\'t upgrade/downgrade.</error>',
......@@ -57,17 +53,15 @@ class Collection implements CollectionInterface
}
$distribution = [];
/** @var array<string,array{path:string,source:string,branch?:string,tag?:string}> $pluginsDist */
foreach ($pluginsDist as $prefix => $fromJson) {
$distribution[] = (new Specification($prefix, $fromJson))->setChanger($changer);
$distribution[] = new Specification($prefix, $fromJson);
}
return new self($distribution, $file);
}
/**
* @codeCoverageIgnore
*/
public function getFile(): ?string
public function getFile(): string
{
return $this->file;
}
......
......@@ -12,5 +12,5 @@ namespace SpipLeague\Composer\Extensions;
*/
interface CollectionInterface extends \Countable, \Iterator, \JsonSerializable, \ArrayAccess
{
public function getFile(): ?string;
public function getFile(): string;
}
<?php
declare(strict_types=1);
namespace SpipLeague\Composer\Extensions;
/**
......@@ -11,85 +13,85 @@ trait CollectionTrait
{
protected int $position = 0;
/**
* @var string[]
*/
/** @var array<string,SpecificationInterface> */
protected array $collection = [];
/** @var string[] */
protected array $keys = [];
/**
* @var array<string,SpecificationInterface>
*/
protected array $collection = [];
private function validOffset(mixed $offset): void
{
if (!(is_string($offset) && strlen($offset) > 0)) {
throw new InvalidCollectionException('wrong offset type. Must be a string.');
}
}
public function count(): int
private function validValue(mixed $value): void
{
return \count($this->collection);
if (!$value instanceof SpecificationInterface) {
throw new InvalidCollectionException('wrong value type. Must be a valid specification.');
}
}
public function jsonSerialize(): mixed
public function count(): int
{
return $this->collection;
return \count($this->collection);
}
/**
* @codeCoverageIgnore
*/
public function current(): SpecificationInterface
{
return $this->collection[$this->keys[$this->position]];
}
/**
* @codeCoverageIgnore
*/
public function next(): void
public function key(): string
{
++$this->position;
return $this->keys[$this->position];
}
/**
* @codeCoverageIgnore
*/
public function valid(): bool
public function next(): void
{
return isset($this->keys[$this->position]);
++$this->position;
}
/**
* @codeCoverageIgnore
*/
public function rewind(): void
{
$this->position = 0;
}
public function valid(): bool
{
return isset($this->keys[$this->position]);
}
/**
* @codeCoverageIgnore
* @return array<string,SpecificationInterface>
*/
public function key(): string
public function jsonSerialize(): array
{
return $this->keys[$this->position];
return $this->collection;
}
public function offsetExists(mixed $offset): bool
{
$this->validOffset($offset);
return isset($this->collection[$offset]);
}
public function offsetGet(mixed $offset): mixed
public function offsetGet(mixed $offset): ?SpecificationInterface
{
$this->validOffset($offset);
return $this->collection[$offset] ?? null;
}
public function offsetSet(mixed $offset, mixed $value): void
{
if (!$value instanceof SpecificationInterface) {
throw new InvalidSpecificationException('A collection must only contain valid specifications.', 3);
}
if ($offset === null || !\is_string($offset)) {
$this->validValue($value);
if (null === $offset) {
$offset = $value->getPrefix();
}
$this->validOffset($offset);
$this->collection[$offset] = $value;
$this->keys = \array_keys($this->collection);
......@@ -97,6 +99,8 @@ trait CollectionTrait
public function offsetUnset(mixed $offset): void
{
$this->validOffset($offset);
unset($this->collection[$offset]);
$this->keys = \array_keys($this->collection);
}
......
<?php
declare(strict_types=1);
namespace SpipLeague\Composer\Extensions;
/**
* @since 0.8.2
*/
class InvalidCollectionException extends \DomainException
{
}
<?php
declare(strict_types=1);
namespace SpipLeague\Composer\Extensions;
/**
* @since 0.7.0
*/
class InvalidSpecificationException extends \DomainException {}
class InvalidSpecificationException extends \DomainException
{
}
<?php
declare(strict_types=1);
namespace SpipLeague\Composer\Extensions;
use Composer\Composer;
......@@ -16,8 +18,6 @@ use SpipLeague\Composer\SpipPaths;
*/
class Specification implements SpecificationInterface
{
private string $prefix;
private string $path;
private string $source;
......@@ -31,14 +31,12 @@ class Specification implements SpecificationInterface
*/
private static ?array $packages = \null;
protected ?RemoteUrlsInterface $changer = null;
private string $validationError = '';
/**
* @param array{path:string,source:string,branch?:string,tag?:string} $fromJson
*/
public function __construct(string $prefix, array $fromJson)
public function __construct(private string $prefix, array $fromJson)
{
if (!$this->validate($prefix, $fromJson)) {
throw new InvalidSpecificationException($this->validationError, 1);
......@@ -62,23 +60,34 @@ class Specification implements SpecificationInterface
return false;
}
if (!(isset($fromJson['path']) && \strlen($fromJson['path']) > 0)) {
$this->validationError = 'empty path for "' . $prefix . '" is invalid';
if (!(isset($fromJson['path']) && \strlen($fromJson['path']) > 0)) {
$this->validationError = 'empty path for "'.$prefix.'" is invalid';
return false;
}
if (!(isset($fromJson['source']) && \strlen($fromJson['source']) > 0)) {
$this->validationError = 'empty source for "'.$prefix.'" is invalid';
return false;
}
if (!(isset($fromJson['source']) && \strlen($fromJson['source']) > 0)) {
$this->validationError = 'empty source for "' . $prefix . '" is invalid';
if (empty($fromJson['branch']) && empty($fromJson['tag'])) {
$this->validationError = 'both empty branch and tag for "'.$prefix.'" is invalid';
return false;
}
$this->validationError = '';
$this->validationError = '';
return true;
}
public static function unsetPackages(): void
{
self::$packages = \null;
}
/**
* Détermine le `prefix` équivalent.
*
......@@ -139,32 +148,16 @@ class Specification implements SpecificationInterface
throw new InvalidSpecificationException('Package "' . $vendorName . '" is not present.', 4);
}
public function setChanger(RemoteUrlsInterface $changer): self
{
$this->changer = $changer;
return $this;
}
/**
* @codeCoverageIgnore
*/
public function getPrefix(): string
{
return $this->prefix;
}
/**
* @codeCoverageIgnore
*/
public function getSource(): string
{
return $this->source;
}
/**
* @codeCoverageIgnore
*/
public function getPath(): string
{
return $this->path;
......@@ -179,13 +172,9 @@ class Specification implements SpecificationInterface
* Limitation: l'extension DOIT être composerisée et être
* distribuée dans un dépôt composer accessible.
*/
public function computeVendorName(): string
public function computeVendorName(RemoteUrlsInterface $changer): string
{
if ($this->changer === null) {
return '';
}
$source = $this->changer->toHttps($this->source);
$source = $changer->toHttps($this->source);
$p = parse_url($source);
$vendorName = $p['path'] ?? '';
......@@ -206,27 +195,29 @@ class Specification implements SpecificationInterface
public function computeConstraint(): string
{
if ($this->tag) {
return '^' . \preg_replace(',^[v|V]?(\d+\.\d+).*$,', '$1', $this->tag);
}
if ($this->branch) {
return '^' . $this->branch . '.x-dev';
return '^'.\preg_replace(',^[v|V]?(\d+\.\d+).*$,', '$1', $this->tag);
}
return '';
return match (\preg_match(',^[\d.]+$,', $this->branch)) {
1 => '^'.$this->branch.'.x-dev',
0, false => 'dev-'.$this->branch,
};
}
public function jsonSerialize(): mixed
/**
* @return array{path:string,source:string,branch?:string,tag?:string}
*/
public function jsonSerialize(): array
{
$json = [
'path' => $this->getPath(),
'source' => $this->getSource(),
];
if (!empty($this->branch)) {
$json['branch'] = $this->branch;
$json['branch'] = $this->branch;
}
if (!empty($this->tag)) {
$json['tag'] = $this->tag;
$json['tag'] = $this->tag;
}
return $json;
......
<?php
declare(strict_types=1);
namespace SpipLeague\Composer\Extensions;
use SpipLeague\Composer\Git\RemoteUrlsInterface;
/**
* Spécifiations d'installation d'un "plugins-dist" en SPIP4.4.
*
......@@ -11,12 +15,14 @@ interface SpecificationInterface extends \JsonSerializable
{
public function getPrefix(): string;
public function getSource(): string;
public function getPath(): string;
/**
* Détermine le `vendor/name` équivalent.
*/
public function computeVendorName(): string;
public function computeVendorName(RemoteUrlsInterface $changer): string;
/**
* Détermine la `constraint` équivalente.
......
......@@ -28,7 +28,7 @@ class PluginsClearCache
/**
* To delete the cache files dedicated of the activated plugins
*/
public static function clearCache(Event $event)
public static function clearCache(Event $event): void
{
$event->getIO()
->write('Clearing the plugins cache ...');
......