-
Notifications
You must be signed in to change notification settings - Fork 0
Feat: patcch record command #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <file/directory>", | ||
| 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) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
| } | ||
|
Comment on lines
+59
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Handle carriage returns in filenames on Windows.
💚 Proposed fix return strings.Split(filesWithChanges, "\n"), nil
+ // Alternative: handles \r\n on Windows
+ // return strings.Fields(filesWithChanges), nil🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Fix error string punctuation and misleading status message. Two issues:
💚 Proposed fixes- return fmt.Errorf("[Error] path is empty!")
+ return fmt.Errorf("[Error] path is empty")- 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")
+ fmt.Printf("[Started] recording patch for files: \n\t\t- %s\n", strings.Join(filesToPatch, "\n\t\t- "))
+ fmt.Println("[Completed] recording new patch into devlocal")📝 Committable suggestion
Suggested change
🧰 Tools🪛 golangci-lint (2.12.2)[error] 38-38: ST1005: error strings should not end with punctuation or newlines (staticcheck) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Use
exec.CommandContextfor timeout/cancellation support.exec.Commandwithout a context means a hunggitprocess will block the CLI indefinitely. The noctx linter also flags this. Consider accepting acontext.Context(or at minimum a timeout) so callers can cancel long-running git operations.🔒️ Proposed fix
🧰 Tools
🪛 golangci-lint (2.12.2)
[error] 23-23: os/exec.Command must not be called. use os/exec.CommandContext
(noctx)
🤖 Prompt for AI Agents
Source: Linters/SAST tools