Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

services/serve: stabilize serve #102

Merged
merged 1 commit into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/karrick/godirwalk v1.15.6 // indirect
github.com/pkg/errors v0.8.1
github.com/radovskyb/watcher v1.0.7
github.com/rogpeppe/go-internal v1.6.0 // indirect
github.com/rogpeppe/go-internal v1.6.1 // indirect
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
github.com/sirupsen/logrus v1.6.0 // indirect
github.com/spf13/cobra v1.0.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.0 h1:IZRgg4sfrDH7nsAD1Y/Nwj+GzIfEwpJSLjCaNC3SbsI=
github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 h1:ZuhckGJ10ulaKkdvJtiAqsLTiPrLaXSdnVgXJKJkTxE=
Expand Down
2 changes: 2 additions & 0 deletions starport/interface/cli/starport/cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package starportcmd

import (
"context"
"fmt"
"os"
"os/signal"

Expand Down Expand Up @@ -42,6 +43,7 @@ func serveHandler(cmd *cobra.Command, args []string) error {

err := starportserve.Serve(ctx, app, verbose)
if err == context.Canceled {
fmt.Println("aborted")
return nil
}
return err
Expand Down
21 changes: 15 additions & 6 deletions starport/pkg/fswatcher/fswatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ import (
)

type watcher struct {
wt *wt.Watcher
workdir string
onChange func()
interval time.Duration
ctx context.Context
done *sync.WaitGroup
wt *wt.Watcher
workdir string
ignoreHidden bool
onChange func()
interval time.Duration
ctx context.Context
done *sync.WaitGroup
}

// Option used to configure watcher.
Expand All @@ -44,6 +45,13 @@ func PollingInterval(d time.Duration) Option {
}
}

// IgnoreHidden ignores hidden(dot) files.
func IgnoreHidden() Option {
return func(w *watcher) {
w.ignoreHidden = true
}
}

// Watch starts watching changes on the paths. options are used to configure the
// behaviour of watch operation.
func Watch(ctx context.Context, paths []string, options ...Option) error {
Expand All @@ -59,6 +67,7 @@ func Watch(ctx context.Context, paths []string, options ...Option) error {
for _, o := range options {
o(w)
}
w.wt.IgnoreHiddenFiles(w.ignoreHidden)
w.addPaths(paths...)
w.done.Add(1)
go w.listen()
Expand Down
104 changes: 78 additions & 26 deletions starport/services/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
"time"

"github.com/pkg/errors"
"github.com/tendermint/starport/starport/pkg/cmdrunner"
"github.com/tendermint/starport/starport/pkg/cmdrunner/step"
"github.com/tendermint/starport/starport/pkg/fswatcher"
"github.com/tendermint/starport/starport/pkg/xexec"
"golang.org/x/sync/errgroup"
)

var (
appBackendWatchPaths = []string{
"app",
"cmd",
"x",
}
)

type App struct {
Expand All @@ -24,36 +34,59 @@ type App struct {
}

type starportServe struct {
ctx context.Context
app App
verbose bool
}

// Serve serves user apps.
func Serve(ctx context.Context, app App, verbose bool) error {
s := &starportServe{
ctx: ctx,
app: app,
verbose: verbose,
}

serveCtx, cancel := context.WithCancel(ctx)
s.serve(serveCtx) // TODO handle error

go s.watchAppFrontend()
go s.runDevServer()
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
return s.watchAppFrontend(ctx)
})
g.Go(func() error {
return s.runDevServer(ctx)
})

changeHook := func() {
cancel()
serveCtx, cancel = context.WithCancel(ctx)
s.serve(serveCtx) // TODO handle error
}
return fswatcher.Watch(
ctx,
[]string{"app", "cmd", "x"},
fswatcher.Workdir(app.Path),
fswatcher.OnChange(changeHook),
var (
serveCtx context.Context
serveCancel context.CancelFunc
serveErr = make(chan error, 1)
)
serve := func() {
if serveCancel != nil {
serveCancel()
}
serveCtx, serveCancel = context.WithCancel(ctx)
if err := s.serve(serveCtx); err != nil && err != context.Canceled {
serveErr <- err
}
}
go serve()

g.Go(func() error {
select {
case err := <-serveErr:
return err
case <-ctx.Done():
return ctx.Err()
}
})
g.Go(func() error {
return fswatcher.Watch(
ctx,
appBackendWatchPaths,
fswatcher.Workdir(app.Path),
fswatcher.OnChange(serve),
fswatcher.IgnoreHidden(),
)
})
return g.Wait()
}

func (s *starportServe) serve(ctx context.Context) error {
Expand All @@ -74,12 +107,17 @@ func (s *starportServe) serve(ctx context.Context) error {
if err := cmdrunner.
New(opts...).
Run(ctx, s.buildSteps()...); err != nil {
log.Fatal(err)
return err
}

go cmdrunner.
if err := cmdrunner.
New(append(opts, cmdrunner.RunParallel())...).
Run(ctx, s.serverSteps()...) // TODO handle err
Run(ctx, s.serverSteps()...); err != nil {
if _, ok := errors.Cause(err).(*exec.ExitError); ok {
return nil
}
return err
}
return nil
}

Expand Down Expand Up @@ -177,16 +215,16 @@ func (s *starportServe) serverSteps() (steps step.Steps) {
return
}

func (s *starportServe) watchAppFrontend() {
cmdrunner.
func (s *starportServe) watchAppFrontend(ctx context.Context) error {
return cmdrunner.
New().
Run(s.ctx, step.New(
Run(ctx, step.New(
step.Exec("npm", "run", "dev"),
step.Workdir(filepath.Join(s.app.Path, "frontend")),
))
}

func (s *starportServe) runDevServer() error {
func (s *starportServe) runDevServer(ctx context.Context) error {
if s.verbose {
fmt.Printf("🔧 Running dev interface at http://localhost:12345\n\n")
} else {
Expand All @@ -198,5 +236,19 @@ func (s *starportServe) runDevServer() error {
AppFrontendAddr: "http://localhost:8080",
DevFrontendAssetsPath: "../../ui/dist",
} // TODO get vals from const
return http.ListenAndServe(":12345", newDevHandler(s.app, conf))
sv := &http.Server{
Addr: ":12345",
Handler: newDevHandler(s.app, conf),
}
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
sv.Shutdown(shutdownCtx)
}()
err := sv.ListenAndServe()
if err == http.ErrServerClosed {
return nil
}
return err
}