package gitops import ( "encoding/json" "log" "time" "github.com/forgeo/forgebucket/internal/events" "github.com/forgeo/forgebucket/internal/models" ) // TriggerSync creates a Deployment record in "pending" state and fires // deployment.started — the same path as a manually-triggered deployment. // GitOps is just the trigger; actual execution is handled externally (or via CI). func (c *Controller) TriggerSync(cfg models.GitOpsConfig, desiredSHA string) { var env models.Environment if found, _ := c.db.ID(cfg.EnvID).Get(&env); !found { log.Printf("gitops: sync env %d not found", cfg.EnvID) return } now := time.Now().UTC() deploy := &models.Deployment{ EnvID: cfg.EnvID, RepoID: cfg.RepoID, SHA: desiredSHA, Ref: "refs/heads/" + cfg.Branch, Status: models.DeployStatusPending, TriggeredBy: "gitops", Description: "GitOps auto-sync", StartedAt: &now, } if _, err := c.db.Insert(deploy); err != nil { log.Printf("gitops: create deployment: %v", err) return } cfg.SyncStatus = "syncing" c.db.ID(cfg.ID).Cols("sync_status").Update(&cfg) //nolint:errcheck c.bus.Publish(events.SubjectDeploymentStarted, events.DeploymentEvent{ //nolint:errcheck DeploymentID: deploy.ID, EnvID: env.ID, EnvName: env.Name, RepoID: deploy.RepoID, SHA: deploy.SHA, Ref: deploy.Ref, Status: string(deploy.Status), TriggeredBy: deploy.TriggeredBy, }) log.Printf("gitops: triggered sync deploy %d for env %d (%s)", deploy.ID, cfg.EnvID, desiredSHA[:7]) } // handleDeploymentSucceeded is called when any deployment.succeeded event fires. // If the deployment was GitOps-triggered, it marks the config as synced. func (c *Controller) handleDeploymentSucceeded(data []byte) { var evt events.DeploymentEvent if err := json.Unmarshal(data, &evt); err != nil { return } // Only act on deployments triggered by gitops. if evt.TriggeredBy != "gitops" { // Still update ActualSHA and resolve drift if this env has a GitOps config — // manual deployments also advance the state. var cfg models.GitOpsConfig if found, _ := c.db.Where("env_id = ?", evt.EnvID).Get(&cfg); found { markSynced(c.db, evt.EnvID, evt.SHA) log.Printf("gitops: env %d synced via manual deploy (%s)", evt.EnvID, sha7(evt.SHA)) } return } markSynced(c.db, evt.EnvID, evt.SHA) log.Printf("gitops: env %d synced (%s)", evt.EnvID, sha7(evt.SHA)) } // handleDeploymentFailed is called when deployment.failed fires. // If the deployment was GitOps-triggered, it reverts SyncStatus back to drifted. func (c *Controller) handleDeploymentFailed(data []byte) { var evt events.DeploymentEvent if err := json.Unmarshal(data, &evt); err != nil { return } if evt.TriggeredBy != "gitops" { return } var cfg models.GitOpsConfig if found, _ := c.db.Where("env_id = ?", evt.EnvID).Get(&cfg); !found { return } cfg.SyncStatus = "drifted" c.db.ID(cfg.ID).Cols("sync_status").Update(&cfg) //nolint:errcheck log.Printf("gitops: env %d sync failed — reverting to drifted", evt.EnvID) }