detached c15a0b8

fix(registry): skip image.pushed event for digest-only manifest PUTs

A docker push sends manifests by both digest (sha256:...) and tag.
Publishing image.pushed for every PUT caused duplicate deploy triggers,
leading to a deploy loop where the second deploy found an orphaned
container from the first and immediately retried.

Only fire the event for tag references so each push triggers exactly
one deploy.
commit
c15a0b8d736e4c65063f83fc580648422d1b5d2a
author
brice · · 2026-02-08 16:08 UTC
parent
83fbdd9
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +5 -2
M internal/usecase/registry/service.go +5 -2
@@ -6,6 +6,7 @@ import (
66 "crypto/sha256"
77 "fmt"
88 "io"
9+ "strings"
910
1011 "github.com/bnema/zerowrap"
1112
@@ -74,8 +75,10 @@ func (s *Service) PutManifest(ctx context.Context, manifest *domain.Manifest) (s
7475 return "", log.WrapErr(err, "failed to store manifest")
7576 }
7677
77- // Publish image pushed event
78- if s.eventBus != nil {
78+ // Publish image pushed event only for tag references (not digests).
79+ // A docker push sends manifests by both digest and tag; firing only on
80+ // tag prevents duplicate deploy triggers for the same push.
81+ if s.eventBus != nil && !strings.HasPrefix(manifest.Reference, "sha256:") {
7982 if err := s.eventBus.Publish(domain.EventImagePushed, domain.ImagePushedPayload{
8083 Name: manifest.Name,
8184 Reference: manifest.Reference,
@@ -6,6 +6,7 @@ import (
6 "crypto/sha256"6 "crypto/sha256"
7 "fmt"7 "fmt"
8 "io"8 "io"
9+ "strings"
9 10
10 "github.com/bnema/zerowrap"11 "github.com/bnema/zerowrap"
11 12
@@ -74,8 +75,10 @@ func (s *Service) PutManifest(ctx context.Context, manifest *domain.Manifest) (s
74 return "", log.WrapErr(err, "failed to store manifest")75 return "", log.WrapErr(err, "failed to store manifest")
75 }76 }
76 77
77- // Publish image pushed event78+ // Publish image pushed event only for tag references (not digests).
78- if s.eventBus != nil {79+ // A docker push sends manifests by both digest and tag; firing only on
80+ // tag prevents duplicate deploy triggers for the same push.
81+ if s.eventBus != nil && !strings.HasPrefix(manifest.Reference, "sha256:") {
79 if err := s.eventBus.Publish(domain.EventImagePushed, domain.ImagePushedPayload{82 if err := s.eventBus.Publish(domain.EventImagePushed, domain.ImagePushedPayload{
80 Name: manifest.Name,83 Name: manifest.Name,
81 Reference: manifest.Reference,84 Reference: manifest.Reference,

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