From 1b71cdeabfda776ab10c23958944f67fae34bf16 Mon Sep 17 00:00:00 2001
From: Philipp Kolmann <philipp@kolmann.at>
Date: Fri, 5 Jul 2024 11:46:56 +0200
Subject: [PATCH 1/6] Implement Container registry API

Implement the functions from the Container registry API described in
https://docs.gitlab.com/ee/api/container_registry.html
---
 src/Api/Groups.php         |  12 ++++
 src/Api/Projects.php       |  28 ++++++++
 src/Api/Registry.php       | 139 +++++++++++++++++++++++++++++++++++++
 tests/Api/GroupsTest.php   |  20 ++++++
 tests/Api/ProjectsTest.php |  41 +++++++++++
 tests/Api/RegistryTest.php | 122 ++++++++++++++++++++++++++++++++
 6 files changed, 362 insertions(+)
 create mode 100644 src/Api/Registry.php
 create mode 100644 tests/Api/RegistryTest.php

diff --git a/src/Api/Groups.php b/src/Api/Groups.php
index 5a97e0cf..67975eb0 100644
--- a/src/Api/Groups.php
+++ b/src/Api/Groups.php
@@ -1031,4 +1031,16 @@ public function search($id, array $parameters = [])
 
         return $this->get('groups/'.self::encodePath($id).'/search', $resolver->resolve($parameters));
     }
+
+    /**
+     * Get a list of registry repositories in a group
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#within-a-group
+     *
+     * @param $id: The ID of a group
+     * @return mixed
+     */
+    public function registryRepositories(int $id)
+    {
+        return $this->get('groups/'.self::encodePath($id).'/registry/repositories');
+    }
 }
diff --git a/src/Api/Projects.php b/src/Api/Projects.php
index 3f5d1962..1178a93e 100644
--- a/src/Api/Projects.php
+++ b/src/Api/Projects.php
@@ -1832,4 +1832,32 @@ public function search($id, array $parameters = [])
 
         return $this->get('projects/'.self::encodePath($id).'/search', $resolver->resolve($parameters));
     }
+
+    /**
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#within-a-project
+     *
+     * @param int|string $project_id
+     * @param array $parameters {
+     *     @var bool $tags       If the parameter is included as true, each repository includes an array of "tags" in the response.
+     *     @var bool $tags_count If the parameter is included as true, each repository includes "tags_count" in the response.
+     * }
+     *
+     * @return mixed
+     */
+    public function registryRepositories($project_id, array $parameters = [])
+    {
+        $resolver = $this->createOptionsResolver();
+        $booleanNormalizer = function (Options $resolver, $value): string {
+            return $value ? 'true' : 'false';
+        };
+
+        $resolver->setDefined('tags')
+            ->setAllowedTypes('tags', 'bool')
+            ->setNormalizer('tags', $booleanNormalizer);
+        $resolver->setDefined('tags_count')
+            ->setAllowedTypes('tags_count', 'bool')
+            ->setNormalizer('tags_count', $booleanNormalizer);
+
+        return $this->get($this->getProjectPath($project_id, 'registry/repositories'), $resolver->resolve($parameters));
+    }
 }
diff --git a/src/Api/Registry.php b/src/Api/Registry.php
new file mode 100644
index 00000000..92c65be6
--- /dev/null
+++ b/src/Api/Registry.php
@@ -0,0 +1,139 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Gitlab API library.
+ *
+ * (c) Matt Humphrey <matth@windsor-telecom.co.uk>
+ * (c) Graham Campbell <hello@gjcampbell.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Gitlab\Api;
+
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class Registry extends AbstractApi
+{
+    /**
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-single-repository
+     *
+     * @param int|string $repository_id The ID of the registry repository accessible by the authenticated user.
+     * @param array $parameters {
+     *      @var bool $tags
+     *      @var bool $tags_count
+     *      @var bool $size
+     * }
+     * @return mixed
+     */
+    public function repositories($repository_id, array $parameters = [])
+    {
+        $resolver = $this->createOptionsResolver();
+        $booleanNormalizer = function (Options $resolver, $value): string {
+            return $value ? 'true' : 'false';
+        };
+
+        $resolver->setDefined('tags')
+            ->setAllowedTypes('tags', 'bool')
+            ->setNormalizer('tags', $booleanNormalizer);
+        $resolver->setDefined('tags_count')
+            ->setAllowedTypes('tags_count', 'bool')
+            ->setNormalizer('tags_count', $booleanNormalizer);
+        $resolver->setDefined('size')
+            ->setAllowedTypes('size', 'bool')
+            ->setNormalizer('size', $booleanNormalizer);
+
+        return $this->get('registry/repositories/'.self::encodePath($repository_id), $resolver->resolve($parameters));
+    }
+
+    /**
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#list-registry-repository-tags
+     *
+     * @param int|string $project_id
+     * @param int $repository_id
+     * @return mixed
+     */
+    public function repositoryTags($project_id, int $repository_id)
+    {
+        return $this->get(
+            $this->getProjectPath($project_id, 'registry/repositories/'.self::encodePath($repository_id).'/tags')
+        );
+    }
+
+
+    /**
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-registry-repository-tag
+     *
+     * @param int|string $project_id
+     * @param int $repository_id
+     * @param string $tag_name
+     * @return mixed
+     */
+    public function repositoryTag($project_id, int $repository_id, string $tag_name)
+    {
+        return $this->get(
+            $this->getProjectPath(
+                $project_id,
+                'registry/repositories/'.self::encodePath($repository_id).'/tags/'.self::encodePath($tag_name)
+            )
+        );
+    }
+
+
+    /**
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#delete-a-registry-repository-tag
+     *
+     * @param int|string $project_id
+     * @param int $repository_id
+     * @param string $tag_name
+     * @return mixed
+     */
+    public function removeRepositoryTag($project_id, int $repository_id, string $tag_name)
+    {
+        return $this->delete(
+            $this->getProjectPath(
+                $project_id,
+                'registry/repositories/'.self::encodePath($repository_id).'/tags/'.self::encodePath($tag_name)
+            )
+        );
+    }
+
+    /**
+     * @see https://docs.gitlab.com/ee/api/container_registry.html#delete-registry-repository-tags-in-bulk
+     *
+     * @param int|string $project_id
+     * @param int $repository_id
+     * @param array $parameters {
+     *      @var string $name_regex_delete
+     *      @var string $name_regex_keep
+     *      @var int $keep_n
+     *      @var string $older_than
+     * }
+     * @return mixed
+     */
+    public function removeRepositoryTags($project_id, int $repository_id, array $parameters = [])
+    {
+        $resolver = $this->createOptionsResolver();
+        $resolver->setRequired('name_regex_delete')
+            ->setAllowedTypes('name_regex_delete', 'string');
+        $resolver->setDefined('name_regex_keep')
+            ->setAllowedTypes('name_regex_keep', 'string');
+        $resolver->setDefined('keep_n')
+            ->setAllowedTypes('keep_n', 'int');
+        $resolver->setDefined('older_than')
+            ->setAllowedTypes('older_than', 'string');
+
+
+        return $this->delete(
+            $this->getProjectPath(
+                $project_id,
+                'registry/repositories/'.self::encodePath($repository_id).'/tags'
+            ),
+            $resolver->resolve($parameters)
+        );
+    }
+}
\ No newline at end of file
diff --git a/tests/Api/GroupsTest.php b/tests/Api/GroupsTest.php
index 19209bfd..bd7ad7fb 100644
--- a/tests/Api/GroupsTest.php
+++ b/tests/Api/GroupsTest.php
@@ -972,4 +972,24 @@ public function shouldSearchGroups(): void
             'sort' => 'desc',
         ]));
     }
+
+    /**
+     * @test
+     */
+    public function shouldGetGroupRegistryRepositories(): void
+    {
+        $expectedArray = [
+            ['id' => 1, 'name' => 'A registry'],
+            ['id' => 2, 'name' => 'Another registry'],
+        ];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('groups/1/registry/repositories')
+            ->will($this->returnValue($expectedArray))
+        ;
+
+        $this->assertEquals($expectedArray, $api->registryRepositories(1));
+    }
 }
diff --git a/tests/Api/ProjectsTest.php b/tests/Api/ProjectsTest.php
index c3296d62..c83c0eda 100644
--- a/tests/Api/ProjectsTest.php
+++ b/tests/Api/ProjectsTest.php
@@ -3049,4 +3049,45 @@ public function shouldSearchGroups(): void
             'sort' => 'desc',
         ]));
     }
+
+    /**
+     * @test
+     */
+    public function shouldGetProjectRegistryRepositories(): void
+    {
+        $expectedArray = [
+            ['id' => 1, 'name' => 'A registry'],
+            ['id' => 2, 'name' => 'Another registry'],
+        ];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/123/registry/repositories')
+            ->will($this->returnValue($expectedArray));
+
+        $this->assertEquals($expectedArray, $api->registryRepositories(123));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetProjectRegistryRepositoriesTags(): void
+    {
+        $expectedArray = [
+            ['id' => 1, 'name' => 'A registry', 'tags' => ['1.0', '1.1'], 'tags_count' => 2],
+            ['id' => 2, 'name' => 'Another registry', 'tags' => ['2.0', '2.1'], 'tags_count' => 2]
+        ];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/123/registry/repositories', [
+                'tags' => true,
+                'tags_count' => true
+            ])
+            ->will($this->returnValue($expectedArray));
+
+        $this->assertEquals($expectedArray, $api->registryRepositories(123, ['tags' => true, 'tags_count' => true]));
+    }
 }
diff --git a/tests/Api/RegistryTest.php b/tests/Api/RegistryTest.php
new file mode 100644
index 00000000..f6eb2cf2
--- /dev/null
+++ b/tests/Api/RegistryTest.php
@@ -0,0 +1,122 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Gitlab API library.
+ *
+ * (c) Matt Humphrey <matth@windsor-telecom.co.uk>
+ * (c) Graham Campbell <hello@gjcampbell.co.uk>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Gitlab\Tests\Api;
+
+use Gitlab\Api\Registry;
+
+class RegistryTest extends TestCase
+{
+    protected function getApiClass(): string
+    {
+        return Registry::class;
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetSingleRepository(): void
+    {
+        $expectedArray = ['id' => 1, 'name' => 'John Doe'];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('registry/repositories/1')
+            ->willReturn($expectedArray);
+
+        $this->assertEquals($expectedArray, $api->repositories(1));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetSingleRepositoryWithParams(): void
+    {
+        $expectedArray = ['id' => 1, 'name' => 'John Doe', 'tags' => ['tag1', 'tag2'], 'tags_count' => 2];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('registry/repositories/1', ['tags' => 'true', 'tags_count' => 'true'])
+            ->willReturn($expectedArray);
+
+        $this->assertEquals($expectedArray, $api->repositories(1, ['tags' => true, 'tags_count' => true]));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldGetRepositoryTags(): void
+    {
+        $expectedArray = [['name' => "A", 'path' => 'group/project:A'], ['name' => "B", 'path' => 'group/project:B']];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/registry/repositories/1/tags')
+            ->willReturn($expectedArray);
+
+        $this->assertEquals($expectedArray, $api->repositoryTags(1, 1));
+    }
+
+
+    /**
+     * @test
+     */
+    public function shouldGetRepositoryTag(): void
+    {
+        $expectedArray = ['name' => "A", 'path' => 'group/project:A'];
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('get')
+            ->with('projects/1/registry/repositories/1/tags/A')
+            ->willReturn($expectedArray);
+
+        $this->assertEquals($expectedArray, $api->repositoryTag(1, 1, 'A'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveRepositoryTag(): void
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/registry/repositories/1/tags/A')
+            ->will($this->returnValue($expectedBool));
+
+        $this->assertEquals($expectedBool, $api->removeRepositoryTag(1, 1, 'A'));
+    }
+
+    /**
+     * @test
+     */
+    public function shouldRemoveRepositoryTags(): void
+    {
+        $expectedBool = true;
+
+        $api = $this->getApiMock();
+        $api->expects($this->once())
+            ->method('delete')
+            ->with('projects/1/registry/repositories/1/tags', ['name_regex_delete' => '.*', 'keep_n' => 12])
+            ->will($this->returnValue($expectedBool));
+
+        $this->assertEquals($expectedBool, $api->removeRepositoryTags(1, 1, ['name_regex_delete' => '.*', 'keep_n' => 12]));
+    }
+}
\ No newline at end of file

From 8b0c8216981f537f201d1f96faa5e98b2ed98123 Mon Sep 17 00:00:00 2001
From: Philipp Kolmann <philipp@kolmann.at>
Date: Fri, 5 Jul 2024 11:56:37 +0200
Subject: [PATCH 2/6] Fix StyleCI issues

---
 src/Api/Groups.php         |  3 ++-
 src/Api/Projects.php       |  1 +
 src/Api/Registry.php       | 11 ++++++++---
 tests/Api/ProjectsTest.php |  4 ++--
 tests/Api/RegistryTest.php |  5 ++---
 5 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/src/Api/Groups.php b/src/Api/Groups.php
index 67975eb0..90f387eb 100644
--- a/src/Api/Groups.php
+++ b/src/Api/Groups.php
@@ -1033,10 +1033,11 @@ public function search($id, array $parameters = [])
     }
 
     /**
-     * Get a list of registry repositories in a group
+     * Get a list of registry repositories in a group.
      * @see https://docs.gitlab.com/ee/api/container_registry.html#within-a-group
      *
      * @param $id: The ID of a group
+     *
      * @return mixed
      */
     public function registryRepositories(int $id)
diff --git a/src/Api/Projects.php b/src/Api/Projects.php
index 1178a93e..ae40a6fa 100644
--- a/src/Api/Projects.php
+++ b/src/Api/Projects.php
@@ -1838,6 +1838,7 @@ public function search($id, array $parameters = [])
      *
      * @param int|string $project_id
      * @param array $parameters {
+     *
      *     @var bool $tags       If the parameter is included as true, each repository includes an array of "tags" in the response.
      *     @var bool $tags_count If the parameter is included as true, each repository includes "tags_count" in the response.
      * }
diff --git a/src/Api/Registry.php b/src/Api/Registry.php
index 92c65be6..0ebaf47e 100644
--- a/src/Api/Registry.php
+++ b/src/Api/Registry.php
@@ -15,19 +15,20 @@
 namespace Gitlab\Api;
 
 use Symfony\Component\OptionsResolver\Options;
-use Symfony\Component\OptionsResolver\OptionsResolver;
 
 class Registry extends AbstractApi
 {
     /**
      * @see https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-single-repository
      *
-     * @param int|string $repository_id The ID of the registry repository accessible by the authenticated user.
+     * @param int|string $repository_id The ID of the registry repository accessible by the authenticated user
      * @param array $parameters {
+     *
      *      @var bool $tags
      *      @var bool $tags_count
      *      @var bool $size
      * }
+     *
      * @return mixed
      */
     public function repositories($repository_id, array $parameters = [])
@@ -55,6 +56,7 @@ public function repositories($repository_id, array $parameters = [])
      *
      * @param int|string $project_id
      * @param int $repository_id
+     *
      * @return mixed
      */
     public function repositoryTags($project_id, int $repository_id)
@@ -71,6 +73,7 @@ public function repositoryTags($project_id, int $repository_id)
      * @param int|string $project_id
      * @param int $repository_id
      * @param string $tag_name
+     *
      * @return mixed
      */
     public function repositoryTag($project_id, int $repository_id, string $tag_name)
@@ -90,6 +93,7 @@ public function repositoryTag($project_id, int $repository_id, string $tag_name)
      * @param int|string $project_id
      * @param int $repository_id
      * @param string $tag_name
+     *
      * @return mixed
      */
     public function removeRepositoryTag($project_id, int $repository_id, string $tag_name)
@@ -108,11 +112,13 @@ public function removeRepositoryTag($project_id, int $repository_id, string $tag
      * @param int|string $project_id
      * @param int $repository_id
      * @param array $parameters {
+     *
      *      @var string $name_regex_delete
      *      @var string $name_regex_keep
      *      @var int $keep_n
      *      @var string $older_than
      * }
+     *
      * @return mixed
      */
     public function removeRepositoryTags($project_id, int $repository_id, array $parameters = [])
@@ -127,7 +133,6 @@ public function removeRepositoryTags($project_id, int $repository_id, array $par
         $resolver->setDefined('older_than')
             ->setAllowedTypes('older_than', 'string');
 
-
         return $this->delete(
             $this->getProjectPath(
                 $project_id,
diff --git a/tests/Api/ProjectsTest.php b/tests/Api/ProjectsTest.php
index c83c0eda..e66b6fca 100644
--- a/tests/Api/ProjectsTest.php
+++ b/tests/Api/ProjectsTest.php
@@ -3076,7 +3076,7 @@ public function shouldGetProjectRegistryRepositoriesTags(): void
     {
         $expectedArray = [
             ['id' => 1, 'name' => 'A registry', 'tags' => ['1.0', '1.1'], 'tags_count' => 2],
-            ['id' => 2, 'name' => 'Another registry', 'tags' => ['2.0', '2.1'], 'tags_count' => 2]
+            ['id' => 2, 'name' => 'Another registry', 'tags' => ['2.0', '2.1'], 'tags_count' => 2],
         ];
 
         $api = $this->getApiMock();
@@ -3084,7 +3084,7 @@ public function shouldGetProjectRegistryRepositoriesTags(): void
             ->method('get')
             ->with('projects/123/registry/repositories', [
                 'tags' => true,
-                'tags_count' => true
+                'tags_count' => true,
             ])
             ->will($this->returnValue($expectedArray));
 
diff --git a/tests/Api/RegistryTest.php b/tests/Api/RegistryTest.php
index f6eb2cf2..70691126 100644
--- a/tests/Api/RegistryTest.php
+++ b/tests/Api/RegistryTest.php
@@ -60,8 +60,7 @@ public function shouldGetSingleRepositoryWithParams(): void
      */
     public function shouldGetRepositoryTags(): void
     {
-        $expectedArray = [['name' => "A", 'path' => 'group/project:A'], ['name' => "B", 'path' => 'group/project:B']];
-
+        $expectedArray = [['name' => 'A', 'path' => 'group/project:A'], ['name' => 'B', 'path' => 'group/project:B']];
         $api = $this->getApiMock();
         $api->expects($this->once())
             ->method('get')
@@ -77,7 +76,7 @@ public function shouldGetRepositoryTags(): void
      */
     public function shouldGetRepositoryTag(): void
     {
-        $expectedArray = ['name' => "A", 'path' => 'group/project:A'];
+        $expectedArray = ['name' => 'A', 'path' => 'group/project:A'];
 
         $api = $this->getApiMock();
         $api->expects($this->once())

From 326920d52d33c59ab6984feea2ce0878946cc838 Mon Sep 17 00:00:00 2001
From: Philipp Kolmann <philipp@kolmann.at>
Date: Fri, 5 Jul 2024 12:03:18 +0200
Subject: [PATCH 3/6] Fix StyleCI issues no 2

---
 src/Api/Groups.php         | 1 +
 src/Api/Projects.php       | 2 +-
 src/Api/Registry.php       | 4 +---
 tests/Api/RegistryTest.php | 3 +--
 4 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/Api/Groups.php b/src/Api/Groups.php
index 90f387eb..e205c173 100644
--- a/src/Api/Groups.php
+++ b/src/Api/Groups.php
@@ -1034,6 +1034,7 @@ public function search($id, array $parameters = [])
 
     /**
      * Get a list of registry repositories in a group.
+     *
      * @see https://docs.gitlab.com/ee/api/container_registry.html#within-a-group
      *
      * @param $id: The ID of a group
diff --git a/src/Api/Projects.php b/src/Api/Projects.php
index ae40a6fa..92b4781c 100644
--- a/src/Api/Projects.php
+++ b/src/Api/Projects.php
@@ -1839,7 +1839,7 @@ public function search($id, array $parameters = [])
      * @param int|string $project_id
      * @param array $parameters {
      *
-     *     @var bool $tags       If the parameter is included as true, each repository includes an array of "tags" in the response.
+     *     @var bool $tags       if the parameter is included as true, each repository includes an array of "tags" in the response
      *     @var bool $tags_count If the parameter is included as true, each repository includes "tags_count" in the response.
      * }
      *
diff --git a/src/Api/Registry.php b/src/Api/Registry.php
index 0ebaf47e..2d8cc3d6 100644
--- a/src/Api/Registry.php
+++ b/src/Api/Registry.php
@@ -66,7 +66,6 @@ public function repositoryTags($project_id, int $repository_id)
         );
     }
 
-
     /**
      * @see https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-registry-repository-tag
      *
@@ -86,7 +85,6 @@ public function repositoryTag($project_id, int $repository_id, string $tag_name)
         );
     }
 
-
     /**
      * @see https://docs.gitlab.com/ee/api/container_registry.html#delete-a-registry-repository-tag
      *
@@ -141,4 +139,4 @@ public function removeRepositoryTags($project_id, int $repository_id, array $par
             $resolver->resolve($parameters)
         );
     }
-}
\ No newline at end of file
+}
diff --git a/tests/Api/RegistryTest.php b/tests/Api/RegistryTest.php
index 70691126..7c3ceeb6 100644
--- a/tests/Api/RegistryTest.php
+++ b/tests/Api/RegistryTest.php
@@ -70,7 +70,6 @@ public function shouldGetRepositoryTags(): void
         $this->assertEquals($expectedArray, $api->repositoryTags(1, 1));
     }
 
-
     /**
      * @test
      */
@@ -118,4 +117,4 @@ public function shouldRemoveRepositoryTags(): void
 
         $this->assertEquals($expectedBool, $api->removeRepositoryTags(1, 1, ['name_regex_delete' => '.*', 'keep_n' => 12]));
     }
-}
\ No newline at end of file
+}

From 959265c93117c88a85e1c60bc9a7c198abc645b8 Mon Sep 17 00:00:00 2001
From: Philipp Kolmann <philipp@kolmann.at>
Date: Fri, 5 Jul 2024 12:16:25 +0200
Subject: [PATCH 4/6] Make Registry() also available via the client...

---
 src/Client.php | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/Client.php b/src/Client.php
index 4082f1ac..dad812a4 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -32,6 +32,7 @@
 use Gitlab\Api\Milestones;
 use Gitlab\Api\ProjectNamespaces;
 use Gitlab\Api\Projects;
+use Gitlab\Api\Registry;
 use Gitlab\Api\Repositories;
 use Gitlab\Api\RepositoryFiles;
 use Gitlab\Api\ResourceIterationEvents;
@@ -336,6 +337,15 @@ public function projects(): Projects
         return new Projects($this);
     }
 
+
+    /**
+     * @return Registry
+     */
+    public function registry(): Registry
+    {
+        return new Registry($this);
+    }
+
     /**
      * @return Repositories
      */

From f5a1c75ead20e672a6ed3236b0f97f485cc95340 Mon Sep 17 00:00:00 2001
From: Philipp Kolmann <philipp@kolmann.at>
Date: Fri, 5 Jul 2024 12:17:36 +0200
Subject: [PATCH 5/6]  Fix StyleCI issues no 3

---
 src/Client.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/Client.php b/src/Client.php
index dad812a4..dae75dfd 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -337,7 +337,6 @@ public function projects(): Projects
         return new Projects($this);
     }
 
-
     /**
      * @return Registry
      */

From c64e181760132c15f89087a245b775eb9ebfdee9 Mon Sep 17 00:00:00 2001
From: Philipp Kolmann <philipp@kolmann.at>
Date: Mon, 24 Feb 2025 16:30:40 +0100
Subject: [PATCH 6/6] Update Registry.php

---
 src/Api/Registry.php | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/src/Api/Registry.php b/src/Api/Registry.php
index 2d8cc3d6..5fdc0103 100644
--- a/src/Api/Registry.php
+++ b/src/Api/Registry.php
@@ -55,7 +55,6 @@ public function repositories($repository_id, array $parameters = [])
      * @see https://docs.gitlab.com/ee/api/container_registry.html#list-registry-repository-tags
      *
      * @param int|string $project_id
-     * @param int $repository_id
      *
      * @return mixed
      */
@@ -70,8 +69,6 @@ public function repositoryTags($project_id, int $repository_id)
      * @see https://docs.gitlab.com/ee/api/container_registry.html#get-details-of-a-registry-repository-tag
      *
      * @param int|string $project_id
-     * @param int $repository_id
-     * @param string $tag_name
      *
      * @return mixed
      */
@@ -89,8 +86,6 @@ public function repositoryTag($project_id, int $repository_id, string $tag_name)
      * @see https://docs.gitlab.com/ee/api/container_registry.html#delete-a-registry-repository-tag
      *
      * @param int|string $project_id
-     * @param int $repository_id
-     * @param string $tag_name
      *
      * @return mixed
      */
@@ -108,7 +103,6 @@ public function removeRepositoryTag($project_id, int $repository_id, string $tag
      * @see https://docs.gitlab.com/ee/api/container_registry.html#delete-registry-repository-tags-in-bulk
      *
      * @param int|string $project_id
-     * @param int $repository_id
      * @param array $parameters {
      *
      *      @var string $name_regex_delete