initial commit
This commit is contained in:
commit
e269ff8aea
|
@ -0,0 +1 @@
|
|||
icon.png
|
|
@ -0,0 +1,8 @@
|
|||
module tildegit.org/jande/gohrl
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/mattn/go-gtk v0.0.0-20191030024613-af2e013261f5 // indirect
|
||||
github.com/mqu/go-notify v0.0.0-20130719194048-ef6f6f49d093 // indirect
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
github.com/mattn/go-gtk v0.0.0-20191030024613-af2e013261f5 h1:GMB3MVJnxysGrSvjWGsgK8L3XGI3F4etQQq37Py6W5A=
|
||||
github.com/mattn/go-gtk v0.0.0-20191030024613-af2e013261f5/go.mod h1:PwzwfeB5syFHXORC3MtPylVcjIoTDT/9cvkKpEndGVI=
|
||||
github.com/mqu/go-notify v0.0.0-20130719194048-ef6f6f49d093 h1:OvySnanP8CQIKS+MTq9AXBwEXzm0YaKeu331bWql3ug=
|
||||
github.com/mqu/go-notify v0.0.0-20130719194048-ef6f6f49d093/go.mod h1:AthsKyBZ9hqwU7DBWFiOxYObyF8nVyYVubXv/pQNC5E=
|
|
@ -0,0 +1,227 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/mqu/go-notify"
|
||||
)
|
||||
|
||||
func logg(v ...any) {
|
||||
spr := fmt.Sprintln(v...)
|
||||
fmt.Print("\x1B[32m", time.Now().Format("15:04:05.000"), " \x1B[93m", spr[:len(spr)-1], "\x1B[0m\n")
|
||||
}
|
||||
|
||||
func getChildPID(pid int) int {
|
||||
get, _ := filepath.Glob("/proc/" + strconv.Itoa(pid) + "/task/*/children")
|
||||
for _, x := range get {
|
||||
dat, err := os.ReadFile(x)
|
||||
if err != nil {
|
||||
logg(err)
|
||||
continue
|
||||
}
|
||||
if len(dat) <= 1 {
|
||||
continue
|
||||
}
|
||||
npid, err := strconv.Atoi(string(dat)[:len(dat)-1])
|
||||
if err != nil {
|
||||
logg(err)
|
||||
continue
|
||||
}
|
||||
cmdl, err := os.ReadFile("/proc/" + strconv.Itoa(npid) + "/cmdline")
|
||||
if err != nil {
|
||||
logg(err)
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(string(cmdl), "/tmp/go-build") {
|
||||
continue
|
||||
}
|
||||
return npid
|
||||
}
|
||||
return pid
|
||||
}
|
||||
|
||||
func main() {
|
||||
notify.Init("gohrl")
|
||||
|
||||
name := os.Args[1]
|
||||
args := os.Args[2:]
|
||||
|
||||
cont := true
|
||||
|
||||
sc := make(chan os.Signal, 1)
|
||||
exit := make(chan struct{}, 1)
|
||||
change := make(chan struct{}, 1)
|
||||
|
||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||
|
||||
var cmd *exec.Cmd
|
||||
|
||||
var changes []string
|
||||
go func() {
|
||||
hash()
|
||||
var ok bool
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
|
||||
changes, ok = hash()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
change <- struct{}{}
|
||||
}
|
||||
}()
|
||||
|
||||
var not *notify.NotifyNotification
|
||||
|
||||
for cont {
|
||||
logg("starting:", name, args)
|
||||
|
||||
cmd = exec.Command(name, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
pid := 0
|
||||
|
||||
wait := make(chan struct{})
|
||||
go func() {
|
||||
cmd.Start()
|
||||
pid = cmd.Process.Pid
|
||||
for i := 0; pid == cmd.Process.Pid && i < 1200; i++ {
|
||||
select {
|
||||
case <-exit:
|
||||
exit<-struct{}{}
|
||||
break
|
||||
case <-change:
|
||||
default:
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
pid = getChildPID(cmd.Process.Pid)
|
||||
}
|
||||
logg("PID:", pid)
|
||||
|
||||
wait <- struct{}{}
|
||||
cmd.Wait()
|
||||
|
||||
logg("program exited")
|
||||
exit <- struct{}{}
|
||||
}()
|
||||
logg("waiting for start")
|
||||
<-wait
|
||||
|
||||
if not != nil {
|
||||
not.Update("updated applied", "now running", "/data/drive/jan/hotreload/icon.png")
|
||||
not.Show()
|
||||
}
|
||||
|
||||
kill := func() {
|
||||
syscall.Kill(pid, syscall.SIGINT)
|
||||
|
||||
t := time.NewTimer(time.Second * 10)
|
||||
|
||||
select {
|
||||
case <-exit:
|
||||
return
|
||||
case <-t.C:
|
||||
logg("program didn't exit: killing")
|
||||
syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-sc:
|
||||
logg("inturrupting")
|
||||
cont = false
|
||||
kill()
|
||||
case <-change:
|
||||
logg("change to code detected")
|
||||
not = notify.NotificationNew("updating", strings.Join(changes, "\n"), "/data/drive/jan/hotreload/icon.png")
|
||||
not.SetTimeout(2500)
|
||||
not.Show()
|
||||
|
||||
logg("waiting for exit")
|
||||
kill()
|
||||
logg("restarting to apply update")
|
||||
|
||||
case <-exit:
|
||||
logg("exit receieved, restarting")
|
||||
if !cmd.ProcessState.Success() {
|
||||
logg("possible error, waiting for next update")
|
||||
<-change
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
logg("releasing")
|
||||
cmd.Process.Release()
|
||||
}
|
||||
}
|
||||
|
||||
var hashes = make(map[string][]byte)
|
||||
|
||||
var pt = false
|
||||
|
||||
func hash() ([]string, bool) {
|
||||
dif := false
|
||||
var changes = []string{}
|
||||
|
||||
err := filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
|
||||
if !strings.HasSuffix(d.Name(), ".go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
dat, err := os.Open(filepath.Join(path))
|
||||
defer dat.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pfx = make([]byte, len("package"))
|
||||
dat.Read(pfx)
|
||||
if string(pfx) != "package" {
|
||||
return nil
|
||||
}
|
||||
dat.Seek(0, 0)
|
||||
|
||||
h := fnv.New128()
|
||||
_, err = io.Copy(h, dat)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var b []byte
|
||||
b = h.Sum(b)
|
||||
|
||||
if bytes.Compare(hashes[dat.Name()], b) != 0 {
|
||||
if pt {
|
||||
logg(dat.Name(), "changed, recompiling and reloading")
|
||||
changes = append(changes, dat.Name())
|
||||
}
|
||||
dif = true
|
||||
}
|
||||
hashes[dat.Name()] = b
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
pt = true
|
||||
|
||||
return changes, dif
|
||||
}
|
Loading…
Reference in New Issue