Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Virtual package type #216

Merged
merged 1 commit into from
Jan 16, 2024
Merged
Changes from all 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
2 changes: 1 addition & 1 deletion src/Composer/PacketonRepositoryFactory.php
Original file line number Diff line number Diff line change
@@ -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),
};
}
7 changes: 7 additions & 0 deletions src/Composer/Repository/CustomJsonRepository.php
Original file line number Diff line number Diff line change
@@ -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
@@ -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)) {
6 changes: 4 additions & 2 deletions src/Controller/Api/ApiController.php
Original file line number Diff line number Diff line change
@@ -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);
@@ -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);
6 changes: 5 additions & 1 deletion src/Controller/PackageController.php
Original file line number Diff line number Diff line change
@@ -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) {
@@ -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),
]
);

@@ -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,
]
);

@@ -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);
2 changes: 1 addition & 1 deletion src/Entity/Package.php
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 3 additions & 1 deletion src/Form/Type/Package/BasePackageType.php
Original file line number Diff line number Diff line change
@@ -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']) {
@@ -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());
}
4 changes: 3 additions & 1 deletion src/Form/Type/Package/CustomPackageType.php
Original file line number Diff line number Diff line change
@@ -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;
@@ -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,
],
]);

30 changes: 22 additions & 8 deletions src/Form/Type/Package/CustomVersionType.php
Original file line number Diff line number Diff line change
@@ -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;
@@ -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',
@@ -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
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
@@ -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
@@ -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,
};
}
@@ -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,
};
}
@@ -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,
};
}
4 changes: 2 additions & 2 deletions src/Package/Updater.php
Original file line number Diff line number Diff line change
@@ -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];
}

/**
@@ -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()),
7 changes: 6 additions & 1 deletion src/Repository/PackageRepository.php
Original file line number Diff line number Diff line change
@@ -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;

@@ -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)
@@ -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
]
);
}
14 changes: 10 additions & 4 deletions src/Service/DistManager.php
Original file line number Diff line number Diff line change
@@ -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;

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

@@ -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;
}
2 changes: 1 addition & 1 deletion src/Validator/Constraint/PackageRepositoryValidator.php
Original file line number Diff line number Diff line change
@@ -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),
};
}