<commit_before>package main

import (
	"errors"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"syscall"
	"path/filepath"
	"strconv"
	"time"
	"bytes"
	"io"
	"strings"

	"github.com/joho/godotenv"
)

type Backend struct {
	appPath      string
	port         int
	process      *os.Process
	startedAt    time.Time
	exited       bool
	activityChan chan interface{}
}

func (b *Backend) Close() {
	log.Println("Terminating", b.appPath, "pid", b.process.Pid)

	err := b.process.Signal(syscall.SIGTERM)
	if err != nil {
		log.Println("failed to kill process: ", err)
		return
	}
	_, err = b.process.Wait()
	if err != nil {
		log.Println("failed to wait for process: ", err)
		return
	}

	log.Println("Terminated", b.appPath)
}

func (b *Backend) IsRestartRequested() bool {
	if b.exited {
		return true
	}
	fi, err := os.Stat(b.appPath + "/tmp/restart.txt")
	if err != nil {
		return false
	}
	return fi.ModTime().After(b.startedAt)
}

type BootCrash struct {
	Log bytes.Buffer
	Env []string
	Cmd string
	Path string
}
func (b BootCrash) Error() string {
	return "app crashed during boot"
}

func SpawnBackend(appName string) (*Backend, error) {
	pathToApp, err := appDir(appName)
	if err != nil {
		return nil, err
	}
	port, err := getFreeTCPPort()
	if err != nil {
		return nil, err
	}
	log.Println("Spawning", pathToApp, "on port", port)

	env := os.Environ()

	pathbytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/.pow/.path")
	path := os.Getenv("PATH")
	if err == nil {
		path = string(pathbytes)
	} else {
		log.Println("while reading path file:", err)
	}
	// remove the old PATH
	for i, v := range env {
		if strings.Index(v, "PATH=") == 0 {
			env = append(env[:i], env[i+1:]...)
		}
	}
	env = append(env, "PATH="+path, "PORT="+strconv.Itoa(port))

	// add .env
	entries, err := godotenv.Read(pathToApp+"/.env")
	if err != nil {
		return nil, err
	}
	for k, v := range entries {
		env = append(env, k+"="+v)
	}

	procfile, err := ReadProcfile(pathToApp+"/Procfile")
	if err != nil {
		return nil, err
	}
	var CmdName string
	for _, v := range procfile.Entries {
		if v.Name == "web" {
			CmdName = v.Command
		}
	}

	if CmdName == "" {
		return nil, errors.New("No 'web' entry found in Procfile")
	}

	cmd := exec.Command("bash", "-c", "exec "+CmdName)

	var bootlog bytes.Buffer

	toStderrWithCapture := io.MultiWriter(os.Stderr, &bootlog)

	cmd.Stdout = toStderrWithCapture // never write to gowd's stdout
	cmd.Stderr = toStderrWithCapture
	cmd.Dir = pathToApp
	cmd.Env = env

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	b := &Backend{appPath: pathToApp, port: port, process: cmd.Process, startedAt: time.Now(), activityChan: make(chan interface{})}
	booting := true
	crashChan := make(chan error, 1)
	go func() {
		cmd.Wait()
		b.exited = true

		if booting {
			crashChan <- BootCrash{Log: bootlog, Env: env, Cmd: CmdName, Path: pathToApp}
		}
	}()

	log.Println("waiting for spawn result for", pathToApp)<commit_msg>don't ever wait(2) twice on the child<commit_after>package main

import (
	"errors"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/exec"
	"syscall"
	"path/filepath"
	"strconv"
	"time"
	"bytes"
	"io"
	"strings"

	"github.com/joho/godotenv"
)

type Backend struct {
	appPath      string
	port         int
	process      *os.Process
	startedAt    time.Time
	exited       bool
	exitChan 		 chan interface{}
	activityChan chan interface{}
}

func (b *Backend) Close() {
	log.Println("Terminating", b.appPath, "pid", b.process.Pid)

	err := b.process.Signal(syscall.SIGTERM)
	if err != nil {
		log.Println("failed to kill process: ", err)
		return
	}

	<-b.exitChan

	log.Println("Terminated", b.appPath)
}

func (b *Backend) IsRestartRequested() bool {
	if b.exited {
		return true
	}
	fi, err := os.Stat(b.appPath + "/tmp/restart.txt")
	if err != nil {
		return false
	}
	return fi.ModTime().After(b.startedAt)
}

type BootCrash struct {
	Log bytes.Buffer
	Env []string
	Cmd string
	Path string
}
func (b BootCrash) Error() string {
	return "app crashed during boot"
}

func SpawnBackend(appName string) (*Backend, error) {
	pathToApp, err := appDir(appName)
	if err != nil {
		return nil, err
	}
	port, err := getFreeTCPPort()
	if err != nil {
		return nil, err
	}
	log.Println("Spawning", pathToApp, "on port", port)

	env := os.Environ()

	pathbytes, err := ioutil.ReadFile(os.Getenv("HOME") + "/.pow/.path")
	path := os.Getenv("PATH")
	if err == nil {
		path = string(pathbytes)
	} else {
		log.Println("while reading path file:", err)
	}
	// remove the old PATH
	for i, v := range env {
		if strings.Index(v, "PATH=") == 0 {
			env = append(env[:i], env[i+1:]...)
		}
	}
	env = append(env, "PATH="+path, "PORT="+strconv.Itoa(port))

	// add .env
	entries, err := godotenv.Read(pathToApp+"/.env")
	if err != nil {
		return nil, err
	}
	for k, v := range entries {
		env = append(env, k+"="+v)
	}

	procfile, err := ReadProcfile(pathToApp+"/Procfile")
	if err != nil {
		return nil, err
	}
	var CmdName string
	for _, v := range procfile.Entries {
		if v.Name == "web" {
			CmdName = v.Command
		}
	}

	if CmdName == "" {
		return nil, errors.New("No 'web' entry found in Procfile")
	}

	cmd := exec.Command("bash", "-c", "exec "+CmdName)

	var bootlog bytes.Buffer

	toStderrWithCapture := io.MultiWriter(os.Stderr, &bootlog)

	cmd.Stdout = toStderrWithCapture // never write to gowd's stdout
	cmd.Stderr = toStderrWithCapture
	cmd.Dir = pathToApp
	cmd.Env = env

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	exitChan := make(chan interface{}, 1)
	b := &Backend{appPath: pathToApp, port: port, process: cmd.Process, startedAt: time.Now(), activityChan: make(chan interface{}), exitChan: exitChan}
	booting := true
	crashChan := make(chan error, 1)
	go func() {
		cmd.Wait()
		b.exited = true
		b.exitChan <- new(interface{})

		if booting {
			crashChan <- BootCrash{Log: bootlog, Env: env, Cmd: CmdName, Path: pathToApp}
		}
	}()

	log.Println("waiting for spawn result for", pathToApp)
{'fraction_non_alphanumeric': 0.1580634278002699, 'fraction_numerical': 0.001349527665317139, 'mean_word_length': 3.437874251497006, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '440457', 'n_tokens_mistral': 2422, 'n_tokens_neox': 2294, 'n_words': 597}