From c03372f76ec6c53a89a5caaec7b0fc0e36275a89 Mon Sep 17 00:00:00 2001 From: dengsgo Date: Mon, 10 Sep 2018 16:23:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=201.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + build-darwin.bat | 8 ++ build-linux.bat | 8 ++ build-windows.bat | 8 ++ fileboy.go | 240 ++++++++++++++++++++++++++++++++++++++++++++++ filegirl.go | 17 ++++ raw.go | 72 ++++++++++++++ util.go | 66 +++++++++++++ 8 files changed, 423 insertions(+) create mode 100644 .gitignore create mode 100644 build-darwin.bat create mode 100644 build-linux.bat create mode 100644 build-windows.bat create mode 100644 fileboy.go create mode 100644 filegirl.go create mode 100644 raw.go create mode 100644 util.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..725e205 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/* +fileboy +fileboy.exe +filegirl.yaml diff --git a/build-darwin.bat b/build-darwin.bat new file mode 100644 index 0000000..62301b8 --- /dev/null +++ b/build-darwin.bat @@ -0,0 +1,8 @@ +@echo off +echo build start... +SET CGO_ENABLED=0 +SET GOOS=darwin +SET GOARCH=amd64 +go build -ldflags "-s -w" +echo build success +pause \ No newline at end of file diff --git a/build-linux.bat b/build-linux.bat new file mode 100644 index 0000000..a729cf5 --- /dev/null +++ b/build-linux.bat @@ -0,0 +1,8 @@ +@echo off +echo build start... +SET CGO_ENABLED=0 +SET GOOS=linux +SET GOARCH=amd64 +go build -ldflags "-s -w" +echo build success +pause \ No newline at end of file diff --git a/build-windows.bat b/build-windows.bat new file mode 100644 index 0000000..54df2ba --- /dev/null +++ b/build-windows.bat @@ -0,0 +1,8 @@ +@echo off +echo build start... +SET CGO_ENABLED=0 +SET GOOS=windows +SET GOARCH=amd64 +go build -ldflags "-s -w" +echo build success +pause \ No newline at end of file diff --git a/fileboy.go b/fileboy.go new file mode 100644 index 0000000..6247503 --- /dev/null +++ b/fileboy.go @@ -0,0 +1,240 @@ +package main + +import ( + "bufio" + "fmt" + "gopkg.in/fsnotify/fsnotify.v1" + "gopkg.in/yaml.v2" + "io" + "io/ioutil" + "log" + "math/rand" + "os" + "os/exec" + "path" + "sync" + "time" +) + +const ( + Version = 1 +) + +var ( + projectFolder string = "." + + cfg *FileGirl + + watcher *fsnotify.Watcher + + cmd *exec.Cmd + + runLock sync.Mutex +) + +type wDirState struct { + Name string + Recursive bool +} + +func parseConfig() { + cfg = new(FileGirl) + fc, err := ioutil.ReadFile(projectFolder + "/filegirl.yaml") + if err != nil { + log.Panicln("read filegirl.yaml file err: ", err) + } + err = yaml.Unmarshal(fc, cfg) + if err != nil { + log.Panicln("parsed filegirl.yaml failed: ", err) + } + if cfg.Core.Version > Version { + log.Panicln("current fileboy support max version : ", Version) + } + log.Println(cfg) +} + +func eventDispatcher(event fsnotify.Event) { + ext := path.Ext(event.Name) + if len(cfg.Monitor.Types) > 0 && + cfg.Monitor.Types[0] != ".*" && + !inStringArray(ext, cfg.Monitor.Types) { + //log.Println(ext, cfg.Monitor.Types, inStringArray(ext, cfg.Monitor.Types)) + return + } + switch event.Op { + case fsnotify.Create: + case fsnotify.Write: + log.Println("event write : ", event.Name) + if cmd != nil { + err := cmd.Process.Kill() + if err != nil { + log.Println("err: ", err) + } + log.Println("stop old process ") + } + go run() + case fsnotify.Remove: + case fsnotify.Rename: + } +} + +func run() { + runLock.Lock() + defer runLock.Unlock() + for i := 0; i < len(cfg.Command.Exec); i++ { + carr := cmdParse2Array(cfg.Command.Exec[i]) + cmd = exec.Command(carr[0], carr[1:]...) + //cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_UNICODE_ENVIRONMENT} + cmd.Stdin = os.Stdin + //cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + stdout, err := cmd.StdoutPipe() + if err != nil { + log.Println("error=>", err.Error()) + return + } + cmd.Start() + reader := bufio.NewReader(stdout) + for { + line, err2 := reader.ReadString('\n') + if err2 != nil || io.EOF == err2 { + break + } + log.Print(line) + } + err = cmd.Wait() + if err != nil { + log.Println("cmd wait err ", err) + break + } + err = cmd.Process.Kill() + if err != nil { + log.Println("cmd cannot kill ", err) + } + } + + log.Println("end ") +} + +func addWatcher() { + dirs := make([]string, 0) + for i := 0; i < len(cfg.Monitor.IncludeDirs); i++ { + darr := dirParse2Array(cfg.Monitor.IncludeDirs[i]) + if len(darr) < 1 || len(darr) > 2 { + log.Fatalln("filegirl section monitor dirs is error. ", cfg.Monitor.IncludeDirs[i]) + } + if darr[0] == "." { + if len(darr) == 2 && darr[1] == "*" { + dirs = make([]string, 0) + dirs = append(dirs, ".") + listFile(projectFolder, func(d string) { + dirs = arrayUniqueAdd(dirs, d) + }) + } else { + dirs = arrayUniqueAdd(dirs, projectFolder) + } + break + } else { + if len(darr) == 2 && darr[1] == "*" { + listFile(projectFolder+"/"+darr[0], func(d string) { + dirs = arrayUniqueAdd(dirs, d) + }) + } else { + dirs = arrayUniqueAdd(dirs, projectFolder+"/"+darr[0]) + } + } + + } + for i := 0; i < len(cfg.Monitor.ExceptDirs); i++ { + p := projectFolder + "/" + cfg.Monitor.ExceptDirs[i] + dirs = arrayRemoveElement(dirs, p) + listFile(p, func(d string) { + dirs = arrayRemoveElement(dirs, d) + }) + } + for _, dir := range dirs { + log.Println("watcher add -> ", dir) + err := watcher.Add(dir) + if err != nil { + log.Fatalln(err) + } + } + log.Println("fileboy is ready.") +} + +func initWatcher() { + parseConfig() + var err error + watcher, err = fsnotify.NewWatcher() + if err != nil { + log.Fatalln(err) + } + defer watcher.Close() + + done := make(chan bool) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + eventDispatcher(event) + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Println("error:", err) + } + } + }() + addWatcher() + <-done +} + +func parseArgs() { + l := len(os.Args) + if l == 1 { + initWatcher() + return + } + if l == 2 { + c := os.Args[1] + switch c { + case "init": + err := ioutil.WriteFile(projectFolder+"/filegirl.yaml", []byte(exampleFileGirl), 0644) + if err != nil { + log.Println("error create filegirl.yaml config! ", err) + return + } + log.Println("create filegirl.yaml ok") + return + case "exec": + parseConfig() + run() + return + default: + fmt.Print(helpStr) + } + return + } +} + +func show() { + fmt.Print(logo) + rand.Seed(time.Now().UnixNano()) + fmt.Println(englishSay[rand.Intn(len(englishSay))], "\r\n") + fmt.Println("Version: ", Version, " Author: deng@yoytang.com") +} + +func main() { + log.SetPrefix("[FileBoy]: ") + log.SetFlags(2) + show() + var err error + projectFolder, err = os.Getwd() + if err != nil { + log.Fatalln(err) + } + parseArgs() +} diff --git a/filegirl.go b/filegirl.go new file mode 100644 index 0000000..a99e5f3 --- /dev/null +++ b/filegirl.go @@ -0,0 +1,17 @@ +package main + +type FileGirl struct { + Core struct { + Version int `yaml:"version"` + } + Monitor struct { + Types []string `yaml:"types"` + IncludeDirs []string `yaml:"includeDirs"` + ExceptDirs []string `yaml:"exceptDirs"` + } + Command struct { + //BeforeExec string `yaml:"beforeExec"` + Exec []string `yaml:"exec"` + //AfterExec string `yaml:"afterExec"` + } +} diff --git a/raw.go b/raw.go new file mode 100644 index 0000000..ea4dca4 --- /dev/null +++ b/raw.go @@ -0,0 +1,72 @@ +package main + +var exampleFileGirl string = `# 主配置 +core: + # 配置版本号 + version: 1 + +# 监控配置 +monitor: + # 要监听的目录 + # test1 监听当前目录下 test1 目录 + # test1/test2 监听当前目录下 test1/test2 目录 + # test1,* 监听当前目录下 test1 目录及其所有子目录(递归) + # .,* 监听当前目录及其所有子目录(递归) + includeDirs: + - .,* + + # 不监听的目录 + # .idea 忽略.idea目录及其所有子目录的监听 + exceptDirs: + - .idea + - .git + - .vscode + + # 监听文件的格式,此类文件更改会执行 commend 中的命令 + # .go 后缀为 .go 的文件更改,会执行 commend 中的命令 + # .* 所有的文件更改都会执行 commend 中的命令 + types: + - .go + +# 命令 +command: + # 监听的文件有更改会执行的命令 + # 可以有多条命令,会依次执行 + # 如有多条命令,每条命令都会等待上一条命令执行完毕后才会执行 + # 如遇交互式命令,允许外部获取输入 + exec: + - go version + - go env +` + +var helpStr = `fileboy [option] +Usage of fileboy: + 无参数 + 读取当前目录下的 filegirl.yaml 配置,开始监听并工作 + init + 初始化 fileboy, 在当前目录生成 filegirl.yaml 配置文件 + exec + 尝试运行定义的 commend 命令 +` + +var englishSay = []string{ + ` Have you, the darkness is no darkness.`, + ` Why do the good girls always love bad boys?`, + ` If love is not madness.`, + ` This world is so lonely without you.`, + ` You lie. Silence in front of me.`, + ` I need him like I need the air to breathe.`, + ` Happiness is when the desolated soul meets love.`, + ` What I can lose, but do not want to lose you.`, + ` The same words, both miss, is also missed.`, + ` Each bathed in the love of the people is a poet.`, +} + +var logo = ` + _______ _____ _ _______ ______ _____ _ _ +(_______|_____) | (_______|____ \ / ___ \| | | | + _____ _ | | _____ ____) ) | | | |___| | +| ___) | | | | | ___) | __ (| | | |\_____/ +| | _| |_| |_____| |_____| |__) ) |___| | ___ +|_| (_____)_______)_______)______/ \_____/ (___) +` diff --git a/util.go b/util.go new file mode 100644 index 0000000..e0e5dff --- /dev/null +++ b/util.go @@ -0,0 +1,66 @@ +package main + +import ( + "io/ioutil" + "strings" +) + +func inStringArray(value string, arr []string) bool { + for _, v := range arr { + if value == v { + return true + } + } + return false +} + +func cmdParse2Array(s string) []string { + a := strings.Split(s, " ") + r := make([]string, 0) + for i := 0; i < len(a); i++ { + if ss := strings.Trim(a[i], " "); ss != "" { + r = append(r, ss) + } + } + return r +} + +func dirParse2Array(s string) []string { + a := strings.Split(s, ",") + r := make([]string, 0) + for i := 0; i < len(a); i++ { + if ss := strings.Trim(a[i], " "); ss != "" { + r = append(r, ss) + } + } + return r +} + +func listFile(folder string, fun func(string)) { + files, _ := ioutil.ReadDir(folder) + for _, file := range files { + if file.IsDir() { + d := folder + "/" + file.Name() + fun(d) + listFile(d, fun) + } + } +} + +func arrayUniqueAdd(a []string, add string) []string { + if inStringArray(add, a) { + return a + } + return append(a, add) +} + +func arrayRemoveElement(a []string, r string) []string { + i := 0 + for k, v := range a { + if v == r { + i = k + break + } + } + return append(a[:i], a[i+1:]...) +}