func Shell() { sighup := autorestart.NotifyOnSighup() args, err := flags.ParseArgs(&Opts, os.Args[1:]) if err != nil { err2, ok := err.(*flags.Error) if ok && err2.Type == flags.ErrHelp { return } alog.Printf("Error parsing command-line options: %s\n", err) return } if Opts.NoColor { alog.DisableColor() } ignorePart = stringset.New(filepath.SplitList(Opts.IgnorePart)...) ignoreSuffix = filepath.SplitList(Opts.IgnoreSuffix) ignoreSubstring = filepath.SplitList(Opts.IgnoreSubstring) deletePart = stringset.New(filepath.SplitList(Opts.DeletePart)...) deleteSuffix = filepath.SplitList(Opts.DeleteSuffix) deleteSubstring = filepath.SplitList(Opts.DeleteSubstring) if Opts.Child { if len(args) < 1 { alog.Fatalln("Not enough arguments, need 1") } RootPath = args[0] execChild() return } if len(args) < 2 { alog.Fatalln("Not enough arguments, need 2") } RootPath = args[0] remoteFullPath := args[1] remoteFullPathParts := strings.SplitN(remoteFullPath, ":", 2) remoteHost, remoteRoot := remoteFullPathParts[0], remoteFullPathParts[1] alog.Printf("@(dim:nsync started, syncing) @(cyan:%s) @(dim:to) @(cyan:%s)@(dim::)@(cyan:%s)\n", RootPath, remoteHost, remoteRoot) onChildExit := make(chan error) go connectChildForever(remoteHost, remoteRoot, onChildExit) go execParent() select { case <-sighup: killChildSshProcess() case <-onChildExit: // Restart everything if the child drops out (this is kludgy but effective) case <-time.After(20 * time.Minute): // Restart periodically, at least until I figure out what causes hangs } }
func main() { log.EnableColorTemplate() log.AddAnsiColorCode("error", 31) log.AddAnsiColorCode("commit", 32) log.AddAnsiColorCode("path", 36) var args []string = os.Args[1:] if len(args) == 0 { fmt.Println("You must specify a gut-command, e.g. `gut sync ...`") os.Exit(1) } var cmd = args[0] if IsGitCommand(cmd) { if IsDangerousGitCommand(cmd) { if len(args) < 2 || args[1] != "--danger" { status := log.New(os.Stderr, "", 0) status.Printf("@(dim:In order to prevent damage caused by accidentally using `)gut %s ...@(dim:`)\n", cmd) status.Printf("@(dim:in cases where `)git %s ...@(dim:` was intended, you must append `)--danger@(dim:`)\n", cmd) status.Printf("@(dim:immediately after the command, i.e. `)gut %s --danger ...@(dim:`.)\n", cmd) status.Printf("@(dim:Alternatively, you could invoke) gut @(dim:directly at) @(path:%s)@(dim:.)\n", GutExePath) status.Printf("@(dim:The commands that require this flag are:) %s\n", strings.Join(DangerousGitCommands, " ")) os.Exit(1) } // Split the "--danger" flag out before handing off the args list to the gut-command: if len(args) > 2 { args = append(args[:1], args[2:]...) } else { args = args[:1] } } homeDir, err := homedir.Dir() if err != nil { log.Bail(err) } var gutExe = path.Join(homeDir, GutExePath[2:]) syscall.Exec(gutExe, append([]string{gutExe}, args...), os.Environ()) fmt.Printf("Failed to exec %s", gutExe) os.Exit(1) } autorestart.CleanUpChildZombiesQuietly() go autorestart.RestartOnChange() status := log.New(os.Stderr, "", 0) args = args[1:] parser := flags.NewParser(&OptsCommon, flags.IgnoreUnknown) var argsRemaining, err = parser.ParseArgs(args) if err != nil { printUsageInfoAndExit() } if OptsCommon.NoColor { log.DisableColor() } if OptsCommon.Version { status.Printf("gut-sync %s\n", GutVersion) os.Exit(0) } bismuth.SetVerbose(OptsCommon.Verbose) go func() { sigintChan := make(chan os.Signal, 1) signal.Notify(sigintChan, os.Interrupt) <-sigintChan Shutdown("Received SIGINT.", 1) }() go func() { sighupChan := autorestart.NotifyOnSighup() <-sighupChan Shutdown("Received SIGHUP.", 0) }() if cmd == "build" { var local = NewSyncContext() err := local.Connect() if err != nil { status.Bail(err) } err = local.CheckLocalDeps() if err != nil { status.Bail(err) } didSomething, err := EnsureBuild(local, local) if err != nil { status.Bail(err) } if !didSomething { status.Printf("@(dim:gut) " + GitVersion + " @(dim:has already been built.)\n") } } else if cmd == "sync" { var remoteArgs, err = flags.ParseArgs(&OptsSync, argsRemaining) if err != nil { printUsageInfoAndExit() } ready := make(chan error) local := NewSyncContext() err = local.ParseSyncPath(OptsSync.Positional.LocalPath) if err != nil { status.Bail(err) } if len(remoteArgs) > 0 && os.Getenv("SSH_AUTH_SOCK") == "" { log.Printf("@(error:SSH_AUTH_SOCK is not set in environment. Start up an ssh agent first before running gut-sync.)\n") Shutdown("", 1) } go func() { err = local.Connect() if err != nil { status.Bail(err) } err = local.CheckLocalDeps() if err != nil { status.Bail(err) } local.KillAllViaPidfiles() local.SaveDaemonPid("gut", os.Getpid()) ready <- nil }() remotes := []*SyncContext{} for _, remotePath := range remoteArgs { remote := NewSyncContext() remotes = append(remotes, remote) err = remote.ParseSyncPath(remotePath) if err != nil { status.Bail(err) } go func(_remote *SyncContext) { err = _remote.Connect() if err != nil { status.Printf("@(error:Failed to connect to %s: %s)\n", remote.Hostname(), err) Shutdown("", 1) } _remote.KillAllViaPidfiles() err = _remote.CheckRemoteDeps() if err != nil { status.Bail(err) } ready <- nil }(remote) } for i := 0; i < len(remotes)+1; i++ { <-ready } err = Sync(local, remotes) if err != nil { status.Bail(err) } } }
func main() { sighup := autorestart.NotifyOnSighup() _, err := flags.ParseArgs(&Opts, os.Args) if err != nil { err2, ok := err.(*flags.Error) if ok && err2.Type == flags.ErrHelp { return } alog.Printf("Error parsing command-line options: %s\n", err) return } if goPath == "" { alog.Printf("GOPATH is not set in the environment. Please set GOPATH first, then retry.\n") alog.Printf("For help setting GOPATH, see https://golang.org/doc/code.html\n") return } if Opts.NoColor { alog.DisableColor() } else { alog.AddAnsiColorCode("time", alog.ColorBlue) } alog.Printf("@(dim:autoinstall started.)\n") if Opts.MaxWorkers == 0 { Opts.MaxWorkers = runtime.GOMAXPROCS(0) } pluralProcess := "" if Opts.MaxWorkers != 1 { pluralProcess = "es" } alog.Printf("@(dim:Building all packages in) @(dim,cyan:%s)@(dim: using up to )@(dim,cyan:%d)@(dim: process%s.)\n", goPath, Opts.MaxWorkers, pluralProcess) if !Opts.Verbose { alog.Printf("@(dim:Use) --verbose @(dim:to show all messages during startup.)\n") } listener := watcher.NewListener() listener.Path = srcRoot // "_workspace" is a kludge to avoid recursing into Godeps workspaces // "node_modules" is a kludge to avoid walking into typically-huge node_modules trees listener.IgnorePart = stringset.New(".git", ".hg", "node_modules", "_workspace", "etld") listener.NotifyOnStartup = true listener.DebounceDuration = 200 * time.Millisecond listener.Start() // Delete any straggler tmp files, carefully files, err := ioutil.ReadDir(tmpdir) if err == nil { for _, file := range files { if filepath.Ext(file.Name()) == ".tmp" { os.Remove(filepath.Join(tmpdir, file.Name())) } } } else if os.IsNotExist(err) { err = os.MkdirAll(tmpdir, 0700) if err != nil { alog.Printf("@(error:Error creating temp directory at %s: %v)\n", tmpdir, err) return } } else { alog.Printf("@(error:Error checking contents of temp directory at %s: %v)\n", tmpdir, err) return } go processPathTriggers(listener.NotifyChan) go dispatcher() <-sighup startupLogger.Close() }