@@ -12,6 +12,7 @@ import (
1212 "io"
1313 "strconv"
1414 "strings"
15+ "time"
1516
1617 "dev.gaijin.team/go/golib/e"
1718 "dev.gaijin.team/go/golib/fields"
@@ -31,6 +32,8 @@ const (
3132
3233const defaultGroups = "read"
3334
35+ const defaultPollInterval = "60s"
36+
3437// Env variable names for connection identity and credentials.
3538const (
3639 EnvURL = "GERRIT_URL"
@@ -39,11 +42,13 @@ const (
3942)
4043
4144var (
42- errEnvMissing = e .New ("required environment variable is not set" )
43- errUnknownGroups = e .New ("unknown capability groups" )
44- errNoGroups = e .New ("no capability groups enabled" )
45- errParseFlags = e .New ("parse flags" )
46- errInvalidBool = e .New ("invalid boolean flag value" )
45+ errEnvMissing = e .New ("required environment variable is not set" )
46+ errUnknownGroups = e .New ("unknown capability groups" )
47+ errNoGroups = e .New ("no capability groups enabled" )
48+ errParseFlags = e .New ("parse flags" )
49+ errInvalidBool = e .New ("invalid boolean flag value" )
50+ errInvalidDuration = e .New ("invalid duration flag value" )
51+ errIntervalNotPositive = e .New ("poll interval must be positive" )
4752)
4853
4954// Config is the resolved server configuration.
@@ -68,6 +73,14 @@ type Config struct {
6873 // (the default), trail-leaving operations are refused on changes not
6974 // owned by the authenticated account.
7075 AllowForeignChanges bool
76+ // ReviewNotifications enables the review-notifications feature: polling
77+ // Gerrit for activity on subscribed changes and pushing it into the
78+ // agent's session. Off by default.
79+ ReviewNotifications bool
80+ // ReviewNotificationsPollInterval is the cadence of the review
81+ // notifications poller. Always resolved and validated, even when the
82+ // feature is disabled.
83+ ReviewNotificationsPollInterval time.Duration
7184}
7285
7386// behaviorFlag is one CLI flag with its GERRIT_MCP_* env mirror. The flag
@@ -115,21 +128,37 @@ func Load(args []string, getenv func(string) string) (*Config, error) {
115128 usage : "refuse trail-leaving operations on changes not owned by the authenticated account" ,
116129 fallback : "true" ,
117130 }
131+ reviewNotifications := behaviorFlag {
132+ name : "review-notifications" ,
133+ mirror : "GERRIT_MCP_REVIEW_NOTIFICATIONS" ,
134+ usage : "poll Gerrit for activity on subscribed changes and push it into the agent's session" ,
135+ fallback : "false" ,
136+ }
137+ pollInterval := behaviorFlag {
138+ name : "review-notifications-poll-interval" ,
139+ mirror : "GERRIT_MCP_REVIEW_NOTIFICATIONS_POLL_INTERVAL" ,
140+ usage : "cadence of the review notifications poller, as a Go duration (e.g. 60s)" ,
141+ fallback : defaultPollInterval ,
142+ }
118143
119- err := resolveFlags (args , getenv , []* behaviorFlag {& groups , & includeTools , & excludeTools , & projects , & ownChanges })
144+ err := resolveFlags (args , getenv , []* behaviorFlag {
145+ & groups , & includeTools , & excludeTools , & projects , & ownChanges , & reviewNotifications , & pollInterval ,
146+ })
120147 if err != nil {
121148 return nil , err
122149 }
123150
124151 cfg := & Config {
125- GerritURL : getenv (EnvURL ),
126- Username : getenv (EnvUsername ),
127- Token : getenv (EnvToken ),
128- Groups : nil ,
129- IncludeTools : splitList (includeTools .value ),
130- ExcludeTools : splitList (excludeTools .value ),
131- Projects : splitList (projects .value ),
132- AllowForeignChanges : false ,
152+ GerritURL : getenv (EnvURL ),
153+ Username : getenv (EnvUsername ),
154+ Token : getenv (EnvToken ),
155+ Groups : nil ,
156+ IncludeTools : splitList (includeTools .value ),
157+ ExcludeTools : splitList (excludeTools .value ),
158+ Projects : splitList (projects .value ),
159+ AllowForeignChanges : false ,
160+ ReviewNotifications : false ,
161+ ReviewNotificationsPollInterval : 0 ,
133162 }
134163
135164 errs := missingEnv (cfg )
@@ -141,6 +170,20 @@ func Load(args []string, getenv func(string) string) (*Config, error) {
141170 cfg .AllowForeignChanges = ! ownOnly
142171 }
143172
173+ notifications , err := parseBool (reviewNotifications )
174+ if err != nil {
175+ errs = append (errs , err )
176+ } else {
177+ cfg .ReviewNotifications = notifications
178+ }
179+
180+ interval , err := parsePollInterval (pollInterval )
181+ if err != nil {
182+ errs = append (errs , err )
183+ } else {
184+ cfg .ReviewNotificationsPollInterval = interval
185+ }
186+
144187 parsed , err := parseGroups (groups .value )
145188 if err != nil {
146189 errs = append (errs , err )
@@ -217,6 +260,28 @@ func parseBool(bf behaviorFlag) (bool, error) {
217260 return v , nil
218261}
219262
263+ // parsePollInterval parses a resolved duration flag value and rejects
264+ // non-positive intervals, naming the flag and the offending value in the
265+ // error.
266+ func parsePollInterval (bf behaviorFlag ) (time.Duration , error ) {
267+ d , err := time .ParseDuration (strings .TrimSpace (bf .value ))
268+ if err != nil {
269+ return 0 , errInvalidDuration .WithFields (
270+ fields .F ("flag" , bf .name ),
271+ fields .F ("value" , bf .value ),
272+ )
273+ }
274+
275+ if d <= 0 {
276+ return 0 , errIntervalNotPositive .WithFields (
277+ fields .F ("flag" , bf .name ),
278+ fields .F ("value" , bf .value ),
279+ )
280+ }
281+
282+ return d , nil
283+ }
284+
220285// splitList splits a comma-separated list, trimming whitespace and dropping
221286// empty entries. An empty input yields nil.
222287func splitList (s string ) []string {
0 commit comments