Skip to content

Commit

Permalink
Implement basic git linker for developer installations
Browse files Browse the repository at this point in the history
  • Loading branch information
ralflang committed Jan 13, 2024
1 parent a9a2f6c commit 82b392d
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 12 deletions.
36 changes: 36 additions & 0 deletions src/Composer/PathRepositoryDefinition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Horde\Components\Composer;

use stdClass;
use Stringable;

class PathRepositoryDefinition implements RepositoryDefinition
{
public readonly string $path;

public function __construct(string|Stringable $path, public readonly stdClass $repositoryOptions = new stdClass)
{
// Cast to string once rather than everywhere.
$this->path = (string) $path;
}

public function getType(): string
{
return 'path';
}

public function getUrl(): string
{
return $this->path;
}

public function dumpStdClass()
{
return (object) [
'type' => 'path',
'options' => $this->repositoryOptions,
'url' => $this->path
];
}
}
7 changes: 3 additions & 4 deletions src/Composer/RepositoryDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@

use stdClass;

class RepositoryDefinition
interface RepositoryDefinition
{
public function __construct(stdClass $definition)
{
public function getType(): string;

}
public function dumpStdClass();
}
16 changes: 16 additions & 0 deletions src/Composer/RepositoryDefinitionFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
namespace Horde\Components\Composer;

use stdClass;
/**
* Create RepositoryDefinition implementations from stdClasses
*/
class RepositoryDefinitionFactory
{
public static function create(stdClass $input): RepositoryDefinition
{
if ($input->type == 'path') {
return new PathRepositoryDefinition($input->path, $input->options ?? new stdClass);
}
}
}
17 changes: 15 additions & 2 deletions src/Composer/RepositoryList.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class RepositoryList implements IteratorAggregate
private array $repositories;
public function __construct(RepositoryDefinition ...$repositories)
{
// TODO: Ensure all members are RepositoryDefinition objects
$this->repositories = $repositories;
}

Expand All @@ -20,9 +21,21 @@ public function getIterator(): Traversable
yield from $this->repositories;
}

/**
* Add an entry to the repository list or update an existing entry
*/
public function ensurePresent(RepositoryDefinition $repository)
{

foreach ($this->repositories as $position => $existingRepository) {
if ($repository->getType() == $existingRepository->getType()) {
// TODO: This only works for path and web repositories but not for some others
if ($repository->getUrl() == $existingRepository->getUrl()) {
$this->repositories[$position] = $repository;
return;
}
}
}
$this->repositories[] = $repository;
}

public function ensureAbsent(RepositoryDefinition $repository)
Expand All @@ -34,7 +47,7 @@ public static function fromStdClasses(stdClass ...$repositories): RepositoryList
{
$promoted = [];
foreach ($repositories as $repository) {
$promoted[] = new RepositoryDefinition($repository);
$promoted[] = RepositoryDefinitionFactory::create($repository);
}
return new RepositoryList(...$promoted);
}
Expand Down
10 changes: 7 additions & 3 deletions src/Composer/RootComposerJsonFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public function __construct(stdClass|string $data)
} else {
$this->content = $data;
}
$this->repositories = new RepositoryList($this->content?->repositories);
$repositoriesStd = $this->content->repositories ?? [];
$this->repositories = RepositoryList::fromStdClasses(...$repositoriesStd);
}

public function getRepositoryList(): RepositoryList
Expand All @@ -38,8 +39,11 @@ public static function loadFile(string $path): self

public function render(): string
{
// $this->content->repositories =
return (string) json_encode($this->content);
$this->content->repositories = [];
foreach ($this->repositories as $id => $repository) {
$this->content->repositories[$id] = $repository->dumpStdClass();
}
return (string) json_encode($this->content, JSON_PRETTY_PRINT);
}

public function writeFile(string $path)
Expand Down
49 changes: 46 additions & 3 deletions src/Runner/InstallRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,63 @@
namespace Horde\Components\Runner;

use Horde\Components\Composer\InstallationDirectory;
use Horde\Components\Composer\PathRepositoryDefinition;
use Horde\Components\Config;
use Horde\Components\Dependencies\GitCheckoutDirectoryFactory;
use Horde\Components\RuntimeContext\GitCheckoutDirectory;
use Horde\Components\Output;
use stdClass;

class InstallRunner
{
public function __construct(
private GitCheckoutDirectoryFactory $gitCheckoutDirectory,
private InstallationDirectory $installationDirectory
private GitCheckoutDirectory $gitCheckoutDirectory,
private InstallationDirectory $installationDirectory,
private readonly Output $output,
)
{
}

public function run(Config $config)
{
if (!$this->gitCheckoutDirectory->exists() || count($this->gitCheckoutDirectory->getGitDirs()) == 0) {
$this->output->warn("The developer checkout directory is missing or empty: " . $this->gitCheckoutDirectory);
$this->output->help("Run horde-components github-clone-org");
return;
}
if (!$this->installationDirectory->exists()) {
$this->output->info("Installation directory is missing: " . $this->installationDirectory);
if (mkdir((string) $this->installationDirectory, recursive: true)) {
$this->output->ok("Created installation directory: " . $this->installationDirectory);
} else {
$this->output->fail("Could not create installation directory: " . $this->installationDirectory);
return;
}
}
if (!$this->installationDirectory->hasComposerJson()) {
// TODO: Make this more flexbible
$targetVersion = 'dev-FRAMEWORK_6_0';
// TODO: Turn this into a proper class
$repository = new stdClass();
$repository->url = $this->gitCheckoutDirectory . DIRECTORY_SEPARATOR . 'bundle';
$repository->type = 'path';
$repository->options = new stdClass;
$repository->options->symlink = false;

$commandString = sprintf(
"COMPOSER_ALLOW_SUPERUSER=1 composer create-project horde/bundle %s %s --no-install --keep-vcs --repository='%s'",
$this->installationDirectory,
$targetVersion,
json_encode($repository)
);
// TODO: Hook into composer instead
$outputString = $resultCode = null;
exec($commandString, $outputString, $resultCode);
}
// Inject all horde apps as local sources.
$composerJson = $this->installationDirectory->getComposerJson();
foreach ($this->gitCheckoutDirectory->getHordeYmlDirs() as $hordeYmlDir) {
$composerJson->getRepositoryList()->ensurePresent(new PathRepositoryDefinition($hordeYmlDir));
}
$composerJson->writeFile($this->installationDirectory->getComposerJsonPath());
}
}

0 comments on commit 82b392d

Please sign in to comment.