diff --git a/.github/workflows/bonk.yml b/.github/workflows/bonk.yml index 626b954..1269468 100644 --- a/.github/workflows/bonk.yml +++ b/.github/workflows/bonk.yml @@ -31,6 +31,9 @@ jobs: with: go-version-file: go.mod + - name: Download Go dependencies + run: go mod download + - name: Run Bonk uses: ask-bonk/ask-bonk/github@main env: diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 9feb6c7..ab4219d 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,6 +20,11 @@ jobs: with: go-version-file: go.mod + - name: Check modernize + run: | + go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./... + git diff --exit-code + - name: Build run: go build ./cmd/artifact-fs diff --git a/bench_test.go b/bench_test.go index 643e42b..2fc361e 100644 --- a/bench_test.go +++ b/bench_test.go @@ -9,6 +9,7 @@ import ( "math" "os" "path/filepath" + "slices" "sort" "strings" "testing" @@ -293,7 +294,6 @@ func benchmarkRepo(t *testing.T, git *gitstore.Store, repo repoSpec) []timing { } results_ch := make(chan hydrateResult, len(freshBatch)) for _, n := range freshBatch { - n := n go func() { _, size, err := h.EnsureHydrated(ctx, cfg, n) results_ch <- hydrateResult{size, err} @@ -437,7 +437,7 @@ func percentiles(durs []time.Duration) (p50, p95, p99 time.Duration) { } sorted := make([]time.Duration, len(durs)) copy(sorted, durs) - sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] }) + slices.Sort(sorted) p50 = sorted[pctIndex(len(sorted), 50)] p95 = sorted[pctIndex(len(sorted), 95)] p99 = sorted[pctIndex(len(sorted), 99)] diff --git a/e2e_bench_test.go b/e2e_bench_test.go index f39bad6..5a9af8b 100644 --- a/e2e_bench_test.go +++ b/e2e_bench_test.go @@ -96,7 +96,6 @@ func TestE2EBenchmarkRepos(t *testing.T) { verboseRuns := os.Getenv("AFS_E2E_BENCH_VERBOSE") == "1" for i := 1; i <= runs; i++ { for _, repo := range repos { - repo := repo t.Run(fmt.Sprintf("%s/run-%02d", repo.name, i), func(t *testing.T) { result := runE2EBenchmarkOnce(t, repo, i, hydratorWorkers, callerWorkers) runsOut = append(runsOut, result) @@ -340,7 +339,7 @@ func hydrateColdObjects(ctx context.Context, git *gitstore.Store, cfg model.Repo } } - for i := 0; i < callerWorkers; i++ { + for range callerWorkers { wg.Add(1) go worker() } diff --git a/e2e_git_test.go b/e2e_git_test.go index 5265288..3614b85 100644 --- a/e2e_git_test.go +++ b/e2e_git_test.go @@ -382,7 +382,7 @@ func gitStatusShortMap(t *testing.T, dir string) map[string]string { if out == "" { return status } - for _, line := range strings.Split(out, "\n") { + for line := range strings.SplitSeq(out, "\n") { if line == "" { continue } @@ -464,7 +464,7 @@ func parseStatusOutput(out string) (map[string]string, error) { if out == "" { return status, nil } - for _, line := range strings.Split(out, "\n") { + for line := range strings.SplitSeq(out, "\n") { if line == "" { continue } diff --git a/e2e_test.go b/e2e_test.go index 12ea7df..a4474ff 100644 --- a/e2e_test.go +++ b/e2e_test.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "strings" "testing" "time" @@ -511,10 +512,8 @@ func gitArgsWithSafeDirectory(dir string, args ...string) []string { func assertContains(t *testing.T, items []string, want string) { t.Helper() - for _, item := range items { - if item == want { - return - } + if slices.Contains(items, want) { + return } t.Fatalf("expected %q in %v", want, items) } diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 9542465..11a87fa 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -28,7 +28,7 @@ func Run(ctx context.Context, args []string, stdout io.Writer, stderr io.Writer) app.Usage = "Git-backed FUSE filesystem with persistent writable overlay" app.Writer = stdout app.ErrWriter = stderr - app.Metadata = map[string]interface{}{"ctx": ctx, "root": root} + app.Metadata = map[string]any{"ctx": ctx, "root": root} app.Commands = []ucli.Command{ { diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 68d5735..d650f6a 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -483,10 +483,7 @@ func (s *Service) refreshLoop(rt *repoRuntime) { s.mu.Unlock() cancel() // Exponential backoff on failure, capped at maxBackoff - backoff = backoff * 2 - if backoff > maxBackoff { - backoff = maxBackoff - } + backoff = min(backoff*2, maxBackoff) ticker.Reset(backoff) continue } diff --git a/internal/daemon/mount_check_darwin.go b/internal/daemon/mount_check_darwin.go index 089c458..4201339 100644 --- a/internal/daemon/mount_check_darwin.go +++ b/internal/daemon/mount_check_darwin.go @@ -14,7 +14,7 @@ func isMounted(path string) bool { if err != nil { return false } - for _, line := range strings.Split(string(out), "\n") { + for line := range strings.SplitSeq(string(out), "\n") { if strings.Contains(line, " on "+path+" (") || strings.Contains(line, " on /private"+path+" (") { return true } diff --git a/internal/daemon/mount_check_linux.go b/internal/daemon/mount_check_linux.go index 9b43f8f..fb0d133 100644 --- a/internal/daemon/mount_check_linux.go +++ b/internal/daemon/mount_check_linux.go @@ -23,7 +23,7 @@ func isMounted(path string) bool { // matchProcMounts checks /proc/mounts where each line is: // func matchProcMounts(output, path string) bool { - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { fields := strings.Fields(line) if len(fields) >= 2 && fields[1] == path { return true @@ -35,7 +35,7 @@ func matchProcMounts(output, path string) bool { // matchMountOutput checks mount(8) output where each line is: // on type () func matchMountOutput(output, path string) bool { - for _, line := range strings.Split(output, "\n") { + for line := range strings.SplitSeq(output, "\n") { if strings.Contains(line, " on "+path+" type ") { return true } diff --git a/internal/fusefs/fuse_unix.go b/internal/fusefs/fuse_unix.go index 237d3d9..3a1108e 100644 --- a/internal/fusefs/fuse_unix.go +++ b/internal/fusefs/fuse_unix.go @@ -74,7 +74,7 @@ func NewArtifactFuse(repo model.RepoConfig, resolver *Resolver, engine *Engine) repo: repo, resolver: resolver, engine: engine, - gitfileContent: []byte(fmt.Sprintf("gitdir: %s\n", repo.GitDir)), + gitfileContent: fmt.Appendf(nil, "gitdir: %s\n", repo.GitDir), inodes: make(map[fuseops.InodeID]*InodeRef), pathToInode: make(map[string]fuseops.InodeID), nextInodeID: fuseops.RootInodeID + 1, @@ -356,10 +356,7 @@ func (fs *ArtifactFuse) ReadFile(ctx context.Context, op *fuseops.ReadFileOp) er op.BytesRead = 0 return nil } - end := start + int(op.Size) - if end > len(fs.gitfileContent) { - end = len(fs.gitfileContent) - } + end := min(start+int(op.Size), len(fs.gitfileContent)) op.Data = [][]byte{fs.gitfileContent[start:end]} op.BytesRead = end - start return nil @@ -560,7 +557,7 @@ func MountRepo(repo model.RepoConfig, resolver *Resolver, engine *Engine) (Mount func TryUnmount(mountPoint string) error { var err error - for i := 0; i < 20; i++ { + for range 20 { err = fuse.Unmount(mountPoint) if err == nil { return nil diff --git a/internal/hydrator/hydrator.go b/internal/hydrator/hydrator.go index e60aff9..22a3187 100644 --- a/internal/hydrator/hydrator.go +++ b/internal/hydrator/hydrator.go @@ -81,7 +81,7 @@ func (s *Service) Start(workers int, repo model.RepoConfig) { } s.started = true s.mu.Unlock() - for i := 0; i < workers; i++ { + for range workers { go s.worker(repo) } } diff --git a/internal/hydrator/hydrator_test.go b/internal/hydrator/hydrator_test.go index 62d0b78..b44b6e0 100644 --- a/internal/hydrator/hydrator_test.go +++ b/internal/hydrator/hydrator_test.go @@ -176,7 +176,7 @@ func TestEnsureHydratedVerifiesUnknownCacheHitOnce(t *testing.T) { const readers = 8 errCh := make(chan error, readers) var wg sync.WaitGroup - for i := 0; i < readers; i++ { + for range readers { wg.Add(1) go func() { defer wg.Done() diff --git a/internal/overlay/store.go b/internal/overlay/store.go index 7c06d48..6c91e23 100644 --- a/internal/overlay/store.go +++ b/internal/overlay/store.go @@ -8,6 +8,7 @@ import ( "io" "os" "path/filepath" + "slices" "time" "github.com/cloudflare/artifact-fs/internal/meta" @@ -294,9 +295,9 @@ func (s *Store) Reconcile(ctx context.Context, baseLookup func(path string) (mod // before commit and the transaction rolled back, DB rows would reference // non-existent files. Reverse order so children are removed before parents // (os.Remove fails on non-empty directories). - for i := len(toRemove) - 1; i >= 0; i-- { - if toRemove[i].BackingPath != "" { - _ = os.Remove(toRemove[i].BackingPath) + for _, v := range slices.Backward(toRemove) { + if v.BackingPath != "" { + _ = os.Remove(v.BackingPath) } } return nil diff --git a/internal/watcher/watcher_test.go b/internal/watcher/watcher_test.go index 5e5840a..6a07d93 100644 --- a/internal/watcher/watcher_test.go +++ b/internal/watcher/watcher_test.go @@ -1,7 +1,6 @@ package watcher import ( - "context" "os" "path/filepath" "testing" @@ -27,8 +26,7 @@ func TestWatchTriggersOnHeadChange(t *testing.T) { } p := New(5 * time.Millisecond) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() changed := make(chan struct{}, 1) go p.Watch(ctx, gitDir, func() { select { @@ -65,8 +63,7 @@ func TestWatchTriggersOnCurrentBranchAdvance(t *testing.T) { } p := New(5 * time.Millisecond) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() changed := make(chan struct{}, 1) go p.Watch(ctx, gitDir, func() { select { @@ -202,8 +199,7 @@ func TestWatchIgnoresIndexOnlyChanges(t *testing.T) { } p := New(5 * time.Millisecond) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() changed := make(chan struct{}, 1) go p.Watch(ctx, gitDir, func() { select {