Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
119 lines
2.8 KiB
Go
119 lines
2.8 KiB
Go
package diff
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/gobwas/glob"
|
|
"github.com/hexops/gotextdiff"
|
|
"github.com/hexops/gotextdiff/myers"
|
|
"github.com/hexops/gotextdiff/span"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
|
|
)
|
|
|
|
// computeFS computes the unified diffs between the origin and modified filesystems.
|
|
// but ignores files that match the given globs.
|
|
func computeFS(origin, modified fs.FS, ignoreGlobs ...string) ([]gotextdiff.Unified, error) {
|
|
compiledGlobs, err := compileGlobs(ignoreGlobs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
marked := make(map[string]struct{})
|
|
unified := make([]gotextdiff.Unified, 0)
|
|
err = fs.WalkDir(origin, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return errors.Errorf("failed to walk origin: %w", err)
|
|
}
|
|
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
if matchGlobs(compiledGlobs, path) {
|
|
return nil
|
|
}
|
|
|
|
marked[path] = struct{}{}
|
|
data, err := fs.ReadFile(origin, path)
|
|
if err != nil {
|
|
return errors.Errorf("failed to read file %q from origin: %w", path, err)
|
|
}
|
|
originFile := string(data)
|
|
|
|
data, err = fs.ReadFile(modified, path)
|
|
if !os.IsNotExist(err) && err != nil {
|
|
return errors.Errorf("failed to read file %q from modified: %w", path, err)
|
|
}
|
|
modifiedFile := string(data)
|
|
|
|
edits := myers.ComputeEdits(span.URIFromURI(fmt.Sprintf("file://%s", path)), originFile, modifiedFile)
|
|
if len(edits) > 0 {
|
|
unified = append(unified, gotextdiff.ToUnified(path, path, originFile, edits))
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = fs.WalkDir(modified, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return errors.Errorf("failed to walk modified: %w", err)
|
|
}
|
|
|
|
if d.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
if _, ok := marked[path]; ok {
|
|
return nil
|
|
}
|
|
|
|
if matchGlobs(compiledGlobs, path) {
|
|
return nil
|
|
}
|
|
|
|
originFile := ""
|
|
data, err := fs.ReadFile(modified, path)
|
|
if err != nil {
|
|
return errors.Errorf("failed to read file %q from modified: %w", path, err)
|
|
}
|
|
modifiedFile := string(data)
|
|
|
|
edits := myers.ComputeEdits(span.URIFromURI(fmt.Sprintf("file://%s", path)), originFile, modifiedFile)
|
|
if len(edits) > 0 {
|
|
unified = append(unified, gotextdiff.ToUnified(path, path, originFile, edits))
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return unified, nil
|
|
}
|
|
|
|
func compileGlobs(globs []string) ([]glob.Glob, error) {
|
|
var compiledGlobs []glob.Glob
|
|
for _, g := range globs {
|
|
compiledGlob, err := glob.Compile(g, filepath.Separator)
|
|
if err != nil {
|
|
return nil, errors.Errorf("failed to compile glob %q: %w", g, err)
|
|
}
|
|
compiledGlobs = append(compiledGlobs, compiledGlob)
|
|
}
|
|
return compiledGlobs, nil
|
|
}
|
|
|
|
func matchGlobs(globs []glob.Glob, path string) bool {
|
|
for _, g := range globs {
|
|
if g.Match(path) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|