fix hako sync
This commit is contained in:
parent
30a510cb17
commit
cd1f23527e
3 changed files with 145 additions and 11 deletions
34
commands.go
34
commands.go
|
@ -130,6 +130,7 @@ func psCommand() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncCommand() error {
|
func syncCommand() error {
|
||||||
|
// TODO use the container for the currently working directory instead of just the first one in the filter...
|
||||||
output, err := runCommandOutput("docker", "ps", "-a", "--filter", "name=hako-", "--format", "{{.Names}}")
|
output, err := runCommandOutput("docker", "ps", "-a", "--filter", "name=hako-", "--format", "{{.Names}}")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -160,44 +161,65 @@ func syncCommand() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func syncFiles(srcDir, destDir string) error {
|
func syncFiles(srcDir, destDir string) error {
|
||||||
|
gitignore, err := NewGitIgnore()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse .gitignore: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
return filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to walk %q: %v", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
relPath, err := filepath.Rel(srcDir, path)
|
relPath, err := filepath.Rel(srcDir, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to calculate relative path for %q: %v", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if relPath == ".git" || relPath == ".gitignore" {
|
// Skip .git directory and its contents
|
||||||
|
if relPath == ".git" {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip files that start with .git (like .gitignore, .gitmodules, etc.)
|
||||||
if strings.HasPrefix(filepath.Base(relPath), ".git") {
|
if strings.HasPrefix(filepath.Base(relPath), ".git") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the file should be ignored according to .gitignore
|
||||||
|
if gitignore.IsIgnored(relPath) {
|
||||||
|
if info.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
destPath := filepath.Join(destDir, relPath)
|
destPath := filepath.Join(destDir, relPath)
|
||||||
|
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
return os.MkdirAll(destPath, info.Mode())
|
return os.MkdirAll(destPath, info.Mode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("cp", path, destPath)
|
||||||
srcFile, err := os.Open(path)
|
srcFile, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
fmt.Fprintf(os.Stderr, "Error: failed to open %s: %v\n", path, err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
defer srcFile.Close()
|
defer srcFile.Close()
|
||||||
|
|
||||||
destFile, err := os.Create(destPath)
|
destFile, err := os.Create(destPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
fmt.Fprintf(os.Stderr, "Error: failed to create %s: %v\n", destPath, err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
defer destFile.Close()
|
defer destFile.Close()
|
||||||
|
|
||||||
_, err = io.Copy(destFile, srcFile)
|
_, err = io.Copy(destFile, srcFile)
|
||||||
return err
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: failed to copy %s to %s: %v\n", path, destPath, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,12 +123,12 @@ func buildLanguageImage(lang string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageExists(imageName string) bool {
|
func imageExists(imageName string) bool {
|
||||||
err := runCommand("docker", "image", "inspect", imageName)
|
err := runCommandSilent("docker", "image", "inspect", imageName)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerExists(containerName string) bool {
|
func containerExists(containerName string) bool {
|
||||||
err := runCommand("docker", "container", "inspect", containerName)
|
err := runCommandSilent("docker", "container", "inspect", containerName)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,9 +145,7 @@ func createContainer(containerName, imageName, workspaceDir string) error {
|
||||||
|
|
||||||
args := []string{"create", "--name", containerName}
|
args := []string{"create", "--name", containerName}
|
||||||
|
|
||||||
// Mount workspace
|
// No mounting - workspace is isolated and copied separately
|
||||||
args = append(args, "-v", workspaceDir+":/workspace")
|
|
||||||
|
|
||||||
args = append(args, "-it", imageName)
|
args = append(args, "-it", imageName)
|
||||||
|
|
||||||
if err := runCommand("docker", args...); err != nil {
|
if err := runCommand("docker", args...); err != nil {
|
||||||
|
|
114
utils.go
114
utils.go
|
@ -1,9 +1,11 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,6 +17,14 @@ func runCommand(name string, args ...string) error {
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runCommandSilent(name string, args ...string) error {
|
||||||
|
cmd := exec.Command(name, args...)
|
||||||
|
fmt.Fprintf(os.Stderr, "+ %s %s\n", name, strings.Join(args, " "))
|
||||||
|
cmd.Stdout = nil
|
||||||
|
cmd.Stderr = nil
|
||||||
|
return cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
func runCommandOutput(name string, args ...string) (string, error) {
|
func runCommandOutput(name string, args ...string) (string, error) {
|
||||||
cmd := exec.Command(name, args...)
|
cmd := exec.Command(name, args...)
|
||||||
fmt.Fprintf(os.Stderr, "+ %s %s\n", name, strings.Join(args, " "))
|
fmt.Fprintf(os.Stderr, "+ %s %s\n", name, strings.Join(args, " "))
|
||||||
|
@ -24,3 +34,107 @@ func runCommandOutput(name string, args ...string) (string, error) {
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(output)), nil
|
return strings.TrimSpace(string(output)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GitIgnore represents a .gitignore parser
|
||||||
|
type GitIgnore struct {
|
||||||
|
patterns []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGitIgnore creates a new GitIgnore parser by reading .gitignore file
|
||||||
|
func NewGitIgnore() (*GitIgnore, error) {
|
||||||
|
gi := &GitIgnore{}
|
||||||
|
|
||||||
|
file, err := os.Open(".gitignore")
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return gi, nil // No .gitignore file is fine
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line != "" && !strings.HasPrefix(line, "#") {
|
||||||
|
gi.patterns = append(gi.patterns, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gi, scanner.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIgnored checks if a file path should be ignored according to .gitignore patterns
|
||||||
|
func (gi *GitIgnore) IsIgnored(path string) bool {
|
||||||
|
for _, pattern := range gi.patterns {
|
||||||
|
if gi.matchPattern(pattern, path) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchPattern implements basic gitignore pattern matching
|
||||||
|
func (gi *GitIgnore) matchPattern(pattern, path string) bool {
|
||||||
|
// Handle negation patterns (starting with !)
|
||||||
|
if strings.HasPrefix(pattern, "!") {
|
||||||
|
return false // Negation patterns would require more complex logic
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert gitignore pattern to filepath.Match compatible pattern
|
||||||
|
// Handle directory patterns (ending with /)
|
||||||
|
if strings.HasSuffix(pattern, "/") {
|
||||||
|
pattern = strings.TrimSuffix(pattern, "/")
|
||||||
|
// Check if any directory component matches
|
||||||
|
parts := strings.Split(path, string(filepath.Separator))
|
||||||
|
for _, part := range parts {
|
||||||
|
if matched, _ := filepath.Match(pattern, part); matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle patterns with path separators
|
||||||
|
if strings.Contains(pattern, "/") {
|
||||||
|
// Exact path match
|
||||||
|
if matched, _ := filepath.Match(pattern, path); matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Check if pattern matches any suffix of the path
|
||||||
|
pathParts := strings.Split(path, string(filepath.Separator))
|
||||||
|
patternParts := strings.Split(pattern, "/")
|
||||||
|
|
||||||
|
if len(patternParts) <= len(pathParts) {
|
||||||
|
for i := 0; i <= len(pathParts)-len(patternParts); i++ {
|
||||||
|
match := true
|
||||||
|
for j, patternPart := range patternParts {
|
||||||
|
if matched, _ := filepath.Match(patternPart, pathParts[i+j]); !matched {
|
||||||
|
match = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple filename pattern - check against basename and any path component
|
||||||
|
basename := filepath.Base(path)
|
||||||
|
if matched, _ := filepath.Match(pattern, basename); matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if pattern matches any directory component
|
||||||
|
parts := strings.Split(path, string(filepath.Separator))
|
||||||
|
for _, part := range parts {
|
||||||
|
if matched, _ := filepath.Match(pattern, part); matched {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue