diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 5b9b638..1a3d1cc 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -41,6 +41,7 @@ jobs:
if: steps.composer-cache.outputs.cache-hit != 'true'
run: |
composer config version 1.9.0
+ composer remove --no-update --dev phan/phan friendsofphp/php-cs-fixer nette/utils phpmd/phpmd phpstan/phpstan squizlabs/php_codesniffer vimeo/psalm
composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress ${{ matrix.php >= 8.1 && '--ignore-platform-req=php' || '' }}
- name: Prepare git config
diff --git a/.github/workflows/phan.yml b/.github/workflows/phan.yml
index 7b8b165..2ad1c70 100644
--- a/.github/workflows/phan.yml
+++ b/.github/workflows/phan.yml
@@ -38,7 +38,9 @@ jobs:
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
- run: composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
+ run: |
+ composer remove --no-update --dev phpunit/phpunit friendsofphp/php-cs-fixer nette/utils phpmd/phpmd phpstan/phpstan squizlabs/php_codesniffer vimeo/psalm
+ composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
- name: Check style with Phan
run: composer run-script phan
diff --git a/.github/workflows/phpcs.yml b/.github/workflows/phpcs.yml
index 503f7b5..2770d61 100644
--- a/.github/workflows/phpcs.yml
+++ b/.github/workflows/phpcs.yml
@@ -32,13 +32,15 @@ jobs:
uses: actions/cache@v4
with:
path: vendor
- key: style-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
+ key: cs-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
- style-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-
+ cs-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
- run: composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
+ run: |
+ composer remove --no-update --dev phpunit/phpunit phan/phan friendsofphp/php-cs-fixer nette/utils phpmd/phpmd phpstan/phpstan vimeo/psalm
+ composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
- name: Check style with PHPCS
run: composer run-script phpcs
diff --git a/.github/workflows/phpcsf.yml b/.github/workflows/phpcsf.yml
index 40cd1a7..0433c3c 100644
--- a/.github/workflows/phpcsf.yml
+++ b/.github/workflows/phpcsf.yml
@@ -32,13 +32,15 @@ jobs:
uses: actions/cache@v4
with:
path: vendor
- key: style-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
+ key: csf-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}
restore-keys: |
- style-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-
+ csf-${{ runner.os }}-${{ matrix.setup }}-${{ matrix.php }}-
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
- run: composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
+ run: |
+ composer remove --no-update --dev phpunit/phpunit phan/phan nette/utils phpmd/phpmd phpstan/phpstan squizlabs/php_codesniffer vimeo/psalm
+ composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
- name: Check style with phpcsf
run: composer run-script phpcsf
diff --git a/.github/workflows/phpmd.yml b/.github/workflows/phpmd.yml
index 0e72a1f..5b76e16 100644
--- a/.github/workflows/phpmd.yml
+++ b/.github/workflows/phpmd.yml
@@ -38,7 +38,9 @@ jobs:
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
- run: composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
+ run: |
+ composer remove --no-update --dev phpunit/phpunit phan/phan friendsofphp/php-cs-fixer nette/utils phpstan/phpstan squizlabs/php_codesniffer vimeo/psalm
+ composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
- name: Check style with phpmd
run: composer run-script phpmd
diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml
index 9ac9887..9a53b77 100644
--- a/.github/workflows/psalm.yml
+++ b/.github/workflows/psalm.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- php: ['8.2']
+ php: ['8.4']
setup: ['stable']
name: PHP ${{ matrix.php }} - ${{ matrix.setup }}
@@ -38,9 +38,8 @@ jobs:
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
- # tests/use-fork.php because of https://github.com/vimeo/psalm/issues/8957
run: |
- php tests/use-fork.php vimeo/psalm 5.14.2 https://github.com/kylekatarnls/psalm.git fix/datetime-inheritance-5.x
+ composer remove --no-update --dev phan/phan phpunit/phpunit friendsofphp/php-cs-fixer nette/utils phpmd/phpmd phpstan/phpstan squizlabs/php_codesniffer
composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress
- name: Check style with psalm
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 22811be..483df81 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -45,6 +45,7 @@ jobs:
if: steps.composer-cache.outputs.cache-hit != 'true'
run: |
composer config version 1.9.0
+ composer remove --no-update --dev phan/phan friendsofphp/php-cs-fixer nette/utils phpmd/phpmd phpstan/phpstan squizlabs/php_codesniffer vimeo/psalm
composer update --prefer-dist ${{ matrix.setup != 'next' && format('--prefer-{0}', matrix.setup) || '' }} --no-progress ${{ matrix.php >= 8.1 && '--ignore-platform-req=php' || '' }} --no-interaction
- name: Prepare git config
diff --git a/composer.json b/composer.json
index a96834b..c43ab8a 100644
--- a/composer.json
+++ b/composer.json
@@ -27,13 +27,13 @@
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15.0 || ^3.23.0",
- "nette/utils": "^3.2.10",
+ "nette/utils": "^3.2.10 || ^4.1.3",
"phan/phan": "^3.2.10 || ^5.4.2",
"phpunit/phpunit": "^8.5.52 || ^9.6.34",
"phpmd/phpmd": "^2.13.0",
"phpstan/phpstan": "^0.11.15 || ^1.10.29",
"squizlabs/php_codesniffer": "^3.7.2",
- "vimeo/psalm": "^4.30.0 || ^5.14.1"
+ "vimeo/psalm": "^4.30.0 || ^5.14.1 || ^6.15.1"
},
"autoload": {
"psr-4": {
diff --git a/psalm.xml b/psalm.xml
index 84a90c8..43845c8 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -32,6 +32,9 @@
+
+
+
diff --git a/src/Phug/Split/Command/Analyze.php b/src/Phug/Split/Command/Analyze.php
index 8cdf516..bf30c3c 100644
--- a/src/Phug/Split/Command/Analyze.php
+++ b/src/Phug/Split/Command/Analyze.php
@@ -3,6 +3,7 @@
namespace Phug\Split\Command;
use Phug\Split;
+use Phug\Split\UnableToListDirectoryItems;
use SimpleCli\SimpleCli;
use Traversable;
@@ -66,7 +67,7 @@ protected function calculatePackagesTree(Split $cli): bool
return $cli->error('Root project directory should contains a '.$this->composerFile.' file.');
}
- $data = (array) json_decode(file_get_contents($this->composerFile), true);
+ $data = (array) json_decode((string) file_get_contents($this->composerFile), true);
$vendorDirectory = ($data['config'] ?? [])['vendor-dir'] ?? 'vendor';
$cli->writeLine((string) $data['name']);
@@ -93,7 +94,7 @@ protected function getPackages(): iterable
protected function dumpPackagesTree(Split $cli, iterable $packages, int $level = 0): bool
{
- $count = is_countable($packages) ? count($packages) : INF;
+ $count = is_countable($packages) ? count($packages) : 0;
foreach ($packages as $index => $package) {
$symbol = $index === $count - 1 ? '└' : '├';
@@ -106,7 +107,13 @@ protected function dumpPackagesTree(Split $cli, iterable $packages, int $level =
protected function mapDirectories(string $directory, callable $callback): iterable
{
- foreach (scandir($directory) as $element) {
+ $elements = scandir($directory);
+
+ if ($elements === false) {
+ throw new UnableToListDirectoryItems($directory);
+ }
+
+ foreach ($elements as $element) {
if (substr($element, 0, 1) === '.') {
continue;
}
@@ -141,7 +148,7 @@ protected function scanDirectories(string $directory): iterable
$composerPath = $directory.DIRECTORY_SEPARATOR.$this->composerFile;
if (file_exists($composerPath)) {
- $data = json_decode(file_get_contents($composerPath), true);
+ $data = json_decode((string) file_get_contents($composerPath), true);
$mainPackage = $this->getPackage($directory, $data);
}
diff --git a/src/Phug/Split/Command/CommandBase.php b/src/Phug/Split/Command/CommandBase.php
index 37c6c67..d23f823 100644
--- a/src/Phug/Split/Command/CommandBase.php
+++ b/src/Phug/Split/Command/CommandBase.php
@@ -51,11 +51,13 @@ protected function gitEscape(string $value): string
* @param array $options CLI git command options
* @param string|null $redirect redirection suffix (like '2>&1')
*
+ * @psalm-param truthy-string|null $redirect
+ *
* @return string
*
- * @psalm-suppress UndefinedThisPropertyFetch RiskyTruthyFalsyComparison
+ * @psalm-suppress UndefinedThisPropertyFetch
*/
- protected function getGitCommand(string $command, array $options = [], string $redirect = null): string
+ protected function getGitCommand(string $command, array $options = [], ?string $redirect = null): string
{
foreach ($options as $name => $value) {
$command .= ' --'.$name.'='.$this->gitEscape($value);
@@ -71,13 +73,15 @@ protected function getGitCommand(string $command, array $options = [], string $r
* @param array $options CLI git command options
* @param string|null $redirect redirection suffix (like '2>&1')
*
- * @return string|null
+ * @psalm-param truthy-string|null $redirect
+ *
+ * @return string
*/
- protected function git(string $command, array $options = [], string $redirect = null): ?string
+ protected function git(string $command, array $options = [], ?string $redirect = null): string
{
$command = $this->getGitCommand($command, $options, $redirect);
- return shell_exec($command);
+ return (string) shell_exec($command);
}
/**
@@ -118,6 +122,8 @@ protected function last(string $directory = ''): Commit
*
* @return string|null
*
+ * @psalm-return truthy-string|null
+ *
* @psalm-suppress UndefinedThisPropertyFetch
*/
protected function getCurrentLinkedCommitHash(): ?string
diff --git a/src/Phug/Split/Command/Copy.php b/src/Phug/Split/Command/Copy.php
index 8a9e817..4889cdb 100644
--- a/src/Phug/Split/Command/Copy.php
+++ b/src/Phug/Split/Command/Copy.php
@@ -50,6 +50,8 @@ class Copy extends CommandBase
* @throws Exception
*
* @return bool
+ *
+ * @SuppressWarnings(PHPMD.ErrorControlOperator)
*/
public function run(SimpleCli $cli): bool
{
@@ -65,7 +67,8 @@ public function run(SimpleCli $cli): bool
return $cli->error('Last commit must be linked to a mono-repository commit.');
}
- $destination = realpath($this->destination);
+ /** @psalm-var truthy-string|false $destination */
+ $destination = @realpath($this->destination);
if (!$destination) {
return $cli->error('Destination directory "'.$this->destination.'" does not seem to exist.');
diff --git a/src/Phug/Split/Command/Dist.php b/src/Phug/Split/Command/Dist.php
index d0fbab7..566a2cf 100644
--- a/src/Phug/Split/Command/Dist.php
+++ b/src/Phug/Split/Command/Dist.php
@@ -72,20 +72,23 @@ protected function distribute(Split $cli): bool
$this->remove($this->output);
$cli->chdir($this->directory);
@mkdir($this->output, 0777, true);
- $this->output = @realpath($this->output);
+ /** @psalm-var truthy-string|false $output */
+ $output = @realpath($this->output);
- if (!$this->output) {
+ if (!$output) {
return $cli->error('Unable to create output directory.');
}
- if (!preg_match('/^\* (.+)$/m', (string) $this->git('branch', [], '2>&1') ?: '', $branch)) {
+ $this->output = $output;
+
+ if (!preg_match('/^\* (.+)$/m', $this->git('branch', [], '2>&1') ?: '', $branch)) {
return $cli->error('You must be on a branch in a git repository to run this command.');
}
$branch = $branch[1];
if (substr($branch, 0, 18) === '(HEAD detached at ') {
- $branch = trim(explode("\n", (string) $this->git('describe --contains --all HEAD', [], '2>&1') ?: '')[0]);
+ $branch = trim(explode("\n", $this->git('describe --contains --all HEAD', [], '2>&1') ?: '')[0]);
}
foreach ($this->getPackages() as $package) {
@@ -103,7 +106,7 @@ protected function distributePackage(Split $cli, array $package, string $branch)
$name = (string) $package['name'];
- $data = (array) json_decode(file_get_contents(strtr($this->api, ['%s' => $name])), true);
+ $data = (array) json_decode((string) file_get_contents(strtr($this->api, ['%s' => $name])), true);
$config = $data['packages'][$name] ?? [];
$config = $config['dev-master'] ?? next($config);
diff --git a/src/Phug/Split/Command/Update.php b/src/Phug/Split/Command/Update.php
index 8a6f3e6..9dcd693 100644
--- a/src/Phug/Split/Command/Update.php
+++ b/src/Phug/Split/Command/Update.php
@@ -45,8 +45,8 @@ class Update extends Dist
protected function getPackage(string $directory, array $data): array
{
return [
- 'name' => $data['name'],
- 'directory' => realpath($directory),
+ 'name' => (string) $data['name'],
+ 'directory' => (string) realpath($directory),
'children' => [],
];
}
@@ -184,7 +184,7 @@ protected function cherryPickCommit(
if (!$this->noPush) {
$name = $package['name'];
- $push = (string) $this->git("push origin $branch", [], '2>&1');
+ $push = $this->git("push origin $branch", [], '2>&1');
$cli->writeLine("Pushing $name\n".$push, strpos($push, 'error:') === false ? 'light_cyan' : 'red');
}
@@ -215,7 +215,7 @@ protected function distributePackage(Split $cli, array $package, string $branch)
}
$hash = $this->getCurrentLinkedCommitHash();
- $distributionDirectory = getcwd();
+ $distributionDirectory = (string) getcwd();
$sourceDirectory = $package['directory'];
$cli->chdir($sourceDirectory);
diff --git a/src/Phug/Split/Git/Commit.php b/src/Phug/Split/Git/Commit.php
index 95b8cd8..bd4a738 100644
--- a/src/Phug/Split/Git/Commit.php
+++ b/src/Phug/Split/Git/Commit.php
@@ -76,7 +76,7 @@ public static function fromGitLogString(string $log): self
new Author(trim($matches['commitName']), trim($matches['commitEmail'])),
new Date($matches['commitDate']),
),
- preg_replace('/^ {4}/m', '', trim($matches['message'])),
+ (string) preg_replace('/^ {4}/m', '', trim($matches['message'])),
);
}
@@ -126,6 +126,10 @@ public function getMessage(): string
* @param non-empty-string $regExp
*
* @return string|null
+ *
+ * @psalm-return truthy-string|null
+ *
+ * @psalm-suppress LessSpecificReturnStatement, MoreSpecificReturnType
*/
public function findInMessage(string $regExp): ?string
{
diff --git a/src/Phug/Split/Git/EmptyLogList.php b/src/Phug/Split/Git/EmptyLogList.php
index 081f7e4..aad6d30 100644
--- a/src/Phug/Split/Git/EmptyLogList.php
+++ b/src/Phug/Split/Git/EmptyLogList.php
@@ -7,7 +7,7 @@
class EmptyLogList extends InvalidArgumentException
{
- public function __construct(int $code = 0, Throwable $previous = null)
+ public function __construct(int $code = 0, ?Throwable $previous = null)
{
parent::__construct('Log list should contain at least one commit.', $code, $previous);
}
diff --git a/src/Phug/Split/Git/ImmutableLogException.php b/src/Phug/Split/Git/ImmutableLogException.php
index a1d0835..9a8174a 100644
--- a/src/Phug/Split/Git/ImmutableLogException.php
+++ b/src/Phug/Split/Git/ImmutableLogException.php
@@ -7,7 +7,7 @@
class ImmutableLogException extends InvalidArgumentException
{
- public function __construct(int $code = 0, Throwable $previous = null)
+ public function __construct(int $code = 0, ?Throwable $previous = null)
{
parent::__construct(
'Log instance is immutable, create a new instance new Log($commits) with a new list of commits',
diff --git a/src/Phug/Split/Git/InvalidGitLogStringException.php b/src/Phug/Split/Git/InvalidGitLogStringException.php
index 64027d2..8e1aa14 100644
--- a/src/Phug/Split/Git/InvalidGitLogStringException.php
+++ b/src/Phug/Split/Git/InvalidGitLogStringException.php
@@ -7,7 +7,7 @@
class InvalidGitLogStringException extends InvalidArgumentException
{
- public function __construct(string $log = '', int $code = 0, Throwable $previous = null)
+ public function __construct(string $log = '', int $code = 0, ?Throwable $previous = null)
{
parent::__construct("Invalid git log string: $log", $code, $previous);
}
diff --git a/src/Phug/Split/Git/InvalidGitLogUnit.php b/src/Phug/Split/Git/InvalidGitLogUnit.php
index d306c86..f6602a8 100644
--- a/src/Phug/Split/Git/InvalidGitLogUnit.php
+++ b/src/Phug/Split/Git/InvalidGitLogUnit.php
@@ -7,7 +7,7 @@
class InvalidGitLogUnit extends InvalidArgumentException
{
- public function __construct(string $unit = '', int $code = 0, Throwable $previous = null)
+ public function __construct(string $unit = '', int $code = 0, ?Throwable $previous = null)
{
parent::__construct("Invalid git log unit: $unit, ".Commit::class.' expected', $code, $previous);
}
diff --git a/src/Phug/Split/Git/Log.php b/src/Phug/Split/Git/Log.php
index f254b8c..44cf128 100644
--- a/src/Phug/Split/Git/Log.php
+++ b/src/Phug/Split/Git/Log.php
@@ -70,13 +70,13 @@ public static function fromGitLogString(string $log): self
{
$commitLogs = preg_split('/[\n\r](?=commit )/', $log);
- return new self(array_map(static function (string $commitLog) {
+ return new self($commitLogs === false ? [] : array_map(static function (string $commitLog) {
return Commit::fromGitLogString($commitLog);
}, $commitLogs));
}
/**
- * Whether a offset exists.
+ * Whether an offset exists.
*
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
*
diff --git a/src/Phug/Split/InvalidCli.php b/src/Phug/Split/InvalidCli.php
index dcc284a..2365b2e 100644
--- a/src/Phug/Split/InvalidCli.php
+++ b/src/Phug/Split/InvalidCli.php
@@ -8,7 +8,7 @@
class InvalidCli extends InvalidArgumentException
{
- public function __construct(object $cli, int $code = 0, Throwable $previous = null)
+ public function __construct(object $cli, int $code = 0, ?Throwable $previous = null)
{
$class = get_class($cli);
diff --git a/src/Phug/Split/UnableToListDirectoryItems.php b/src/Phug/Split/UnableToListDirectoryItems.php
new file mode 100644
index 0000000..09f2d70
--- /dev/null
+++ b/src/Phug/Split/UnableToListDirectoryItems.php
@@ -0,0 +1,14 @@
+ 'vcs',
- 'url' => $url,
-];
-$composer['repositories'] = $repositories;
-$dev = isset($composer['require-dev'][$package]) ? ' --dev' : '';
-file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
-
-echo shell_exec(
- 'composer require -n --no-update' . $dev . ' "' . $package . ':dev-' . $branch . ' as ' . $version . '"'
-);