From 451cfa8e51b084edb5d2682706c534009a4525c5 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 31 Oct 2025 10:22:48 +0000 Subject: [PATCH 1/6] chore: migrate Golangci-lint config file to v2 Most of it is done by running the official migration tool: golangci-lint migrate and then some polishing. Signed-off-by: Babak K. Shandiz --- .golangci.yml | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2d5cc27..d7b4d75 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,11 +1,25 @@ +version: "2" linters: enable: - - gofmt - - godot - -linters-settings: - godot: - # comments to be checked: `declarations`, `toplevel`, or `all` - scope: declarations - # check that each sentence starts with a capital letter - capital: true + - godot + settings: + godot: + # comments to be checked: `declarations`, `toplevel`, or `all` + scope: declarations + # check that each sentence starts with a capital letter + capital: true + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling +formatters: + enable: + - gofmt + exclusions: + generated: lax +issues: + max-issues-per-linter: 0 + max-same-issues: 0 From c6bd23532a9abb91c091c186d8abeda6fbae9614 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 31 Oct 2025 10:52:57 +0000 Subject: [PATCH 2/6] chore: remove redundant/deprecated `// +build` tags Signed-off-by: Babak K. Shandiz --- pkg/term/console.go | 1 - pkg/term/console_windows.go | 1 - 2 files changed, 2 deletions(-) diff --git a/pkg/term/console.go b/pkg/term/console.go index 4ff7e95..995df14 100644 --- a/pkg/term/console.go +++ b/pkg/term/console.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows package term diff --git a/pkg/term/console_windows.go b/pkg/term/console_windows.go index 55b1e42..ae5ad49 100644 --- a/pkg/term/console_windows.go +++ b/pkg/term/console_windows.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package term From ff8ebd0585f28c8142be4feb92c4e998ef001af8 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 31 Oct 2025 11:10:06 +0000 Subject: [PATCH 3/6] chore: disable `QF1008` from `staticcheck` rules Signed-off-by: Babak K. Shandiz --- .golangci.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index d7b4d75..a528b06 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -3,6 +3,25 @@ linters: enable: - godot settings: + staticcheck: + # Here, some checks are disabled (note the leading minus sign). + checks: + - all + # These are disabled by the linter's default. We need to repeat them here + # since we're overriding to disable more checks. + - -ST1000 + - -ST1003 + - -ST1016 + - -ST1020 + - -ST1021 + - -ST1022 + + # This check is to enforce "omitting embedded fields from selector + # expression". For example, `m.Kind` in favour of `m.Node.Kind` (where + # `Node` is an embedded struct field of `m`) + # + # Disabled to keep the current explicit style. + - -QF1008 godot: # comments to be checked: `declarations`, `toplevel`, or `all` scope: declarations From 32287ae0783b8894ca0e5f9c7d6215daa1f5147c Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 31 Oct 2025 11:16:40 +0000 Subject: [PATCH 4/6] refactor: lift break condition into the loop This is to fix staticcheck's error code QF1006. Signed-off-by: Babak K. Shandiz --- internal/yamlmap/yaml_map.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/yamlmap/yaml_map.go b/internal/yamlmap/yaml_map.go index a440047..d68c8e2 100644 --- a/internal/yamlmap/yaml_map.go +++ b/internal/yamlmap/yaml_map.go @@ -170,10 +170,7 @@ func (m *Map) SetModified() { func (m *Map) SetUnmodified() { i := 0 queue := []*yaml.Node{m.Node} - for { - if i > (len(queue) - 1) { - break - } + for i < len(queue) { q := queue[i] i = i + 1 if q.Kind != yaml.MappingNode { @@ -188,10 +185,7 @@ func (m *Map) SetUnmodified() { func (m *Map) IsModified() bool { i := 0 queue := []*yaml.Node{m.Node} - for { - if i > (len(queue) - 1) { - break - } + for i < len(queue) { q := queue[i] i = i + 1 if q.Kind != yaml.MappingNode { From b7798dc6fcd12434da38002e12840701a010aa5d Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 31 Oct 2025 11:33:33 +0000 Subject: [PATCH 5/6] docs: fix incorrect godoc usages Signed-off-by: Babak K. Shandiz --- gh.go | 2 +- internal/git/remote.go | 2 +- internal/git/url.go | 2 +- internal/yamlmap/yaml_map.go | 6 ++++-- pkg/api/errors.go | 4 ++-- pkg/api/graphql_client.go | 3 ++- pkg/api/http_client.go | 3 ++- pkg/api/rest_client.go | 3 ++- pkg/config/config.go | 8 ++++++++ pkg/config/errors.go | 6 +++--- pkg/jq/jq.go | 4 ++-- pkg/markdown/markdown.go | 2 +- pkg/repository/repository.go | 2 +- pkg/term/env_test.go | 2 -- 14 files changed, 30 insertions(+), 19 deletions(-) diff --git a/gh.go b/gh.go index b176780..425eaef 100644 --- a/gh.go +++ b/gh.go @@ -36,7 +36,7 @@ func ExecContext(ctx context.Context, args ...string) (stdout, stderr bytes.Buff return } -// Exec invokes a gh command in a subprocess with its stdin, stdout, and stderr streams connected to +// ExecInteractive invokes a gh command in a subprocess with its stdin, stdout, and stderr streams connected to // those of the parent process. This is suitable for running gh commands with interactive prompts. func ExecInteractive(ctx context.Context, args ...string) error { ghExe, err := Path() diff --git a/internal/git/remote.go b/internal/git/remote.go index 917440b..c969f36 100644 --- a/internal/git/remote.go +++ b/internal/git/remote.go @@ -51,7 +51,7 @@ func Remotes() (RemoteSet, error) { return remotes, nil } -// Filter remotes by given hostnames, maintains original order. +// FilterByHosts filters remotes by given hostnames, maintains original order. func (rs RemoteSet) FilterByHosts(hosts []string) RemoteSet { filtered := make(RemoteSet, 0) for _, remote := range rs { diff --git a/internal/git/url.go b/internal/git/url.go index 8e503c6..cf2bda9 100644 --- a/internal/git/url.go +++ b/internal/git/url.go @@ -64,7 +64,7 @@ func ParseURL(rawURL string) (u *url.URL, err error) { return } -// Extract GitHub repository information from a git remote URL. +// RepoInfoFromURL extracts GitHub repository information from a git remote URL. func RepoInfoFromURL(u *url.URL) (host string, owner string, name string, err error) { if u.Hostname() == "" { return "", "", "", fmt.Errorf("no hostname detected") diff --git a/internal/yamlmap/yaml_map.go b/internal/yamlmap/yaml_map.go index d68c8e2..b4725d3 100644 --- a/internal/yamlmap/yaml_map.go +++ b/internal/yamlmap/yaml_map.go @@ -149,6 +149,8 @@ func (m *Map) SetEntry(key string, value *Map) { m.AddEntry(key, value) } +// SetModified marks the map as modified. +// // Note: This is a hack to introduce the concept of modified/unmodified // on top of gopkg.in/yaml.v3. This works by setting the Value property // of a MappingNode to a specific value and then later checking if the @@ -166,7 +168,7 @@ func (m *Map) SetModified() { } } -// Traverse map using BFS to set all nodes as unmodified. +// SetUnmodified traverses the map using BFS to set all nodes as unmodified. func (m *Map) SetUnmodified() { i := 0 queue := []*yaml.Node{m.Node} @@ -181,7 +183,7 @@ func (m *Map) SetUnmodified() { } } -// Traverse map using BFS to searach for any nodes that have been modified. +// IsModified traverses the map using BFS to search for any nodes that have been modified. func (m *Map) IsModified() bool { i := 0 queue := []*yaml.Node{m.Node} diff --git a/pkg/api/errors.go b/pkg/api/errors.go index e8fb93f..aaf1642 100644 --- a/pkg/api/errors.go +++ b/pkg/api/errors.go @@ -27,7 +27,7 @@ type HTTPErrorItem struct { Resource string } -// Allow HTTPError to satisfy error interface. +// Error allows HTTPError to satisfy error interface. func (err *HTTPError) Error() string { if msgs := strings.SplitN(err.Message, "\n", 2); len(msgs) > 1 { return fmt.Sprintf("HTTP %d: %s (%s)\n%s", err.StatusCode, msgs[0], err.RequestURL, msgs[1]) @@ -55,7 +55,7 @@ type GraphQLErrorItem struct { Type string } -// Allow GraphQLError to satisfy error interface. +// Error allows GraphQLError to satisfy error interface. func (gr *GraphQLError) Error() string { errorMessages := make([]string, 0, len(gr.Errors)) for _, e := range gr.Errors { diff --git a/pkg/api/graphql_client.go b/pkg/api/graphql_client.go index 3317eef..a985f70 100644 --- a/pkg/api/graphql_client.go +++ b/pkg/api/graphql_client.go @@ -26,7 +26,8 @@ func DefaultGraphQLClient() (*GraphQLClient, error) { return NewGraphQLClient(ClientOptions{}) } -// GraphQLClient builds a client to send requests to GitHub GraphQL API endpoints. +// NewGraphQLClient builds a client to send requests to GitHub GraphQL API endpoints. +// // As part of the configuration a hostname, auth token, default set of headers, // and unix domain socket are resolved from the gh environment configuration. // These behaviors can be overridden using the opts argument. diff --git a/pkg/api/http_client.go b/pkg/api/http_client.go index 1d7c517..2195306 100644 --- a/pkg/api/http_client.go +++ b/pkg/api/http_client.go @@ -37,7 +37,8 @@ func DefaultHTTPClient() (*http.Client, error) { return NewHTTPClient(ClientOptions{}) } -// HTTPClient builds a client that can be passed to another library. +// NewHTTPClient builds a client that can be passed to another library. +// // As part of the configuration a hostname, auth token, default set of headers, // and unix domain socket are resolved from the gh environment configuration. // These behaviors can be overridden using the opts argument. In this instance diff --git a/pkg/api/rest_client.go b/pkg/api/rest_client.go index 68ad8af..097e637 100644 --- a/pkg/api/rest_client.go +++ b/pkg/api/rest_client.go @@ -22,7 +22,8 @@ func DefaultRESTClient() (*RESTClient, error) { return NewRESTClient(ClientOptions{}) } -// RESTClient builds a client to send requests to GitHub REST API endpoints. +// NewRESTClient builds a client to send requests to GitHub REST API endpoints. +// // As part of the configuration a hostname, auth token, default set of headers, // and unix domain socket are resolved from the gh environment configuration. // These behaviors can be overridden using the opts argument. diff --git a/pkg/config/config.go b/pkg/config/config.go index 0de46fe..9789a06 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -244,6 +244,8 @@ func mapFromString(str string) (*yamlmap.Map, error) { return yamlmap.Unmarshal([]byte(str)) } +// ConfigDir returns the path to the configuration directory. +// // Config path precedence: GH_CONFIG_DIR, XDG_CONFIG_HOME, AppData (windows only), HOME. func ConfigDir() string { var path string @@ -260,6 +262,8 @@ func ConfigDir() string { return path } +// StateDir returns the path to the state directory. +// // State path precedence: XDG_STATE_HOME, LocalAppData (windows only), HOME. func StateDir() string { var path string @@ -274,6 +278,8 @@ func StateDir() string { return path } +// DataDir returns the path to the data directory. +// // Data path precedence: XDG_DATA_HOME, LocalAppData (windows only), HOME. func DataDir() string { var path string @@ -288,6 +294,8 @@ func DataDir() string { return path } +// CacheDir returns the path to the cache directory. +// // Cache path precedence: XDG_CACHE_HOME, LocalAppData (windows only), HOME, legacy gh-cli-cache. func CacheDir() string { if a := os.Getenv(xdgCacheHome); a != "" { diff --git a/pkg/config/errors.go b/pkg/config/errors.go index 1aefd19..f30e9c9 100644 --- a/pkg/config/errors.go +++ b/pkg/config/errors.go @@ -10,12 +10,12 @@ type InvalidConfigFileError struct { Err error } -// Allow InvalidConfigFileError to satisfy error interface. +// Error allows InvalidConfigFileError to satisfy error interface. func (e *InvalidConfigFileError) Error() string { return fmt.Sprintf("invalid config file %s: %s", e.Path, e.Err) } -// Allow InvalidConfigFileError to be unwrapped. +// Unwrap allows InvalidConfigFileError to be unwrapped. func (e *InvalidConfigFileError) Unwrap() error { return e.Err } @@ -26,7 +26,7 @@ type KeyNotFoundError struct { Key string } -// Allow KeyNotFoundError to satisfy error interface. +// Error allows KeyNotFoundError to satisfy error interface. func (e *KeyNotFoundError) Error() string { return fmt.Sprintf("could not find key %q", e.Key) } diff --git a/pkg/jq/jq.go b/pkg/jq/jq.go index ade2308..5f19e1f 100644 --- a/pkg/jq/jq.go +++ b/pkg/jq/jq.go @@ -16,7 +16,7 @@ import ( "github.com/itchyny/gojq" ) -// Evaluate a jq expression against an input and write it to an output. +// Evaluate evaluates a jq expression against an input and write it to an output. // Any top-level scalar values produced by the jq expression are written out // directly, as raw values and not as JSON scalars, similar to how jq --raw // works. @@ -24,7 +24,7 @@ func Evaluate(input io.Reader, output io.Writer, expr string) error { return EvaluateFormatted(input, output, expr, "", false) } -// Evaluate a jq expression against an input and write it to an output, +// EvaluateFormatted evaluates a jq expression against an input and write it to an output, // optionally with indentation and colorization. Any top-level scalar values // produced by the jq expression are written out directly, as raw values and not // as JSON scalars, similar to how jq --raw works. diff --git a/pkg/markdown/markdown.go b/pkg/markdown/markdown.go index c7f589d..3c61fda 100644 --- a/pkg/markdown/markdown.go +++ b/pkg/markdown/markdown.go @@ -25,7 +25,7 @@ func WithoutIndentation() glamour.TermRendererOption { return glamour.WithStylesFromJSONBytes(overrides) } -// WithoutWrap is a rendering option that set the character limit for soft wraping the markdown rendering. +// WithWrap is a rendering option that set the character limit for soft wrapping the markdown rendering. func WithWrap(w int) glamour.TermRendererOption { return glamour.WithWordWrap(w) } diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go index 2d3400b..eb7f129 100644 --- a/pkg/repository/repository.go +++ b/pkg/repository/repository.go @@ -67,7 +67,7 @@ func Parse(s string) (Repository, error) { } } -// Parse extracts the repository information from the following +// ParseWithHost extracts the repository information from the following // string formats: "OWNER/REPO", "HOST/OWNER/REPO", and a full URL. // If the format does not specify a host, use the host provided. func ParseWithHost(s, host string) (Repository, error) { diff --git a/pkg/term/env_test.go b/pkg/term/env_test.go index 50bd3ba..1e7346b 100644 --- a/pkg/term/env_test.go +++ b/pkg/term/env_test.go @@ -1,5 +1,3 @@ -// Package term provides information about the terminal that the current process is connected to (if any), -// for example measuring the dimensions of the terminal and inspecting whether it's safe to output color. package term import ( From f6d1f605e343beb2b8d89fb0440b95a4554db3c1 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Fri, 31 Oct 2025 11:52:37 +0000 Subject: [PATCH 6/6] chore: upgrade to Golangci-lint `v2.6` Signed-off-by: Babak K. Shandiz --- .github/workflows/lint.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 66aaefa..fd74c11 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,8 +20,8 @@ jobs: go mod tidy git diff --exit-code go.mod - - name: Lint - uses: golangci/golangci-lint-action@v6 + - name: lint + uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 with: - version: v1.64 + version: v2.6.0 problem-matchers: true