Skip to content

Commit 52e4028

Browse files
authored
Merge pull request #12 from codedmonkey/revision-configuration
Revision configuration
2 parents 82a1034 + c7a8ef6 commit 52e4028

9 files changed

Lines changed: 218 additions & 7 deletions

File tree

.github/workflows/database.yaml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,37 @@ jobs:
102102
- name: Verify existing migrations are not modified or deleted
103103
run: |
104104
BASE_REF="${{ steps.base.outputs.base_ref }}"
105-
CHANGED=$(git diff --name-only --diff-filter=MD origin/$BASE_REF...HEAD -- migrations/)
105+
CHANGED=$(git diff --name-only --diff-filter=MD origin/$BASE_REF...HEAD -- migrations/ | grep -E 'Version[0-9]{14}\.php$' || true)
106106
if [ -n "$CHANGED" ]; then
107107
echo "Error: The following existing migrations were modified or deleted:"
108108
echo "$CHANGED"
109109
exit 1
110110
fi
111111
echo "No existing migrations were modified or deleted."
112112
113+
- name: Verify new migrations follow naming convention
114+
run: |
115+
BASE_REF="${{ steps.base.outputs.base_ref }}"
116+
NEW_FILES=$(git diff --name-only --diff-filter=A origin/$BASE_REF...HEAD -- migrations/ | grep '\.php$' || true)
117+
if [ -z "$NEW_FILES" ]; then
118+
echo "No new migrations added."
119+
exit 0
120+
fi
121+
122+
FAILED=false
123+
for file in $NEW_FILES; do
124+
if ! echo "$file" | grep -qE 'Version[0-9]{14}\.php$'; then
125+
echo "Error: $file does not follow the migration naming convention (Version{14digits}.php)"
126+
FAILED=true
127+
else
128+
echo "OK: $file"
129+
fi
130+
done
131+
132+
if [ "$FAILED" = true ]; then
133+
exit 1
134+
fi
135+
113136
- name: Verify new migrations have higher version numbers than existing migrations
114137
run: |
115138
BASE_REF="${{ steps.base.outputs.base_ref }}"
@@ -128,11 +151,10 @@ jobs:
128151
129152
FAILED=false
130153
for file in $NEW_MIGRATIONS; do
131-
VERSION=$(echo "$file" | grep -oE '[0-9]{14}')
132-
if [ -z "$VERSION" ]; then
133-
echo "Warning: Could not extract version number from $file"
154+
if ! echo "$file" | grep -qE 'Version[0-9]{14}\.php$'; then
134155
continue
135156
fi
157+
VERSION=$(echo "$file" | grep -oE '[0-9]{14}')
136158
if [ "$VERSION" -le "$BASE_MAX" ]; then
137159
echo "Error: $file has version $VERSION which is not higher than the existing maximum $BASE_MAX"
138160
FAILED=true

docs/configuration-reference.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ dirigent:
3535
dev_packages: false
3636
metadata:
3737
mirror_vcs_repositories: false
38+
retain_pruned_versions:
39+
enabled: true
40+
tagged_versions: true
41+
dev_versions: false
42+
retain_stale_revisions:
43+
enabled: true
44+
tagged_versions: true
45+
dev_versions: false
3846
```
3947
4048
## dirigent (root)
@@ -131,6 +139,46 @@ Fetch mirrored packages from their VCS repositories by default when possible.
131139

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

142+
### retain_pruned_versions
143+
144+
#### enabled
145+
146+
Type: `boolean` | Default: `true`
147+
148+
Whether to enable or disable retaining pruned versions of packages.
149+
150+
#### tagged_versions
151+
152+
Type: `boolean` | Default: `true`
153+
154+
Retain pruned tagged package versions.
155+
156+
#### dev_versions
157+
158+
Type: `boolean` | Default: `false`
159+
160+
Retain pruned development package versions.
161+
162+
### retain_stale_revisions
163+
164+
#### enabled
165+
166+
Type: `boolean` | Default: `true`
167+
168+
Whether to enable or disable retaining stale revisions of packages.
169+
170+
#### tagged_versions
171+
172+
Type: `boolean` | Default: `true`
173+
174+
Retain stale revisions of tagged package versions.
175+
176+
#### dev_versions
177+
178+
Type: `boolean` | Default: `false`
179+
180+
Retain stale revisions of development package versions.
181+
134182
[iso-8601-durations]: https://en.wikipedia.org/wiki/ISO_8601#Durations
135183
[symfony]: https://symfony.com
136184
[symfony-docs-config]: https://symfony.com/doc/current/configuration.html

migrations/AGENTS.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Agent guidelines for Dirigent development: migrations/
2+
3+
## Generate new migrations
4+
5+
To generate a new migration, execute the `symfony console doctrine:migrations:diff --nowdoc --formatted` command.
6+
7+
## Coding style
8+
9+
- Migration files must follow the naming convention of `Version[0-9]{14}.php`.
10+
- Migrations must have a non-empty description.
11+
- 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.
12+
13+
## Required columns
14+
15+
If a required (non-nullable) column is added to the schema, add it with the following queries:
16+
17+
1. Add a nullable column.
18+
2. Set a default value for every row in the table.
19+
3. Remove the nullable flag from the column.

migrations/CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Agent guidelines for Dirigent development in Claude Code: migrations/
2+
3+
@AGENTS.md
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
final class Version20260416081737 extends AbstractMigration
11+
{
12+
public function getDescription(): string
13+
{
14+
return 'Add pruned property to version table';
15+
}
16+
17+
public function up(Schema $schema): void
18+
{
19+
$this->addSql(<<<'SQL'
20+
ALTER TABLE version ADD pruned BOOLEAN DEFAULT NULL
21+
SQL);
22+
$this->addSql(<<<'SQL'
23+
UPDATE version SET pruned = false
24+
SQL);
25+
$this->addSql(<<<'SQL'
26+
ALTER TABLE version ALTER pruned SET NOT NULL
27+
SQL);
28+
}
29+
30+
public function down(Schema $schema): void
31+
{
32+
$this->addSql(<<<'SQL'
33+
ALTER TABLE version DROP pruned
34+
SQL);
35+
}
36+
}

src/DependencyInjection/DirigentConfiguration.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,32 @@ private function addMetadataSection(ArrayNodeDefinition|NodeDefinition $rootNode
9393
->defaultFalse()
9494
->info('Fetch mirrored packages from their VCS repositories by default when possible.')
9595
->end()
96+
->arrayNode('retain_pruned_versions')
97+
->canBeDisabled('Retain pruned package versions.')
98+
->children()
99+
->booleanNode('tagged_versions')
100+
->defaultTrue()
101+
->info('Retain pruned tagged package versions.')
102+
->end()
103+
->booleanNode('dev_versions')
104+
->defaultFalse()
105+
->info('Retain pruned development package versions.')
106+
->end()
107+
->end()
108+
->end()
109+
->arrayNode('retain_stale_revisions')
110+
->canBeDisabled('Retain stale revisions of package versions.')
111+
->children()
112+
->booleanNode('tagged_versions')
113+
->defaultTrue()
114+
->info('Retain stale revisions of tagged package versions.')
115+
->end()
116+
->booleanNode('dev_versions')
117+
->defaultFalse()
118+
->info('Retain stale revisions of development package versions.')
119+
->end()
120+
->end()
121+
->end()
96122
->end()
97123
->end();
98124
}

src/DependencyInjection/DirigentExtension.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,31 @@ private function registerEncryptionConfiguration(array $config, ContainerBuilder
5656
}
5757

5858
/**
59-
* @param array{mirror_vcs_repositories: bool} $config
59+
* @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
6060
*/
6161
private function registerMetadataConfiguration(array $config, ContainerBuilder $container): void
6262
{
6363
$container->setParameter('dirigent.metadata.mirror_vcs_repositories', $config['mirror_vcs_repositories']);
64+
65+
$retainPrunedVersions = $config['retain_pruned_versions']['enabled'];
66+
$container->setParameter(
67+
name: 'dirigent.metadata.retain_pruned_versions.tagged_versions',
68+
value: $retainPrunedVersions && $config['retain_pruned_versions']['tagged_versions'],
69+
);
70+
$container->setParameter(
71+
name: 'dirigent.metadata.retain_pruned_versions.dev_versions',
72+
value: $retainPrunedVersions && $config['retain_pruned_versions']['dev_versions'],
73+
);
74+
75+
$retainStaleRevisions = $config['retain_stale_revisions']['enabled'];
76+
$container->setParameter(
77+
name: 'dirigent.metadata.retain_stale_revisions.tagged_versions',
78+
value: $retainStaleRevisions && $config['retain_stale_revisions']['tagged_versions'],
79+
);
80+
$container->setParameter(
81+
name: 'dirigent.metadata.retain_stale_revisions.dev_versions',
82+
value: $retainStaleRevisions && $config['retain_stale_revisions']['dev_versions'],
83+
);
6484
}
6585

6686
/**

src/Doctrine/Entity/Version.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ class Version extends TrackedEntity implements \Stringable
2323
#[ORM\Column]
2424
private bool $development;
2525

26+
#[ORM\Column]
27+
private bool $pruned = false;
28+
2629
#[ORM\Column]
2730
private bool $defaultBranch = false;
2831

@@ -91,6 +94,16 @@ public function setDevelopment(bool $development): void
9194
$this->development = $development;
9295
}
9396

97+
public function isPruned(): bool
98+
{
99+
return $this->pruned;
100+
}
101+
102+
public function setPruned(bool $pruned): void
103+
{
104+
$this->pruned = $pruned;
105+
}
106+
94107
public function isDefaultBranch(): bool
95108
{
96109
return $this->defaultBranch;

src/Package/PackageMetadataResolver.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Composer\Pcre\Preg;
2424
use Composer\Repository\Vcs\VcsDriverInterface;
2525
use Doctrine\ORM\EntityManagerInterface;
26+
use Symfony\Component\DependencyInjection\Attribute\Autowire;
2627
use Symfony\Component\Messenger\MessageBusInterface;
2728
use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;
2829
use Symfony\Component\Messenger\Stamp\TransportNamesStamp;
@@ -36,6 +37,14 @@ public function __construct(
3637
private KeywordRepository $keywordRepository,
3738
private RegistryRepository $registryRepository,
3839
private PackageRepository $packageRepository,
40+
#[Autowire(param: 'dirigent.metadata.retain_stale_revisions.tagged_versions')]
41+
private bool $retainStaleRevisionsTagged,
42+
#[Autowire(param: 'dirigent.metadata.retain_stale_revisions.dev_versions')]
43+
private bool $retainStaleRevisionsDev,
44+
#[Autowire(param: 'dirigent.metadata.retain_pruned_versions.tagged_versions')]
45+
private bool $retainPrunedVersionsTagged,
46+
#[Autowire(param: 'dirigent.metadata.retain_pruned_versions.dev_versions')]
47+
private bool $retainPrunedVersionsDev,
3948
) {
4049
}
4150

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

205214
// Remove outdated versions
206215
foreach ($existingVersionMetadata as $version) {
207-
$this->entityManager->remove($version);
216+
$removeVersion = $version->isDevelopment() ? !$this->retainPrunedVersionsDev : !$this->retainPrunedVersionsTagged;
217+
if ($removeVersion) {
218+
$this->entityManager->remove($version);
219+
} elseif (!$version->isPruned()) {
220+
$version->setPruned(true);
221+
$version->setUpdatedAt(new \DateTimeImmutable());
222+
223+
$this->entityManager->persist($version);
224+
}
208225
}
209226

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

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

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

222240
$this->entityManager->persist($metadata);
241+
242+
$removePreviousMetadata = $version->isDevelopment() ? !$this->retainStaleRevisionsDev : !$this->retainStaleRevisionsTagged;
243+
if (null !== $currentMetadata && $removePreviousMetadata) {
244+
$this->entityManager->remove($currentMetadata);
245+
}
223246
}
224247

225248
$version->setDefaultBranch($data->isDefaultBranch());
249+
$version->setPruned(false);
226250
$version->setUpdatedAt(new \DateTimeImmutable());
227251

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

0 commit comments

Comments
 (0)