Skip to content

Commit 4b43aee

Browse files
authored
Replace os.IsNotExist and friends with errors.Is (#4969)
## Summary - Replace all uses of `os.IsNotExist`, `os.IsExist`, and `os.IsPermission` with their `errors.Is` equivalents (`errors.Is(err, fs.ErrNotExist)`, etc.) - Add `forbidigo` linter rules to reject future use of these functions - The [Go documentation](https://pkg.go.dev/os#IsNotExist) recommends `errors.Is` because it unwraps the error chain, while the `os.Is*` functions only recognize errors returned by the `os` package directly ## Test plan - [x] `go build ./...` passes - [x] `make lint` passes with 0 issues This pull request was AI-assisted by Isaac.
1 parent c9e08e7 commit 4b43aee

32 files changed

Lines changed: 100 additions & 52 deletions

File tree

.golangci.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ linters:
6060
msg: Use slices.Sort from the standard library instead.
6161
- pattern: 'sort\.Float64s'
6262
msg: Use slices.Sort from the standard library instead.
63+
- pattern: 'os\.IsNotExist'
64+
msg: Use errors.Is(err, fs.ErrNotExist) instead.
65+
- pattern: 'os\.IsExist'
66+
msg: Use errors.Is(err, fs.ErrExist) instead.
67+
- pattern: 'os\.IsPermission'
68+
msg: Use errors.Is(err, fs.ErrPermission) instead.
6369
analyze-types: true
6470
copyloopvar:
6571
check-alias: true

acceptance/acceptance_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"flag"
1111
"fmt"
1212
"io"
13+
"io/fs"
1314
"maps"
1415
"net/http"
1516
"os"
@@ -1486,7 +1487,7 @@ func setupTerraform(t *testing.T, cwd, buildDir string, repls *testdiff.Replacem
14861487

14871488
func loadUserReplacements(t *testing.T, repls *testdiff.ReplacementsContext, tmpDir string) {
14881489
b, err := os.ReadFile(filepath.Join(tmpDir, userReplacementsFilename))
1489-
if os.IsNotExist(err) {
1490+
if errors.Is(err, fs.ErrNotExist) {
14901491
return
14911492
}
14921493
require.NoError(t, err)

acceptance/internal/config.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package internal
22

33
import (
4+
"errors"
45
"hash/fnv"
6+
"io/fs"
57
"maps"
68
"os"
79
"path/filepath"
@@ -181,7 +183,7 @@ func FindConfigs(t *testing.T, dir string) []string {
181183

182184
dir = filepath.Dir(dir)
183185

184-
if err == nil || os.IsNotExist(err) {
186+
if err == nil || errors.Is(err, fs.ErrNotExist) {
185187
continue
186188
}
187189

bundle/direct/dstate/state.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package dstate
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
8+
"io/fs"
79
"os"
810
"path/filepath"
911
"strings"
@@ -118,7 +120,7 @@ func (db *DeploymentState) Open(path string) error {
118120

119121
data, err := os.ReadFile(path)
120122
if err != nil {
121-
if os.IsNotExist(err) {
123+
if errors.Is(err, fs.ErrNotExist) {
122124
// Create new database with serial=0, will be incremented to 1 in Finalize()
123125
db.Data = NewDatabase("", 0)
124126
db.Path = path

bundle/docsgen/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package main
22

33
import (
4+
"errors"
45
"fmt"
6+
"io/fs"
57
"log"
68
"os"
79
"path"
@@ -30,7 +32,7 @@ func main() {
3032
outputDir := path.Join(docsDir, "output")
3133
templatesDir := path.Join(docsDir, "templates")
3234

33-
if _, err := os.Stat(outputDir); os.IsNotExist(err) {
35+
if _, err := os.Stat(outputDir); errors.Is(err, fs.ErrNotExist) {
3436
if err := os.MkdirAll(outputDir, 0o755); err != nil {
3537
log.Fatal(err)
3638
}

bundle/statemgmt/state_pull.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (s *StateDesc) HasRemoteTerraformState() bool {
5959
func localRead(ctx context.Context, fullPath string, engine engine.EngineType) *StateDesc {
6060
content, err := os.ReadFile(fullPath)
6161
if err != nil {
62-
if !os.IsNotExist(err) {
62+
if !errors.Is(err, fs.ErrNotExist) {
6363
logdiag.LogError(ctx, fmt.Errorf("reading %s: %w", filepath.ToSlash(fullPath), err))
6464
}
6565
return nil

cmd/apps/dev.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"errors"
77
"fmt"
8+
"io/fs"
89
"net"
910
"net/url"
1011
"os"
@@ -144,7 +145,7 @@ Examples:
144145
ctx := cmd.Context()
145146

146147
// Validate client path early (before any network calls)
147-
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
148+
if _, err := os.Stat(clientPath); errors.Is(err, fs.ErrNotExist) {
148149
return fmt.Errorf("client directory not found: %s", clientPath)
149150
}
150151

cmd/apps/import.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"context"
77
"errors"
88
"fmt"
9+
"io/fs"
910
"os"
1011
"path/filepath"
1112
"slices"
@@ -171,7 +172,7 @@ Examples:
171172
// Check if output directory already exists
172173
if _, err := os.Stat(outputDir); err == nil {
173174
return fmt.Errorf("directory '%s' already exists. Please remove it or choose a different output directory", outputDir)
174-
} else if !os.IsNotExist(err) {
175+
} else if !errors.Is(err, fs.ErrNotExist) {
175176
return fmt.Errorf("failed to check if directory exists: %w", err)
176177
}
177178

cmd/apps/init.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,10 +791,10 @@ func runCreate(ctx context.Context, opts createOptions) error {
791791

792792
// Check for generic subdirectory first (default for multi-template repos)
793793
templateDir := filepath.Join(resolvedPath, "generic")
794-
if _, err := os.Stat(templateDir); os.IsNotExist(err) {
794+
if _, err := os.Stat(templateDir); errors.Is(err, fs.ErrNotExist) {
795795
// Fall back to the provided path directly
796796
templateDir = resolvedPath
797-
if _, err := os.Stat(templateDir); os.IsNotExist(err) {
797+
if _, err := os.Stat(templateDir); errors.Is(err, fs.ErrNotExist) {
798798
return fmt.Errorf("template not found at %s (also checked %s/generic)", resolvedPath, resolvedPath)
799799
}
800800
}

cmd/apps/manifest.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"io/fs"
78
"os"
89
"path/filepath"
910

@@ -46,9 +47,9 @@ func runManifestOnly(ctx context.Context, templatePath, branch, version string)
4647
}
4748

4849
templateDir := filepath.Join(resolvedPath, "generic")
49-
if _, err := os.Stat(templateDir); os.IsNotExist(err) {
50+
if _, err := os.Stat(templateDir); errors.Is(err, fs.ErrNotExist) {
5051
templateDir = resolvedPath
51-
if _, err := os.Stat(templateDir); os.IsNotExist(err) {
52+
if _, err := os.Stat(templateDir); errors.Is(err, fs.ErrNotExist) {
5253
return fmt.Errorf("template not found at %s (also checked %s/generic)", resolvedPath, resolvedPath)
5354
}
5455
}

0 commit comments

Comments
 (0)