@@ -75,9 +75,11 @@ func (s *GitHubWebhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7575
7676 logger .Info ("GitHub webhook signature verified" , "event" , event )
7777
78+ deliveryID := r .Header .Get ("X-GitHub-Delivery" )
79+
7880 switch event {
7981 case "push" :
80- s .handlePush (r .Context (), w , body )
82+ s .handlePush (r .Context (), w , body , deliveryID )
8183 case "installation" :
8284 logger .Info ("Installation event received" )
8385 w .WriteHeader (http .StatusOK )
@@ -91,7 +93,7 @@ func (s *GitHubWebhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
9193// handlePush processes push events by creating a deployment record and
9294// starting the deploy workflow. Maps branches to environments: the project's
9395// default branch deploys to production, all others to preview.
94- func (s * GitHubWebhook ) handlePush (ctx context.Context , w http.ResponseWriter , body []byte ) {
96+ func (s * GitHubWebhook ) handlePush (ctx context.Context , w http.ResponseWriter , body []byte , deliveryID string ) {
9597 var payload pushPayload
9698 if err := json .Unmarshal (body , & payload ); err != nil {
9799 logger .Error ("failed to parse push payload" , "error" , err )
@@ -220,6 +222,29 @@ func (s *GitHubWebhook) handlePush(ctx context.Context, w http.ResponseWriter, b
220222 }
221223 }
222224
225+ // Deduplicate: skip if a deployment already exists for this commit + app + env
226+ if payload .After != "" {
227+ exists , existsErr := db .Query .DeploymentExistsByCommitShaAppAndEnv (ctx , s .db .RO (), db.DeploymentExistsByCommitShaAppAndEnvParams {
228+ GitCommitSha : sql.NullString {String : payload .After , Valid : true },
229+ AppID : app .ID ,
230+ EnvironmentID : env .ID ,
231+ })
232+ if existsErr != nil {
233+ logger .Error ("failed to check for existing deployment" , "error" , existsErr , "delivery_id" , deliveryID )
234+ http .Error (w , "failed to check for existing deployment" , http .StatusInternalServerError )
235+ return
236+ }
237+ if exists {
238+ logger .Info ("skipping duplicate deployment" ,
239+ "delivery_id" , deliveryID ,
240+ "commit_sha" , payload .After ,
241+ "app_id" , app .ID ,
242+ "environment_id" , env .ID ,
243+ )
244+ continue
245+ }
246+ }
247+
223248 // Create deployment record
224249 deploymentID := uid .New (uid .DeploymentPrefix )
225250 now := time .Now ().UnixMilli ()
@@ -259,6 +284,7 @@ func (s *GitHubWebhook) handlePush(ctx context.Context, w http.ResponseWriter, b
259284
260285 logger .Info ("Created deployment record" ,
261286 "deployment_id" , deploymentID ,
287+ "delivery_id" , deliveryID ,
262288 "project_id" , project .ID ,
263289 "repository" , payload .Repository .FullName ,
264290 "commit_sha" , payload .After ,
@@ -289,6 +315,7 @@ func (s *GitHubWebhook) handlePush(ctx context.Context, w http.ResponseWriter, b
289315 logger .Info ("Deployment workflow started" ,
290316 "invocation_id" , invocation .Id ,
291317 "deployment_id" , deploymentID ,
318+ "delivery_id" , deliveryID ,
292319 "project_id" , project .ID ,
293320 "repository" , payload .Repository .FullName ,
294321 "commit_sha" , payload .After ,
0 commit comments