Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/Yakwilik/go-yamlvalidator v0.1.0 h1:mj+HF3jIUKhPwFp1izfKWB0FUAn32C0Vxp/g7jQplDs=
github.com/Yakwilik/go-yamlvalidator v0.1.0/go.mod h1:/2WJvPxGxWLfbdKaKEPzYkiuFjLuvCrqsMb2NdQSdWI=
github.com/Yakwilik/go-yamlvalidator v0.2.1 h1:9j9pj6pXf5KOgxKzoxhxaN2/Ed4ummIoDtOGj69TBK0=
github.com/Yakwilik/go-yamlvalidator v0.2.1/go.mod h1:/2WJvPxGxWLfbdKaKEPzYkiuFjLuvCrqsMb2NdQSdWI=
github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY=
Expand Down
36 changes: 31 additions & 5 deletions internal/adapters/go_git/get_dir_walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,49 @@ package go_git
import (
"errors"
"fmt"
pathpkg "path"
"path/filepath"

"github.com/go-git/go-git/v5"
gogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"

"github.com/easyp-tech/easyp/internal/core"
"github.com/easyp-tech/easyp/internal/fs/go_git"
)

func (g *GoGit) GetDirWalker(workingDir, gitRef, path string) (core.DirWalker, error) {
repository, err := git.PlainOpen(workingDir)
// Open the repository by searching upward from workingDir, so --root can be
// any subdirectory of the git repository (not only the config directory).
repository, err := gogit.PlainOpenWithOptions(workingDir, &gogit.PlainOpenOptions{
DetectDotGit: true,
})
if err != nil {
if errors.Is(err, git.ErrRepositoryNotExists) {
if errors.Is(err, gogit.ErrRepositoryNotExists) {
return nil, core.ErrRepositoryDoesNotExist
}

return nil, fmt.Errorf("git.PlainOpen: %w", err)
return nil, fmt.Errorf("git.PlainOpenWithOptions: %w", err)
}

// Determine the repo root so we can compute the correct git-tree path.
wt, err := repository.Worktree()
if err != nil {
return nil, fmt.Errorf("repository.Worktree: %w", err)
}
repoRoot := wt.Filesystem.Root()

// Compute the path from the repo root to workingDir.
rel, err := filepath.Rel(repoRoot, workingDir)
if err != nil {
return nil, fmt.Errorf("filepath.Rel: %w", err)
}
if !filepath.IsLocal(rel) {
return nil, fmt.Errorf("%w: %q is outside the git repository %q", core.ErrRootOutsideProject, workingDir, repoRoot)
}

relSlash := filepath.ToSlash(rel)
gitPath := pathpkg.Join(relSlash, filepath.ToSlash(path))

refName := plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", gitRef))

refAgainst, err := repository.Reference(refName, false)
Expand All @@ -37,6 +63,6 @@ func (g *GoGit) GetDirWalker(workingDir, gitRef, path string) (core.DirWalker, e
return nil, fmt.Errorf("commitAgainst.Tree: %w", err)
}

gitTreeWalker := go_git.NewGitTreeWalker(treeAgainst, path)
gitTreeWalker := go_git.NewGitTreeWalker(treeAgainst, relSlash, gitPath)
return gitTreeWalker, nil
}
23 changes: 17 additions & 6 deletions internal/api/breaking_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ var (
Value: "master",
}

flagBreakingCheckRoot = &cli.StringFlag{
Name: "root",
Usage: "set root directory for file search (default: current working directory)",
Required: false,
HasBeenSet: false,
Value: "",
Aliases: []string{"r"},
Comment on lines +32 to +38
}

ErrBreakingCheckIssue = errors.New("has breaking check issue")
)

Expand All @@ -49,6 +58,7 @@ func (b BreakingCheck) Command() *cli.Command {
Flags: []cli.Flag{
flagLintDirectoryPath,
flagAgainstBranchName,
flagBreakingCheckRoot,
},
SkipFlagParsing: false,
HideHelp: false,
Expand Down Expand Up @@ -86,12 +96,12 @@ func (b BreakingCheck) Action(ctx *cli.Context) error {
}

func (b BreakingCheck) action(ctx *cli.Context, log logger.Logger) error {
workingDir, err := os.Getwd()
configPath, projectRoot, breakingCheckRoot, err := resolveRoots(ctx, flagBreakingCheckRoot.Name)
if err != nil {
return fmt.Errorf("os.Getwd: %w", err)
return fmt.Errorf("resolveRoots: %w", err)
}

cfg, err := config.New(ctx.Context, ctx.String(flags.Config.Name))
cfg, err := config.New(ctx.Context, configPath)
if err != nil {
return fmt.Errorf("config.New: %w", err)
}
Expand All @@ -102,13 +112,14 @@ func (b BreakingCheck) action(ctx *cli.Context, log logger.Logger) error {
cfg.BreakingCheck.AgainstGitRef = against
}

dirWalker := fs.NewFSWalker(workingDir, ".")
app, err := buildCore(ctx.Context, log, *cfg, dirWalker)
// Walker for Core (lockfile etc) - strictly based on project root
projectWalker := fs.NewFSWalker(projectRoot, ".")
app, err := buildCore(ctx.Context, log, *cfg, projectWalker)
if err != nil {
return fmt.Errorf("buildCore: %w", err)
}

issues, err := app.BreakingCheck(ctx.Context, workingDir, path)
issues, err := app.BreakingCheck(ctx.Context, projectRoot, breakingCheckRoot, path)
if err != nil {
return fmt.Errorf("app.BreakingCheck: %w", err)
}
Expand Down
21 changes: 19 additions & 2 deletions internal/core/breaking_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package core
import (
"context"
"fmt"
"log/slog"
"path/filepath"

"github.com/yoheimuta/go-protoparser/v4/interpret/unordered"
Expand All @@ -12,21 +13,35 @@ import (
"github.com/easyp-tech/easyp/internal/fs/fs"
)

var ErrRootOutsideProject = fmt.Errorf("breaking check root must be inside the git repository")

type BreakingCheckConfig struct {
// branch name to compare with
AgainstGitRef string
// dirs should be ignored
IgnoreDirs []string
}

func (c *Core) BreakingCheck(ctx context.Context, workingDir, path string) ([]IssueInfo, error) {
fsWalker := fs.NewFSWalker(workingDir, path)
func (c *Core) BreakingCheck(ctx context.Context, projectRoot, workingDir, path string) ([]IssueInfo, error) {
c.logger.Debug(
ctx, "Paths for breaking check",
slog.String("projectRoot", projectRoot),
slog.String("workingDir", workingDir),
slog.String("path", path),
)

if err := c.Download(ctx); err != nil {
return nil, fmt.Errorf("c.Download: %w", err)
}

// read current state
fsWalker := fs.NewFSWalker(workingDir, path)
currentProtoFiles, err := c.readProtoFiles(ctx, fsWalker)
if err != nil {
return nil, fmt.Errorf("c.readCurrentProtoFiles: %w", err)
}

// read from ref branch
againstFSWalker, err := c.currentProjectGitWalker.GetDirWalker(
workingDir, c.breakingCheckConfig.AgainstGitRef, path,
)
Expand All @@ -38,6 +53,8 @@ func (c *Core) BreakingCheck(ctx context.Context, workingDir, path string) ([]Is
return nil, fmt.Errorf("c.readAgainstProtoFiles: %w", err)
}

// ---

currentProtoData, err := collect(currentProtoFiles)
if err != nil {
return nil, fmt.Errorf("collect(current): %w", err)
Expand Down
33 changes: 33 additions & 0 deletions internal/fs/go_git/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,35 @@ package go_git

import (
"errors"
"fmt"
"io"
"path"
"path/filepath"
"strings"

"github.com/go-git/go-git/v5/plumbing/object"
)

type GitTreeDiskAdapter struct {
*object.Tree
root string
}

func (a *GitTreeDiskAdapter) Open(name string) (io.ReadCloser, error) {
if a.root != "" {
// Reject non-local import paths (e.g., those containing ".." or absolute paths)
if !isLocalPath(name) {
return nil, fmt.Errorf("non-local import path: %q", name)
}
// Prefer root-prefixed lookup to match the on-disk walker behaviour
withRoot := path.Join(a.root, name)
gitFile, err := a.File(withRoot)
if err == nil {
return gitFile.Reader()
}
}

// Fall back to unrooted lookup (e.g., caller already passed a fully-qualified path)
gitFile, err := a.File(name)
if err != nil {
return nil, err
Expand All @@ -20,6 +39,20 @@ func (a *GitTreeDiskAdapter) Open(name string) (io.ReadCloser, error) {
return gitFile.Reader()
}

// isLocalPath reports whether p does not escape its base directory
// (no ".." components, not an absolute path).
func isLocalPath(p string) bool {
if filepath.IsAbs(p) {
return false
}
for _, segment := range strings.Split(p, "/") {
if segment == ".." {
return false
}
}
return true
}

func (a *GitTreeDiskAdapter) Create(name string) (io.WriteCloser, error) {
return nil, errors.New("not implemented")
}
Expand Down
4 changes: 2 additions & 2 deletions internal/fs/go_git/dir_walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"github.com/easyp-tech/easyp/internal/core/path_helpers"
)

func NewGitTreeWalker(tree *object.Tree, path string) *GitTreeWalker {
func NewGitTreeWalker(tree *object.Tree, root, path string) *GitTreeWalker {
return &GitTreeWalker{
GitTreeDiskAdapter: &GitTreeDiskAdapter{tree},
GitTreeDiskAdapter: &GitTreeDiskAdapter{tree, root},
tree: tree,
path: path,
}
Expand Down
Loading