@@ -38,14 +38,15 @@ var ErrNotFound = errors.New("repo not found")
3838// Fields with empty defaults (Branch, Path, PollInterval) are filled
3939// in by the caller using the same defaults the `git_repos` table uses.
4040type CreateParams struct {
41- Name string
42- URL string
43- Branch string
44- Path string
45- PollInterval string
46- Credential string
47- AutoApply bool
48- Prune bool
41+ Name string
42+ URL string
43+ Branch string
44+ Path string
45+ PollInterval string
46+ Credential string
47+ AutoApply bool
48+ Prune bool
49+ WebhookSecret string // empty → NULL (no HMAC verification)
4950}
5051
5152// Create inserts a new repo row and emits a repo.added audit event.
@@ -68,27 +69,38 @@ func (s *Store) Create(ctx context.Context, p CreateParams) (*Repo, error) {
6869
6970 teamID := auth .TeamIDFromContext (ctx )
7071
72+ var webhookSecret any
73+ if p .WebhookSecret != "" {
74+ webhookSecret = p .WebhookSecret
75+ } else {
76+ webhookSecret = nil
77+ }
78+
7179 tx , err := s .DB .BeginTx (ctx , nil )
7280 if err != nil {
7381 return nil , fmt .Errorf ("starting transaction: %w" , err )
7482 }
7583 defer tx .Rollback () //nolint:errcheck
7684
7785 var r Repo
86+ var returnedWebhookSecret sql.NullString
7887 err = tx .QueryRowContext (ctx ,
7988 `INSERT INTO git_repos
80- (team_id, name, url, branch, path, poll_interval, credential, auto_apply, prune)
81- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
89+ (team_id, name, url, branch, path, poll_interval, credential, auto_apply, prune, webhook_secret )
90+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10 )
8291 RETURNING id, name, url, branch, path, poll_interval, credential,
83- auto_apply, prune, enabled, created_at, updated_at` ,
92+ auto_apply, prune, enabled, webhook_secret, created_at, updated_at` ,
8493 teamID , p .Name , p .URL , p .Branch , p .Path , p .PollInterval , p .Credential ,
85- p .AutoApply , p .Prune ,
94+ p .AutoApply , p .Prune , webhookSecret ,
8695 ).Scan (& r .ID , & r .Name , & r .URL , & r .Branch , & r .Path , & r .PollInterval ,
87- & r .Credential , & r .AutoApply , & r .Prune , & r .Enabled ,
96+ & r .Credential , & r .AutoApply , & r .Prune , & r .Enabled , & returnedWebhookSecret ,
8897 & r .CreatedAt , & r .UpdatedAt )
8998 if err != nil {
9099 return nil , fmt .Errorf ("creating repo %q: %w" , p .Name , err )
91100 }
101+ if returnedWebhookSecret .Valid {
102+ r .WebhookSecret = returnedWebhookSecret .String
103+ }
92104
93105 if err := audit .EmitTx (ctx , tx , audit.Event {
94106 Timestamp : time .Now (),
@@ -155,13 +167,14 @@ func (s *Store) List(ctx context.Context) ([]Repo, error) {
155167// intentionally omitted — changing either requires delete + recreate so
156168// that audit history clearly reflects the identity change.
157169type UpdateParams struct {
158- Branch string
159- Path string
160- PollInterval string
161- Credential string
162- AutoApply bool
163- Prune bool
164- Enabled bool
170+ Branch string
171+ Path string
172+ PollInterval string
173+ Credential string
174+ AutoApply bool
175+ Prune bool
176+ Enabled bool
177+ WebhookSecret string // empty → NULL (no HMAC verification)
165178}
166179
167180// Update replaces the mutable fields of a repo by name and emits a
@@ -176,31 +189,43 @@ func (s *Store) Update(ctx context.Context, name string, p UpdateParams) (*Repo,
176189
177190 teamID := auth .TeamIDFromContext (ctx )
178191
192+ var updateWebhookSecret any
193+ if p .WebhookSecret != "" {
194+ updateWebhookSecret = p .WebhookSecret
195+ } else {
196+ updateWebhookSecret = nil
197+ }
198+
179199 tx , err := s .DB .BeginTx (ctx , nil )
180200 if err != nil {
181201 return nil , fmt .Errorf ("starting transaction: %w" , err )
182202 }
183203 defer tx .Rollback () //nolint:errcheck
184204
185205 var r Repo
206+ var updatedWebhookSecret sql.NullString
186207 err = tx .QueryRowContext (ctx ,
187208 `UPDATE git_repos
188209 SET branch = $3, path = $4, poll_interval = $5, credential = $6,
189- auto_apply = $7, prune = $8, enabled = $9, updated_at = NOW()
210+ auto_apply = $7, prune = $8, enabled = $9, webhook_secret = $10,
211+ updated_at = NOW()
190212 WHERE name = $1 AND team_id = $2
191213 RETURNING id, name, url, branch, path, poll_interval, credential,
192- auto_apply, prune, enabled, created_at, updated_at` ,
214+ auto_apply, prune, enabled, webhook_secret, created_at, updated_at` ,
193215 name , teamID , p .Branch , p .Path , p .PollInterval , p .Credential ,
194- p .AutoApply , p .Prune , p .Enabled ,
216+ p .AutoApply , p .Prune , p .Enabled , updateWebhookSecret ,
195217 ).Scan (& r .ID , & r .Name , & r .URL , & r .Branch , & r .Path , & r .PollInterval ,
196- & r .Credential , & r .AutoApply , & r .Prune , & r .Enabled ,
218+ & r .Credential , & r .AutoApply , & r .Prune , & r .Enabled , & updatedWebhookSecret ,
197219 & r .CreatedAt , & r .UpdatedAt )
198220 if errors .Is (err , sql .ErrNoRows ) {
199221 return nil , fmt .Errorf ("%w: %q" , ErrNotFound , name )
200222 }
201223 if err != nil {
202224 return nil , fmt .Errorf ("updating repo %q: %w" , name , err )
203225 }
226+ if updatedWebhookSecret .Valid {
227+ r .WebhookSecret = updatedWebhookSecret .String
228+ }
204229
205230 if err := audit .EmitTx (ctx , tx , audit.Event {
206231 Timestamp : time .Now (),
0 commit comments