Skip to content

Commit

Permalink
Merge pull request #216 from vtsykun/feat/virtual-package
Browse files Browse the repository at this point in the history
Added Virtual package type
  • Loading branch information
vtsykun committed Jan 16, 2024
2 parents 64c1175 + 09cf8e0 commit de63f80
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/Composer/PacketonRepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function create(array $repoConfig, IOInterface $io, Config $config, ?stri

return match ($type) {
RepTypes::ARTIFACT => new ArtifactRepository($repoConfig, $this->zipballStorage, $this->registry, $io, $config, $httpDownloader),
RepTypes::CUSTOM => new CustomJsonRepository($repoConfig, $this->registry, $io, $config, $httpDownloader),
RepTypes::CUSTOM, RepTypes::VIRTUAL => new CustomJsonRepository($repoConfig, $this->registry, $io, $config, $httpDownloader),
default => new VcsRepository($repoConfig, $io, $config, $httpDownloader, $this->driverFactory, null, $process),
};
}
Expand Down
7 changes: 7 additions & 0 deletions src/Composer/Repository/CustomJsonRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Composer\Util\ProcessExecutor;
use Doctrine\Persistence\ManagerRegistry;
use Packeton\Entity\Zipball;
use Packeton\Package\RepTypes;
use Packeton\Service\DistConfig;

class CustomJsonRepository extends ArrayRepository implements PacketonRepositoryInterface
Expand Down Expand Up @@ -128,6 +129,12 @@ protected function loadVersion(array $version): BasePackage
'reference' => $dist->getReference(),
'url' => DistConfig::HOSTNAME_PLACEHOLDER,
];
} elseif (($this->repoConfig['repoType'] ?? null) === RepTypes::VIRTUAL) {
$data['dist'] = [
'type' => 'zip',
'reference' => sha1(json_encode($data)),
'url' => DistConfig::HOSTNAME_PLACEHOLDER,
];
}

if (($url = $version['dist']['url'] ?? null) && preg_match('{^(\.|[a-z]:|/)}i', $url)) {
Expand Down
6 changes: 4 additions & 2 deletions src/Controller/Api/ApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public function createPackageAction(Request $request): Response
$form = $this->createForm($formType, $package, [
'csrf_protection' => false,
'validation_groups' => ['Create', 'Default'],
'is_created' => true
'is_created' => true,
'repo_type' => RepTypes::normalizeType($type),
]);

$form->submit($payload);
Expand Down Expand Up @@ -169,7 +170,8 @@ public function editPackageAction(Request $request, #[Vars] Package $package): R
$form = $this->createForm($formType, $package, [
'csrf_protection' => false,
'validation_groups' => ['Update', 'Default'],
'is_created' => false
'is_created' => false,
'repo_type' => $package->getRepoType(),
]);

$form->submit($payload, false);
Expand Down
6 changes: 5 additions & 1 deletion src/Controller/PackageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public function submitPackageAction(Request $req, string $type = null): Response
'action' => $this->generateUrl('submit', ['type' => $type]),
'validation_groups' => ['Create', 'Default'],
'is_created' => true,
'repo_type' => RepTypes::normalizeType($type),
]);

if ($this->getUser() instanceof User) {
Expand Down Expand Up @@ -166,6 +167,7 @@ public function fetchInfoAction(Request $req, string $type = null): Response
[
'validation_groups' => ['Create', 'Default'],
'is_created' => true,
'repo_type' => RepTypes::normalizeType($type),
]
);

Expand Down Expand Up @@ -233,7 +235,8 @@ public function fetchMonoRepoInfo(Request $req): Response
[
'validation_groups' => ['Update'],
'is_created' => false,
'allow_extra_fields' => true
'allow_extra_fields' => true,
'repo_type' => RepTypes::MONO_REPO,
]
);

Expand Down Expand Up @@ -813,6 +816,7 @@ public function editAction(Request $req, #[Vars] Package $package): Response
$form = $this->createForm($formTypeClass, $package, [
'action' => $this->generateUrl('edit_package', ['name' => $package->getName()]),
'validation_groups' => ['Update', 'Default'],
'repo_type' => $package->getRepoType(),
]);

$form->handleRequest($req);
Expand Down
2 changes: 1 addition & 1 deletion src/Entity/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ public function setRepositoryPath(?string $path): void
$this->repository = $path;
}

if ($this->getRepoType() === RepTypes::CUSTOM) {
if ($this->getRepoType() === RepTypes::CUSTOM || $this->getRepoType() === RepTypes::VIRTUAL) {
$this->customDriver = $this->driverError = null;
$this->repository = $path;
}
Expand Down
4 changes: 3 additions & 1 deletion src/Form/Type/Package/BasePackageType.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'MonoRepos (only GIT)' => RepTypes::MONO_REPO,
'Artifacts' => RepTypes::ARTIFACT,
'Custom (JSON)' => RepTypes::CUSTOM,
'Satis / Packagist.com / VCS Import' => 'import'
'Virtual (only JSON metadata)' => RepTypes::VIRTUAL,
'Satis / Packagist.com / VCS Import' => 'import', // only redirect
];

if ($options['has_active_integration']) {
Expand All @@ -52,6 +53,7 @@ protected function hasActiveIntegration(): bool
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('is_created', false);
$resolver->setDefault('repo_type', null);
$resolver->setDefault('data_class', Package::class);
$resolver->setDefault('has_active_integration', $this->hasActiveIntegration());
}
Expand Down
4 changes: 3 additions & 1 deletion src/Form/Type/Package/CustomPackageType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Doctrine\Persistence\ManagerRegistry;
use Packeton\Form\Handler\CustomPackageHandler;
use Packeton\Form\Type\EmbedCollectionType;
use Packeton\Package\RepTypes;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
Expand Down Expand Up @@ -38,7 +39,8 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'allow_add' => true,
'allow_delete' => true,
'entry_options' => [
'dist_choices' => $this->getChoices($options['is_created'])
'dist_choices' => $this->getChoices($options['is_created']),
'with_dist' => $options['repo_type'] !== RepTypes::VIRTUAL,
],
]);

Expand Down
30 changes: 22 additions & 8 deletions src/Form/Type/Package/CustomVersionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\NotBlank;
Expand All @@ -29,13 +30,19 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
$builder
->add('version', TextType::class, [
'constraints' => [new NotBlank()]
])
->add('dist', ChoiceType::class, [
'required' => false,
'label' => 'Uploaded dist',
'choices' => $options['dist_choices'],
'attr' => ['class' => 'jselect2 archive-select']
])
]);

if ($options['with_dist']) {
$builder
->add('dist', ChoiceType::class, [
'required' => false,
'label' => 'Uploaded dist',
'choices' => $options['dist_choices'],
'attr' => ['class' => 'jselect2 archive-select']
]);
}

$builder
->add('definition', JsonTextType::class, [
'required' => false,
'label' => 'composer.json config',
Expand All @@ -50,8 +57,15 @@ public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'dist_choices' => null,
'constraints' => [new Callback($this->validateData(...))]
'with_dist' => true,
]);

$resolver->addNormalizer('constraints', function(Options $options): array {
if ($options['with_dist']) {
return [new Callback($this->validateData(...))];
}
return [];
});
}

public function validateData($value, ExecutionContextInterface $context): void
Expand Down
54 changes: 54 additions & 0 deletions src/Model/VirtualPackageManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace Packeton\Model;

use Packeton\Entity\Package;
use Packeton\Service\DistConfig;

class VirtualPackageManager
{
public function __construct(
private readonly DistConfig $config,
) {
}

public function buildArchive(Package $package, ?string $reference = null): ?string
{
$version = $package->getVersionByReference($reference) ?: $package->getVersions()->first();
if (null === $version) {
throw new \RuntimeException("VirtualPackage. Not found any versions for reference '$reference' of package '{$package->getName()}'");
}

$keyName = $this->config->buildName($package->getName(), $version->getReference(), $version->getVersion());
$cachedName = $this->config->resolvePath($keyName);
if (file_exists($cachedName)) {
return $cachedName;
}

$selected = [];
$serialized = $package->getCustomVersions();
foreach ($serialized as $data) {
$verName = $data['version'] ?? null;
if ($verName === $version->getVersion() || $verName === $version->getNormalizedVersion()) {
$selected = $data['definition'] ?? [];
$selected['version'] = $version->getVersion();
}
}

$selected['name'] = $package->getName();
$dir = dirname($cachedName);
if (!is_dir($dir)) {
@mkdir($dir, 0777, true);
}

$zip = new \ZipArchive();
$zip->open($cachedName, \ZipArchive::CREATE);
$zip->addFromString('composer.json', json_encode($selected, \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES | \JSON_PRETTY_PRINT));
$zip->close();

return $cachedName;
}

}
14 changes: 11 additions & 3 deletions src/Package/RepTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ class RepTypes
public const ARTIFACT = 'artifact';
public const INTEGRATION = 'integration';
public const CUSTOM = 'custom';
public const VIRTUAL = 'virtual';

private static $types = [
self::ARTIFACT,
self::MONO_REPO,
self::INTEGRATION,
self::VCS,
self::CUSTOM,
self::VIRTUAL,
];

public static function getFormType(?string $type): string
Expand All @@ -32,15 +34,20 @@ public static function getFormType(?string $type): string
self::MONO_REPO => MonoRepoPackageType::class,
self::ARTIFACT => ArtifactPackageType::class,
self::INTEGRATION => IntegrationPackageType::class,
self::CUSTOM => CustomPackageType::class,
self::CUSTOM, self::VIRTUAL => CustomPackageType::class,
default => PackageType::class,
};
}

public static function isNotAutoCrawled(): array
{
return [self::VIRTUAL, self::CUSTOM, self::ARTIFACT];
}

public static function isBuildInDist(?string $type): bool
{
return match ($type) {
self::ARTIFACT, self::CUSTOM => true,
self::ARTIFACT, self::CUSTOM, self::VIRTUAL => true,
default => false,
};
}
Expand All @@ -49,7 +56,7 @@ public static function getUITemplate(?string $type, string $action): ?string
{
return match ($type) {
self::ARTIFACT => "package/{$action}Artifact.html.twig",
self::CUSTOM => "package/{$action}Custom.html.twig",
self::CUSTOM, self::VIRTUAL => "package/{$action}Custom.html.twig",
default => null,
};
}
Expand All @@ -61,6 +68,7 @@ public static function normalizeType(?string $type): string
self::ARTIFACT => self::ARTIFACT,
self::INTEGRATION => self::INTEGRATION,
self::CUSTOM => self::CUSTOM,
self::VIRTUAL => self::VIRTUAL,
default => self::VCS,
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/Package/Updater.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function __construct(
*/
public static function supportRepoTypes(): iterable
{
return [RepTypes::VCS, RepTypes::ARTIFACT, RepTypes::INTEGRATION, RepTypes::CUSTOM];
return [RepTypes::VCS, RepTypes::ARTIFACT, RepTypes::INTEGRATION, RepTypes::CUSTOM, RepTypes::VIRTUAL];
}

/**
Expand Down Expand Up @@ -556,7 +556,7 @@ private function updateArchive(PackageInterface $data, Package $package): ?array
// Process local path repos
if (is_string($distUrl = $data->getDistUrl())
&& (str_starts_with($distUrl, '/') || $distUrl === DistConfig::HOSTNAME_PLACEHOLDER)
&& (empty($data->getSourceUrl()) || $package->getRepoType() === RepTypes::CUSTOM)
&& (empty($data->getSourceUrl()) || in_array($package->getRepoType(), [RepTypes::CUSTOM, RepTypes::VIRTUAL], true))
) {
return [
'url' => $this->distConfig->generateRoute($data->getName(), $data->getDistReference(), $data->getDistType()),
Expand Down
7 changes: 6 additions & 1 deletion src/Repository/PackageRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Packeton\Entity\Package;
use Packeton\Entity\User;
use Packeton\Entity\Version;
use Packeton\Package\RepTypes;
use Packeton\Service\SubRepositoryHelper;
use Packeton\Util\PacketonUtils;

Expand Down Expand Up @@ -219,7 +220,7 @@ public function getStalePackages($interval = null)
"SELECT p.id FROM package p
WHERE p.abandoned = false
AND p.parent_id is NULL
AND (p.repo_type NOT IN ('artifact', 'custom') OR p.repo_type IS NULL)
AND (p.repo_type NOT IN (:notcrawled) OR p.repo_type IS NULL)
AND (
p.crawledAt IS NULL
OR (p.autoUpdated = false AND p.crawledAt < :crawled)
Expand All @@ -231,6 +232,10 @@ public function getStalePackages($interval = null)
'crawled' => date('Y-m-d H:i:s', time() - ($interval ?: 14400)),
// crawl auto-updated packages once a week just in case
'autocrawled' => date('Y-m-d H:i:s', strtotime('-7day')),
'notcrawled' => RepTypes::isNotAutoCrawled() ?: ['na'],
],
[
'notcrawled' => ArrayParameterType::STRING
]
);
}
Expand Down
14 changes: 10 additions & 4 deletions src/Service/DistManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Packeton\Integrations\IntegrationRegistry;
use Packeton\Integrations\ZipballInterface;
use Packeton\Model\UploadZipballStorage;
use Packeton\Model\VirtualPackageManager;
use Packeton\Package\RepTypes;
use Symfony\Component\Filesystem\Filesystem;

Expand All @@ -34,6 +35,7 @@ public function __construct(
private readonly IntegrationRegistry $integrations,
private readonly FilesystemOperator $baseStorage,
private readonly Filesystem $fs,
private readonly VirtualPackageManager $virtualPackageManager,
) {
}

Expand Down Expand Up @@ -169,17 +171,21 @@ private function guessCompletePackage(string $reference, array $versions): ?Comp

private function downloadArtifact(string $reference, Package $package): ?string
{
if ($package->getRepoType() === RepTypes::VIRTUAL) {
return $this->virtualPackageManager->buildArchive($package, $reference);
}

if ($path = $this->artifact->moveToLocal($reference)) {
return $path;
}

$repository = $this->createRepositoryAndIo($package);
$packages = $repository->getPackages();
$found = array_filter($packages, fn($p) => $reference === $p->getDistReference());
$found = array_filter($packages, static fn($p) => $reference === $p->getDistReference());

/** @var PackageInterface $package */
if ($package = reset($found)) {
$distUrl = $package->getDistUrl();
/** @var PackageInterface $pkg */
if ($pkg = reset($found)) {
$distUrl = $pkg->getDistUrl();
if (is_string($distUrl) && str_starts_with($distUrl, '/')) {
return $distUrl;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Validator/Constraint/PackageRepositoryValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function validate(mixed $value, Constraint $constraint): void

match ($value->getRepoType()) {
RepTypes::ARTIFACT => $this->validateArtifactPackage($value),
RepTypes::CUSTOM => $this->validateCustomPackage($value),
RepTypes::CUSTOM, RepTypes::VIRTUAL => $this->validateCustomPackage($value),
default => $this->validateVcsPackage($value),
};
}
Expand Down

0 comments on commit de63f80

Please sign in to comment.