- Add project-release.yml with embedded vendor files for standalone builds - Implement YAML include mechanism in ProjectYamlLoader with circular reference detection - Support single file or list of files in include directive with ordered merging - Add recursive map merging and list replacement for included configurations - Create CompilerProjectConfigurationTest to verify release compiler embedding - Add ProjectYamlLoaderTest with comprehensive include scenarios - Update GitHub workflows to build standalone compiler using project-release.yml - Document include feature usage in README files with examples - Remove embedded-files from development project.yml to reduce initialization cost - Add validation for absolute paths and proper error handling for missing filesmaster
parent
cf1f6fb97d
commit
d5e748e54c
10 changed files with 290 additions and 7 deletions
@ -0,0 +1,26 @@ |
||||
<?php |
||||
|
||||
namespace TypePhp\Tests; |
||||
|
||||
use PHPUnit\Framework\TestCase; |
||||
use TypePhp\Config\ProjectYamlLoader; |
||||
|
||||
final class CompilerProjectConfigurationTest extends TestCase |
||||
{ |
||||
public function testOnlyReleaseCompilerEmbedsComposerRuntime(): void |
||||
{ |
||||
$root = dirname(__DIR__, 2); |
||||
$loader = new ProjectYamlLoader( |
||||
PHP_VERSION, |
||||
static fn(string $message): never => throw new \RuntimeException($message), |
||||
); |
||||
$development = $loader->load($root . '/project.yml'); |
||||
$release = $loader->load($root . '/project-release.yml'); |
||||
|
||||
$this->assertArrayNotHasKey('embedded-files', $development); |
||||
$this->assertSame(['./vendor'], $release['embedded-files'] ?? null); |
||||
|
||||
unset($release['embedded-files']); |
||||
$this->assertSame($development, $release); |
||||
} |
||||
} |
||||
@ -0,0 +1,125 @@ |
||||
<?php |
||||
|
||||
namespace TypePhp\Tests\Config; |
||||
|
||||
use PHPUnit\Framework\TestCase; |
||||
use RuntimeException; |
||||
use TypePhp\Config\ProjectYamlLoader; |
||||
|
||||
final class ProjectYamlLoaderTest extends TestCase |
||||
{ |
||||
private string $directory; |
||||
private ProjectYamlLoader $loader; |
||||
|
||||
protected function setUp(): void |
||||
{ |
||||
$this->directory = sys_get_temp_dir() . '/typephp-project-yaml-' . bin2hex(random_bytes(6)); |
||||
mkdir($this->directory, 0777, true); |
||||
$this->loader = new ProjectYamlLoader( |
||||
'8.4.14', |
||||
static fn(string $message): never => throw new RuntimeException($message), |
||||
); |
||||
} |
||||
|
||||
protected function tearDown(): void |
||||
{ |
||||
foreach (glob($this->directory . '/*.yml') ?: [] as $file) { |
||||
unlink($file); |
||||
} |
||||
rmdir($this->directory); |
||||
} |
||||
|
||||
public function testIncludedConfigurationIsMergedBeforeRootConfiguration(): void |
||||
{ |
||||
file_put_contents($this->directory . '/common.yml', <<<'YAML' |
||||
name: common |
||||
sources: |
||||
- src |
||||
defines: |
||||
- COMMON=1 |
||||
resource: |
||||
version-info: |
||||
company-name: Common Team |
||||
product-name: Common Product |
||||
YAML); |
||||
file_put_contents($this->directory . '/project.yml', <<<'YAML' |
||||
include: common.yml |
||||
name: app |
||||
defines: |
||||
- APP=1 |
||||
resource: |
||||
version-info: |
||||
company-name: Application Team |
||||
YAML); |
||||
|
||||
self::assertSame([ |
||||
'name' => 'app', |
||||
'sources' => ['src'], |
||||
'defines' => ['APP=1'], |
||||
'resource' => [ |
||||
'version-info' => [ |
||||
'company-name' => 'Application Team', |
||||
'product-name' => 'Common Product', |
||||
], |
||||
], |
||||
], $this->loader->load($this->directory . '/project.yml')); |
||||
} |
||||
|
||||
public function testMultipleIncludesAreAppliedInOrder(): void |
||||
{ |
||||
file_put_contents($this->directory . '/first.yml', "name: first\noptimize: 1\n"); |
||||
file_put_contents($this->directory . '/second.yml', "name: second\njob: 4\n"); |
||||
file_put_contents($this->directory . '/project.yml', <<<'YAML' |
||||
include: |
||||
- first.yml |
||||
- second.yml |
||||
name: project |
||||
YAML); |
||||
|
||||
self::assertSame([ |
||||
'name' => 'project', |
||||
'optimize' => 1, |
||||
'job' => 4, |
||||
], $this->loader->load($this->directory . '/project.yml')); |
||||
} |
||||
|
||||
public function testNestedIncludesAreSupported(): void |
||||
{ |
||||
file_put_contents($this->directory . '/grandparent.yml', "optimize: 2\n"); |
||||
file_put_contents($this->directory . '/parent.yml', "include: grandparent.yml\njob: 4\n"); |
||||
file_put_contents($this->directory . '/project.yml', "include: parent.yml\nname: app\n"); |
||||
|
||||
self::assertSame([ |
||||
'optimize' => 2, |
||||
'job' => 4, |
||||
'name' => 'app', |
||||
], $this->loader->load($this->directory . '/project.yml')); |
||||
} |
||||
|
||||
public function testCompletedIncludeCanBeLoadedAgain(): void |
||||
{ |
||||
file_put_contents($this->directory . '/shared.yml', "optimize: 2\n"); |
||||
file_put_contents($this->directory . '/first.yml', "include: shared.yml\njob: 2\n"); |
||||
file_put_contents($this->directory . '/second.yml', "include: shared.yml\njob: 4\n"); |
||||
file_put_contents($this->directory . '/project.yml', <<<'YAML' |
||||
include: |
||||
- first.yml |
||||
- second.yml |
||||
YAML); |
||||
|
||||
self::assertSame([ |
||||
'optimize' => 2, |
||||
'job' => 4, |
||||
], $this->loader->load($this->directory . '/project.yml')); |
||||
} |
||||
|
||||
public function testCircularIncludeIsRejectedWhileFileIsBeingParsed(): void |
||||
{ |
||||
file_put_contents($this->directory . '/parent.yml', "include: project.yml\n"); |
||||
file_put_contents($this->directory . '/project.yml', "include: parent.yml\n"); |
||||
|
||||
$this->expectException(RuntimeException::class); |
||||
$this->expectExceptionMessage('Circular project YAML include'); |
||||
$this->loader->load($this->directory . '/project.yml'); |
||||
} |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
include: project.yml |
||||
|
||||
# Release binaries carry their Composer runtime as embedded opcodes and files. |
||||
# Development and PHPT bootstrap builds use project.yml to avoid paying this |
||||
# initialization cost for every compiler process. |
||||
embedded-files: |
||||
- ./vendor |
||||
Loading…
Reference in new issue