Skip to content
Open
Show file tree
Hide file tree
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
30 changes: 26 additions & 4 deletions .github/workflows/database.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,37 @@ jobs:
- name: Verify existing migrations are not modified or deleted
run: |
BASE_REF="${{ steps.base.outputs.base_ref }}"
CHANGED=$(git diff --name-only --diff-filter=MD origin/$BASE_REF...HEAD -- migrations/)
CHANGED=$(git diff --name-only --diff-filter=MD origin/$BASE_REF...HEAD -- migrations/ | grep -E 'Version[0-9]{14}\.php$' || true)
if [ -n "$CHANGED" ]; then
echo "Error: The following existing migrations were modified or deleted:"
echo "$CHANGED"
exit 1
fi
echo "No existing migrations were modified or deleted."

- name: Verify new migrations follow naming convention
run: |
BASE_REF="${{ steps.base.outputs.base_ref }}"
NEW_FILES=$(git diff --name-only --diff-filter=A origin/$BASE_REF...HEAD -- migrations/ | grep '\.php$' || true)
if [ -z "$NEW_FILES" ]; then
echo "No new migrations added."
exit 0
fi

FAILED=false
for file in $NEW_FILES; do
if ! echo "$file" | grep -qE 'Version[0-9]{14}\.php$'; then
echo "Error: $file does not follow the migration naming convention (Version{14digits}.php)"
FAILED=true
else
echo "OK: $file"
fi
done

if [ "$FAILED" = true ]; then
exit 1
fi

- name: Verify new migrations have higher version numbers than existing migrations
run: |
BASE_REF="${{ steps.base.outputs.base_ref }}"
Expand All @@ -128,11 +151,10 @@ jobs:

FAILED=false
for file in $NEW_MIGRATIONS; do
VERSION=$(echo "$file" | grep -oE '[0-9]{14}')
if [ -z "$VERSION" ]; then
echo "Warning: Could not extract version number from $file"
if ! echo "$file" | grep -qE 'Version[0-9]{14}\.php$'; then
continue
fi
VERSION=$(echo "$file" | grep -oE '[0-9]{14}')
if [ "$VERSION" -le "$BASE_MAX" ]; then
echo "Error: $file has version $VERSION which is not higher than the existing maximum $BASE_MAX"
FAILED=true
Expand Down
48 changes: 48 additions & 0 deletions docs/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ dirigent:
dev_packages: false
metadata:
mirror_vcs_repositories: false
retain_pruned_versions:
enabled: true
tagged_versions: true
dev_versions: false
retain_stale_revisions:
enabled: true
tagged_versions: true
dev_versions: false
```

## dirigent (root)
Expand Down Expand Up @@ -131,6 +139,46 @@ Fetch mirrored packages from their VCS repositories by default when possible.

Sets the fetch strategy of new mirrored packages to **Fetch from VCS**.

### retain_pruned_versions

#### enabled

Type: `boolean` | Default: `true`

Whether to enable or disable retaining pruned versions of packages.

#### tagged_versions

Type: `boolean` | Default: `true`

Retain pruned tagged package versions.

#### dev_versions

Type: `boolean` | Default: `false`

Retain pruned development package versions.

### retain_stale_revisions

#### enabled

Type: `boolean` | Default: `true`

Whether to enable or disable retaining stale revisions of packages.

#### tagged_versions

Type: `boolean` | Default: `true`

Retain stale revisions of tagged package versions.

#### dev_versions

Type: `boolean` | Default: `false`

Retain stale revisions of development package versions.

[iso-8601-durations]: https://en.wikipedia.org/wiki/ISO_8601#Durations
[symfony]: https://symfony.com
[symfony-docs-config]: https://symfony.com/doc/current/configuration.html
19 changes: 19 additions & 0 deletions migrations/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Agent guidelines for Dirigent development: migrations/

## Generate new migrations

To generate a new migration, execute the `symfony console doctrine:migrations:diff --nowdoc --formatted` command.

## Coding style

- Migration files must follow the naming convention of `Version[0-9]{14}.php`.
- Migrations must have a non-empty description.
- Queries should be wrapped in nowdoc by default. Only if a PHP variable is used in the query is it allowed to be wrapped in heredoc.

## Required columns

If a required (non-nullable) column is added to the schema, add it with the following queries:

1. Add a nullable column.
2. Set a default value for every row in the table.
3. Remove the nullable flag from the column.
3 changes: 3 additions & 0 deletions migrations/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Agent guidelines for Dirigent development in Claude Code: migrations/

@AGENTS.md
36 changes: 36 additions & 0 deletions migrations/Version20260416081737.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20260416081737 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add pruned property to version table';
}

public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE version ADD pruned BOOLEAN DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
UPDATE version SET pruned = false
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE version ALTER pruned SET NOT NULL
SQL);
}

public function down(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE version DROP pruned
SQL);
}
}
26 changes: 26 additions & 0 deletions src/DependencyInjection/DirigentConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,32 @@ private function addMetadataSection(ArrayNodeDefinition|NodeDefinition $rootNode
->defaultFalse()
->info('Fetch mirrored packages from their VCS repositories by default when possible.')
->end()
->arrayNode('retain_pruned_versions')
->canBeDisabled('Retain pruned package versions.')
->children()
->booleanNode('tagged_versions')
->defaultTrue()
->info('Retain pruned tagged package versions.')
->end()
->booleanNode('dev_versions')
->defaultFalse()
->info('Retain pruned development package versions.')
->end()
->end()
->end()
->arrayNode('retain_stale_revisions')
->canBeDisabled('Retain stale revisions of package versions.')
->children()
->booleanNode('tagged_versions')
->defaultTrue()
->info('Retain stale revisions of tagged package versions.')
->end()
->booleanNode('dev_versions')
->defaultFalse()
->info('Retain stale revisions of development package versions.')
->end()
->end()
->end()
->end()
->end();
}
Expand Down
22 changes: 21 additions & 1 deletion src/DependencyInjection/DirigentExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,31 @@ private function registerEncryptionConfiguration(array $config, ContainerBuilder
}

/**
* @param array{mirror_vcs_repositories: bool} $config
* @param array{mirror_vcs_repositories: bool, retain_stale_revisions: array{enabled: bool, tagged_versions: bool, dev_versions: bool}, retain_pruned_versions: array{enabled: bool, tagged_versions: bool, dev_versions: bool}} $config
*/
private function registerMetadataConfiguration(array $config, ContainerBuilder $container): void
{
$container->setParameter('dirigent.metadata.mirror_vcs_repositories', $config['mirror_vcs_repositories']);

$retainPrunedVersions = $config['retain_pruned_versions']['enabled'];
$container->setParameter(
name: 'dirigent.metadata.retain_pruned_versions.tagged_versions',
value: $retainPrunedVersions && $config['retain_pruned_versions']['tagged_versions'],
);
$container->setParameter(
name: 'dirigent.metadata.retain_pruned_versions.dev_versions',
value: $retainPrunedVersions && $config['retain_pruned_versions']['dev_versions'],
);

$retainStaleRevisions = $config['retain_stale_revisions']['enabled'];
$container->setParameter(
name: 'dirigent.metadata.retain_stale_revisions.tagged_versions',
value: $retainStaleRevisions && $config['retain_stale_revisions']['tagged_versions'],
);
$container->setParameter(
name: 'dirigent.metadata.retain_stale_revisions.dev_versions',
value: $retainStaleRevisions && $config['retain_stale_revisions']['dev_versions'],
);
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/Doctrine/Entity/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Version extends TrackedEntity implements \Stringable
#[ORM\Column]
private bool $development;

#[ORM\Column]
private bool $pruned = false;

#[ORM\Column]
private bool $defaultBranch = false;

Expand Down Expand Up @@ -91,6 +94,16 @@ public function setDevelopment(bool $development): void
$this->development = $development;
}

public function isPruned(): bool
{
return $this->pruned;
}

public function setPruned(bool $pruned): void
{
$this->pruned = $pruned;
}

public function isDefaultBranch(): bool
{
return $this->defaultBranch;
Expand Down
28 changes: 26 additions & 2 deletions src/Package/PackageMetadataResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Composer\Pcre\Preg;
use Composer\Repository\Vcs\VcsDriverInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;
use Symfony\Component\Messenger\Stamp\TransportNamesStamp;
Expand All @@ -36,6 +37,14 @@ public function __construct(
private KeywordRepository $keywordRepository,
private RegistryRepository $registryRepository,
private PackageRepository $packageRepository,
#[Autowire(param: 'dirigent.metadata.retain_stale_revisions.tagged_versions')]
private bool $retainStaleRevisionsTagged,
#[Autowire(param: 'dirigent.metadata.retain_stale_revisions.dev_versions')]
private bool $retainStaleRevisionsDev,
#[Autowire(param: 'dirigent.metadata.retain_pruned_versions.tagged_versions')]
private bool $retainPrunedVersionsTagged,
#[Autowire(param: 'dirigent.metadata.retain_pruned_versions.dev_versions')]
private bool $retainPrunedVersionsDev,
) {
}

Expand Down Expand Up @@ -204,7 +213,15 @@ private function updatePackage(Package $package, array $composerPackages, ?VcsDr

// Remove outdated versions
foreach ($existingVersionMetadata as $version) {
$this->entityManager->remove($version);
$removeVersion = $version->isDevelopment() ? !$this->retainPrunedVersionsDev : !$this->retainPrunedVersionsTagged;
if ($removeVersion) {
$this->entityManager->remove($version);
} elseif (!$version->isPruned()) {
$version->setPruned(true);
$version->setUpdatedAt(new \DateTimeImmutable());

$this->entityManager->persist($version);
}
}

$package->setUpdatedAt(new \DateTimeImmutable());
Expand All @@ -214,15 +231,22 @@ private function updatePackage(Package $package, array $composerPackages, ?VcsDr

private function updateVersion(Version $version, CompletePackageInterface $data, ?VcsDriverInterface $driver = null): void
{
$currentMetadata = $version->hasCurrentMetadata() ? $version->getCurrentMetadata() : null;
$metadata = $this->createMetadata($version, $data, $driver);

if (!$version->hasCurrentMetadata() || $this->hasMetadataChanged($version->getCurrentMetadata(), $metadata)) {
if (null === $currentMetadata || $this->hasMetadataChanged($currentMetadata, $metadata)) {
$version->setCurrentMetadata($metadata);

$this->entityManager->persist($metadata);

$removePreviousMetadata = $version->isDevelopment() ? !$this->retainStaleRevisionsDev : !$this->retainStaleRevisionsTagged;
if (null !== $currentMetadata && $removePreviousMetadata) {
$this->entityManager->remove($currentMetadata);
}
}

$version->setDefaultBranch($data->isDefaultBranch());
$version->setPruned(false);
$version->setUpdatedAt(new \DateTimeImmutable());

$this->entityManager->persist($version);
Expand Down
Loading