Skip to content

Commit 8682db1

Browse files
committed
feat(upgrade): release metadata
Signed-off-by: Maxence Lange <[email protected]>
1 parent 2f771df commit 8682db1

File tree

5 files changed

+287
-124
lines changed

5 files changed

+287
-124
lines changed

core/Command/Db/Migrations/GenerateMetadataCommand.php

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,21 @@
88
*/
99
namespace OC\Core\Command\Db\Migrations;
1010

11-
use OC\DB\Connection;
12-
use OC\DB\MigrationService;
11+
use OC\Migration\MetadataManager;
1312
use OCP\App\IAppManager;
14-
use ReflectionClass;
1513
use Symfony\Component\Console\Command\Command;
1614
use Symfony\Component\Console\Input\InputInterface;
1715
use Symfony\Component\Console\Output\OutputInterface;
1816

1917
class GenerateMetadataCommand extends Command {
2018
public function __construct(
21-
private readonly Connection $connection,
19+
private readonly MetadataManager $metadataManager,
2220
private readonly IAppManager $appManager,
2321
) {
2422
parent::__construct();
2523
}
2624

27-
protected function configure() {
25+
protected function configure(): void {
2826
$this->setName('migrations:generate-metadata')
2927
->setHidden(true)
3028
->setDescription('Generate metadata from DB migrations - internal and should not be used');
@@ -45,15 +43,17 @@ public function execute(InputInterface $input, OutputInterface $output): int {
4543
return 0;
4644
}
4745

48-
private function extractMigrationMetadata(): array {
46+
47+
48+
public function extractMigrationMetadata(): array {
4949
return [
5050
'core' => $this->extractMigrationMetadataFromCore(),
5151
'apps' => $this->extractMigrationMetadataFromApps()
5252
];
5353
}
5454

5555
private function extractMigrationMetadataFromCore(): array {
56-
return $this->extractMigrationAttributes('core');
56+
return $this->metadataManager->extractMigrationAttributes('core');
5757
}
5858

5959
/**
@@ -72,35 +72,11 @@ private function extractMigrationMetadataFromApps(): array {
7272
if (!$alreadyLoaded) {
7373
$this->appManager->loadApp($appId);
7474
}
75-
$metadata[$appId] = $this->extractMigrationAttributes($appId);
75+
$metadata[$appId] = $this->metadataManager->extractMigrationAttributes($appId);
7676
if (!$alreadyLoaded) {
7777
$this->appManager->disableApp($appId);
7878
}
7979
}
8080
return $metadata;
8181
}
82-
83-
/**
84-
* We get all migrations from an app, and for each migration we extract attributes
85-
*
86-
* @param string $appId
87-
*
88-
* @return array
89-
* @throws \Exception
90-
*/
91-
private function extractMigrationAttributes(string $appId): array {
92-
$ms = new MigrationService($appId, $this->connection);
93-
94-
$metadata = [];
95-
foreach($ms->getAvailableVersions() as $version) {
96-
$metadata[$version] = [];
97-
$class = new ReflectionClass($ms->createInstance($version));
98-
$attributes = $class->getAttributes();
99-
foreach ($attributes as $attribute) {
100-
$metadata[$version][] = $attribute->newInstance();
101-
}
102-
}
103-
104-
return $metadata;
105-
}
10682
}

core/Command/Db/Migrations/PreviewCommand.php

Lines changed: 29 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@
88
*/
99
namespace OC\Core\Command\Db\Migrations;
1010

11-
use OC\DB\Connection;
12-
use OC\DB\MigrationService;
13-
use OCP\Migration\Attributes\GenericMigrationAttribute;
11+
use OC\Migration\MetadataManager;
12+
use OC\Updater\ReleaseMetadata;
1413
use OCP\Migration\Attributes\MigrationAttribute;
15-
use OCP\Migration\Exceptions\AttributeException;
16-
use Psr\Log\LoggerInterface;
1714
use Symfony\Component\Console\Command\Command;
1815
use Symfony\Component\Console\Helper\Table;
1916
use Symfony\Component\Console\Helper\TableCell;
@@ -24,14 +21,15 @@
2421
use Symfony\Component\Console\Output\OutputInterface;
2522

2623
class PreviewCommand extends Command {
24+
private bool $initiated = false;
2725
public function __construct(
28-
private readonly Connection $connection,
29-
private readonly LoggerInterface $logger,
26+
private readonly MetadataManager $metadataManager,
27+
private readonly ReleaseMetadata $releaseMetadata,
3028
) {
3129
parent::__construct();
3230
}
3331

34-
protected function configure() {
32+
protected function configure(): void {
3533
$this
3634
->setName('migrations:preview')
3735
->setDescription('Get preview of available DB migrations in case of initiating an upgrade')
@@ -42,21 +40,37 @@ protected function configure() {
4240

4341
public function execute(InputInterface $input, OutputInterface $output): int {
4442
$version = $input->getArgument('version');
43+
if (filter_var($version, FILTER_VALIDATE_URL)) {
44+
$metadata = $this->releaseMetadata->downloadMetadata($version);
45+
} elseif (str_starts_with($version, '/')) {
46+
$metadata = json_decode(file_get_contents($version), true, flags: JSON_THROW_ON_ERROR);
47+
} else {
48+
$metadata = $this->releaseMetadata->getMetadata($version);
49+
}
4550

46-
$metadata = $this->getMetadata($version);
47-
$parsed = $this->getMigrationsAttributes($metadata);
51+
$parsed = $this->metadataManager->getMigrationsAttributesFromReleaseMetadata($metadata['migrations'] ?? [], true);
4852

4953
$table = new Table($output);
50-
$this->displayMigrations($table, 'core', $parsed['core']);
51-
54+
$this->displayMigrations($table, 'core', $parsed['core'] ?? []);
55+
foreach ($parsed['apps'] as $appId => $migrations) {
56+
if (!empty($migrations)) {
57+
$this->displayMigrations($table, $appId, $migrations);
58+
}
59+
}
5260
$table->render();
5361

5462
return 0;
5563
}
5664

5765
private function displayMigrations(Table $table, string $appId, array $data): void {
58-
$done = $this->getDoneMigrations($appId);
59-
$done = array_diff($done, ['30000Date20240429122720']);
66+
if (empty($data)) {
67+
return;
68+
}
69+
70+
if ($this->initiated) {
71+
$table->addRow(new TableSeparator());
72+
}
73+
$this->initiated = true;
6074

6175
$table->addRow(
6276
[
@@ -70,13 +84,9 @@ private function displayMigrations(Table $table, string $appId, array $data): vo
7084
]
7185
)->addRow(new TableSeparator());
7286

87+
/** @var MigrationAttribute[] $attributes */
7388
foreach($data as $migration => $attributes) {
74-
if (in_array($migration, $done)) {
75-
continue;
76-
}
77-
7889
$attributesStr = [];
79-
/** @var MigrationAttribute[] $attributes */
8090
foreach($attributes as $attribute) {
8191
$definition = '<info>' . $attribute->definition() . "</info>";
8292
$definition .= empty($attribute->getDescription()) ? '' : "\n " . $attribute->getDescription();
@@ -85,78 +95,5 @@ private function displayMigrations(Table $table, string $appId, array $data): vo
8595
}
8696
$table->addRow([$migration, implode("\n", $attributesStr)]);
8797
}
88-
89-
}
90-
91-
92-
93-
94-
95-
private function getMetadata(string $version): array {
96-
$metadata = json_decode(file_get_contents('/tmp/nextcloud-' . $version . '.metadata'), true);
97-
if (!$metadata) {
98-
throw new \Exception();
99-
}
100-
return $metadata['migrations'] ?? [];
101-
}
102-
103-
private function getDoneMigrations(string $appId): array {
104-
$ms = new MigrationService($appId, $this->connection);
105-
return $ms->getMigratedVersions();
106-
}
107-
108-
private function getMigrationsAttributes(array $metadata): array {
109-
$appsAttributes = [];
110-
foreach (array_keys($metadata['apps']) as $appId) {
111-
$appsAttributes[$appId] = $this->parseMigrations($metadata['apps'][$appId] ?? []);
112-
}
113-
114-
return [
115-
'core' => $this->parseMigrations($metadata['core'] ?? []),
116-
'apps' => $appsAttributes
117-
];
118-
}
119-
120-
private function parseMigrations(array $migrations): array {
121-
$parsed = [];
122-
foreach (array_keys($migrations) as $entry) {
123-
$items = $migrations[$entry];
124-
$parsed[$entry] = [];
125-
foreach ($items as $item) {
126-
try {
127-
$parsed[$entry][] = $this->createAttribute($item);
128-
} catch (AttributeException $e) {
129-
$this->logger->warning(
130-
'exception while trying to create attribute',
131-
['exception' => $e, 'item' => json_encode($item)]
132-
);
133-
$parsed[$entry][] = new GenericMigrationAttribute($item);
134-
}
135-
}
136-
}
137-
138-
return $parsed;
139-
}
140-
141-
/**
142-
* @param array $item
143-
*
144-
* @return MigrationAttribute|null
145-
* @throws AttributeException
146-
*/
147-
private function createAttribute(array $item): ?MigrationAttribute {
148-
$class = $item['class'] ?? '';
149-
$namespace = 'OCP\Migration\Attributes\\';
150-
if (!str_starts_with($class, $namespace)
151-
|| !ctype_alpha(substr($class, strlen($namespace)))) {
152-
throw new AttributeException('class name does not looks valid');
153-
}
154-
155-
try {
156-
$attribute = new $class();
157-
return $attribute->import($item);
158-
} catch (\Error) {
159-
throw new AttributeException('cannot import Attribute');
160-
}
16198
}
16299
}

0 commit comments

Comments
 (0)