Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ packages.json
results.sarif
infection.log
.churn.cache
tools/chorale/composer.lock
19 changes: 19 additions & 0 deletions chorale.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: 1
repo_host: git@github.com
repo_vendor: SonsOfPHP
repo_name_template: '{name:kebab}.git'
default_repo_template: '{repo_host}:{repo_vendor}/{repo_name_template}'
default_branch: main
splitter: splitsh
tag_strategy: inherit-monorepo-tag
rules:
keep_history: true
skip_if_unchanged: true
require_files:
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have the ability to replace variables in the files.

- composer.json
- LICENSE
patterns:
-
match: 'src/**'
include:
- '**'
2 changes: 1 addition & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
earlyReturn: true,
strictBooleans: true,
phpunitCodeQuality: true,
phpunit: true,
//phpunit: true,
)
->withImportNames(
importShortClasses: false,
Expand Down
111 changes: 111 additions & 0 deletions tools/chorale/bin/chorale
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env php
<?php declare(strict_types=1);

require __DIR__ . '/../vendor/autoload.php';

use Symfony\Component\Console\Application;
use Chorale\Console\Style\ConsoleStyleFactory;
use Chorale\Console\SetupCommand;
use Chorale\Repo\TemplateRenderer;
use Chorale\Util\PathUtils;
use Chorale\Util\Sorting;
use Chorale\Discovery\PackageIdentity;
use Chorale\Config\ConfigDefaults;
use Chorale\Config\SchemaValidator;
use Chorale\IO\BackupManager;
use Chorale\IO\JsonReporter;
use Chorale\Telemetry\RunSummary;
use Chorale\Config\ConfigLoader;
use Chorale\Config\ConfigWriter;
use Chorale\Config\ConfigNormalizer;
use Chorale\Discovery\ComposerMetadata;
use Chorale\Discovery\PackageScanner;
use Chorale\Discovery\PatternMatcher;
use Chorale\Repo\RepoResolver;
use Chorale\Rules\RequiredFilesChecker;
use Chorale\Rules\ConflictDetector;
use Chorale\Composer\ComposerJsonReader;
use Chorale\Composer\DependencyMerger;
use Chorale\Composer\RuleEngine;
use Chorale\Split\ContentHasher;
use Chorale\Split\SplitDecider;
use Chorale\State\FilesystemStateStore;
use Chorale\Util\DiffUtil;
use Chorale\Plan\PlanBuilder;
use Chorale\Console\PlanCommand;

$paths = new PathUtils();
$renderer = new TemplateRenderer();
$sorting = new Sorting();
$identity = new PackageIdentity();
$defaults = new ConfigDefaults();
$schema = new SchemaValidator();
$backup = new BackupManager();
$json = new JsonReporter();
$summary = new RunSummary();
$loader = new ConfigLoader();
$composerMeta = new ComposerMetadata();
$composerReader = new ComposerJsonReader();
$stateStore = new FilesystemStateStore();
$hasher = new ContentHasher();
$diffs = new DiffUtil();

$ruleEngine = new RuleEngine($renderer);
$writer = new ConfigWriter($backup);
$normalizer = new ConfigNormalizer($sorting, $defaults);
$scanner = new PackageScanner($paths);
$matcher = new PatternMatcher($paths);
$resolver = new RepoResolver($renderer, $paths);
$required = new RequiredFilesChecker();
$conflicts = new ConflictDetector($matcher);
$depMerger = new DependencyMerger($composerReader);
$splitDecider = new SplitDecider($stateStore, $hasher);

$planner = new PlanBuilder(
defaults: $defaults,
scanner: $scanner,
matcher: $matcher,
resolver: $resolver,
paths: $paths,
composerReader: $composerReader,
depMerger: $depMerger,
ruleEngine: $ruleEngine,
splitDecider: $splitDecider,
diffs: $diffs,
);

// -----------------------------------------------------------------------------
$app = new Application('Chorale', '0.1.0');
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
$app->add(new SetupCommand(
styleFactory: new ConsoleStyleFactory(),
configLoader: $loader,
configWriter: $writer,
configNormalizer: $normalizer,
schemaValidator: $schema,
defaults: $defaults,
scanner: $scanner,
matcher: $matcher,
resolver: $resolver,
identity: $identity,
requiredFiles: $required,
//conflicts: $conflicts,
jsonReporter: $json,
summary: $summary,
composerMeta: $composerMeta,
));
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
$app->add(new PlanCommand(
styleFactory: new ConsoleStyleFactory(),
configLoader: $loader,
planner: $planner,
));
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
$app->run();
// -----------------------------------------------------------------------------
35 changes: 35 additions & 0 deletions tools/chorale/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "sonsofphp/chorale",
"description": "Chorale: a CLI tool to help manage PHP monorepos.",
"type": "project",
"license": "MIT",
"require": {
"php": "^8.3",
"ext-mbstring": "*",
"symfony/console": "^7.0",
"symfony/yaml": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"symfony/var-dumper": "^7.3"
},
"autoload": {
"psr-4": {
"Chorale\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Chorale\\Tests\\": "src/Tests/"
}
},
"bin": [
"bin/chorale"
],
"config": {
"sort-packages": true,
"preferred-install": "dist"
},
"minimum-stability": "stable",
"prefer-stable": true
}
47 changes: 47 additions & 0 deletions tools/chorale/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheDirectory=".phpunit.cache"
requireCoverageMetadata="true"
backupGlobals="false"
colors="true"
cacheResult="true"
executionOrder="defects"
beStrictAboutCoverageMetadata="true"
stopOnDefect="true"
stopOnError="true"
stopOnFailure="true"
stopOnWarning="true"
stopOnDeprecation="true"
stopOnNotice="true"
displayDetailsOnIncompleteTests="true"
displayDetailsOnSkippedTests="true"
displayDetailsOnTestsThatTriggerDeprecations="true"
displayDetailsOnPhpunitDeprecations="true"
displayDetailsOnTestsThatTriggerErrors="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true"
>

<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Chorale Test Suite">
<directory>src/Tests</directory>
</testsuite>
</testsuites>

<coverage includeUncoveredFiles="true" pathCoverage="false" ignoreDeprecatedCodeUnits="true" disableCodeCoverageIgnore="false" />

<source>
<include>
<directory suffix=".php">src</directory>
</include>
<exclude>
<directory>src/Tests</directory>
</exclude>
</source>
</phpunit>

24 changes: 24 additions & 0 deletions tools/chorale/src/Composer/ComposerJsonReader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace Chorale\Composer;

final class ComposerJsonReader implements ComposerJsonReaderInterface
{
public function read(string $absolutePath): array
{
if (!is_file($absolutePath)) {
return [];
}

$raw = @file_get_contents($absolutePath);
if ($raw === false) {
return [];
}

$json = json_decode($raw, true);

return is_array($json) ? $json : [];
}
}
14 changes: 14 additions & 0 deletions tools/chorale/src/Composer/ComposerJsonReaderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Chorale\Composer;

interface ComposerJsonReaderInterface
{
/**
* @return array<string, mixed>
* if missing/invalid, it will return an empty array
*/
public function read(string $absolutePath): array;
}
Loading