detached ae1a7a4

fix(registry): change DeleteBlob to return size of removed blob

commit
ae1a7a40746646e2bd550a78f3e18187e39f825c
author
brice · · 2026-03-27 18:35 UTC
parent
511705f
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +20 -9
M internal/adapters/out/filesystem/blob.go +14 -6
@@ -136,26 +136,34 @@ func (s *BlobStorage) PutBlob(digest string, data io.Reader, size int64) error {
136136 }
137137
138138 // DeleteBlob removes a blob by digest.
139-func (s *BlobStorage) DeleteBlob(digest string) error {
139+// Returns the size in bytes of the removed blob.
140+func (s *BlobStorage) DeleteBlob(digest string) (int64, error) {
140141 blobPath, err := s.getBlobPath(digest)
141142 if err != nil {
142- return fmt.Errorf("invalid path: %w", err)
143+ return 0, fmt.Errorf("invalid path: %w", err)
143144 }
144145
145- if err := os.Remove(blobPath); err != nil {
146+ info, err := os.Stat(blobPath)
147+ if err != nil {
146148 if os.IsNotExist(err) {
147- return fmt.Errorf("blob not found: %s", digest)
149+ return 0, fmt.Errorf("blob not found: %s", digest)
148150 }
149- return fmt.Errorf("failed to delete blob: %w", err)
151+ return 0, fmt.Errorf("failed to stat blob: %w", err)
152+ }
153+ size := info.Size()
154+
155+ if err := os.Remove(blobPath); err != nil {
156+ return 0, fmt.Errorf("failed to delete blob: %w", err)
150157 }
151158
152159 s.log.Info().
153160 Str(zerowrap.FieldLayer, "adapter").
154161 Str(zerowrap.FieldAdapter, "filesystem").
155162 Str("digest", digest).
163+ Int64(zerowrap.FieldSize, size).
156164 Msg("blob deleted")
157165
158- return nil
166+ return size, nil
159167 }
160168
161169 // BlobExists checks if a blob exists.
@@ -136,26 +136,34 @@ func (s *BlobStorage) PutBlob(digest string, data io.Reader, size int64) error {
136 }136 }
137 137
138 // DeleteBlob removes a blob by digest.138 // DeleteBlob removes a blob by digest.
139-func (s *BlobStorage) DeleteBlob(digest string) error {139+// Returns the size in bytes of the removed blob.
140+func (s *BlobStorage) DeleteBlob(digest string) (int64, error) {
140 blobPath, err := s.getBlobPath(digest)141 blobPath, err := s.getBlobPath(digest)
141 if err != nil {142 if err != nil {
142- return fmt.Errorf("invalid path: %w", err)143+ return 0, fmt.Errorf("invalid path: %w", err)
143 }144 }
144 145
145- if err := os.Remove(blobPath); err != nil {146+ info, err := os.Stat(blobPath)
147+ if err != nil {
146 if os.IsNotExist(err) {148 if os.IsNotExist(err) {
147- return fmt.Errorf("blob not found: %s", digest)149+ return 0, fmt.Errorf("blob not found: %s", digest)
148 }150 }
149- return fmt.Errorf("failed to delete blob: %w", err)151+ return 0, fmt.Errorf("failed to stat blob: %w", err)
152+ }
153+ size := info.Size()
154+
155+ if err := os.Remove(blobPath); err != nil {
156+ return 0, fmt.Errorf("failed to delete blob: %w", err)
150 }157 }
151 158
152 s.log.Info().159 s.log.Info().
153 Str(zerowrap.FieldLayer, "adapter").160 Str(zerowrap.FieldLayer, "adapter").
154 Str(zerowrap.FieldAdapter, "filesystem").161 Str(zerowrap.FieldAdapter, "filesystem").
155 Str("digest", digest).162 Str("digest", digest).
163+ Int64(zerowrap.FieldSize, size).
156 Msg("blob deleted")164 Msg("blob deleted")
157 165
158- return nil166+ return size, nil
159 }167 }
160 168
161 // BlobExists checks if a blob exists.169 // BlobExists checks if a blob exists.
M internal/adapters/out/filesystem/blob_test.go +4 -2
@@ -175,8 +175,9 @@ func TestBlobStorage_DeleteBlob(t *testing.T) {
175175 assert.True(t, storage.BlobExists(testDigest2))
176176
177177 // Delete blob
178- err = storage.DeleteBlob(testDigest2)
178+ size, err := storage.DeleteBlob(testDigest2)
179179 require.NoError(t, err)
180+ assert.Equal(t, int64(len(blobData)), size)
180181
181182 // Verify it's gone
182183 assert.False(t, storage.BlobExists(testDigest2))
@@ -189,9 +190,10 @@ func TestBlobStorage_DeleteBlob_NotFound(t *testing.T) {
189190 storage, err := NewBlobStorage(tmpDir, log)
190191 require.NoError(t, err)
191192
192- err = storage.DeleteBlob(testDigestMissing)
193+ size, err := storage.DeleteBlob(testDigestMissing)
193194
194195 assert.Error(t, err)
196+ assert.Equal(t, int64(0), size)
195197 assert.Contains(t, err.Error(), "blob not found")
196198 }
197199
@@ -175,8 +175,9 @@ func TestBlobStorage_DeleteBlob(t *testing.T) {
175 assert.True(t, storage.BlobExists(testDigest2))175 assert.True(t, storage.BlobExists(testDigest2))
176 176
177 // Delete blob177 // Delete blob
178- err = storage.DeleteBlob(testDigest2)178+ size, err := storage.DeleteBlob(testDigest2)
179 require.NoError(t, err)179 require.NoError(t, err)
180+ assert.Equal(t, int64(len(blobData)), size)
180 181
181 // Verify it's gone182 // Verify it's gone
182 assert.False(t, storage.BlobExists(testDigest2))183 assert.False(t, storage.BlobExists(testDigest2))
@@ -189,9 +190,10 @@ func TestBlobStorage_DeleteBlob_NotFound(t *testing.T) {
189 storage, err := NewBlobStorage(tmpDir, log)190 storage, err := NewBlobStorage(tmpDir, log)
190 require.NoError(t, err)191 require.NoError(t, err)
191 192
192- err = storage.DeleteBlob(testDigestMissing)193+ size, err := storage.DeleteBlob(testDigestMissing)
193 194
194 assert.Error(t, err)195 assert.Error(t, err)
196+ assert.Equal(t, int64(0), size)
195 assert.Contains(t, err.Error(), "blob not found")197 assert.Contains(t, err.Error(), "blob not found")
196 }198 }
197 199
M internal/boundaries/out/storage.go +2 -1
@@ -17,7 +17,8 @@ type BlobStorage interface {
1717 PutBlob(digest string, data io.Reader, size int64) error
1818
1919 // DeleteBlob removes a blob by digest.
20- DeleteBlob(digest string) error
20+ // Returns the size in bytes of the removed blob.
21+ DeleteBlob(digest string) (int64, error)
2122
2223 // BlobExists checks if a blob exists.
2324 BlobExists(digest string) bool
@@ -17,7 +17,8 @@ type BlobStorage interface {
17 PutBlob(digest string, data io.Reader, size int64) error17 PutBlob(digest string, data io.Reader, size int64) error
18 18
19 // DeleteBlob removes a blob by digest.19 // DeleteBlob removes a blob by digest.
20- DeleteBlob(digest string) error20+ // Returns the size in bytes of the removed blob.
21+ DeleteBlob(digest string) (int64, error)
21 22
22 // BlobExists checks if a blob exists.23 // BlobExists checks if a blob exists.
23 BlobExists(digest string) bool24 BlobExists(digest string) bool

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close