@@ -81,6 +81,12 @@ func (h *Handlers) settingsBranchesUpsert(w http.ResponseWriter, r *http.Request |
| 81 | 81 | } |
| 82 | 82 | dismissStale := r.PostFormValue("dismiss_stale_reviews_on_push") == "on" |
| 83 | 83 | |
| 84 | + // S24 required-status-check names: comma-separated input. Empty |
| 85 | + // list means no required checks. Names are matched verbatim |
| 86 | + // against `check_runs.name` at gate time. |
| 87 | + requiredChecks := splitCommaList(r.PostFormValue("required_status_check_names")) |
| 88 | + dismissStaleChecks := r.PostFormValue("dismiss_stale_status_checks_on_push") == "on" |
| 89 | + |
| 84 | 90 | allowed, err := resolveUsernameList(r, h, r.PostFormValue("allowed_pushers")) |
| 85 | 91 | if err != nil { |
| 86 | 92 | http.Error(w, err.Error(), http.StatusBadRequest) |
@@ -114,6 +120,13 @@ func (h *Handlers) settingsBranchesUpsert(w http.ResponseWriter, r *http.Request |
| 114 | 120 | }); err != nil { |
| 115 | 121 | h.d.Logger.WarnContext(r.Context(), "branch-protection: review settings", "error", err) |
| 116 | 122 | } |
| 123 | + if err := h.rq.UpdateBranchProtectionCheckSettings(r.Context(), h.d.Pool, reposdb.UpdateBranchProtectionCheckSettingsParams{ |
| 124 | + ID: newID, |
| 125 | + StatusChecksRequired: requiredChecks, |
| 126 | + DismissStaleStatusChecksOnPush: dismissStaleChecks, |
| 127 | + }); err != nil { |
| 128 | + h.d.Logger.WarnContext(r.Context(), "branch-protection: check settings", "error", err) |
| 129 | + } |
| 117 | 130 | _ = h.d.Audit.Record(r.Context(), h.d.Pool, viewer.ID, |
| 118 | 131 | audit.ActionRepoCreated, audit.TargetRepo, row.ID, |
| 119 | 132 | map[string]any{"branch_protection_rule_id": newID, "pattern": pattern, "action": "create"}) |
@@ -149,6 +162,13 @@ func (h *Handlers) settingsBranchesUpsert(w http.ResponseWriter, r *http.Request |
| 149 | 162 | }); err != nil { |
| 150 | 163 | h.d.Logger.WarnContext(r.Context(), "branch-protection: review settings", "error", err) |
| 151 | 164 | } |
| 165 | + if err := h.rq.UpdateBranchProtectionCheckSettings(r.Context(), h.d.Pool, reposdb.UpdateBranchProtectionCheckSettingsParams{ |
| 166 | + ID: id, |
| 167 | + StatusChecksRequired: requiredChecks, |
| 168 | + DismissStaleStatusChecksOnPush: dismissStaleChecks, |
| 169 | + }); err != nil { |
| 170 | + h.d.Logger.WarnContext(r.Context(), "branch-protection: check settings", "error", err) |
| 171 | + } |
| 152 | 172 | _ = h.d.Audit.Record(r.Context(), h.d.Pool, viewer.ID, |
| 153 | 173 | audit.ActionRepoCreated, audit.TargetRepo, row.ID, |
| 154 | 174 | map[string]any{"branch_protection_rule_id": id, "pattern": pattern, "action": "update"}) |
@@ -245,6 +265,30 @@ func (h *Handlers) settingsDefaultBranch(w http.ResponseWriter, r *http.Request) |
| 245 | 265 | http.Redirect(w, r, "/"+owner.Username+"/"+row.Name+"/settings/branches?notice=default-changed", http.StatusSeeOther) |
| 246 | 266 | } |
| 247 | 267 | |
| 268 | +// splitCommaList parses a comma-separated string into a deduplicated, |
| 269 | +// trimmed slice. Empty entries drop. Used by S24's required-check |
| 270 | +// name input on the protection-rule form. |
| 271 | +func splitCommaList(raw string) []string { |
| 272 | + raw = strings.TrimSpace(raw) |
| 273 | + if raw == "" { |
| 274 | + return []string{} |
| 275 | + } |
| 276 | + seen := map[string]struct{}{} |
| 277 | + out := []string{} |
| 278 | + for _, p := range strings.Split(raw, ",") { |
| 279 | + name := strings.TrimSpace(p) |
| 280 | + if name == "" { |
| 281 | + continue |
| 282 | + } |
| 283 | + if _, dup := seen[name]; dup { |
| 284 | + continue |
| 285 | + } |
| 286 | + seen[name] = struct{}{} |
| 287 | + out = append(out, name) |
| 288 | + } |
| 289 | + return out |
| 290 | +} |
| 291 | + |
| 248 | 292 | // resolveUsernameList parses a comma-separated username list and |
| 249 | 293 | // resolves each to a user_id. Empty input returns an empty slice |
| 250 | 294 | // (no allowed-pushers restriction). Unknown usernames produce an |