Exemple #1
0
// FileAppender takes the giving data of type FileWriter and appends the value out into a endpoint which is the combination of the name and the toPath value provided
func FileAppender(fx func(string) string) flux.Reactor {
	if fx == nil {
		fx = defaultMux
	}
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if file, ok := data.(*FileWrite); ok {
			// endpoint := filepath.Join(toPath, file.Path)

			endpoint := fx(file.Path)
			endpointDir := filepath.Dir(endpoint)

			//make the directory part incase it does not exists
			os.MkdirAll(endpointDir, 0700)

			osfile, err := os.Open(endpoint)

			if err != nil {
				root.ReplyError(err)
				return
			}

			defer osfile.Close()

			// io.Copy(osfile, file.Data)

			osfile.Write(file.Data)
			root.Reply(&FileWrite{Path: endpoint})
		}
	}))
}
Exemple #2
0
// BinaryLauncher returns a new Task generator that builds a binary runner from the given properties, which causing a relaunch of a binary file everytime it recieves a signal,  it sends out a signal onces its done running all commands
func BinaryLauncher(bin string, args []string) flux.Reactor {
	var channel chan bool

	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if channel == nil {
			channel = RunBin(bin, args, func() {
				root.Reply(true)
			}, func() {
				go root.Close()
			})
		}

		select {
		case <-root.CloseNotify():
			close(channel)
			return
		case <-time.After(0):
			//force check of boolean values to ensure we can use correct signal
			if cmd, ok := data.(bool); ok {
				channel <- cmd
				return
			}

			//TODO: should we fallback to sending true if we receive a signal normally? or remove this
			// channel <- true
		}

	}))
}
Exemple #3
0
// GoRunner calls `go run` with the command it receives from its data pipes
func GoRunner() flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if cmd, ok := data.(string); ok {
			root.Reply(GoRun(cmd))
		}
	}))
}
Exemple #4
0
// ModFileWrite provides a task that allows building a fileWrite modder,where you mod out the values for a particular FileWrite struct
func ModFileWrite(fx func(*FileWrite)) flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if fw, ok := data.(*FileWrite); ok {
			fx(fw)
			root.Reply(fw)
		}
	}))
}
Exemple #5
0
// GoInstallerWith calls `go install` everysingle time to the provided path once a signal is received
func GoInstallerWith(path string) flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, _ interface{}) {
		if err := GoDeps(path); err != nil {
			root.ReplyError(err)
			return
		}
		root.Reply(true)
	}))
}
Exemple #6
0
// GoBuilder calls `go run` with the command it receives from its data pipes, using the GoBuild function
func GoBuilder() flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if cmd, ok := data.(BuildConfig); ok {
			if err := Gobuild(cmd.Path, cmd.Name, cmd.Args); err != nil {
				root.ReplyError(err)
			}
		}
	}))
}
Exemple #7
0
// GoArgsBuilderWith calls `go run` everysingle time to the provided path once a signal is received using the GobuildArgs function
func GoArgsBuilderWith(cmd []string) flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, _ interface{}) {
		if err := GobuildArgs(cmd); err != nil {
			root.ReplyError(err)
			return
		}
		root.Reply(true)
	}))
}
Exemple #8
0
// ByteRenderer provides a baseline worker for building rendering tasks eg markdown. It expects to receive a *RenderFile and then it returns another *RenderFile containing the outputed rendered data with the path from the previous RenderFile,this allows chaining with other ByteRenderers
func ByteRenderer(fx RenderMux) flux.Reactor {
	if fx == nil {
		panic("RenderMux cant be nil for ByteRender")
	}
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if databytes, ok := data.(*RenderFile); ok {
			root.Reply(&RenderFile{Path: databytes.Path, Data: fx(databytes.Data)})
		}
	}))
}
Exemple #9
0
// GoBuilderWith calls `go run` everysingle time to the provided path once a signal is received using the GoBuild function
func GoBuilderWith(cmd BuildConfig) flux.Reactor {
	validateBuildConfig(cmd)
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, _ interface{}) {
		if err := Gobuild(cmd.Path, cmd.Name, cmd.Args); err != nil {
			root.ReplyError(err)
			return
		}
		root.Reply(true)
	}))
}
Exemple #10
0
// GoInstaller calls `go install` from the path it receives from its data pipes
func GoInstaller() flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if path, ok := data.(string); ok {
			if err := GoDeps(path); err != nil {
				root.ReplyError(err)
				return
			}
			root.Reply(true)
		}
	}))
}
Exemple #11
0
// GoArgsBuilder calls `go run` with the command it receives from its data pipes usingthe GobuildArgs function
func GoArgsBuilder() flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if cmd, ok := data.([]string); ok {
			if err := GobuildArgs(cmd); err != nil {
				root.ReplyError(err)
				return
			}
			root.Reply(true)
		}
	}))
}
Exemple #12
0
// FileAllRemover takes a *RemoveFile as the data and removes the path using the os.RemoveAll
func FileAllRemover() flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if file, ok := data.(*RemoveFile); ok {
			err := os.RemoveAll(file.Path)

			if err != nil {
				root.ReplyError(err)
				return
			}
		}
	}))
}
Exemple #13
0
func addCommander(pm *PluginManager) {
	pm.Add("commandWatch", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format

		  tag: dirWatch
		  config:
		    path: "./static/less"
		  args:
		    - lessc ./static/less/main.less ./static/css/main.css
		    - lessc ./static/less/svg.less ./static/css/svg.css

		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		dir := options.Config["path"]

		//get the command we should run on change
		commands := options.Args

		if dir == "" {
			fmt.Printf("---> dirWatch.error: no path set in config map for plug")
			return
		}

		//get the absolute path
		absDir := filepath.Join(pwd, dir)

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> commandWatch:File as changed: %+s\n", ev.String())
			}
		}), true)
		// create the command runner set to run the args
		watcher.Bind(builders.CommandLauncher(commands), true)

		flux.GoDefer("CommandWatch:kill", func() {
			<-c
			watcher.Close()
		})
	})
}
Exemple #14
0
// CommandLauncher returns a new Task generator that builds a command executor that executes a series of command every time it receives a signal, it sends out a signal onces its done running all commands
func CommandLauncher(cmd []string) flux.Reactor {
	var channel chan bool
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, _ interface{}) {
		if channel == nil {
			channel = RunCMD(cmd, func() {
				root.Reply(true)
			})
		}

		select {
		case <-root.CloseNotify():
			close(channel)
			return
		case <-time.After(0):
			channel <- true
		}

	}))
}
Exemple #15
0
// GoFileLauncher returns a new Task generator that builds a binary runner from the given properties, which causing a relaunch of a binary file everytime it recieves a signal,  it sends out a signal onces its done running all commands
func GoFileLauncher(goFile string, args []string) flux.Reactor {
	var channel chan bool

	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if channel == nil {
			channel = RunGo(goFile, args, func() {
				root.Reply(true)
			}, func() {
				go root.Close()
			})
		}

		select {
		case <-root.CloseNotify():
			close(channel)
			return
		case <-time.After(0):
			channel <- true
		}

	}))
}
Exemple #16
0
// JSBuildLauncher returns a Task generator that builds a new jsbuild task giving the specific configuration and on every reception of signals rebuilds and sends off a FileWrite for each file i.e the js and js.map file
func JSBuildLauncher(config JSBuildConfig) flux.Reactor {
	if config.Package == "" {
		panic("JSBuildConfig.Package can not be empty")
	}

	if config.FileName == "" {
		config.FileName = "jsapp.build"
	}

	// var session *JSSession
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		// if session == nil {
		session := NewJSSession(config.Tags, config.Verbose, false)
		// }

		// session.Session.
		//do we have an optional PackageDir that is not empty ? if so we use session.BuildDir
		//else session.BuildPkg
		var js, jsmap *bytes.Buffer
		var err error

		if config.PackageDir != "" {
			js, jsmap, err = session.BuildDir(config.PackageDir, config.Package, config.FileName)
		} else {
			js, jsmap, err = session.BuildPkg(config.Package, config.FileName)
		}

		if err != nil {
			root.ReplyError(err)
			return
		}

		jsfile := fmt.Sprintf("%s.js", config.FileName)
		jsmapfile := fmt.Sprintf("%s.js.map", config.FileName)

		root.Reply(&fs.FileWrite{Data: js.Bytes(), Path: filepath.Join(config.Folder, jsfile)})
		root.Reply(&fs.FileWrite{Data: jsmap.Bytes(), Path: filepath.Join(config.Folder, jsmapfile)})
	}))
}
Exemple #17
0
// BinaryBuildLauncher combines the builder and binary runner to provide a simple and order-based process,
// the BinaryLauncher is only created to handling a binary lunching making it abit of a roundabout to time its response to wait until another process finishes, but BinaryBuildLuncher cleans out the necessity and provides a reactor that embedds the necessary call routines while still response the: Build->Run or StopRunning->Build->Run process in development
func BinaryBuildLauncher(cmd BinaryBuildConfig) flux.Reactor {
	validateBinaryBuildConfig(cmd)

	// first generate the output file name from the config
	var basename = cmd.Name

	if runtime.GOOS == "windows" {
		basename = fmt.Sprintf("%s.exe", basename)
	}

	binfile := filepath.Join(cmd.Path, basename)

	//create the root stack which connects all the sequence of build and run together
	buildStack := flux.ReactorStack()

	//package builder
	builder := GoBuilderWith(BuildConfig{Path: cmd.Path, Name: cmd.Name, Args: cmd.BuildArgs})

	//package runner
	runner := BinaryLauncher(binfile, cmd.RunArgs)

	//when buildStack receives a signal, we will send a bool(false) signal to runner to kill the current process
	buildStack.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		//tell runner to kill process
		// log.Printf("sending to runner")
		runner.Send(false)
		//forward the signal down the chain
		root.Reply(data)
	}), true)

	//connect the build stack first then the runn stack to force order
	buildStack.Bind(builder, true)
	buildStack.Bind(runner, true)

	return buildStack
}
Exemple #18
0
// FileReader returns a new flux.Reactor that takes a path and reads out returning the file path
func FileReader() flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
		if pr, ok := data.(*FileRead); ok {
			root.Reply(pr)
			return
		}

		if path, ok := data.(string); ok {
			if _, err := os.Stat(path); err == nil {
				file, err := os.Open(path)

				if err != nil {
					root.ReplyError(err)
					return
				}

				defer file.Close()

				var buf bytes.Buffer

				//copy over data
				_, err = io.Copy(&buf, file)

				//if we have an error and its not EOF then reply with error
				if err != nil && err != io.EOF {
					root.ReplyError(err)
					return
				}

				root.Reply(&FileRead{Data: buf.Bytes(), Path: path})
			} else {
				root.ReplyError(err)
			}
		}
	}))
}
Exemple #19
0
func addJsClient(pm *PluginManager) {
	//these are internally used for js building
	pm.Add("jsClients", func(config *BuildConfig, options Plugins, c chan bool) {
		for _, pkg := range options.Args {
			var pg Plugins
			pg.Config = make(PluginConfig)
			pg.Tag = "jsClient"
			pg.Config["package"] = pkg
			pg.Args = nil
			pm.Activate(pg, config, c)
		}
	})

	pm.Add("jsClient", func(config *BuildConfig, options Plugins, c chan bool) {
		pkg := options.Config["package"]

		_, jsName := filepath.Split(pkg)

		pkgs := append([]string{}, pkg)
		packages, err := assets.GetAllPackageLists(pkgs)

		if err != nil {
			panic(err)
		}

		dir, err := assets.GetPackageDir(pkg)

		if err != nil {
			panic(err)
		}

		jsbuild := builders.JSLauncher(builders.JSBuildConfig{
			Package:  pkg,
			Folder:   dir,
			FileName: jsName,
			Tags:     options.Args,
			Verbose:  config.Client.UseVerbose,
		})

		jsbuild.React(func(root flux.Reactor, err error, _ interface{}) {
			if err != nil {
				fmt.Printf("--> Js.client.Build complete: Dir: %s \n -----> Error: %s \n", dir, err)
			}
		}, true)

		watcher := fs.WatchSet(fs.WatchSetConfig{
			Path: packages,
			Validator: func(base string, info os.FileInfo) bool {
				if strings.Contains(base, ".git") {
					return false
				}

				if info != nil && info.IsDir() {
					return true
				}

				if filepath.Ext(base) != ".go" {
					return false
				}

				return true
			},
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> Client:File as changed: %+s\n", ev.String())
			}
		}), true)

		watcher.Bind(jsbuild, true)

		jsbuild.Send(true)

		flux.GoDefer("jsClient:kill", func() {
			<-c
			watcher.Close()
			jsbuild.Close()
		})
	})
}
Exemple #20
0
func addGoStaticBundle(pm *PluginManager) {
	pm.Add("goStatic", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format: you can control all aspects of the assets.BindFS using the following

		      tag: gostatic
					# add commands to run on file changes
					args:
						- touch ./templates/smirf.go
		      config:
		        in: ./markdown
		        out: ./templates
						package: smirf
						file: smirf
						gzipped: true
						nodecompression: true
						production: true // generally you want to leave this to the cli to set

		  		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		inDir := options.Config["in"]
		outDir := options.Config["out"]
		packageName := options.Config["package"]
		fileName := options.Config["file"]
		ignore := options.Config["ignore"]
		absDir := filepath.Join(pwd, inDir)
		absFile := filepath.Join(pwd, outDir, fileName+".go")

		if inDir == "" || outDir == "" || packageName == "" || fileName == "" {
			fmt.Println("---> goStatic.error: the following keys(in,out,package,file) must not be empty")
			return
		}

		//set up the boolean values
		var prod bool
		var gzip bool
		var nodcom bool
		var err error

		if gz, err := strconv.ParseBool(options.Config["gzipped"]); err == nil {
			gzip = gz
		} else {
			if config.Mode > 0 {
				gzip = true
			}
		}

		if br, err := strconv.ParseBool(options.Config["nodecompression"]); err == nil {
			nodcom = br
		}

		if pr, err := strconv.ParseBool(options.Config["production"]); err == nil {
			prod = pr
		} else {
			if config.Mode <= 0 {
				prod = false
			} else {
				prod = true
			}
		}

		var ignoreReg *regexp.Regexp

		if ignore != "" {
			ignoreReg = regexp.MustCompile(ignore)
		}

		gostatic, err := builders.BundleAssets(&assets.BindFSConfig{
			InDir:           inDir,
			OutDir:          outDir,
			Package:         packageName,
			File:            fileName,
			Gzipped:         gzip,
			NoDecompression: nodcom,
			Production:      prod,
		})

		if err != nil {
			fmt.Printf("---> goStatic.error: %s", err)
			return
		}

		gostatic.React(func(root flux.Reactor, err error, data interface{}) {
			fmt.Printf("--> goStatic.Reacted: State %t Error: (%+s)\n", data, err)
		}, true)

		//bundle up the assets for the main time
		gostatic.Send(true)

		var command []string

		if prod {
			if runtime.GOOS != "windows" {
				command = append(command, fmt.Sprintf("touch %s", absFile))
			} else {
				command = append(command, fmt.Sprintf("copy /b %s+,,", absFile))
				// command = append(command, fmt.Sprintf("powershell  (ls %s).LastWriteTime = Get-Date", absFile))
			}
		}

		//add the args from the options
		command = append(command, options.Args...)
		// log.Printf("command %s", command)

		//adds a CommandLauncher to touch the output file to force a file change notification
		touchCommand := builders.CommandLauncher(command)
		gostatic.Bind(touchCommand, true)

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
			Validator: func(path string, info os.FileInfo) bool {
				if ignoreReg != nil && ignoreReg.MatchString(path) {
					return false
				}
				return true
			},
		})

		// create the command runner set to run the args
		watcher.Bind(gostatic, true)

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> goStatic:File as changed: %+s\n", ev.String())
			}
		}), true)

		flux.GoDefer("goStatic:kill", func() {
			<-c
			gostatic.Close()
		})
	})
}
Exemple #21
0
func addGoFriday(pm *PluginManager) {
	pm.Add("goFriday", func(config *BuildConfig, options Plugins, c chan bool) {
		/*Expects to receive a plugin config follow this format

		      tag: gofriday
		      config:
		        markdown: ./markdown
		        templates: ./templates

		  		  where the config.path is the path to be watched

		*/

		//get the current directory
		pwd, _ := os.Getwd()

		//get the dir we should watch
		markdownDir := options.Config["markdown"]
		templateDir := options.Config["templates"]

		//optional args
		ext := options.Config["ext"]
		//must be a bool
		sanitizeString := options.Config["sanitize"]

		var sanitize bool

		if svz, err := strconv.ParseBool(sanitizeString); err == nil {
			sanitize = svz
		}

		if markdownDir == "" || templateDir == "" {
			fmt.Println("---> gofriday.error: expected to find keys (markdown and templates) in config map")
			return
		}

		//get the absolute path
		absDir := filepath.Join(pwd, markdownDir)
		tbsDir := filepath.Join(pwd, templateDir)

		gofriday, err := builders.GoFridayStream(builders.MarkStreamConfig{
			InputDir: absDir,
			SaveDir:  tbsDir,
			Ext:      ext,
			Sanitize: sanitize,
		})

		if err != nil {
			fmt.Printf("---> gofriday.error: %s", err)
			return
		}

		//create the file watcher
		watcher := fs.Watch(fs.WatchConfig{
			Path: absDir,
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> goFriday:File as changed: %+s\n", ev.String())
			}
		}), true)
		// create the command runner set to run the args
		watcher.Bind(gofriday, true)

		flux.GoDefer("goFiday:kill", func() {
			<-c
			watcher.Close()
		})
	})
}
Exemple #22
0
func addJSWatchBuild(pm *PluginManager) {
	//these are internally used for js building
	pm.Add("jsWatchBuild", func(config *BuildConfig, options Plugins, c chan bool) {
		pwd, _ := os.Getwd()
		_, binName := filepath.Split(config.Package)
		binDir := filepath.Join(pwd, config.Bin)
		binfile := filepath.Join(binDir, binName)

		pkgs := append([]string{}, config.ClientPackage)

		packages, err := assets.GetAllPackageLists(pkgs)

		if err != nil {
			panic(err)
		}

		// packages = append(packages, pwd)
		fmt.Printf("--> Retrieved js package directories %s \n", config.Package)

		var clientdir string

		outputdir := filepath.Join(pwd, config.Client.StaticDir)

		if config.Client.Dir != "" {
			clientdir = filepath.Join(pwd, config.Client.Dir)
		}

		jsbuild := builders.JSLauncher(builders.JSBuildConfig{
			Package:    config.ClientPackage,
			Folder:     outputdir,
			FileName:   config.Client.Name,
			Tags:       config.Client.BuildTags,
			Verbose:    config.Client.UseVerbose,
			PackageDir: clientdir,
		})

		jsbuild.React(func(root flux.Reactor, err error, _ interface{}) {
			if err != nil {
				fmt.Printf("--> Js.client.Build complete: Dir: %s \n -----> Error: %s \n", clientdir, err)
			}
		}, true)

		fmt.Printf("--> Initializing File Watcher using js package dependecies at %d\n", len(packages))

		watcher := fs.WatchSet(fs.WatchSetConfig{
			Path: packages,
			Validator: func(base string, info os.FileInfo) bool {
				if strings.Contains(base, ".git") {
					return false
				}

				if strings.Contains(base, binDir) || base == binDir {
					return false
				}

				if strings.Contains(base, binfile) || base == binfile {
					return false
				}

				if info != nil && info.IsDir() {
					return true
				}

				if filepath.Ext(base) != ".go" {
					return false
				}

				// log.Printf("allowed: %s", base)
				return true
			},
		})

		watcher.React(flux.SimpleMuxer(func(root flux.Reactor, data interface{}) {
			if ev, ok := data.(fsnotify.Event); ok {
				fmt.Printf("--> Client:File as changed: %+s\n", ev.String())
			}
		}), true)

		watcher.Bind(jsbuild, true)

		jsbuild.Send(true)

		flux.GoDefer("jsWatchBuild:kill", func() {
			<-c
			//close our builders
			watcher.Close()
			jsbuild.Close()
		})
	})
}
Exemple #23
0
// GoRunnerWith calls `go run` everysingle time to the provided path once a signal is received
func GoRunnerWith(cmd string) flux.Reactor {
	return flux.Reactive(flux.SimpleMuxer(func(root flux.Reactor, _ interface{}) {
		root.Reply(GoRun(cmd))
	}))
}