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
- cc76aca3aabb801123acf0d3c673702a7f09839b
- author
- brice · · 2026-02-08 16:08 UTC
- parent
- 17304b4
- signature
- unsigned
1 file · +5 -2
M internal/usecase/registry/service.go +5 -2
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | 6 | "crypto/sha256" | |
| 7 | 7 | "fmt" | |
| 8 | 8 | "io" | |
| 9 | + | "strings" | |
| 9 | 10 | ||
| 10 | 11 | "github.com/bnema/zerowrap" | |
| 11 | 12 | ||
| @@ -74,8 +75,10 @@ func (s *Service) PutManifest(ctx context.Context, manifest *domain.Manifest) (s | |||
| 74 | 75 | return "", log.WrapErr(err, "failed to store manifest") | |
| 75 | 76 | } | |
| 76 | 77 | ||
| 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:") { | |
| 79 | 82 | if err := s.eventBus.Publish(domain.EventImagePushed, domain.ImagePushedPayload{ | |
| 80 | 83 | Name: manifest.Name, | |
| 81 | 84 | 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 event | 78 | + | // 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, | ||