mirror of
https://github.com/samuelncui/acp.git
synced 2026-08-16 10:56:01 +00:00
feat: add ignore paths
This commit is contained in:
+85
-2
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samuelncui/acp"
|
||||
@@ -32,6 +34,7 @@ type rewriteState struct {
|
||||
Root string `json:"root"`
|
||||
Pending []rewriteEntry `json:"pending,omitempty"`
|
||||
Busy []rewriteEntry `json:"busy,omitempty"`
|
||||
Missing []rewriteEntry `json:"missing,omitempty"`
|
||||
TmpFiles []string `json:"tmp_files,omitempty"`
|
||||
}
|
||||
|
||||
@@ -41,6 +44,12 @@ func main() {
|
||||
statePath := flag.String("state", ".acp-rewrite-state.json", "state storage path")
|
||||
reportPath := flag.String("report", "", "json report storage path")
|
||||
reportIndent := flag.Bool("report-indent", false, "json report with indent")
|
||||
ignorePaths := make([]string, 0, 4)
|
||||
|
||||
flag.Func("ignore", "ignore path, file or dir", func(s string) error {
|
||||
ignorePaths = append(ignorePaths, s)
|
||||
return nil
|
||||
})
|
||||
|
||||
flag.Parse()
|
||||
if flag.NArg() == 0 {
|
||||
@@ -63,6 +72,10 @@ func main() {
|
||||
logrus.Fatalf("get abs report path fail, %s", err)
|
||||
}
|
||||
}
|
||||
ignoreAbs, err := normalizeIgnorePaths(rootAbs, ignorePaths)
|
||||
if err != nil {
|
||||
logrus.Fatalf("normalize ignore path fail, %s", err)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer stop()
|
||||
@@ -87,8 +100,11 @@ func main() {
|
||||
}
|
||||
|
||||
if len(state.Pending) == 0 && len(state.Busy) == 0 {
|
||||
entries, err := scanEntries(rootAbs, stateAbs, reportAbs)
|
||||
entries, err := scanEntries(ctx, rootAbs, stateAbs, reportAbs, ignoreAbs)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
logrus.Fatalf("scan path fail, %s", err)
|
||||
}
|
||||
state.Pending = entries
|
||||
@@ -128,6 +144,22 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := os.Stat(entry.Path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
state.Missing = append(state.Missing, entry)
|
||||
if bar != nil {
|
||||
_ = bar.Add(1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
logrus.Warnf("stat fail, path= '%s', err= %s", entry.Path, err)
|
||||
state.Pending = append(state.Pending, entry)
|
||||
if bar != nil {
|
||||
_ = bar.Add(1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
busy, err := isFileBusy(entry.Path)
|
||||
if err != nil {
|
||||
logrus.Warnf("check busy fail, path= '%s', err= %s", entry.Path, err)
|
||||
@@ -170,6 +202,9 @@ func main() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
_ = os.Remove(tmpPath)
|
||||
}
|
||||
logrus.Warnf("rewrite fail, path= '%s', err= %s", entry.Path, err)
|
||||
state.Pending = append(state.Pending, entry)
|
||||
if bar != nil {
|
||||
@@ -204,14 +239,23 @@ func main() {
|
||||
printDuplicates(reportJobs)
|
||||
}
|
||||
|
||||
func scanEntries(root, statePath, reportPath string) ([]rewriteEntry, error) {
|
||||
func scanEntries(ctx context.Context, root, statePath, reportPath string, ignorePaths []string) ([]rewriteEntry, error) {
|
||||
groups := make(map[fileIdentity][]string)
|
||||
entries := make([]rewriteEntry, 0, 128)
|
||||
|
||||
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if shouldIgnorePath(p, ignorePaths) {
|
||||
if d.IsDir() {
|
||||
return fs.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if p == statePath || (reportPath != "" && p == reportPath) {
|
||||
return nil
|
||||
}
|
||||
@@ -256,6 +300,45 @@ func scanEntries(root, statePath, reportPath string) ([]rewriteEntry, error) {
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func normalizeIgnorePaths(root string, ignores []string) ([]string, error) {
|
||||
if len(ignores) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
normalized := make([]string, 0, len(ignores))
|
||||
for _, p := range ignores {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
if !filepath.IsAbs(p) {
|
||||
p = filepath.Join(root, p)
|
||||
}
|
||||
abs, err := filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
normalized = append(normalized, filepath.Clean(abs))
|
||||
}
|
||||
return normalized, nil
|
||||
}
|
||||
|
||||
func shouldIgnorePath(p string, ignores []string) bool {
|
||||
if len(ignores) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, ig := range ignores {
|
||||
if ig == "" {
|
||||
continue
|
||||
}
|
||||
if p == ig {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(p, ig+string(os.PathSeparator)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newTmpPath(path string, rnd *rand.Rand) (string, error) {
|
||||
suffix := func() string {
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -38,7 +39,7 @@ func TestScanEntriesHardlinks(t *testing.T) {
|
||||
t.Fatalf("write c: %v", err)
|
||||
}
|
||||
|
||||
entries, err := scanEntries(root, statePath, reportPath)
|
||||
entries, err := scanEntries(context.Background(), root, statePath, reportPath, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("scan entries: %v", err)
|
||||
}
|
||||
@@ -69,6 +70,52 @@ func TestScanEntriesHardlinks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanEntriesCanceled(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
root := t.TempDir()
|
||||
entries, err := scanEntries(ctx, root, "", "", nil)
|
||||
if err == nil || err != context.Canceled {
|
||||
t.Fatalf("expected canceled, got %v", err)
|
||||
}
|
||||
if len(entries) != 0 {
|
||||
t.Fatalf("entries count = %d", len(entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanEntriesIgnorePaths(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
keep := filepath.Join(root, "keep.txt")
|
||||
skipDir := filepath.Join(root, "skip")
|
||||
skipFile := filepath.Join(root, "skip.txt")
|
||||
|
||||
if err := os.MkdirAll(skipDir, 0o755); err != nil {
|
||||
t.Fatalf("mkdir skip: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(skipDir, "a.txt"), []byte("a"), 0o644); err != nil {
|
||||
t.Fatalf("write skip a: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(skipFile, []byte("skip"), 0o644); err != nil {
|
||||
t.Fatalf("write skip file: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(keep, []byte("keep"), 0o644); err != nil {
|
||||
t.Fatalf("write keep: %v", err)
|
||||
}
|
||||
|
||||
ignorePaths, err := normalizeIgnorePaths(root, []string{"skip", "skip.txt"})
|
||||
if err != nil {
|
||||
t.Fatalf("normalize ignore: %v", err)
|
||||
}
|
||||
entries, err := scanEntries(context.Background(), root, "", "", ignorePaths)
|
||||
if err != nil {
|
||||
t.Fatalf("scan entries: %v", err)
|
||||
}
|
||||
if len(entries) != 1 || entries[0].Path != keep {
|
||||
t.Fatalf("entries = %+v", entries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRelinkOnePreserveLinkAttrs(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("hardlink attrs unsupported on windows")
|
||||
|
||||
Reference in New Issue
Block a user