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..95888e0 --- /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 { + path := args[0] + return patch.Run(path) + }, +} + +func init() { + patchCmd.AddCommand(patchRecordCmd) +} diff --git a/internal/filesystem/utils.go b/internal/filesystem/utils.go index 476bb25..65fbeb3 100644 --- a/internal/filesystem/utils.go +++ b/internal/filesystem/utils.go @@ -9,7 +9,8 @@ import ( "gopkg.in/yaml.v3" ) -func FileExists(path string) (bool, error) { +// bool: isDir err: if path doesnt exists +func PathExists(path string) (bool, error) { info, err := os.Stat(path) if os.IsNotExist(err) { @@ -20,7 +21,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/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 new file mode 100644 index 0000000..2dee435 --- /dev/null +++ b/internal/service/patch/patcher.go @@ -0,0 +1,56 @@ +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!") + } + + 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