From 5df028eae19b5faa5966c32459d8d714a6dd2eb6 Mon Sep 17 00:00:00 2001 From: Rohit Ravindra Date: Tue, 7 Jul 2026 23:38:17 +0530 Subject: [PATCH 1/3] feat: seed flow for apply --- cmd/patch.go | 14 ++++++++++++++ cmd/patch_record.go | 28 ++++++++++++++++++++++++++++ internal/service/patch/patcher.go | 21 +++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 cmd/patch.go create mode 100644 cmd/patch_record.go create mode 100644 internal/service/patch/patcher.go diff --git a/cmd/patch.go b/cmd/patch.go new file mode 100644 index 0000000..1860699 --- /dev/null +++ b/cmd/patch.go @@ -0,0 +1,14 @@ +package cmd + +import ( + "github.com/spf13/cobra" +) + +var patchCmd = &cobra.Command{ + Use: "patch", + Short: "Patch(es) management root command.", +} + +func init() { + rootCmd.AddCommand(patchCmd) +} diff --git a/cmd/patch_record.go b/cmd/patch_record.go new file mode 100644 index 0000000..eab911d --- /dev/null +++ b/cmd/patch_record.go @@ -0,0 +1,28 @@ +package cmd + +import ( + "fmt" + + "github.com/RohitRavindra-dev/devlocal/internal/filesystem" + "github.com/RohitRavindra-dev/devlocal/internal/service/patch" + "github.com/spf13/cobra" +) + +var patchRecordCmd = &cobra.Command{ + Use: "record ", + Short: "Record local modifications as a DevLocal patch", + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + fmt.Println("[Started] to record a new patch") + // check for setup + return filesystem.ValidateDevLocalFilesystem() + }, + RunE: func(cmd *cobra.Command, args []string) error { + file := args[0] + return patch.Run(file) + }, +} + +func init() { + patchCmd.AddCommand(patchRecordCmd) +} diff --git a/internal/service/patch/patcher.go b/internal/service/patch/patcher.go new file mode 100644 index 0000000..74a3b10 --- /dev/null +++ b/internal/service/patch/patcher.go @@ -0,0 +1,21 @@ +package patch + +import ( + "fmt" + + "github.com/RohitRavindra-dev/devlocal/internal/filesystem" +) + +func Run(filePath string) error { + + if filePath == "" { + return fmt.Errorf("[Error] file path is empty!") + } + + if _, err := filesystem.FileExists(filePath); err != nil { + return err + } + + fmt.Println("[Completed] recording new patch into devlocal") + return nil +} From 9a2bc9544254764c0a50d66ce359981e01bf3f5e Mon Sep 17 00:00:00 2001 From: Rohit Ravindra Date: Wed, 8 Jul 2026 09:44:50 +0530 Subject: [PATCH 2/3] feat: allow dirs --- cmd/patch_record.go | 6 +++--- internal/filesystem/utils.go | 12 ++++++++++-- internal/service/patch/patcher.go | 8 ++++---- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/cmd/patch_record.go b/cmd/patch_record.go index eab911d..95888e0 100644 --- a/cmd/patch_record.go +++ b/cmd/patch_record.go @@ -9,7 +9,7 @@ import ( ) var patchRecordCmd = &cobra.Command{ - Use: "record ", + Use: "record ", Short: "Record local modifications as a DevLocal patch", Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { @@ -18,8 +18,8 @@ var patchRecordCmd = &cobra.Command{ return filesystem.ValidateDevLocalFilesystem() }, RunE: func(cmd *cobra.Command, args []string) error { - file := args[0] - return patch.Run(file) + path := args[0] + return patch.Run(path) }, } diff --git a/internal/filesystem/utils.go b/internal/filesystem/utils.go index 476bb25..d672d3b 100644 --- a/internal/filesystem/utils.go +++ b/internal/filesystem/utils.go @@ -9,7 +9,7 @@ import ( "gopkg.in/yaml.v3" ) -func FileExists(path string) (bool, error) { +func PathExists(path string) (bool, error) { info, err := os.Stat(path) if os.IsNotExist(err) { @@ -20,7 +20,15 @@ func FileExists(path string) (bool, error) { return false, err } - if info.IsDir() { + return info.IsDir(), nil +} + +func FileExists(path string) (bool, error) { + isDir, err := PathExists(path) + + if err != nil { + return false, err + } else if isDir { return false, fmt.Errorf("%s is a directory, expected a file", path) } diff --git a/internal/service/patch/patcher.go b/internal/service/patch/patcher.go index 74a3b10..dc25356 100644 --- a/internal/service/patch/patcher.go +++ b/internal/service/patch/patcher.go @@ -6,13 +6,13 @@ import ( "github.com/RohitRavindra-dev/devlocal/internal/filesystem" ) -func Run(filePath string) error { +func Run(path string) error { - if filePath == "" { - return fmt.Errorf("[Error] file path is empty!") + if path == "" { + return fmt.Errorf("[Error] path is empty!") } - if _, err := filesystem.FileExists(filePath); err != nil { + if _, err := filesystem.PathExists(path); err != nil { return err } From 9b6f1c58b0c153b585229d020fb0abd61545701d Mon Sep 17 00:00:00 2001 From: Rohit Ravindra Date: Wed, 8 Jul 2026 23:55:30 +0530 Subject: [PATCH 3/3] feat: figure out who to patch --- internal/filesystem/utils.go | 1 + internal/git/utils.go | 48 +++++++++++++++++++++++++++++++ internal/service/patch/patcher.go | 37 +++++++++++++++++++++++- todos.md | 10 ++++++- 4 files changed, 94 insertions(+), 2 deletions(-) diff --git a/internal/filesystem/utils.go b/internal/filesystem/utils.go index d672d3b..65fbeb3 100644 --- a/internal/filesystem/utils.go +++ b/internal/filesystem/utils.go @@ -9,6 +9,7 @@ import ( "gopkg.in/yaml.v3" ) +// bool: isDir err: if path doesnt exists func PathExists(path string) (bool, error) { info, err := os.Stat(path) diff --git a/internal/git/utils.go b/internal/git/utils.go index 6ca50c3..1715a5f 100644 --- a/internal/git/utils.go +++ b/internal/git/utils.go @@ -3,6 +3,7 @@ package git import ( "fmt" "os/exec" + "strings" ) func executeGitCommand(args []string) error { @@ -17,6 +18,18 @@ func executeGitCommand(args []string) error { return nil } +// TODO make this the only git command util +func executeGitCommandWithOutput(args []string) (string, error) { + cmd := exec.Command("git", args...) + + out, err := cmd.CombinedOutput() + if err != nil { + return string(out), fmt.Errorf("failed to run git %v: %w\n%s", args, err, out) + } + + return string(out), nil +} + func isFileTracked(file string) bool { trackCheckArgs := []string{ "ls-files", @@ -28,3 +41,38 @@ func isFileTracked(file string) bool { return err == nil } + +func FileHasChanges(filePath string) (bool, error) { + out, err := executeGitCommandWithOutput([]string{ + "diff", + "--name-only", + "--", + filePath, + }) + if err != nil { + return false, err + } + + return strings.TrimSpace(out) != "", nil +} + +func ListFilesWithChangesInDir(dirpath string) ([]string, error) { + filesWithChanges, err := executeGitCommandWithOutput([]string{ + "diff", + "--name-only", + "--", + dirpath, + }) + + if err != nil { + return nil, err + } + + filesWithChanges = strings.TrimSpace(filesWithChanges) + + if filesWithChanges == "" { + return []string{}, nil + } + + return strings.Split(filesWithChanges, "\n"), nil +} diff --git a/internal/service/patch/patcher.go b/internal/service/patch/patcher.go index dc25356..2dee435 100644 --- a/internal/service/patch/patcher.go +++ b/internal/service/patch/patcher.go @@ -2,20 +2,55 @@ package patch import ( "fmt" + "strings" "github.com/RohitRavindra-dev/devlocal/internal/filesystem" + "github.com/RohitRavindra-dev/devlocal/internal/git" ) +func listFilesToPatch(path string, isDir bool) ([]string, error) { + + filesToPatch := []string{} + + if isDir { + filesWithChanges, err := git.ListFilesWithChangesInDir(path) + if err != nil { + return nil, err + } + filesToPatch = append(filesToPatch, filesWithChanges...) + } else { + hasChanges, err := git.FileHasChanges(path) + if err != nil { + return nil, err + } + if hasChanges { + filesToPatch = append(filesToPatch, path) + } + } + + return filesToPatch, nil + +} + func Run(path string) error { if path == "" { return fmt.Errorf("[Error] path is empty!") } - if _, err := filesystem.PathExists(path); err != nil { + isDir, err := filesystem.PathExists(path) + if err != nil { return err } + filesToPatch, err := listFilesToPatch(path, isDir) + if err != nil { + return err + } + if len(filesToPatch) == 0 { + return fmt.Errorf("[Error] no files with changes at indicated path: %s", path) + } + fmt.Printf("[Started] applying patch to files: \n\t\t- %s\n", strings.Join(filesToPatch, "\n\t\t- ")) fmt.Println("[Completed] recording new patch into devlocal") return nil } diff --git a/todos.md b/todos.md index 5a2e981..d71d6fa 100644 --- a/todos.md +++ b/todos.md @@ -5,4 +5,12 @@ - status - conflicts - overlook -- config \ No newline at end of file +- config + + +2. record + 1. we validate the path is not empty string + 2. we validate the file is valid + 3. we validate that the file has changes else an empty patch is rejected + 4. we check if a patch exists for that file already, if yes defer/todo + 5. else we create a patch \ No newline at end of file