@@ -16,6 +16,7 @@ type Detector interface {
1616 PullRequestFeatures () (PullRequestFeatures , error )
1717 RepositoryFeatures () (RepositoryFeatures , error )
1818 ProjectsV1 () gh.ProjectsV1Support
19+ SearchFeatures () (SearchFeatures , error )
1920}
2021
2122type IssueFeatures struct {
@@ -55,6 +56,43 @@ var allRepositoryFeatures = RepositoryFeatures{
5556 AutoMerge : true ,
5657}
5758
59+ type SearchFeatures struct {
60+ // AdvancedIssueSearch indicates whether the host supports advanced issue
61+ // search via API calls.
62+ AdvancedIssueSearchAPI bool
63+ // AdvancedIssueSearchOptIn indicates whether the host supports advanced
64+ // issue search as an opt-in feature, which has to be explicitly enabled in
65+ // API calls.
66+ AdvancedIssueSearchAPIOptIn bool
67+
68+ // TODO advancedSearchFuture
69+ // When advanced issue search is supported in Pull Requests tab, or in
70+ // global search we can introduce more fields to reflect the support status.
71+ }
72+
73+ // advancedIssueSearchNotSupported mimics GHE <3.18 where advanced issue search
74+ // is either not supported or is not meant to be used due to not being stable
75+ // enough (i.e. in preview).
76+ var advancedIssueSearchNotSupported = SearchFeatures {
77+ AdvancedIssueSearchAPI : false ,
78+ }
79+
80+ // advancedIssueSearchSupportedAsOptIn mimics github.com and GHE >=3.18 before
81+ // the full cleanup of temp types (i.e. ISSUE_ADVANCED search type is still
82+ // present on the schema).
83+ var advancedIssueSearchSupportedAsOptIn = SearchFeatures {
84+ AdvancedIssueSearchAPI : true ,
85+ AdvancedIssueSearchAPIOptIn : true ,
86+ }
87+
88+ // advancedIssueSearchSupportedAsOnlyBackend mimics github.com and GHE >=3.18
89+ // after the full cleanup of temp types (i.e. ISSUE_ADVANCED search type is
90+ // removed from the schema).
91+ var advancedIssueSearchSupportedAsOnlyBackend = SearchFeatures {
92+ AdvancedIssueSearchAPI : true ,
93+ AdvancedIssueSearchAPIOptIn : false ,
94+ }
95+
5896type detector struct {
5997 host string
6098 httpClient * http.Client
@@ -225,6 +263,101 @@ func (d *detector) ProjectsV1() gh.ProjectsV1Support {
225263 return gh .ProjectsV1Unsupported
226264}
227265
266+ const (
267+ // enterpriseAdvancedIssueSearchSupport is the minimum version of GHES that
268+ // supports advanced issue search and gh should use it.
269+ //
270+ // Note that advanced issue search is also available on GHES 3.17, but it's
271+ // at the preview stage and is not as mature as it is on github.com or later
272+ // GHES version.
273+ enterpriseAdvancedIssueSearchSupport = "3.18.0"
274+ )
275+
276+ func (d * detector ) SearchFeatures () (SearchFeatures , error ) {
277+ // TODO advancedIssueSearchCleanup
278+ // Once GHES 3.17 support ends, we don't need this and, probably, the entire search feature detection.
279+
280+ // Regarding the release of advanced issue search (AIS, for short), there
281+ // are three time spans/periods:
282+ //
283+ // 1. Pre-deprecation: where both legacy search and AIS are available
284+ // - GraphQL: `ISSUE` and `ISSUE_ADVANCED` search types in GraphQL behave differently
285+ // - REST: `advance_search=true` query parameter can be used to switch to AIS
286+ // 2. Deprecation: only AIS available
287+ // - GraphQL: `ISSUE` and `ISSUE_ADVANCED` search types in GraphQL behave the same (AIS)
288+ // - REST: `advance_search` query parameter has no effect (AIS)
289+ // 3. Cleanup: only AIS available
290+ // - GraphQL: `ISSUE` search type in GraphQL is the only available option (AIS)
291+ // - REST: `advance_search` query parameter has no effect (AIS)
292+ //
293+ // Since there's no schema-wise difference between pre-deprecation and
294+ // deprecation periods (i.e. `ISSUE_ADVANCED` is available during both),
295+ // we cannot figure out the exact time period. The consensus is to to use
296+ // the advanced search syntax during both periods.
297+
298+ var feature SearchFeatures
299+
300+ if ghauth .IsEnterprise (d .host ) {
301+ enterpriseAISSupportVersion , err := version .NewVersion (enterpriseAdvancedIssueSearchSupport )
302+ if err != nil {
303+ return SearchFeatures {}, err
304+ }
305+
306+ hostVersion , err := resolveEnterpriseVersion (d .httpClient , d .host )
307+ if err != nil {
308+ return SearchFeatures {}, err
309+ }
310+
311+ if hostVersion .GreaterThanOrEqual (enterpriseAISSupportVersion ) {
312+ // As of August 2025, advanced issue search is going to be available
313+ // on GHES 3.18+, including Issues tabs in repositories.
314+ feature .AdvancedIssueSearchAPI = true
315+
316+ // TODO advancedSearchFuture
317+ // When the advanced search syntax is supported in global search or
318+ // Pull Requests tabs (in repositories), we can add and enable the
319+ // corresponding fields.
320+ }
321+ } else {
322+ // As of August 2025, advanced issue search is available on github.com,
323+ // including Issues tabs in repositories.
324+ feature .AdvancedIssueSearchAPI = true
325+
326+ // TODO advancedSearchFuture
327+ // When the advanced search syntax is supported in global search or
328+ // Pull Requests tabs (in repositories), we can add and enable the
329+ // corresponding fields.
330+ }
331+
332+ if ! feature .AdvancedIssueSearchAPI {
333+ return feature , nil
334+ }
335+
336+ var searchTypeFeatureDetection struct {
337+ SearchType struct {
338+ EnumValues []struct {
339+ Name string
340+ } `graphql:"enumValues(includeDeprecated: true)"`
341+ } `graphql:"SearchType: __type(name: \"SearchType\")"`
342+ }
343+
344+ gql := api .NewClientFromHTTP (d .httpClient )
345+ if err := gql .Query (d .host , "SearchType_enumValues" , & searchTypeFeatureDetection , nil ); err != nil {
346+ return SearchFeatures {}, err
347+ }
348+
349+ for _ , enumValue := range searchTypeFeatureDetection .SearchType .EnumValues {
350+ if enumValue .Name == "ISSUE_ADVANCED" {
351+ // As long as ISSUE_ADVANCED is present on the schema, we should
352+ // explicitly opt-in when making API calls.
353+ feature .AdvancedIssueSearchAPIOptIn = true
354+ break
355+ }
356+ }
357+
358+ return feature , nil
359+ }
360+
228361func resolveEnterpriseVersion (httpClient * http.Client , host string ) (* version.Version , error ) {
229362 var metaResponse struct {
230363 InstalledVersion string `json:"installed_version"`
0 commit comments